{"record":{"id":"64745f06e236b324","repo":"tursodatabase/turso","slug":"row-count-mismatch-for-case","errorCode":null,"errorMessage":"row count mismatch for {case:?}","messagePattern":"row count mismatch for (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"perf/fts/src/main.rs","lineNumber":338,"sourceCode":"    queries: usize,\n    rows: usize,\n    sum: i64,\n) -> Result<()> {\n    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 [","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/tursodatabase/turso/blob/8d4a589f8d13ac184700d2a8f724f27e1995be3b/perf/fts/src/main.rs#L320-L356","documentation":"For all non-ranked query cases (And, Or, Phrase), validate_result asserts the returned row count equals the full expected match count times the number of queries executed. ensure! at perf/fts/src/main.rs:338 fails when the engine returned a different number of rows than the seeded-data predicate predicts for that case.","triggerScenarios":"validate_result invoked with case And/Or/Phrase where rows != expected_rows * queries — the engine under- or over-returned rows for the repeated query, e.g. wrong tokenization, missing index entries, or visibility differences.","commonSituations":"FTS index not fully populated before querying; predicate in validate_result out of sync with the actual query text; MVCC/WAL snapshot hiding committed rows; a regression in the FTS match implementation.","solutions":["Verify the FTS index is fully built/populated before running validation queries","Run with --queries 1 to see whether a single query already returns the wrong count","Compare against sqlite3 FTS5 output for the same corpus (differential check)","Align the predicate in validate_result with the actual query being executed"],"exampleFix":"// before\nensure!(rows == expected_rows, \"row count mismatch\");\n// after\nensure!(rows == expected_rows * queries, \"row count mismatch for {case:?}\");","handlingStrategy":"try-catch","validationCode":"let expected: usize = (0..n).filter(|&id| predicate(id)).count() * queries;","typeGuard":null,"tryCatchPattern":"if let Err(e) = validate_result(...) {\n    if e.to_string().starts_with(\"row count mismatch\") {\n        eprintln!(\"got {rows} rows, expected {expected}; check FTS index population\");\n    }\n    return Err(e);\n}","preventionTips":["Fully populate and commit the FTS index before benchmarking","Keep the row-count model in validate_result aligned with the actual query text","Diff results against sqlite3 FTS5 for the same corpus when counts drift","Run MVCC/WAL modes with quiesced writers during validation"],"tags":["validation","fts","result-mismatch","benchmark"],"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"}