feat(api): trim the library feed, so its expiry answer is finally exercised - #166
Conversation
…rcised RFC-007 decision 7, built. Until now nothing purged: `library.events_purged_through` sat at 0 by construction, which meant the feed's whole expiry path — the watermark read, the `Conflict`, the comment explaining why a derived floor would be wrong — was correct code that had never once run. Two bounds, and the floor wins. Thirty days by default, never fewer than ten thousand events per library, both refused at startup if zero or negative rather than quietly defaulted. The age bound is exclusive: an event exactly thirty days old lives. **The delete and the watermark are one transaction.** That is the whole of what makes the expiry answer honest — written separately there is a window where the watermark claims less than has gone, and a client reading into it is handed a catch-up that looks complete while silently skipping the gap. `MAX` on the update, so a pass cutting an older tail cannot lower a watermark an earlier one raised. One transaction per library rather than one for all of them: the writer gate is process-wide, and holding it across every feed on a server with fifty libraries would stall every other mutation for the length of the pass. Three rules removed to watch the test fall: make the bound inclusive and the event sitting on it is cut; drop the floor and three rows go where one should; stop moving the watermark and the feed hands back a tail it should have refused. ## The test was flaky, and it said so on the second run It read the clock, then let the purge read it again — and with an exclusive bound, "exactly thirty days old" is a single instant. The milliseconds between the two reads put the boundary event on the wrong side, so it passed once and failed the next time for nothing that had changed. `purge_library_events` takes `now_ms` now, the way `sweep_expired_sessions` already did. A caller that cannot name the instant cannot place an event on it — it can only aim near it and hope, which is not a test of a boundary. Five consecutive runs green, and it was caught because the suite was run again rather than once. Claude-Session: https://claude.ai/code/session_01GtAmdsaBCg8Cs2rvrdLD7Z Signed-off-by: InstaZDLL <github.105mh@8shield.net>
The `Implémentée par` line stopped at #152 and had missed #159 since this morning — the album events, decision 6 — while the line right below it claims to be the one thing in this document that can be checked. A line that says what runs is worth exactly as much as its last update. #166 joins it for decision 7. Decision 8 stays absent, because it is decided and not built, and that is the distinction the line exists to carry. Claude-Session: https://claude.ai/code/session_01GtAmdsaBCg8Cs2rvrdLD7Z Signed-off-by: InstaZDLL <github.105mh@8shield.net>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Included review availability: 1 review is currently available. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour. 📝 WalkthroughWalkthroughLa configuration définit les seuils de rétention. Le serveur démarre une purge immédiate puis quotidienne. Chaque bibliothèque est purgée dans une transaction, avec conservation d’un nombre minimal d’événements et mise à jour du watermark. ChangesRétention des événements de bibliothèque
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The change adds transactional library-event retention with bounded configuration and exercises the expiry path; no actionable merge-blocking risk remains after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant Serveur
participant DomainServices
participant BaseDeDonnees
Serveur->>DomainServices: Démarrer spawn_library_event_purge()
DomainServices->>DomainServices: Exécuter purge_library_events(now_ms)
DomainServices->>BaseDeDonnees: Purger les événements anciens par bibliothèque
BaseDeDonnees-->>DomainServices: Retourner les suppressions et le watermark
DomainServices-->>Serveur: Journaliser le résultat ou l’erreur
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Description checkExplanation La description est détaillée et couvre l’objectif, les règles de rétention, les garanties transactionnelles, les tests et l’état de la RFC. Elle ne reprend pas exactement les sections du modèle et ne contient pas la confirmation DCO, mais elle reste globalement complète et pertinente. Full details: Docstring CoverageExplanation Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 5 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Warning Your free Security trial is over. An organization admin can activate Security or dismiss this notice. Comment |
Three pull requests landed since this file last described the work left: #165 decided the four questions RFC-007 and RFC-009 were carrying, #166 built decision 7, and #163 was already recorded. Batched rather than patched after each merge, which is the promise made after the third amendment this morning. The reason to keep amending at all is the same one this file's own traps section gives — a document that lists finished work as pending is how another agent lost three batches — but doing it per pull request turns the record into a changelog of itself. What the next agent picks up shrinks to three, and two of them are no longer questions: - Decision 8, the acknowledgement table: decided by #165 and not built, which is the distinction the `Implémentée par` line carries. - Sweeping `artwork_dir`: queued work, not an arbitration. The canvas store has a sweeper; the cover store has the same property and none. - §4, the four Subsonic clients, still the gate on `v2.0-beta` and still not something a session can do. Claude-Session: https://claude.ai/code/session_01GtAmdsaBCg8Cs2rvrdLD7Z Signed-off-by: InstaZDLL <github.105mh@8shield.net>
RFC-007 decision 7, built.
Until now nothing purged.
library.events_purged_throughsat at 0 by construction, which meant the feed's whole expiry path — the watermark read, theConflict, the comment explaining why a floor derived from surviving rows would be wrong — was correct code that had never once run.The rules
Two bounds, and the floor wins. Thirty days by default, never fewer than ten thousand events per library. Both are refused at startup if zero or negative rather than quietly defaulted, which is what
parse_positive_envalready does for every other bound here.The age bound is exclusive: an event exactly thirty days old lives.
The delete and the watermark are one transaction. That is the whole of what makes the expiry answer honest — written separately there is a window where the watermark claims less than has gone, and a client reading into it is handed a catch-up that looks complete while silently skipping the gap.
MAXon the update, so a pass cutting an older tail cannot lower a watermark an earlier one raised.One transaction per library rather than one for all: the writer gate is process-wide, and holding it across every feed on a server with fifty libraries would stall every other mutation for the length of the pass.
Three rules removed to watch the test fall
The test was flaky, and it said so on the second run
It read the clock, then let the purge read it again — and with an exclusive bound, "exactly thirty days old" is a single instant. The milliseconds between the two reads put the boundary event on the wrong side, so it passed once and failed the next time for nothing that had changed.
purge_library_eventstakesnow_msnow, the waysweep_expired_sessionsalready did. A caller that cannot name the instant cannot place an event on it — it can only aim near it and hope, which is not a test of a boundary. Five consecutive runs green.It was caught because the suite was run again rather than once.
Still decided and not built
Decision 8, the acknowledgement table. RFC-007's section says so, and the
Implémentée parline names only what runs.Gates
cargo fmt --all --check,cargo clippy --all-targets --all-features -- -D warnings,cargo test --all-features— 155 tests across fourteen targets, all green.Summary by CodeRabbit
Nouvelles fonctionnalités
Documentation