fix(memory2): drain callbacks before store close - #3010
Conversation
Greptile SummaryThis PR makes Memory2 shutdown wait for recorder work before closing SQLite.
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (8): Last reviewed commit: "fix(memory2): keep store open after tear..." | Re-trigger Greptile |
|
I love you @greptile-apps ❤️ |
|
Thanks! Great work on this PR — the drain-before-close approach is solid and the fix is well-scoped. 🤝 |
|
Thanks! @greptile-apps why is this pr important? |
|
This PR fixes a real data corruption / crash bug in the Memory2 shutdown path, which matters for a few reasons:
In short: without this fix, graceful shutdown of a robot session has undefined behavior around its persistent storage layer. That's a correctness guarantee you need before relying on Memory2 data for replay, debugging, or training. |
|
Hey hey @leshy @paul-nechifor @spomichter, could someone approve the pending workflows and review? Both are mergeable and up to date with main. thanks! |
|
Fixed. Shutdown now blocks new recorder callbacks, unsubscribes, lets anything already resolving a pose or writing to SQLite finish, then cancels the dispatcher and closes the store. I added tests for in-flight work and setup finishing during shutdown. @greptile-apps mind taking another look? |
|
Updated the remaining shutdown cases. Recorder inputs and TF now drain against one shared two-second deadline. If a callback cannot finish, teardown fails closed and leaves SQLite open. This also covers setup rollback and dispatcher setup racing with stop. Added deterministic regression coverage for those paths. @greptile-apps could you take another look at the latest commit? |
6285b40 to
7d0ec07
Compare
Contribution path
Problem
Memory2 could close SQLite while an admitted input or TF callback was still resolving a timestamp or pose, or appending data. Recorder setup and rollback could also cross shutdown before cleanup ownership was established, and teardown could wait indefinitely when a callback needed the store lock.
Solution
Keep the store separate from generic module disposables and close it last. Shutdown blocks callback admission, unsubscribes ordinary inputs and TF, drains admitted callbacks, disposes dispatchers, completes generic teardown, and only then closes SQLite.
Input and TF drains share one two-second deadline. If any admitted callback does not finish, shutdown raises, marks teardown failed, and leaves SQLite open. Cleanup continues after other errors; a drain failure takes precedence while retaining the earlier error as context.
The deadline fits inside DimOS's five-second process shutdown grace. Setup rollback follows the same ordering and is serialized with normal stop. If dispatcher setup finishes after shutdown wins, its disposable is cleaned up and setup is rejected.
Normal stop still drains while
_memory_stop_lockis held. A callback that needs that lock therefore takes the bounded fail-closed path. Moving the drain outside the lock is a separate lifecycle change.TeleopRecorderuses the same protected store lifecycle. Reception timestamps, poseless streams, remapping, recording contents, and robot-control behavior are unchanged.How to Test
Validated locally at
6285b405c:git diff --check: passedThe private-LFS codec tests were excluded because their credentials are unavailable and they are unrelated to this change.
AI assistance
Used GPT-5 for implementation and tests, and Qodo, CodeRabbit, and Greptile for review.
Checklist