Arquitectura de sincronización con wearables en tiempo real
Javier Mora Acabas tu tirada larga, paras el reloj y, cuando abres MyAlbatross, el entreno ya está ahí. Abrimos el capó para contarte, sin tecnicismos, qué ocurre en esos segundos y cómo evitamos el gran enemigo de cualquier plataforma deportiva: la actividad duplicada.
Acabas tu tirada larga. Paras el reloj, estiras un poco, y cuando abres MyAlbatross el entreno ya está ahí: tus kilómetros, tu carga, y tu entrenador viéndolo en su panel. Parece magia, pero detrás hay una pequeña cadena de montaje que tiene que funcionar bien miles de veces al día.
En este post abrimos el capó. Vamos a contar, sin necesidad de ser ingeniero, qué ocurre exactamente en esos segundos: quién avisa a quién, dónde espera tu entreno su turno, qué pasa si algo falla y, sobre todo, cómo evitamos el gran enemigo de cualquier plataforma deportiva: la actividad duplicada.
El viaje completo
El recorrido tiene seis paradas, y ninguna depende de que la anterior vaya rápida: cada una hace su parte y le pasa el testigo a la siguiente.

Si algo falla en el procesado, el entreno vuelve a la cola para otro intento; solo tras tres fallos se aparta para revisión.
Webhooks
Nosotros no le preguntamos a Garmin cada cinco minutos “¿hay algo nuevo?”. Eso sería como ir al buzón cada rato por si ha llegado una carta. En su lugar, Garmin, Strava, Polar y Suunto nos llaman a nuestra API cuando hay novedades. A ese aviso que nos llega lo llamamos webhook: una dirección web nuestra a la que ellos envían un aviso automático.
Cada marca llama de una forma distinta:
- Garmin no nos manda el entreno, nos manda una nota: “este atleta tiene un archivo nuevo, lo puedes recoger aquí”. Luego somos nosotros quienes vamos a buscar el archivo FIT (el formato en bruto que graba el reloj, con cada segundo de pulso, ritmo y GPS).
- Strava nos avisa de que se ha creado o borrado una actividad, y después le pedimos los detalles.
- Polar y Suunto firman cada aviso con una clave secreta compartida (lo que se llama firma HMAC). Así sabemos que el mensaje viene de ellos y no de un impostor.
La regla de oro al recibir el aviso es contestar rápido. Si tardamos en responder, la marca piensa que estamos caídos y vuelve a llamar, o directamente deja de avisarnos. Así que no procesamos nada en ese momento: decimos “recibido” (un 200 OK, en jerga técnica) y dejamos el trabajo pesado para después.
La cola: la sala de espera de tu entreno
Entre “recibido” y “procesado” hay una sala de espera: una cola de mensajes (usamos Amazon SQS). Cada aviso entra en la cola y un trabajador independiente (una función Lambda) los va sacando de 10 en 10 y se los pasa a nuestro backend para procesarlos.
¿Por qué no procesarlo directamente? Por tres motivos:
- Los picos. Un domingo a las 11:00 terminan su tirada cientos de corredores a la vez. La cola absorbe el golpe y los entrenos esperan su turno unos segundos en vez de tumbar el servidor.
- La memoria. La cola guarda cada mensaje hasta 7 días. Si reiniciamos un servidor o desplegamos una versión nueva, el entreno no se pierde: sigue esperándonos.
- El orden. Usamos una cola FIFO (“el primero que entra, el primero que sale”) agrupada por atleta. Tus entrenos se procesan en orden y de uno en uno, mientras los de otros atletas avanzan en paralelo. Esto evita que dos entrenos tuyos se pisen al recalcular tu carga.
Cuando llega su turno, el entreno pasa por la cadena de montaje: descargamos el archivo, lo decodificamos, guardamos el original en S3 (nuestro almacén de archivos), y recalculamos todo lo que depende de él: carga de entrenamiento, ratio agudo:crónico, índice de riesgo de lesión (IEG) y tu estado de forma. Si tenías un entreno planificado ese día, lo enlazamos con lo que realmente hiciste.
Al final, avisamos: el atleta recibe una notificación push en el móvil y el entrenador recibe el aviso en su panel al instante, gracias a una conexión abierta con el servidor.
Idempotencia: por qué tu tirada no aparece dos veces
Aquí viene la palabra fea del post: idempotencia. Significa algo muy sencillo: hacer lo mismo dos veces tiene el mismo efecto que hacerlo una. Pulsar el botón del ascensor cinco veces no trae cinco ascensores.
Y lo necesitamos porque en el mundo de los webhooks los avisos repetidos no son un error, son la norma. Las marcas prefieren avisarte dos veces antes que ninguna. Una red lenta, un reintento automático, una resincronización del histórico… y el mismo entreno puede llamar a la puerta varias veces.
Por eso tenemos varios porteros, uno detrás de otro:
- La propia cola. SQS descarta por sí sola un mensaje idéntico que llegue en un margen de 5 minutos.
- El registro de avisos. Cada entreno de cada marca tiene un identificador único (el ID de actividad de Garmin, de Strava…). Antes de procesar nada apuntamos “atleta X, actividad Y” en una tabla que la base de datos obliga a que sea única. Si el mismo par llega otra vez, la base de datos lo rechaza y lo ignoramos. Que la regla viva en la base de datos, y no solo en el código, es clave: dos servidores a la vez no pueden colarse el uno al otro.
- Una sola marca por atleta. ¿Y si tu Garmin también sube el entreno a Strava? Sería la misma carrera con dos identificadores distintos, imposible de detectar por ID. Nuestra decisión de producto: cada atleta conecta una sola fuente de datos. Menos magia, cero duplicados entre marcas.

