{"record":{"id":"1a62622cdbb16b69","repo":"block/buzz","slug":"count-entry-too-old","errorCode":null,"errorMessage":"count, entry too old","messagePattern":"count, entry too old","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-db/src/runtime/tests.rs","lineNumber":1649,"sourceCode":"    db.set_replica_read_max_age_for_tests(Some(std::time::Duration::from_secs(5)));\n    let n = db\n        .count_events_routed(\"test_count\", &q)\n        .await\n        .expect(\"count, gate on\");\n    assert_eq!(n, 1, \"budget set must count on the replica (bounded)\");\n\n    // Entry older than the budget ⇒ bounded fails ⇒ writer. Covered\n    // would still hold here (upper <= wall) — proving count never\n    // consults it.\n    db.fence().close();\n    db.fence().force_open_for_tests_at(\n        chrono::Utc::now(),\n        std::time::Instant::now() - std::time::Duration::from_secs(10),\n    );\n    let n = db\n        .count_events_routed(\"test_count\", &q)\n        .await\n        .expect(\"count, entry too old\");\n    assert_eq!(\n        n, 2,\n        \"an over-budget entry must fail the count closed to the writer, \\\n             even when the covered arm would admit the shape\"\n    );\n\n    drop_scratch_db(&admin, replica, &rname).await;\n    drop_scratch_db(&admin, writer, &wname).await;\n}\n\n/// Routed relay-membership check: budget unset ⇒ writer; budget set +\n/// fresh proved entry ⇒ replica (bounded arm); over-budget entry ⇒\n/// writer. Divergent membership rows prove which pool answered.\n#[tokio::test]\n#[ignore = \"requires Postgres\"]\nasync fn is_relay_member_is_bounded_routed_and_fails_closed() {\n    let admin = PgPool::connect(&admin_url().await)\n        .await","sourceCodeStart":1631,"sourceCodeEnd":1667,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-db/src/runtime/tests.rs#L1631-L1667","documentation":"This is `.expect(\"count, entry too old\")` on `count_events_routed` after the fence entry was force-opened with an Instant 10s in the past, making it older than the 5s budget; routing must fail closed to the writer. The expect panics if that writer-served count itself returns Err. It exists to prove the over-budget fallback path still executes a valid query.","triggerScenarios":"Calling `count_events_routed` after `fence().close()` + `force_open_for_tests_at(now, now-10s)` when the writer pool errors (connection lost, schema missing), or when the fence/ring state manipulation itself leaves routing in an error state instead of failing closed.","commonSituations":"Writer pool idle-timed out during the earlier replica arms of the test; scratch writer DB dropped; a routing-code regression where over-budget entries produce Err instead of falling back to the writer (which is exactly what this test is designed to catch).","solutions":["Check the writer pool is still alive after the earlier arms; increase idle timeout or acquire timeout on the pool.","Verify the routing code fails closed (returns writer result) rather than Err for over-budget fence entries.","Print the inner sqlx error to distinguish a routing bug from an infra failure.","Ensure `force_open_for_tests_at` was called with a past Instant as in the test, not the future.","Re-create scratch DBs fresh; leftover state from prior runs can corrupt seed assumptions."],"exampleFix":"// before\nlet n = db.count_events_routed(\"test_count\", &q).await.expect(\"count, entry too old\");\n// after\nlet n = db.count_events_routed(\"test_count\", &q).await\n    .unwrap_or_else(|e| panic!(\"count (entry too old / fail-closed writer) failed: {e}\"));","handlingStrategy":"fallback","validationCode":"// Ensure fence state matches the intended over-budget scenario\ndb.fence().close();\ndb.fence().force_open_for_tests_at(chrono::Utc::now(),\n    std::time::Instant::now() - std::time::Duration::from_secs(10));","typeGuard":"fn entry_is_stale(opened_at: std::time::Instant, budget: std::time::Duration) -> bool {\n    opened_at.elapsed() > budget\n}","tryCatchPattern":"let n = db.count_events_routed(\"test_count\", &q).await\n    .unwrap_or_else(|e| panic!(\"fail-closed count failed: {e}\"));","preventionTips":["Remember routing should fail CLOSED (writer), not Err — an Err here is a routing bug","Keep writer pool alive across all test arms","Pass a past Instant to force_open_for_tests_at as intended","Recreate scratch DBs fresh for each run"],"tags":["postgres","database","test-panic","fail-closed"],"backgroundTag":"postgres-query-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}