feat(messaging): race-safe dedup + opt-in retention (ADR-0030 hardening)#1458
Merged
xuyushun441-sys merged 1 commit intoJun 1, 2026
Merged
Conversation
Two correctness/ops gaps in the notification pipeline. Race-safe dedup: - `sys_notification.dedup_key` is now a UNIQUE index (was plain). SQL treats NULLs as distinct, so the common no-dedup_key events are unconstrained. - `emit()` converges on a unique-key conflict: the pre-insert check is a fast-path; if a concurrent emit inserted first, our insert hits the violation and we converge to the winner's event (a dedup hit) rather than throwing or double-emitting. Mirrors the delivery outbox's enqueue convergence; stops a record-change storm from producing duplicate bell notifications. Enforcement is per-driver (the catch is simply never taken where the index isn't materialized — the same situation the delivery/receipt unique indexes are in today; tracked separately). Opt-in retention: - New `NotificationRetention` sweeper + plugin `retentionDays`/`retentionSweepMs`. Every emit writes an event (+ delivery/materialization/receipt), so a high-frequency periodic flow grows the tables unbounded. When `retentionDays > 0`, a low-frequency sweep (default hourly, timer unref'd) bulk-deletes rows older than the cutoff across all four objects — a notification ages out wholesale (no dangling notification_id), the bell (recent-only) is unaffected. Delivery's epoch-ms created_at vs the others' ISO created_at is handled per target. Default OFF — no data deleted without explicit operator policy. Targets are isolated; sweep runs under a system (cross-tenant) context. Tests: +7 service-messaging cases — 102 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two correctness/ops gaps in the ADR-0030 notification pipeline, flagged as remaining hardening work.
1. Race-safe dedup
emit()idempotency was a non-transactional check-then-insert against a non-uniquesys_notification.dedup_keyindex — under a record-change storm two concurrent emits with the samededupKeycould both write events → duplicate bell notifications.dedup_keyis now declared a UNIQUE index. SQL treats NULLs as distinct, so the (common) events with nodedup_keyare unconstrained.emit()converges on a unique-key conflict: the pre-insert check stays as a fast-path, but if a concurrent emit inserted first, our insert hits the violation — caught and converged to the winner's event (a dedup hit) instead of throwing or double-emitting. Mirrors the delivery outbox's enqueue convergence.2. Opt-in retention
Every
emit()writes asys_notificationevent (+ delivery/materialization/receipt), so a high-frequency periodic flow grows the tables unbounded.NotificationRetentionsweeper + plugin optionsretentionDays/retentionSweepMs.retentionDays > 0, a low-frequency sweep (default hourly, timerunref'd) bulk-deletes events, deliveries, inbox messages and receipts older than the cutoff — a notification ages out wholesale (no danglingnotification_id); the bell (recent-only) is unaffected.created_atvs the others' ISOcreated_atis handled per target.Tests
+7
@objectstack/service-messagingcases (converge-on-conflict, non-conflict rethrow, retention cutoff-formatting per target, no-engine / non-positive no-ops, failure isolation, missing-count). 102 messaging tests + 58 platform-objects tests green; package builds clean.🤖 Generated with Claude Code