// PRJ_002 · 2025
Worker de sincronización GPSGPS sync worker
Concurrencia y locks · PythonConcurrency & locks · Python
La sincronización del GPS de la flota se demoraba tanto que una corrida se pisaba con la siguiente. Ahora consulta los dispositivos en paralelo y nunca procesa el mismo lote dos veces.The fleet GPS sync took so long that one run overlapped the next. Now it queries devices in parallel and never processes the same batch twice.
En detalleIn detailWorker que sincroniza el estado de los dispositivos GPS de la flota contra la API del proveedor. Tenía un cuello de botella de latencia por peticiones HTTP en serie; lo reemplacé por ejecución concurrente con ThreadPoolExecutor, sesiones con keep-alive y un lock distribuido en Redis que impide que dos ejecuciones procesen el mismo lote.Worker that syncs the fleet's GPS device state against the provider's API. It had a latency bottleneck from serial HTTP requests; I replaced it with concurrent execution via ThreadPoolExecutor, keep-alive sessions and a distributed Redis lock that stops two runs from processing the same batch.
// IMPACTO// IMPACT
El problemaThe problem
El worker consultaba la API del proveedor GPS dispositivo por dispositivo, en serie y abriendo una conexión nueva cada vez. Con la flota creciendo, el ciclo de sincronización se alargaba hasta solaparse consigo mismo: dos ejecuciones simultáneas escribiendo el mismo estado.The worker queried the GPS provider's API device by device, serially, opening a fresh connection each time. As the fleet grew, the sync cycle stretched until it overlapped itself: two simultaneous runs writing the same state.
Cómo lo resolvíHow I solved it
Reescribí la fase de consulta con ThreadPoolExecutor para lanzar las peticiones en paralelo y reusé conexiones con sesiones keep-alive. Alrededor del ciclo completo puse un lock distribuido en Redis: una segunda ejecución encuentra el lock tomado y termina en vez de duplicar el trabajo.I rewrote the query phase with ThreadPoolExecutor to fire requests in parallel and reused connections through keep-alive sessions. Around the whole cycle I put a distributed Redis lock: a second run finds the lock held and exits instead of duplicating work.
Arquitectura y stackArchitecture & stack
- [01]Cloud Scheduler
- [02]Cloud Run Job
- [03]Redis lock
- [04]API del proveedor GPS
- [05]PostgreSQL
- Python
- Redis
- Cloud Run
- GCP
- PostgreSQL
Lo que aprendíWhat I learned
01 //
El lock antes que la velocidad
Paralelizar sin exclusión mutua solo consigue que las ejecuciones duplicadas choquen más rápido.
02 //
keep-alive importa
Buena parte del tiempo por dispositivo se iba en el handshake TLS, no en la respuesta del proveedor.
03 //
Un job, no un servicio
Una tarea periódica y sin estado encaja mejor en un Cloud Run Job que en un servicio siempre encendido.
01 //
Lock before speed
Parallelizing without mutual exclusion only makes duplicate runs collide faster.
02 //
keep-alive matters
A good share of the per-device time went into the TLS handshake, not the provider's response.
03 //
A job, not a service
A periodic, stateless task fits a Cloud Run Job better than an always-on service.