Architecture ​
The full message pipeline, from ADS-B reception through to archived flights in S3, including the RabbitMQ offline-fallback path at each hop.
Scaling Processors ​
Each processor handles the subset of aircraft whose ICAO hex modulo PROCESSOR_COUNT equals the processor's ID. To add a second processor:
- Uncomment the
processor-1block indocker-compose.processor.yamland its volume entry. - Increment
processor_countin the receiver'ssettings.json(e.g."processor_count": 2). - Restart the receiver — it will pre-declare the new queue and begin routing to it.
- Start the new processor container:
docker compose -f docker-compose.processor.yaml up -d.
Each processor must run on a separate host (or at least with a unique PROCESSOR_ID). The processor claims its ID in Redis on startup and exits if the ID is already claimed by another instance.
Increasing PROCESSOR_COUNT reshuffles every aircraft's target processor (icao_hex % PROCESSOR_COUNT), not just the newly-added capacity — it's a modulo over the new count, so most aircraft that used to hash to an existing processor now hash somewhere else. Existing processors will see some of their in-flight flights go stale as traffic reroutes to the new processor; those flights age out and archive normally via the crash-durable eviction path (see Crash Recovery & Backlog Replay below), while the new processor starts a fresh flight for that aircraft the next time it's seen. This is an accepted split — the same category of fragmentation flight_ttl_seconds already causes elsewhere — not data loss or corruption. No special drain is required to resize upward safely, though performing it during a quiet period will minimize the number of flights that get split.
Removing a processor works the same way in reverse:
- Stop the processor container(s) being removed.
- Decrement
processor_countin the receiver'ssettings.json, and comment out (or remove) the corresponding block/volume indocker-compose.processor.yaml. - Restart the receiver so the new routing takes effect.
- Decommission the removed processor's container and volume.
Splitting an in-progress flight across the resize boundary is an accepted trade-off, same as resizing up. The removed processor's residual active_flights.db data does not need to be manually drained or preserved before decommissioning — any flights it was still tracking are simply abandoned; surviving processors will start fresh flights for those aircraft under the new routing the next time they're seen.
Future enhancement, tracked in #474 (not built): the archive-processor could eventually detect and stitch together adjacent split-flight records (same icao_hex, contiguous time ranges, matching aircraft/operator) produced by a resize-down event.
Crash Recovery & Backlog Replay ​
The processor's active flight store is file-backed and survives a process restart — deliberate or a crash — without losing in-progress flights. See Fault Tolerance in the processor's README for the full behavior, including how recovery avoids archiving flights just because wall-clock time passed while the container was down.
Receiver Offline Fallback ​
Independent of whether a processor is up, the receiver itself tolerates RabbitMQ being unreachable: publish attempts that fail buffer to a local queue.db (SQLite WAL) and drain oldest-first once RabbitMQ is reachable again.