filter: decide the reaping policy for the filter_fired claim ledger #409

Closed
opened 2026-08-12 16:04:01 +00:00 by KofTwentyTwo · 1 comment
KofTwentyTwo commented 2026-08-12 16:04:01 +00:00 (Migrated from github.com)

Raised by the final review of #394 (PR #405). Needs a decision, not obviously a patch.

filter_fired (rule_id, message_key, fired_at) is the fire-once guard: a rule claims a message atomically before running its actions, so one message arriving in several folders cannot fire FORWARD or CALL WEBHOOK twice. delete_placements reaps the messages row when its last placement goes, but never touches the ledger.

Two consequences:

  1. A deleted-and-re-arriving message never re-fires. Delete a message, have the sender re-send it byte-identically, and the claim from the first arrival still stands.
  2. The table only grows, bounded by (rules × distinct messages ever seen) at roughly 150 bytes a row. The user_version upgrade reset clears it, but nothing else does.

Neither is urgent. Growth is slow and bounded, and consequence 1 is arguably correct: under content-addressed identity a byte-identical redelivery genuinely is the same message, and the failure direction is "does not re-forward", which matches the priority that side-effecting actions must never double-fire.

The reason not to just reap alongside the message: doing so silently re-enables third-party side effects for anything that leaves and returns — a mailing-list message that is deleted and re-delivered would forward again. That is a semantic change, so it wants a decision.

Options

  • Leave it. Document the semantics as "a claim outlives the message" and add a periodic bound only if the table ever becomes a problem.
  • Reap with the message, accepting re-fire on redelivery.
  • Age it out — drop claims older than some window, trading a bounded table for a bounded re-fire risk.

Whichever is chosen, state it in claim_filter_fire's doc comment, which currently explains the claim but not its lifetime.

Raised by the final review of #394 (PR #405). Needs a decision, not obviously a patch. `filter_fired (rule_id, message_key, fired_at)` is the fire-once guard: a rule claims a message atomically before running its actions, so one message arriving in several folders cannot fire `FORWARD` or `CALL WEBHOOK` twice. `delete_placements` reaps the `messages` row when its last placement goes, but **never touches the ledger**. Two consequences: 1. **A deleted-and-re-arriving message never re-fires.** Delete a message, have the sender re-send it byte-identically, and the claim from the first arrival still stands. 2. **The table only grows**, bounded by (rules × distinct messages ever seen) at roughly 150 bytes a row. The `user_version` upgrade reset clears it, but nothing else does. Neither is urgent. Growth is slow and bounded, and consequence 1 is arguably *correct*: under content-addressed identity a byte-identical redelivery genuinely is the same message, and the failure direction is "does not re-forward", which matches the priority that side-effecting actions must never double-fire. The reason not to just reap alongside the message: doing so silently re-enables third-party side effects for anything that leaves and returns — a mailing-list message that is deleted and re-delivered would forward again. That is a semantic change, so it wants a decision. ### Options - **Leave it.** Document the semantics as "a claim outlives the message" and add a periodic bound only if the table ever becomes a problem. - **Reap with the message**, accepting re-fire on redelivery. - **Age it out** — drop claims older than some window, trading a bounded table for a bounded re-fire risk. Whichever is chosen, state it in `claim_filter_fire`'s doc comment, which currently explains the claim but not its lifetime.
KofTwentyTwo commented 2026-08-14 22:57:26 +00:00 (Migrated from github.com)

Decided in #413 (1a94716): leave the behaviour as-is, documented rather than changed.

Reaping the claim alongside the message would silently re-enable third-party side effects — a mailing-list message deleted and re-delivered would FORWARD again. Under content-addressed identity a byte-identical redelivery genuinely is the same message, and the failure direction ("does not re-forward") matches the priority that side-effecting actions must never double-fire. Growth is bounded by (rules x messages) at ~150 B/row and cleared by the user_version reset.

claim_filter_fire's doc comment now states that a claim outlives the message, why that is deliberate, and what it costs.

Decided in #413 (`1a94716`): **leave the behaviour as-is**, documented rather than changed. Reaping the claim alongside the message would silently re-enable third-party side effects — a mailing-list message deleted and re-delivered would `FORWARD` again. Under content-addressed identity a byte-identical redelivery genuinely *is* the same message, and the failure direction ("does not re-forward") matches the priority that side-effecting actions must never double-fire. Growth is bounded by (rules x messages) at ~150 B/row and cleared by the `user_version` reset. `claim_filter_fire`'s doc comment now states that a claim outlives the message, why that is deliberate, and what it costs.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/nuncio#409