Cada portero para un tipo distinto de copia; solo lo que pasa los tres llega a tu calendario.
Reintentos: cuando algo sale mal (y siempre acaba saliendo)
Trabajamos con servidores de terceros, y los servidores de terceros tienen días malos. Garmin puede tardar, Strava puede decirnos “vas muy rápido, espera” y a veces un archivo llega corrupto. La pregunta no es si fallará, sino qué hacemos cuando falle.
- Reintento automático. Si procesar un entreno falla de forma inesperada, el mensaje no se borra: vuelve a la cola y se reintenta, hasta 3 veces.
- La cola de “casos raros”. Si tras 3 intentos sigue fallando, el mensaje pasa a una cola aparte (una dead letter queue, o DLQ) donde se guarda 14 días. No se pierde, no bloquea al resto y una persona del equipo puede revisarlo.
- Llaves que caducan. Para descargar tus datos usamos un permiso temporal (un token OAuth) que caduca. Lo renovamos antes de que caduque, con 5 minutos de margen, para que la descarga no falle a mitad.
- El histórico. Al conectar tu reloj pedimos a Garmin los últimos 60 días de entrenos. Llegan por el mismo webhook que los nuevos, así que pasan por los mismos porteros: si alguno ya lo teníamos, se ignora.
Aquí la idempotencia y los reintentos van de la mano: como procesar dos veces no duplica nada, podemos permitirnos reintentar sin miedo.
War story: el portero demasiado celoso
Este verano un atleta nos escribió: “he hecho un entreno con mi Suunto y no aparece”. Miramos los registros y el aviso había llegado. La cola lo había entregado. Y sin embargo, el entreno no estaba.
Lo que había pasado era casi irónico. Para no duplicar, nuestro sistema apuntaba “este entreno ya está en marcha” antes de descargarlo. Esa descarga falló por un problema momentáneo del lado del proveedor. Cuando llegó el reintento, el portero miró la lista, vio el entreno apuntado y dijo: “este ya lo tengo, fuera”. El sistema anti-duplicados estaba bloqueando el reintento que debía arreglarlo.
En ingeniería a esto le llamamos un centinela envenenado: una marca de “ya procesado” que se escribe aunque el proceso no haya terminado bien.
La solución (desplegada el 30 de julio de 2026) fue cambiar el orden de las cosas:
- Apuntamos el entreno como “en proceso”.
- Si todo va bien, lo marcamos como completado.
- Si algo falla, borramos la marca, para que el siguiente reintento pueda entrar.
La lección: la idempotencia no consiste en “no dejar pasar nada dos veces”. Consiste en “no dejar pasar dos veces lo que ya ha salido bien”. Parecen lo mismo, pero no lo son.
Lo que hemos aprendido
- Contesta rápido, trabaja después. El webhook solo dice “recibido”; la cola hace el resto.
- Los duplicados se frenan en la base de datos, no solo en el código. El código puede equivocarse; una restricción única, no.
- Reintentar solo es seguro si procesar dos veces no hace daño. Idempotencia y reintentos son dos caras de la misma moneda.
- Una marca de “hecho” solo se escribe cuando está hecho. Si no, tu sistema anti-duplicados acaba comiéndose entrenos.
- Simplificar el producto también es ingeniería. Una fuente de datos por atleta nos ahorra el problema más difícil de todos: saber si dos carreras distintas son en realidad la misma.
La próxima vez que pares el reloj y veas tu entreno en MyAlbatross, ya sabes: ha llamado al webhook, ha esperado su turno, ha pasado tres controles y ha recalculado tu estado de forma. Todo para que tu entrenador tenga la foto real, y ni una carrera de más.
¿Quieres ver cómo funciona con tus propios atletas?
Planifica entrenamientos con inteligencia artificial, sincroniza datos de Garmin, Suunto y Strava, supervisa el riesgo de lesiones de tus atletas y recibe alertas antes de que aparezcan los problemas.