Live topology
Sources → pipeline → infrastructure. Depth = architectural layer.
—
devices · sources
loading…
pipeline · inside the app container
Model queues + workers
Decision Output
infrastructure · docker-compose.yml
external
httpx GET
TLS :5014
TLS :5014
compose network · default
psycopg2 :5432
redis-py :6379
redis-py :6379
critical = the device's own alarm limit was crossed (a fact, not a forecast)
worker thread down
traced device stopped reporting
reachable / flowing
idle · no traffic observed
unreachable / down
stale — stopped reporting
How this was verified
Infrastructure — read from docker-compose.yml + Dockerfile
- Exactly three compose services exist:
postgres(postgres:16, :5432),redis(redis:7-alpine, :6379),app(built from./Dockerfile, uvicorn on :8000). No others are drawn because no others exist. appdeclaresdepends_onboth stores with real healthchecks (pg_isready/redis-cli ping), so it starts only once both accept connections.- app → postgres is a SQLAlchemy/psycopg2 TCP connection
(
postgres:5432/water_anomaly_v2) — the compose env overrides.env's localhost value for the container only. - app → redis is plain key/value + list commands over
redis:6379/0(SET/GET/SADD/SMEMBERS/LPUSH/LTRIM/SCAN, TTL'd) fromRedisStateCacheandRawHistoryCache, which share one client. There is no pub/sub anywhere in the app — a grep forpubsub/publish/subscribereturns nothing, so no message-bus edge is drawn. - app → device API is an outbound
httpxcall toapi.enelsa.com:5014, which is outside the compose network — hence drawn outside the dashed boundary. - Volumes: named
postgres_data/redis_data, plus two bind mounts (./config,./storage) that exist so the device roster and the model artifacts survivedown/up --build.
Pipeline — read from the live code
MEASUREMENT_TYPESis 6 types includingec_combined— not 6 channels plus a 7th.GET /system-healthreports those 6 worker threads plusnullandpersistence, so this page draws 8 threads, not the 6 the previous version showed.- The persistence worker is a real registered thread with its own queue; it was missing from the previous diagram and is drawn here.
- Critical is never emitted by a model — the deployed models are 3-class. It is decided after
aggregate()bycritical_threshold_rule(ph, orp, temperature, ec_in) anddevice_percent_rule(ec_combined). Normal / watch / warning come from the models on every channel; critical means the device's own alarm limit was crossed.
Data sources — all pre-existing and read-only
GET /system-health— worker liveness, queue depths, Redis/DB reachability, counters.GET /devices/status— per-device rollup, last-updated, data age.GET /monitoring/barrier-timeouts— real barrier timeouts per device.GET /devices/{id}/status— fetched only when you click a device to trace it.
This page adds no failure-detection logic of its own and exposes no action — every state shown is something the system already tracks. Connectors animate only while the pipeline can actually move data; they burst on a real increase in barrier completions between polls, red out a downed lane, and freeze (keeping the last known picture) if a poll fails.