{"record":{"id":"76558b045c7a1cd3","repo":"clockworklabs/SpacetimeDB","slug":"close-was-called-on-this-nodurability-instance","errorCode":null,"errorMessage":"`close` was called on this `NoDurability` instance","messagePattern":"`close` was called on this `NoDurability` instance","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/durability/src/imp/mod.rs","lineNumber":46,"sourceCode":"    }\n\n    impl<T> Default for NoDurability<T> {\n        fn default() -> Self {\n            let (durable_offset, _) = watch::channel(None);\n            Self {\n                durable_offset,\n                closed: AtomicBool::new(false),\n                _txdata: PhantomData,\n            }\n        }\n    }\n\n    impl<T: Send + Sync> Durability for NoDurability<T> {\n        type TxData = T;\n\n        fn append_tx(&self, _: PreparedTx<Self::TxData>) {\n            if self.closed.load(Ordering::Relaxed) {\n                panic!(\"`close` was called on this `NoDurability` instance\");\n            }\n        }\n\n        fn durable_tx_offset(&self) -> DurableOffset {\n            self.durable_offset.subscribe().into()\n        }\n\n        fn close(&self) -> Close {\n            self.closed.store(true, Ordering::Relaxed);\n            future::ready(*self.durable_offset.borrow()).boxed()\n        }\n    }\n}\n","sourceCodeStart":28,"sourceCodeEnd":60,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/durability/src/imp/mod.rs#L28-L60","documentation":"NoDurability is the Durability implementation used when no commitlog is configured (e.g. dev/standalone mode). close() sets an AtomicBool (Relaxed ordering) and append_tx checks it, panicking on append-after-close because the transaction would otherwise be silently dropped; it fails loudly instead. Hitting it means a commit raced shutdown: append_tx ran after close() had been called on the same instance.","triggerScenarios":"Durability::close() on NoDurability followed by another Durability::append_tx, e.g. a commit worker still draining while shutdown starts, or a test closing the datastore then committing.","commonSituations":"Standalone/test runs without a commitlog; test harness teardown ordering that closes durability before joining commit futures; shutdown-ordering regressions.","solutions":["Quiesce committers first: stop accepting commits and await all in-flight commit tasks before calling close().","In tests, join outstanding write futures (task tracker) before teardown.","Upgrade spacetimedb: shutdown-ordering races here are treated as bugs and get patched."],"exampleFix":"// before: close while commits may still land\ndurability.close().await;\n\n// after: quiesce writers first, then close\ncommit_workers.shutdown().await; // joins all append_tx futures\ndurability.close().await;","handlingStrategy":"validation","validationCode":"// shutdown ordering: quiesce committers before closing durability\ncommit_pool.shutdown().await; // joins outstanding append_tx futures, no new commits after\ndurability.close().await;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Single owner for shutdown: stop accepting commits, drain, then close.","In tests, use task tracking to await all commit tasks before teardown.","Treat any 'append after close' failure as a shutdown-ordering bug to fix, not noise."],"tags":["rust","durability","shutdown","race-condition","spacetimedb"],"backgroundTag":"use-after-close","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}