{"record":{"id":"663d5f76a11532ab","repo":"tursodatabase/turso","slug":"write-write-conflict-transaction-rolled-back","errorCode":null,"errorMessage":"write-write conflict (transaction rolled back)","messagePattern":"write-write conflict \\(transaction rolled back\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/mvcc_repl.rs","lineNumber":142,"sourceCode":"}\n\nfn execute_and_display(conn: &Arc<Connection>, sql: &str, conn_name: &str) -> anyhow::Result<()> {\n    let mut stmt = conn.prepare(sql).map_err(|e| anyhow::anyhow!(\"{}\", e))?;\n\n    match stmt.run_collect_rows() {\n        Ok(rows) => {\n            if rows.is_empty() {\n                println!(\"[{conn_name}] OK\");\n            } else {\n                for row in rows {\n                    let formatted: Vec<String> = row.iter().map(fmt_value).collect();\n                    println!(\"[{conn_name}] {}\", formatted.join(\" | \"));\n                }\n            }\n            Ok(())\n        }\n        Err(LimboError::WriteWriteConflict) => Err(anyhow::anyhow!(\n            \"write-write conflict (transaction rolled back)\"\n        )),\n        Err(e) => Err(anyhow::anyhow!(\"{e}\")),\n    }\n}\n\nfn fmt_value(v: &Value) -> String {\n    use turso_core::Numeric;\n    match v {\n        Value::Null => \"NULL\".to_string(),\n        Value::Numeric(Numeric::Integer(i)) => i.to_string(),\n        Value::Numeric(Numeric::Float(f)) => {\n            let fval: f64 = (*f).into();\n            // Format floats without trailing zeros for cleaner display\n            if fval.fract() == 0.0 && fval.abs() < 1e10 {\n                format!(\"{fval:.1}\")\n            } else {\n                format!(\"{fval}\")\n            }","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/tursodatabase/turso/blob/0e69fa4af1a4ff174ecb61d19f728df0ab41c218/cli/mvcc_repl.rs#L124-L160","documentation":"Reported by the MVCC concurrent-transaction REPL (tursodb --mvcc) when a statement fails with turso_core::LimboError::WriteWriteConflict. Experimental MVCC mode uses optimistic snapshot isolation: each transaction validates its writes against concurrently committed changes. When another connection already committed writes to the same rows/pages since this transaction's snapshot was taken, the engine rolls the losing transaction back and the REPL prints 'ERROR: write-write conflict (transaction rolled back)'.","triggerScenarios":"In the REPL, run 'conn1 BEGIN CONCURRENT' and a write (INSERT/UPDATE/DELETE) on conn1, then on conn2 (also in a transaction) write to the same table/rows and let either side commit first; the other connection's next write statement or commit returns WriteWriteConflict via stmt.run_collect_rows().","commonSituations":"Interactive multi-connection conflict testing (the module doc shows exactly this conn1/conn2 INSERT scenario); scripts that assume lock-based blocking writes instead of optimistic validation; long-running read-then-write transactions whose snapshots go stale while another session commits.","solutions":["Accept the rollback and retry: the losing transaction is already rolled back, so re-run BEGIN CONCURRENT plus the write statements on the failed connection","Keep transactions short so fewer concurrent snapshots overlap on the same rows","Route hot-row writes through a single connection or an application-level lock so commits never interleave","Re-read data after the other connection commits so the retried transaction starts from a fresh snapshot"],"exampleFix":"// before (one-shot, fails on conflict):\nmvcc> conn2 INSERT INTO t VALUES (42)\n[conn2] ERROR: write-write conflict (transaction rolled back)\n\n// after (rollback + retry):\nmvcc> conn2 ROLLBACK\nmvcc> conn2 BEGIN CONCURRENT\nmvcc> conn2 INSERT INTO t VALUES (42)","handlingStrategy":"retry","validationCode":null,"typeGuard":"fn is_write_write_conflict(err: &turso_core::LimboError) -> bool {\n    matches!(err, turso_core::LimboError::WriteWriteConflict)\n}","tryCatchPattern":"loop {\n    conn.execute(\"BEGIN CONCURRENT\")?;\n    match run_writes(conn) {\n        Ok(_) => { conn.execute(\"COMMIT\")?; break; }\n        Err(e) if is_write_write_conflict(&e) => {\n            let _ = conn.execute(\"ROLLBACK\"); // already rolled back; keep for hygiene\n            continue;                       // optionally with backoff\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Keep transactions short so snapshots rarely go stale between read and write","Serialize writes to hot rows through one connection or an app-level lock","Treat retry-on-conflict as mandatory application logic under optimistic MVCC, not an edge case","Exercise conflict paths with testing/concurrent-simulator before shipping"],"tags":["mvcc","concurrency","snapshot-isolation","transaction","write-conflict"],"backgroundTag":"write-write-conflict","analyzedSha":"0e69fa4af1a4ff174ecb61d19f728df0ab41c218","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}