{"record":{"id":"bdd258da6d38c03b","repo":"tursodatabase/turso","slug":"id-sum-mismatch-for-case","errorCode":null,"errorMessage":"ID sum mismatch for {case:?}","messagePattern":"ID sum mismatch for (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"perf/fts/src/main.rs","lineNumber":342,"sourceCode":"    let ids = (0..documents).filter(|id| match case {\n        QueryCase::Rare => id % 100 == 0,\n        QueryCase::Common => true,\n        QueryCase::And => id % 6 == 0,\n        QueryCase::Or | QueryCase::Ranked => id % 2 == 0 || id % 3 == 0,\n        QueryCase::Phrase => id % 200 == 0,\n    });\n    let expected_rows = ids.clone().count();\n    if matches!(case, QueryCase::Ranked) {\n        ensure!(\n            rows == expected_rows.min(10) * queries,\n            \"ranked row count mismatch\"\n        );\n    } else {\n        ensure!(\n            rows == expected_rows * queries,\n            \"row count mismatch for {case:?}\"\n        );\n        ensure!(\n            sum == ids.map(|id| id as i64).sum::<i64>() * queries as i64,\n            \"ID sum mismatch for {case:?}\"\n        );\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[tokio::test]\n    async fn rejects_mixing_search_latency_and_throughput_options() -> Result<()> {\n        for options in [\n            vec![\n                \"--benchmark\",\n                \"search\",\n                \"--connections\",","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/tursodatabase/turso/blob/8d4a589f8d13ac184700d2a8f724f27e1995be3b/perf/fts/src/main.rs#L324-L360","documentation":"After the row count check, validate_result sums the returned ID column and compares it against the sum of expected matching IDs times the query count. ensure! at perf/fts/src/main.rs:342 fails when the row count matches but the returned IDs differ — the engine returned the right number of rows containing wrong values (duplicates, wrong ids, corruption).","triggerScenarios":"validate_result with case And/Or/Phrase where sum != sum(expected ids) * queries — correct row count but wrong id values, e.g. rows substituted, ids computed differently, or duplicated/missing ids that coincidentally balance the count.","commonSituations":"Integer/id encoding bugs in the FTS result projection; ordering-independent comparison missing duplicate ids; storage corruption under WAL/MVCC; docid assignment drift after incremental inserts/deletes.","solutions":["Collect and sort the returned ids and diff them against ids.filter(predicate) to see which ids differ","Check for duplicate ids in the result set (count matches but sum wrong often implies substitution)","Rebuild the FTS index from scratch and re-validate to rule out index corruption","Verify id type/projection in the query matches the i64 sum expectation"],"exampleFix":"// before\nensure!(sum == expected_sum * queries as i64, \"sum mismatch\");\n// after\nlet mut got: Vec<i64> = collected_ids; got.sort();\nlet mut want: Vec<i64> = expected_ids; want.sort();\nassert_eq!(got, want, \"ID set mismatch for {case:?}\");","handlingStrategy":"try-catch","validationCode":"let expected_sum: i64 = (0..n).filter(|&id| predicate(id)).map(|id| id as i64).sum::<i64>() * queries as i64;","typeGuard":null,"tryCatchPattern":"if let Err(e) = validate_result(...) {\n    if e.to_string().starts_with(\"ID sum mismatch\") {\n        eprintln!(\"sum {sum} != expected {expected_sum}; compare sorted id sets\");\n    }\n    return Err(e);\n}","preventionTips":["Assert on the full sorted id set, not just count and sum, to catch substitutions","Rebuild the FTS index after deletes/updates before validating","Verify the id column type/projection in the query matches the model","Watch for duplicate docids after incremental index maintenance"],"tags":["validation","fts","result-mismatch","data-integrity"],"backgroundTag":"unexpected-response-shape","analyzedSha":"8d4a589f8d13ac184700d2a8f724f27e1995be3b","analyzedAt":"2026-09-20T13:18:14.658Z","contentChangedAt":"2026-09-20T13:18:14.658Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}