docs(redundancy): record the per-Akka-cluster role-election limitation

Corrects two claims made while writing the Phase 2 gate record.

The docker-dev rig is NOT misconfigured: central-1/central-2 carry admin,driver
deliberately — the compose says so — because they are themselves a pair that runs
the MAIN cluster's drivers.

And a redundant pair is not a missing concept. The application Cluster (ClusterId)
already is one: it owns drivers, devices and ClusterNode rows, and Phase 1's
deployment-artifact cache is keyed by it precisely so a pair shares one entry.

So the root cause is wider than the alarm drain. RedundancyStateActor elects one
Primary per AKKA cluster while every real unit of redundancy is an APPLICATION
cluster, and ServiceLevel, the alerts emit gate and the inbound device-write gate
all inherit that mis-scoping. On a fleet of pairs in one Akka cluster, exactly one
driver node cluster-wide services Primary-gated work.

Recommendation revised in the gate doc: fix the election scope (its own change,
its own gate, because it touches the safety-critical write gate), and explicitly
do NOT derive pair identity from LocalDb:Replication:PeerAddress — that would add
a second parallel notion of 'my partner' that the real fix obsoletes, and the
library has no initiator flag, so both halves would have to dial just to obtain a
config value.

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-21 06:18:57 -04:00
parent f9f1b8fcee
commit ad385eb70f
2 changed files with 59 additions and 13 deletions
+17
View File
@@ -69,6 +69,23 @@ Roles come from `RedundancyStateActor.BuildSnapshot`: a node with the `driver`
role is `Primary` when it holds the `driver` role-leader lease, otherwise
`Secondary`; a node without the `driver` role is `Detached`.
> **KNOWN LIMITATION — the election is per *Akka* cluster, not per application `Cluster`.**
> `RoleLeader("driver")` yields exactly **one** Primary across the whole Akka cluster. A fleet that
> runs several application clusters (each with its own redundant pair) inside one Akka cluster —
> which is what `docker-dev/docker-compose.yml` models, with MAIN, SITE-A and SITE-B — therefore has
> one Primary among *all* its driver nodes, not one per pair. Every consumer of the role inherits
> this: `ServiceLevel`, the `alerts` emit gate, and the inbound device-write gate below.
>
> The unit of redundancy everywhere else in the product is the application `Cluster` (`ClusterId`) —
> it owns drivers, devices and `ClusterNode` rows, and the LocalDb deployment-artifact cache is
> already keyed by it *so that a pair shares one entry*. Scoping the election the same way is the
> fix; `RedundancyStateActor` is an admin-role singleton in the same assembly as
> `AdminOperationsActor`, which already reads and groups `ClusterNodes` by cluster.
>
> Surfaced by the LocalDb Phase 2 live gate, where it stopped the alarm-history drain on every node
> in the fleet — see `docs/plans/2026-07-20-localdb-phase2-live-gate.md`. The alarm drain no longer
> depends on this (it defers only to a node that shares its queue), but the other three gates still do.
## Data flow
```