← PORTAFOLIOPORTFOLIO / proyectos / worker-gps

// 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.

RolRole
Desarrollador backendBackend developer
AñoYear
2025
CódigoSource
privado — trabajo de empresaprivate — company work
Stack
Python · Redis · Cloud Run

// IMPACTO// IMPACT

ThreadPoolThreadPool
concurrenciaconcurrency
Redis lockRedis lock
exclusiónmutual exclusion
Cloud RunCloud Run
runtimeruntime

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

  1. [01]Cloud Scheduler
  2. [02]Cloud Run Job
  3. [03]Redis lock
  4. [04]API del proveedor GPS
  5. [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.