Skip to content

fix(daemon): list mounted graphs without leasing project servers - #4251

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
fix/4175-eviction-cleanup-park
Oct 18, 2026
Merged

ScriptedAlchemy merged 1 commit into
masterfrom
fix/4175-eviction-cleanup-park

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Refs #4175

Cause. StoreAdministration::mounted_project_graphs cloned every Arc<McpServer> out of the registry, then awaited each server.cg(). A bounded route bind (bind_bounded) treats any extra server handle as a client lease and never evicts a leased server. A listing parked on a graph read (held swap) therefore made an idle server unevictable and the open was refused with project_server_capacity_reached, which is the "eviction cleanup never parked" failure. Maintenance, doctor, branch-add, LSP and PR-autotrack all call this listing.

Fix. Under the registry lock, take each server's graph slot (McpServer::graph_slot, a clone of the inner Arc<RwLock<Arc<TraceDecay>>>, not the server). Drop the lock, then read the slots. No server handle and no registry lock is held across an await.

Test. mounted_graph_listing_takes_no_server_lease_while_it_reads parks the listing on a held swap and asserts the server strong count is unchanged and the registry lock is free. On master's listing it fails (left: 5, right: 4); with the fix it passes.

Also. Boxes the release_capacity_retired_stores future in project_composition.rs: master's --config=clippy already fails on large_futures there.

Verification. :unit_test, :daemon_suite, clippy, rustfmt_check_2024 green. capacity_retirement_completes_while_a_maintenance_tick_runs plus the parked-cleanup test: 30/30 at 15-way concurrency, 47/48 and 23/24 at 24-way (the failures were host stalls, see comments).


Devin Review

A daemon-wide graph listing cloned every server Arc out of the registry and then awaited each graph read. A bounded route bind counts any extra server handle as a client lease and never evicts a leased server, so a listing parked on a graph read made an idle server unevictable and refused the open with project_server_capacity_reached.

Take each server's graph slot under the registry lock, drop the lock, then read the slots. The listing holds no server handle and no registry lock across an await.

Also box the release_capacity_retired_stores future that master's clippy rejects as large_futures.

Refs #4175
@changeset-bot

changeset-bot Bot commented Oct 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ee96ca8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Devin Review

Comment on lines +1074 to +1075
for slot in &slots {
graphs.push(slot.read().await.clone());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Evicted graphs remain visible to listings

If eviction removes a project while mounted_project_graphs waits on its graph slot, the listing can return that retired graph. Background refresh can then select it as a mounted graph and run against a retired project.

Learn more

A graph slot remains alive after its server leaves the registry. The listing captures slots, drops the registry lock, and waits for each slot's read lock. A concurrent capacity retirement can remove a server during that wait; its slot still yields the graph, even though the server is no longer mounted. coordinated_background_refresh_writer selects a graph from this listing and sends it into refresh work. A mere snapshot of the graph is not proof that its project remains served.

Example: Project A's slot is write-locked while the listing waits. An open for project B evicts A. When the write lock releases, the listing returns A's graph; background refresh selects A and attempts work on its retired store instead of reporting the retained graph unavailable.

Recommended fix: Tie the listing's returned graphs to registry membership at the point of use, or return a lease that keeps the selected server eligible until consumers finish. Ensure capacity eviction remains possible while the listing is parked; revalidate the selected server's identity before starting refresh or maintenance work.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@ScriptedAlchemy
ScriptedAlchemy merged commit 5cff32d into master Oct 18, 2026
13 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant