Plan de implementación¶
Restricciones: despliegue en Docker Swarm (nodo único) y validación funcional en cada capa. Estrategia: rebanada vertical primero — un solo producto (N0B) de punta a punta antes de ensanchar.
Implicaciones Swarm¶
- Colocación LDM ↔ procesador. Se comunican por directorio compartido (FILE + watcher). Con nodo único basta un volumen local nombrado; si el swarm crece, ambos servicios llevan
placement.constraintsal mismo nodo (labelnode.labels.nexrad==true). - Stack file, no compose plano.
docker stack deployignorarestart:; se usadeploy:(replicas, restart_policy, resources). LDM = 1 réplica siempre (cola de productos con estado); procesador = 1 réplica (inotify sobre directorio local no se paraleliza). - Registry obligatorio. Swarm hace pull, no build. CI construye y empuja a
ghcr.io. - Credenciales R2/D1 = Docker secrets, no variables de entorno en el stack file.
- HEALTHCHECK nativo en cada imagen → Swarm reinicia containers unhealthy automáticamente.
Mecanismos de validación¶
| Capa | Mecanismo | Qué prueba |
|---|---|---|
| Unit/integración | pytest + muestras reales cacheadas del bucket unidata-nexrad-level3 (golden tests: decode → grid → COG con valores esperados) |
lógica de dominio, sin red ni LDM |
| Integración storage | MinIO en CI para R2 (S3-compatible); D1 de test real (tier gratuito) | subida, schema, batching |
| E2E sin LDM | Injector de replay: baja productos recientes del bucket S3 y los deja caer en el directorio de entrada — misma ruta que producción | procesador completo → R2 + D1 |
| Healthcheck LDM | proceso ldmd vivo + edad del último producto recibido < umbral |
conectividad IDD real |
| Healthcheck procesador | heartbeat (mtime de fichero) + backlog del directorio de entrada < umbral | servicio vivo y al día |
| Monitor de frescura E2E | servicio del stack: por cada sitio configurado, D1 tiene raster < 30 min y HEAD del objeto R2 correspondiente responde |
cadena completa IDD → LDM → procesador → R2/D1 |
| Reconciliación | el sweep de retención reporta huérfanos R2↔D1 (métrica, no solo limpieza) | consistencia entre almacenes |
| CI | GitHub Actions: ruff + pytest + build/push de imágenes a ghcr | cada commit |
El monitor de frescura es viable porque el feed es continuo (volumen cada 4–10 min por sitio según VCP); el umbral de 30 min separa "roto" de "cielo despejado". Alerta por Telegram (bot existente) cuando un sitio pasa a rojo y cuando se recupera; el estado unhealthy en Swarm queda como señal local.
Fases¶
Cada fase tiene una puerta de validación: no se avanza sin pasarla.
F0 — Andamiaje¶
pyproject.toml (uv), ruff, pytest, estructura ingest/, CI esqueleto (lint + tests + build).
Puerta: CI verde.
F1 — Núcleo offline (solo N0B)¶
Decoder (MetPy) + grillado AEQD + escritura COG, expuesto como CLI: l3proc process <fichero> → .tif.
Puerta: golden tests con muestras de KAMX/TJUA;
gdalinfoconfirma CRS/geotransform/overviews; el COG abre correctamente en QGIS.
F2 — Storage¶
Schema D1 + migraciones en db/, cliente R2 (S3 API), cliente D1 (HTTP API con batching). El CLI ahora publica.
Puerta: test de integración contra MinIO + D1 de test; fila D1 y objeto R2 coinciden (clave, tamaño, metadata).
F3 — Servicio + replay¶
Watcher (watchdog) como servicio persistente + injector de replay desde el bucket S3.
Puerta: e2e local — el injector mete 20 productos; script verifica: 20 COGs en R2, 20 filas en D1, backlog vacío, fallidos preservados en directorio de errores.
F4 — LDM + stack Swarm¶
Contenedor LDM (ldmd.conf con request real, pqact.conf con patrón único), stack file con volúmenes/secrets/healthchecks, deploy al swarm.
Puerta: monitor de frescura verde 24 h seguidas con los 3 sitios.
F5 — Retención + monitor como servicio¶
Sweep de 72 h + reconciliación + monitor de frescura como servicio del stack con alertas Telegram.
Puerta: inyectar datos con timestamps viejos → el sweep los borra; borrar una fila D1 a mano → la reconciliación lo reporta; apagar el procesador → llega alerta Telegram y llega la recuperación al reencenderlo.
F6 — Resto de productos + fenómenos¶
N0G/EET/DVL/DAA/DU3/DTA vía config (mismo camino raster) + parsing propio de NMD/NST/NHI/NTV + NVW → D1.
Puerta: golden tests por producto; fenómenos visibles en D1 con un caso real de tormenta sacado del bucket.
El demo es visible para el viewer desde F4 con un solo producto — por eso N0B primero y el resto al final.
Documentación¶
Esta documentación (MkDocs Material) se despliega automáticamente a Cloudflare Pages cuando cambian docs/** o mkdocs.yml en main (workflow docs.yml: build con uvx mkdocs build --strict + wrangler pages deploy).
Desarrollo local:
uvx --with mkdocs-material mkdocs serve # preview en http://localhost:8000
uvx --with mkdocs-material mkdocs build --strict
Setup una sola vez (ya no repetir):
- Crear el proyecto Pages:
wrangler pages project create nexrad-l3-docs --production-branch main. - Secrets en GitHub:
CLOUDFLARE_API_TOKEN(permiso Cloudflare Pages — Edit) yCLOUDFLARE_ACCOUNT_ID.