Distancias (función)
Autor: Iker Acevedo¶
Fecha creacion: 2026-11-06
Estado: produccion (v8 desplegada en prod 2026-11-08, observado en la ficha del dominio; Scheduler de reportes apagado)
Actualizado: 2026-11-09 (v4 a v8)
Lambda: ApiLambdaNovedadesInterDistancias¶
Trigger: Step Function (Step_Procesos_Logigho), rama propia del Parallel
AOT: No (runtime administrado dotnet10, 512 MB, timeout 300 s)
Historia: 027 · Spec: 012 · Proyecto: el mismo de ApiLambdaNovedadesInter (un zip, tres funciones; ver ADR-003)
Convención de marcas: observado (ruta o id citado), Iker (fecha) o hipótesis. Esta página sigue el código de
Lambdas_NET_LogiGho(ramamain, PR 635); donde la spec 012 o una versión anterior de esta página dicen otra cosa, se anota en Observaciones.
¿Qué hace?¶
Calcula a cuántos metros por calle está el punto donde el mensajero de Inter marcó cada intento de la dirección
del pedido, y lo deja en SeguimientoNovedadesInter (metros, color, estado y enlace a la ruta). Desde la v6 la distancia por calle es el mínimo
entre la ruta a pie y las rutas en carro (ida y vuelta). Desde la v8 además guarda la dirección en texto del mensajero (geocodificación inversa, una vez
por punto; ADR-006). Sirve para que la gerencia
reclame a Inter las novedades marcadas lejos de la dirección y para que la vista de novedades muestre la distancia
(decisión de Iker 2026-11-05).
Las reglas de negocio (confianza de la geocodificación, colores, confirmación del rojo, estados) están en Reglas de la distancia. Esta página describe la función.
Accionador¶
| Origen | Entrada | Auth |
|---|---|---|
Step_Procesos_Logigho (rama nueva del Parallel) |
{"origen":"step"} (invocación directa, no formato API Gateway) |
IAM (rol de la Step) |
| A mano (soporte) | {} o {"modo":"backfill"} |
IAM |
- Handler:
ApiLambdaNovedadesInter::ApiLambdaNovedadesInter.Handlers.DistanciasHandler::EjecutarAsync(observado:aws-lambda-distancias.json). modo = backfillno cambia el algoritmo: solo queda anotado en el log. La selección ya cubre todo lo auditado, por tandas (observado:DistanciasHandler.cs). Desde la v8 la misma selección completa la dirección del mensajero de lo ya procesado (runbook D8). Cualquier otro valor se registra comomodo-desconocidoy se trata como normal.- La Step corre cada 3 h. La rama de Distancias va en paralelo a la de solicitar novedades y no espera a nadie: encuentra
completos los callbacks del ciclo anterior, así que el retraso máximo de una novedad es de 3 h (observado en CloudWatch prod
2026-11-05; fuente:
specs/012_novedades_distancia_reportes/spec.md§2.1 del workspace).
Request¶
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
origen |
string |
No | Solo informativo (step, directo); queda en el log |
modo |
string |
No | backfill o vacío. Solo informativo |
Response¶
Exitoso (resumen de la corrida)¶
La función devuelve el resumen en JSON y emite las métricas EMF de la sección Métricas.
{
"CorridaId": "<id de la invocación>",
"CandadoOcupado": false,
"Interrumpida": false,
"CorridaCortada": false,
"GuiasSeleccionadas": 120,
"GuiasProcesadas": 118,
"GuiasConError": 0,
"GuiasOmitidas": 2,
"Geocodificaciones": 40,
"Rutas": 95,
"RutasCarro": 60,
"Confirmaciones": 12,
"GeocodificacionesInversas": 30,
"LlamadasGoogle": 237,
"PendientesPorTope": 0,
"PendientesPorTiempo": 0
}
(Valores de ejemplo inventados; la forma sale de ResumenesFunciones.cs.)
| Campo | Significado |
|---|---|
CandadoOcupado |
Otra corrida vigente tiene el candado: esta no hizo nada |
Interrumpida |
Se acabó el presupuesto de 240 s o el tope de la lambda: lo pendiente queda para la siguiente corrida |
CorridaCortada |
FALLAS_SEGUIDAS_PARA_CORTAR fallas seguidas de Google (o de escrituras): se cortó y se avisó por Slack |
GuiasOmitidas |
Siguen sin datos del pedido (sin pedido o sin dirección) y nada cambió: no se tocan ni se llama a Google |
Rutas / RutasCarro |
Rutas a pie / rutas en carro (ida y vuelta) pedidas para sacar el mínimo (v6) |
GeocodificacionesInversas |
Llamadas de la dirección en texto del mensajero (v8) |
LlamadasGoogle |
Geocodificaciones + Rutas + RutasCarro + Confirmaciones + GeocodificacionesInversas. En una segunda corrida sin cambios debe ser 0 (CA5) |
PendientesPorTope / PendientesPorTiempo |
Guías que quedaron para la siguiente corrida por el tope o por el tiempo |
Errores¶
| Situación | Qué pasa |
|---|---|
| Google responde 429, 5xx o hay timeout | Reintenta 2 veces (esperas de 1 s y 3 s) |
| Google responde 400 o 404 | Resultado definitivo de esa petición (dirección o ruta imposible); no es falla del servicio |
401/403 o REQUEST_DENIED |
Error de servicio (llave o permisos): sin reintento; cuenta como falla seguida |
FALLAS_SEGUIDAS_PARA_CORTAR fallas seguidas |
Corta la corrida y manda alerta a Slack («Google Maps está fallando: se cortó la corrida de distancias») |
| Error en una guía | Se registra guia-con-error y la corrida sigue; cuenta como falla seguida |
| Excepción no controlada | Se registra excepcion-no-controlada y se relanza: el Catch de la rama la absorbe y las demás ramas de la Step terminan igual (CA6) |
Observado en: CalcularDistanciasUseCase.cs, ClienteGoogleMaps.cs, DistanciasHandler.cs.
Flujo interno¶
Step_Procesos_Logigho
Parallel
rama 1: Lambda Novedades Inter (solicitar) …
rama 2: Lambda Distancias Novedades ── {"origen":"step"} ──> DistanciasHandler
-> CalcularDistanciasUseCase
1. ICandado: toma "distancias" en CandadosNovedadesInter (TTL 15 min); si está ocupado, termina
2. IRepositorioDistancias.ObtenerPendientesAsync(tope)
(HuellaMotivos con valor y: HuellaDistancias distinta de "{HuellaMotivos}|v4|walk1", o última distancia
por evaluar contra el carro, o falta DireccionMensajero; activos primero, luego FechaActualizacion descendente)
3. ObtenerDireccionesAsync: PedidosInter por Numeropreenvio (Direccion, Ciudad, Departamento) — solo lectura
4. por guía, con concurrencia CONCURRENCIA_GOOGLE y presupuesto de 240 s:
a. IGeocodificador (Google Geocoding) solo si nunca se geocodificó o cambió la huella de la dirección
b. IRuteador (Google Routes) una vez por punto nuevo (caché por HuellaGeo): primero A PIE (WALK); solo si a pie
pasa de 250 m o no existe, 2 rutas en carro (DRIVE, ida y vuelta); Metros = el mínimo
c. si el mínimo da rojo: UNA geocodificación de confirmación con la dirección completa
d. v8: geocodificación INVERSA del punto del último intento (DireccionMensajero), si hace falta
e. GuardarAsync: $set parcial de DireccionPedido, Distancias, DistanciaUltima, HuellaDistancias, DireccionMensajero
(guía ya al día: $set de solo DireccionMensajero, sin rutas ni geocodificar el pedido)
6. libera el candado y emite log [RESULT] + métricas
- La Step de PreProd la maneja Terraform (
environments/preprod/procesos-automaticos): elParallelse llamaProcesos en paraleloy la rama esLambda Distancias Novedades→Fin Distancias, conTimeoutSeconds: 330,Retrysolo para errores transitorios de Lambda yCatchdeStates.ALLhacia el fin (observado:step_procesos_logigho.asl.json.tftpl). - La Step de producción no está en Terraform: la rama se agrega con
pipelines/novedades/scripts/agregar_distancias_a_step_function.py(ensayo,aplicar,revertir), que añade una rama alParallel Inity una política IAM que permite invocar solo esta función. Pendiente de ejecutar (lo hace Iker).
Candado¶
- Colección
CandadosNovedadesInter, documento{ _id: "distancias", Dueno, ExpiraEn }. - Se toma con un
upsertatómico cuyo filtro solo coincide si el candado no existe o ya venció; si está vigente, Mongo responde clave duplicada y la función entiende «ocupado». El vencimiento se comprueba en el código, así que funciona aunque el índice TTL tarde en limpiar (observado:CandadoMongo.cs). Duración: 15 min (ConfiguracionDistancias.DuracionCandado).
Tope por corrida y presupuesto de tiempo¶
| Parámetro | Valor | Origen |
|---|---|---|
MAX_GUIAS_DISTANCIA_POR_CORRIDA |
1500 por defecto | variable de entorno |
| Presupuesto de tiempo | 240 s fijos | ConfiguracionDistancias |
CONCURRENCIA_GOOGLE |
4 por defecto | variable de entorno |
FALLAS_SEGUIDAS_PARA_CORTAR |
5 por defecto | variable de entorno |
| Tope de la función | 300 s; se reservan 10 s antes del tope para cerrar limpio | aws-lambda-distancias.json, DistanciasHandler.cs |
Lo que no alcanza queda pendiente y se retoma en la siguiente corrida (cada 3 h). El relleno de lo ya auditado no es otro proceso: son las mismas corridas hasta vaciar los pendientes. El relleno de producción cubre todas las novedades, sin límite (Iker 2026-11-06).
Variables de entorno (solo nombres)¶
| Variable | Origen | ¿Cifrada? | Descripción |
|---|---|---|---|
ENTORNO |
Terraform | No | preprod o prod (etiqueta de métricas y alertas) |
DATABASE_NAME |
Terraform | No | Base Mongo |
CADENA_CONEXION |
Pipeline | Sí (AES) | Conexión a Mongo |
GOOGLE_MAPS_API_KEY |
Pipeline | Sí (AES) | Llave de servidor de Google Maps Platform (Geocoding + Routes) |
MAX_GUIAS_DISTANCIA_POR_CORRIDA |
Opcional | No | Tope de guías por corrida (1500) |
CONCURRENCIA_GOOGLE |
Opcional | No | Guías en paralelo (4) |
FALLAS_SEGUIDAS_PARA_CORTAR |
Opcional | No | Fallas seguidas para cortar la corrida (5) |
SLACK_WEBHOOK_ALERTAS |
Pipeline | Sí (AES) | Alertas de la corrida cortada (opcional: sin él solo queda el log) |
Detalle de cada entorno en Variables de entorno.
Dependencias externas¶
| Servicio | Uso |
|---|---|
MongoDB (Atlas en PreProd, DocumentDB en producción) |
Lee SeguimientoNovedadesInter y PedidosInter (solo lectura); escribe SeguimientoNovedadesInter ($set parcial) y CandadosNovedadesInter |
Google Geocoding API |
GET https://maps.googleapis.com/maps/api/geocode/json con components=country:CO, region=co, language=es |
Google Routes API |
POST https://routes.googleapis.com/directions/v2:computeRoutes. travelMode = WALK (sin routingPreference, con WALK Google responde error) y DRIVE con routingPreference = TRAFFIC_UNAWARE; X-Goog-FieldMask = routes.distanceMeters,routes.duration |
Google Geocoding API (inversa, v8) |
GET .../geocode/json?latlng=<lat>,<lng>&language=es, sin result_type; primer formatted_address que no sea un plus code, máximo 300 caracteres |
Slack (webhook entrante) |
Alerta de corrida cortada |
CloudWatch |
Logs, métricas EMF y alarmas |
En producción la función corre en la misma VPC, subred y grupo de seguridad que ApiLambdaNovedadesInter (DocumentDB privado y
salida por NAT hacia Google y Slack); en PreProd no necesita VPC (Atlas público). Observado: environments/prod/novedades/main.tf.
Métricas¶
Namespace Logigho/NovedadesInter, dimensiones Entorno y Operacion = distancias (observado: DistanciasHandler.cs):
GuiasSeleccionadas, GuiasProcesadas, GuiasConError, Geocodificaciones, Rutas, GeocodificacionesInversas, LlamadasGoogle, PendientesPorTope,
PendientesPorTiempo, CandadoOcupado, CorridaCortada, CorridaInterrumpida.
Las alarmas están en Observabilidad.
Logs¶
Una línea por evento, con corridaId y contadores: [REQUEST] al inicio y en pendientes, [RESULT] en fin
(seleccionadas, procesadas, conError, omitidas, geocodificaciones, rutas, rutasCarro, confirmaciones, inversas, llamadasGoogle,
pendientesPorTope, pendientesPorTiempo, interrumpida, cortada, duracionMs).
Nunca se loguea la dirección (ni la del pedido ni la del mensajero), el nombre, el teléfono, la llave de Google ni el token; solo guía, conteos, estados y códigos HTTP (CA13).
Historial de cambios¶
| Fecha | Autor | Cambio |
|---|---|---|
| 2026-11-06 | Iker Acevedo | Creación de la función (PR de Lambdas 619, 622, 623, 624) y primera validación en PreProd |
| 2026-11-09 | Iker Acevedo (redactado con IA) | v4 a v8: regla v4 y walk1, distancia mínima a pie/carro, dirección del mensajero (PR 635, desplegada en prod 2026-11-08), costos medidos y brechas |
Observaciones¶
- Resultado observado en PreProd (2026-11-06, 277 guías copiadas de prod): 125 calculadas, 0 en
Error, 38 rojas confirmadas; corrida de 17 s y 136 MB de memoria (ver plan de pruebas). Sobre DocumentDB de producción: el relleno de prod se ejecutó el 2026-11-06 (decisiones del workspace) y las proyecciones de la v8 se verificaron el 2026-11-08 (M-273, hecho). - Memoria sobredimensionada: usa ~136 MB de 512. Probar 256 (mejora M-261).
- Cupo del tope: las guías sin dirección en
PedidosIntersiguen ocupando cupo del tope de 1500 aunque se omiten sin llamar a Google (mejora M-258). - Lectura completa por corrida: lee una proyección mínima de todos los seguimientos y compara
HuellaDistanciasen memoria (en DocumentDB$exprse soporta a medias). Vigilar duración cuando la colección pase de ~50 000 (mejora M-257). - Criterio de ciudad: la coincidencia de ciudad es por contención en un sentido u otro («CALI» dentro de «CALIMA» pasaría); hay que revisarlo (mejora M-263).
- Alarma de corrida cortada:
CorridaCortadade Distancias se avisa por Slack y se emite como métrica, pero no existe una alarma de CloudWatch propia para esa métrica (las alarmascorrida-cortadamiranOperacion = solicitar). Observado enmodules/novedades-inter/alarmas.tf. - Versión de la regla (corregido 2026-11-09): el código vigente es v4 (
VersionReglaGeocodificacion.Actual) y la ruta eswalk1(VersionReglaRuta.Actual);HuellaDistancias = "{HuellaMotivos}|v4|walk1"(observado:ReglasDireccion.cs). Esta página decía v3 y DRIVE: era anterior a la v5. Ver Reglas de la distancia. - Medido en prod 2026-11-08 (observado, ficha del dominio y M-272): 8 112 guías con
DireccionMensajero.Estado = Calculada, 0SinResultadoy 0Error; unas 8 100 llamadas, ~USD 40 una sola vez. Falta vigilar el costo del ciclo de 3 h y evaluarresult_type. - Cuota de Routes (M-268): en el recálculo con carro de prod (2026-11-07) una ráfaga superó la cuota (3 000/min) y cortó la corrida por 5 fallas seguidas; falta un limitador de tasa (~2 000/min) o menor concurrencia.
- Carrera con la Step (M-270, hipótesis sobre la causa): Distancias corre en paralelo a la rama de novedades; las guías nuevas pueden quedar «Pendiente» de distancia y de dirección del mensajero hasta 3 h.
- Spec desactualizada: la spec 012 §1 sigue diciendo «ruta a pie (
WALK)» como único modo; la v6 (código) usa el mínimo entre a pie y carro. La documentación sigue al código. - Costo de la recarga con carro (spec, estimado): ~1 800 guías no verdes × 2 rutas ≈ 3 600 llamadas a Routes, 0 geocodificaciones (hipótesis de Iker 2026-11-07).