fix(client): remove obsolete device-registry cleanup task - #1070
Conversation
The background cleanup task held a strong Arc<Client>, so the client - and thus its store's open SQLite file handle - stayed alive until the task observed the shutdown signal and exited asynchronously. A consumer deleting a session then raced that teardown and could fail to remove the store file on Windows, which refuses to unlink a file with an open handle. Hold the client Weak and take the 'static shutdown future without pinning it, matching the event-delivery drainer's downgrade.
📝 WalkthroughWalkthroughThe device-registry cleanup loop and its constructor-spawned task are removed. A Tokio integration test verifies that dropping the last ChangesClient cleanup lifecycle
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Filename | Overview |
|---|---|
| src/client/device_registry.rs | Removes the shutdown-only device-registry cleanup placeholder. |
| src/client/lifecycle.rs | Stops spawning the detached task that retained the client. |
| src/client/tests.rs | Adds coverage for releasing a fresh client without an explicit shutdown. |
Reviews (3): Last reviewed commit: "fix(client): remove obsolete registry cl..." | Re-trigger Greptile
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/client/device_registry.rs (1)
996-1002: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winShip the guardrail with a test, not just a comment.
This function's whole reason for existing (per this PR) is the fast-exit-on-dropped-client path. Right now nothing in the test module actually exercises
client.upgrade()returningNone. A one-line regression test locks in the behavior this PR is delivering, and it's cheap to write sincedevice_registry_cleanup_loopis already reachable from#[cfg(test)] mod testsviause super::*;.✅ Suggested test
#[tokio::test] async fn cleanup_loop_exits_immediately_when_client_already_dropped() { let client = create_test_client().await; let weak = Arc::downgrade(&client); drop(client); tokio::time::timeout( std::time::Duration::from_millis(200), Client::device_registry_cleanup_loop(weak), ) .await .expect("cleanup loop must exit immediately when the client is already gone"); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/client/device_registry.rs` around lines 996 - 1002, Add a Tokio test in the existing tests module named cleanup_loop_exits_immediately_when_client_already_dropped that creates a test client, downgrades and drops it, then awaits Client::device_registry_cleanup_loop with a short timeout and asserts completion. This must exercise the client.upgrade() == None path and verify the loop exits promptly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/client/device_registry.rs`:
- Around line 996-1002: Add a Tokio test in the existing tests module named
cleanup_loop_exits_immediately_when_client_already_dropped that creates a test
client, downgrades and drops it, then awaits
Client::device_registry_cleanup_loop with a short timeout and asserts
completion. This must exercise the client.upgrade() == None path and verify the
loop exits promptly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e10add80-367f-4a5e-b9d6-546f5c351aca
📒 Files selected for processing (2)
src/client/device_registry.rssrc/client/lifecycle.rs
|
@Bot-Dev-RPA nice catch. I'll review this soon. Thank you! |
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
jlucaso1
left a comment
There was a problem hiding this comment.
Reproduced the client-retention bug on the current main with a lifecycle regression test: after the last consumer Arc was dropped, the obsolete device-registry task kept one strong reference alive. The original Weak fix released the client, but retained a shutdown-only detached task whose real cleanup work was removed in #204. The follow-up removes the dead method and spawn entirely and keeps the behavioral regression test.
Validated with cargo fmt --all -- --check, cargo clippy --all --tests, and cargo test --workspace --exclude e2e-tests. The E2E-only run requires the mock server and failed locally with the expected connection-refused errors when it was absent.
The background device-registry task retained a strong
Arc<Client>, which kept the client and its SQLite store handle alive after the last consumer reference was dropped. On Windows, deleting the session database could then race the delayed task teardown and fail because the file was still open.The real registry cleanup was removed during the storage-trait simplification in #204, but its shutdown-only placeholder and detached spawn remained. Remove both instead of replacing the captured
ArcwithWeak: there is no cleanup work left to preserve, and deleting the task also avoids leaving an orphaned shutdown waiter when a client is dropped without signaling shutdown.Add
dropping_fresh_client_releases_it_without_shutdownas a lifecycle regression test. On the currentmain, it times out with one background-owned strong reference remaining; with this fix, the client and persistence handles are released promptly.Validation:
cargo fmt --all -- --checkcargo clippy --all --testscargo test --workspace --exclude e2e-testscargo test --allwas also attempted; the E2E package failed withConnection refusedbecause its required mock server was not running.