{"record":{"id":"74c3d7f9b4285b84","repo":"clockworklabs/SpacetimeDB","slug":"write-skew-you-need-to-implement-retries-my-man","errorCode":null,"errorMessage":"Write skew, you need to implement retries my man, T-dawg.","messagePattern":"Write skew, you need to implement retries my man, T-dawg\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/core/src/subscription/module_subscription_actor.rs","lineNumber":203,"sourceCode":"\n/// `Ok` side of a [`CommitAndBroadcastEventResult`].\npub struct CommitAndBroadcastEventSuccess {\n    pub tx_offset: TransactionOffset,\n    pub event: Arc<ModuleEvent>,\n    pub metrics: ExecutionMetrics,\n}\n\n/// Commits `tx`\n/// and evaluates and broadcasts subscriptions updates.\npub(crate) fn commit_and_broadcast_event(\n    subs: &ModuleSubscriptions,\n    client: Option<Arc<ClientConnectionSender>>,\n    event: ModuleEvent,\n    tx: MutTxId,\n) -> CommitAndBroadcastEventSuccess {\n    match subs.commit_and_broadcast_event(client, event, tx).unwrap() {\n        Ok(res) => res,\n        Err(WriteConflict) => todo!(\"Write skew, you need to implement retries my man, T-dawg.\"),\n    }\n}\n\ntype AssertTxFn = Arc<dyn Fn(&Tx) + Send + Sync + 'static>;\ntype SubscriptionUpdate =\n    ws_v1::FormatSwitch<ws_v1::TableUpdate<ws_v1::BsatnFormat>, ws_v1::TableUpdate<ws_v1::JsonFormat>>;\ntype FullSubscriptionUpdate =\n    ws_v1::FormatSwitch<ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>, ws_v1::DatabaseUpdate<ws_v1::JsonFormat>>;\n\nstruct CompiledQueryBatch {\n    queries: Vec<Arc<Plan>>,\n    physical_plans: HashMap<QueryHash, Vec<ProjectPlan>>,\n    auth: AuthCtx,\n    mut_tx: MutTxId,\n    compile_timer: HistogramTimer,\n}\n\n#[derive(Clone, Copy)]","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/9e0d92412ff2248f401a8ad12d535f2b5ac30912/crates/core/src/subscription/module_subscription_actor.rs#L185-L221","documentation":"A `todo!()` panic in the subscription actor: the transaction that evaluates and broadcasts subscription updates failed to commit with `WriteConflict` (write skew against a concurrent transaction), and retrying that commit is not implemented yet. The informal message marks a known unimplemented path, so the server aborts instead of retrying.","triggerScenarios":"A workload where the subscription-evaluation transaction reads rows that another committed transaction concurrently modified, so `commit_and_broadcast_event` loses the race and returns Err(WriteConflict). For example, two reducers mutating rows covered by the same subscription at the same moment.","commonSituations":"High-concurrency writes to subscribed tables (leaderboards, counters, chat); load tests with many parallel reducers; scheduled or init reducers racing client reducers on the same tables.","solutions":["Reduce contention: batch or stagger writes to rows covered by the same subscription","Retry the failed reducer call from the client with backoff — the server will not retry it for you","Upgrade SpacetimeDB: retry handling on this path is tracked as a todo and may exist in newer releases","Report the panic with a reproduction if it persists on the latest version"],"exampleFix":"// before\nclient.call_reducer(\"increment\").await?; // server panics under write skew\n\n// after\nfor attempt in 0..5u32 {\n    match client.call_reducer(\"increment\").await {\n        Ok(_) => break,\n        Err(e) if attempt < 4 => {\n            tokio::time::sleep(std::time::Duration::from_millis(50 * (1 << attempt))).await;\n        }\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in 0..5u32 {\n    match client.call_reducer(\"increment\").await {\n        Ok(_) => break,\n        Err(e) if attempt < 4 => {\n            tokio::time::sleep(std::time::Duration::from_millis(50 * (1 << attempt))).await;\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Avoid many concurrent reducers writing the same subscribed rows","Prefer a single-writer or scheduled reducer for hot rows","Keep server and SDK versions current so implemented retries are picked up"],"tags":["rust","server","transaction","write-skew","panic","todo","subscriptions","concurrency"],"backgroundTag":"transaction-write-conflict","analyzedSha":"9e0d92412ff2248f401a8ad12d535f2b5ac30912","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}