You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Notifications have no retention — 4 476 rows and counting, including the legacy tail the addressing change left in place #3250
Notifications accumulate forever, and nothing reads the old ones
nodeType:Notification on memex-cloud was measured at 4 476 rows on 2026-09-03 (#3238) — every
notification ever raised, versioned, kept. The bell shows the newest first and nobody scrolls to
2026-08. There is no retention pass of any kind.
Two things make this worth a ticket now rather than never:
1. Addressing left a legacy tail in place, deliberately. MeshWeaver#3156 moved delivery to the
addressee's partition ({addressee}/_Notification/{id}), and Doc/Architecture/AddressedNotifications §6
ruled that the pre-existing rows are neither migrated nor deleted — the anchored bell simply
stops reading the partitions they sit in, and they age out. That ruling is explicitly conditional on
retention eventually existing; without it, "age out" means "never".
2. They are the cheapest rows on the platform to expire. A notification is ephemeral by
construction: it says something happened, it is read within minutes or not at all, and its subject
(TargetNodePath) outlives it and carries the durable record. Nothing links to a notification.
Shape
A per-partition sweep that removes _Notification rows older than N days, or beyond the newest M
per addressee — whichever is easier to make idempotent and bounded. It wants:
to be bounded per run (never a single statement over 201 schemas);
to run as a scheduled/LogonAction-shaped operation, not a startup Job — a retention pass must
never be able to stop a portal from serving the way a failed migration can (DbVersionGate);
🚨 never a raw psql DELETE — that bypasses the workspace cache
(Doc/Architecture/PostgresSchemaArchitecture).
What it is NOT
Not a fix for the row COUNT growth rate — that was #3213 (an update reminder is a state, told once
per candidate, not once per poll), which removed the dominant producer. This is about the tail that
is already there and the tail every future year adds.
Policy — age selects, a per-run count bounds. Age rather than "keep the newest M per addressee", because the cutoff is a pure function of one instant: two runs select the same set and the second finds it gone, whereas a "newest M" rule computed over a paged result selects a different set per page — and the pass has to be paged, because that is what bounds it. The clock is MeshNode.LastModified, not Notification.CreatedAt, so the ORDER and the PREDICATE are the same quantity. Defaults: 90 days, 200 deletions per run, armed, all three configurable and rendered in the chart (Notifications__Retention__{Enabled,MaxAge,MaxDeletionsPerRun}), with the window clamped to a 7-day floor at the configuration edge so a 0.00:00:00 typo cannot empty a bell.
Trigger — an EveryLogonILogonAction, not a startup Job (that can stop a portal serving via DbVersionGate), not a timer (no reader to scope it to, so it would enumerate partitions and sweep as System), and not a Code-MeshNode operation — that one is right for a one-off audited purge, but a tail that only shrinks when somebody presses Run does not shrink.
Bounded, in four ways, the first structural: nothing enumerates partitions — the swept set is the people who signed in, one at a time, plus Admin for a global admin. Each run issues one anchored capped query, path:{partition} scope:descendants nodeType:Notification sort:LastModified-asc limit:{cap}, deletes sequentially through meshService.DeleteNode (never raw SQL), and re-runs are no-ops.
The legacy tail is covered. The anchor is the PARTITION rather than {addressee}/_Notification precisely so the pass reaches pre-addressing rows filed under entities. Doc/Architecture/AddressedNotifications §6 — whose "neither migrate nor delete" ruling was explicitly conditional on retention existing — now records that the condition is met. Stated limit, unchanged in kind: partitions nobody signs into (spaces, plugin partitions, Doc) are still not swept.
Activity
fix(#3250): the Helm defaults do not arm retention — the code does; a…
rbuergi commentedon Sep 4, 2026
Done in #3279, merged to
mainas33b91449b.Policy — age selects, a per-run count bounds. Age rather than "keep the newest M per addressee", because the cutoff is a pure function of one instant: two runs select the same set and the second finds it gone, whereas a "newest M" rule computed over a paged result selects a different set per page — and the pass has to be paged, because that is what bounds it. The clock is
MeshNode.LastModified, notNotification.CreatedAt, so the ORDER and the PREDICATE are the same quantity. Defaults: 90 days, 200 deletions per run, armed, all three configurable and rendered in the chart (Notifications__Retention__{Enabled,MaxAge,MaxDeletionsPerRun}), with the window clamped to a 7-day floor at the configuration edge so a0.00:00:00typo cannot empty a bell.Trigger — an
EveryLogonILogonAction, not a startupJob(that can stop a portal serving viaDbVersionGate), not a timer (no reader to scope it to, so it would enumerate partitions and sweep asSystem), and not a Code-MeshNode operation — that one is right for a one-off audited purge, but a tail that only shrinks when somebody presses Run does not shrink.Bounded, in four ways, the first structural: nothing enumerates partitions — the swept set is the people who signed in, one at a time, plus
Adminfor a global admin. Each run issues one anchored capped query,path:{partition} scope:descendants nodeType:Notification sort:LastModified-asc limit:{cap}, deletes sequentially throughmeshService.DeleteNode(never raw SQL), and re-runs are no-ops.The legacy tail is covered. The anchor is the PARTITION rather than
{addressee}/_Notificationprecisely so the pass reaches pre-addressing rows filed under entities.Doc/Architecture/AddressedNotifications§6 — whose "neither migrate nor delete" ruling was explicitly conditional on retention existing — now records that the condition is met. Stated limit, unchanged in kind: partitions nobody signs into (spaces, plugin partitions,Doc) are still not swept.Design:
Doc/Architecture/NotificationRetention.