# 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.