2.7 KiB
Runtime and Concurrency
Decision: Do not replace Twisted as the first FreeDMR 2 architecture move.
Rationale
Current packet behaviour is subtle. Twisted's single-threaded reactor is currently a safety boundary. Replacing the event loop and the state model at the same time mixes too many changes.
The first goal is architectural clarity and testability, not event-loop novelty.
Immediate Runtime Strategy
- Keep Twisted initially as the transport shell.
- Use Twisted's single-threaded reactor as a safety boundary while the core is extracted.
- Do not replace the event loop and state model at the same time.
- Extract the protocol/routing/subscription core behind deterministic interfaces.
- Keep packet-plane state local and deterministic.
- No blocking work in reactor callbacks.
- No dashboard/API/database/MQTT waits in the packet path.
- Single-owner state is preferred.
- Explicit messages/events are preferred over shared mutable dictionaries across threads/processes.
Eventual Capacity Strategy
FreeDMR 2 should support worker-process scaling once state ownership and message boundaries are explicit and tested.
The purpose of worker processes is not merely performance. It is also:
- Clearer state ownership.
- Failure isolation.
- Safer concurrency.
- Testable boundaries.
- Future capacity scaling.
Prefer process/actor ownership over shared-memory no-GIL threading for authoritative routing state. No-GIL Python does not remove the need for clear ownership of mutable packet-plane state.
Offload non-packet-path work first, including reporting, MQTT publishing, global export, SQL writes, dashboard aggregation, alias refresh, analytics, and lab/codec work.
Routing-core workers are a later stage. Multi-worker sharding should only be considered after single-worker message-boundary behaviour is proven.
Twisted can remain the transport shell while reporting/export/control workers move out-of-process.
Ownership Split
Twisted parent/transport process may own:
- UDP sockets.
- HBP/FBP packet receive/send.
- Timers.
- Process supervision.
Routing core should eventually own:
- Stream state.
- Subscription state.
- Dial-a-TG state.
- Loop-control state.
- Duplicate suppression.
- Routing decisions.
Migration must be staged and covered by tests. FreeDMR should remain deployable on ordinary low-cost systems such as cheap VPS instances and Raspberry Pi-class hardware.
See 13-worker-process-scaling.md for the eventual worker-process capacity model.
External Databases
External stores can be useful for configuration, reporting snapshots, global lastheard, operator UI, and coordination. They should not sit in the packet hot path.
Packet-plane state should stay local and in memory unless a future design proves a bounded, non-blocking alternative.