Repository navigation
fix(recycle): prune expired Admin/_Recycle requests with their reports (#6813) - #6820
Merged
Merged
Conversation
#6813) A declared retention (ActivationRecycle:Retention:{Enabled,MaxAge,MaxDeletionsPerRun}, default 30 days / 50 per run) applied by the writer: ActivationRecycle.Request prunes expired requests right after it writes a new one, as System, through DeleteNode (which cascades to the per-process reports). No timer, same owner-prunes-on-write shape as CompileActivityRetention. Fail-closed selection; the request just written is never pruned. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Cleanup delays request acceptance, and nonpositive retention values bypass the documented minimum-age clamp.
2 open findings
What changed in this PR
Adds configurable retention for activation-recycle requests and their reports, addressing audit growth as NodeType recycling becomes more frequent.
Changes:
- Adds an age-based retention policy and deletion cap.
- Triggers cascading cleanup after new requests.
- Adds retention tests and configuration documentation.
| File | Description |
|---|---|
| test/MeshWeaver.Graph.Test/ActivationRecycleRetentionTest.cs | Tests pruning, report retention, selection, and configuration. |
| src/MeshWeaver.Graph/Configuration/ActivationRecycleRetention.cs | Defines retention configuration and candidate selection. |
| src/MeshWeaver.Graph/Configuration/ActivationRecycle.cs | Registers retention and invokes cleanup after creation. |
| src/MeshWeaver.Documentation/Data/Architecture/StaleStateUntilRecycle.md | Documents retention settings and cleanup behavior. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
Test Results 15 files - 11 15 suites - 11 50m 59s ⏱️ - 8m 58s Results for commit 4ddbbba. ± Comparison against base commit cab0ae9. This pull request removes 1636 and adds 13 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
…ge is clamped (#6813) The prune that follows a new request ran inside the ticket's pipeline, so a slow listing (up to ReadBudget) plus sequential deletes could time a caller out on a request already committed and acted on. It is now its own subscription, started once the write lands. A configured zero or negative MaxAge parsed but fell back to the 30-day default instead of the documented one-day floor. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

Closes #6813
What
Admin/_Recycle/{id}requests and their{request}/{process}reports were never deleted. Once every NodeType definition recycle issues one (#6810), they grow without bound.ActivationRecycleRetention, read from configuration and registered as a mesh-scoped singleton byAddActivationRecycle:ActivationRecycle:Retention:Enabled, defaulttrueActivationRecycle:Retention:MaxAge, default30.00:00:00; a configured value below 1 day is clamped to 1 dayActivationRecycle:Retention:MaxDeletionsPerRun, default50ActivationRecycle.Requestcreates a new request, it callsActivationRecycle.Prune. Prune lists the oldest requests and deletes the expired ones as System throughIMeshService.DeleteNode. The delete cascades, so each request's reports go with it. This is the same owner-prunes-on-write shapeCompileActivityRetentionuses. The rows grow only through that write, so pruning on it is enough to bound them.Admin/_Recycleis kept. A failed listing or delete is logged and retried by the next request's prune. The caller's ticket does not depend on the prune.Doc/Architecture/StaleStateUntilRecycle→ "Recycling a type".No new config-key allow file applies in core.
Evidence
ActivationRecycleRetentionTest(MeshWeaver.Graph.Test) has 4 new tests. The full~ActivationRecyclefilter givestotal: 17, failed: 0, succeeded: 17.ANewRequest_PrunesAnExpiredRequestWithItsReports_AndKeepsItself: with a 1 ms window, the old request and its report are gone and the new request remains.ARequestInsideTheWindow_SurvivesThePrune_WithItsReportSelectForPruning_TakesExpiredRequestsOldestFirst_AndFailsClosed(pure)FromConfiguration_ClampsTheWindow_AndIgnoresMalformedValuesNegative control: with
Prunedisabled (if (!retention.Enabled || true) return 0), the run givestotal: 4, failed: 1:ANewRequest_PrunesAnExpiredRequestWithItsReports_AndKeepsItselffails with "Expected the observable to emit a value within 36s because the old request is past the window and the new request's prune removes it, but it did not." After restoring the code, the run gives 17/17.dotnet build -c Release -warnaserrorreports 0 errors and 0 warnings for MeshWeaver.Graph, MeshWeaver.Graph.Test and MeshWeaver.Documentation.Pairs-with: none — additive only (a new public type and a new public method; nothing is removed or changed)
🤖 Generated with Claude Code