diff --git a/docs/DesignDecisions.md b/docs/DesignDecisions.md index 628dc6e..85c9aa1 100644 --- a/docs/DesignDecisions.md +++ b/docs/DesignDecisions.md @@ -82,6 +82,18 @@ fan-out may be added later with explicit backpressure semantics. Rationale: one subscriber preserves simple event ordering and failure behavior while parity is being proven. +### Alarms — superseded for the alarm subsystem + +The single-subscriber rule above no longer applies to alarms. The gateway runs +an always-on central alarm monitor (`GatewayAlarmMonitor`) that owns one +gateway-managed worker session, caches the active-alarm set, and fans it out to +any number of clients through the session-less `StreamAlarms` RPC. Per-session +alarm auto-subscribe is removed; `AcknowledgeAlarm` is session-less and routes +through the monitor. Data-side `StreamEvents` remains one subscriber per +session. Rationale: alarm state is gateway-wide, not session-scoped — every +client wants the same current set plus updates, and forcing each to own a +worker would multiply AVEVA polling load for no benefit. + ## Authentication Decision: API key authentication for the public gateway. diff --git a/gateway.md b/gateway.md index 2cb494c..1465da0 100644 --- a/gateway.md +++ b/gateway.md @@ -120,13 +120,24 @@ snapshot interval without mutating session or worker state. The dashboard uses local Bootstrap CSS and JavaScript plus a small local stylesheet; it does not use a Blazor UI component library. -`/dashboard/browse` and `/dashboard/alarms` go beyond read-only snapshots: they -read live MXAccess data through `IDashboardLiveDataService`, which owns one -shared, lazily-opened gateway session (and therefore one worker) for the whole -dashboard. Browse walks the `IGalaxyHierarchyCache` tree and reads subscribed -tag values; Alarms lists the worker's currently-active alarm set. See +`/dashboard/browse` walks the `IGalaxyHierarchyCache` tree and reads subscribed +tag values live through `IDashboardLiveDataService`, which owns one shared, +lazily-opened gateway session for the whole dashboard. `/dashboard/alarms` +reads the central alarm monitor's in-process cache directly. See `docs/GatewayDashboardDesign.md`. +The gateway runs an always-on central alarm monitor (`GatewayAlarmMonitor`): +one gateway-owned worker session subscribes the configured AVEVA alarm +provider, caches the active-alarm set (reconciled periodically against the +worker's snapshot), and fans it out to every client through the session-less +`StreamAlarms` RPC — the stream opens with the current active-alarm snapshot, +then streams live transitions. `AcknowledgeAlarm` is session-less and routes +through the monitor. Clients never open a worker session to see alarms, and +alarm monitoring is independent of client lifecycle; the monitor re-opens its +session if the worker faults. Gated by `MxGateway:Alarms:Enabled` — see +`docs/DesignDecisions.md` for why this reverses the v1 single-subscriber rule +for the alarm subsystem. + Dashboard routes use the same API-key verifier as gRPC. `/dashboard/login` accepts the API key in a form body, validates the configured `admin` scope, and issues an HTTP-only secure cookie for subsequent dashboard requests.