fix(historian): preserve stable alarm delivery identities
v2-ci / build (pull_request) Successful in 5m52s
v2-ci / unit-tests (pull_request) Failing after 1h23m55s

This commit is contained in:
Joseph Doherty
2026-10-13 07:10:58 -04:00
parent 0ccfbbee5c
commit 109163621c
13 changed files with 341 additions and 27 deletions
+13
View File
@@ -153,6 +153,19 @@ Ordering follows from that: the drain reads in `enqueued_at_utc` order (with `id
as a tiebreak so the ordering is total), because a hashed key carries no
insertion sequence.
Gateway delivery carries a separate nonzero `DeliveryId` GUID. Direct event instances
create it once; retries and record copies preserve it. It is excluded from queue JSON
and business equality, so the existing payload fingerprint and pair deduplication stay
unchanged. The drain derives its delivery GUID solely from the persisted row `id`,
including migrated legacy rows. It never hashes a reconstructed payload or mints an ID
per send. The versioned encoding in `AlarmDeliveryIds` therefore survives retry,
restart, payload edits and replicated failover without a schema migration.
Different business payloads at the same timestamp remain distinct; completely identical
payloads deliberately converge. An explicitly empty direct delivery ID is rejected.
The Gateway mapper sends this GUID as the wire event ID; the live alarm readback test
requires that exact ID. SQL Server and QuestDB retain their existing event-ID schemes.
`EnqueueAsync` does a single `INSERT … ON CONFLICT DO NOTHING` on the hot path.
To avoid a `SELECT COUNT(*)` on every enqueue, the sink keeps an in-memory
non-dead-lettered row counter (seeded at startup, kept current by every mutation,
@@ -0,0 +1,9 @@
# Alarm delivery identity results — lmxopcua #705
Alarm events now carry a nonzero delivery GUID created once for direct delivery and preserved across retries and record copies. It is excluded from queue JSON and business-field equality/hash. Queued delivery overrides it with a frozen versioned UUID encoding derived only from the persisted queue row key, preserving existing payload JSON, fingerprints, schema, deduplication and AlarmId. The Gateway mapper sends that GUID and rejects explicitly empty identities. SQL Server and QuestDB identity logic is unchanged.
Coverage includes literal historical JSON/fingerprint/GUID vectors; ten-field equality and hash behavior; direct copies/retries; queue retry/reopen/payload edits/dead-letter revival; identical-input convergence; distinct same-time business events; real replicated queue convergence; and legacy migration. Reconstructed direct objects receive new identities; durable reconstruction uses the queue. Completely identical business payloads remain intentionally deduplicated.
TDD: direct wire regression failed on an empty ID; queued retry/reopen regression failed on changing IDs. Final affected runs: Core51/51, Host migration/convergence16/16, Gateway453passed/6live skipped/0failed. Full offline solution:14,635passed/611skipped/0failed across62suites, exit0. Independent final review and diffcheck clear. Fixture configuration variables were removed before all runs.
No live event writes, deployed sender rollout or historical row rewrite occurred. The live round-trip test now requires exact delivery GUID readback; actual persistence remains the open Gateway #28 gate, alongside its separate #39 timestamp-offset provider failure. Upstream History controls #29 and lmxopcua #694/#700 remain open.