{"record":{"id":"39a52ac8c3f3c0ab","repo":"block/buzz","slug":"search-db-connection-failed-e","errorCode":null,"errorMessage":"Search DB connection failed: {e}","messagePattern":"Search DB connection failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/buzz-relay/src/main.rs","lineNumber":498,"sourceCode":"    // sockets (via the consumer loop below), enforcing live disconnect fan-out.\n    let pubsub_for_conn_ctrl = Arc::clone(&pubsub);\n    tokio::spawn(async move { pubsub_for_conn_ctrl.run_conn_control_subscriber().await });\n\n    let auth = AuthService::new(config.auth.clone());\n\n    // Postgres FTS: the searchable row IS the persisted event row (its\n    // `tsvector` column is populated by the `insert_event` write), so there is\n    // no external collection to provision — the search service just queries the\n    // same Postgres over its own pool. Search is lag-tolerant, so it prefers\n    // the read replica when one is configured.\n    let search_db_url = config\n        .read_database_url\n        .as_deref()\n        .unwrap_or(&config.database_url);\n    let search_pool = sqlx::postgres::PgPoolOptions::new()\n        .connect(search_db_url)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Search DB connection failed: {e}\"))?;\n    let search_metrics_pool = search_pool.clone();\n    let search = SearchService::new(search_pool);\n    info!(\n        replica = config.read_database_url.is_some(),\n        \"Search service ready (Postgres FTS)\"\n    );\n\n    let workflow_config = buzz_workflow::WorkflowConfig::default();\n    let workflow_engine = Arc::new(WorkflowEngine::new(db.clone(), workflow_config));\n\n    config\n        .media\n        .validate()\n        .map_err(|e| anyhow::anyhow!(\"invalid media config: {e}\"))?;\n    let media_storage = buzz_media::MediaStorage::new(&config.media)\n        .map_err(|e| anyhow::anyhow!(\"failed to initialize media storage: {e}\"))?;\n    info!(\"Media storage connected\");\n","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/block/buzz/blob/ef2aa1ae38fadcc0bc22b8bf6ed96b35933146be/crates/buzz-relay/src/main.rs#L480-L516","documentation":"Fatal startup error: the search service's dedicated Postgres pool (buzz-search, Postgres FTS) failed its eager connect. The search pool prefers BUZZ_READ_DATABASE_URL when set and falls back to DATABASE_URL — so this can fire even though the main DB pool connected, because the main pool's read side is lazy (min_connections=0) while this connect is immediate.","triggerScenarios":"BUZZ_READ_DATABASE_URL points at a read replica that is unreachable, firewalled, has wrong credentials, or does not exist; or the fallback DATABASE_URL target stopped accepting connections between the main pool connect and here.","commonSituations":"Stale replica URL after infrastructure changes; replica credentials rotated separately from the writer; security groups allowing writer but not replica traffic; pointing BUZZ_READ_DATABASE_URL at a not-yet-provisioned replica.","solutions":["Test the exact URL: psql \"$BUZZ_READ_DATABASE_URL\" -c 'select 1' (or DATABASE_URL if unset)","Fix or update the replica URL / credentials / network rules","If no healthy replica exists, unset BUZZ_READ_DATABASE_URL so search uses the writer","Restart the relay once the target accepts connections"],"exampleFix":"# before\nexport BUZZ_READ_DATABASE_URL=postgres://relay@replica.internal:5432/buzz  # stale\n\n# after\nexport BUZZ_READ_DATABASE_URL=postgres://relay@replica-2.internal:5432/buzz\n# or, to run search off the writer:\n# unset BUZZ_READ_DATABASE_URL","handlingStrategy":"validation","validationCode":"# Only configure the replica when it is actually reachable:\nif [ -n \"$BUZZ_READ_DATABASE_URL\" ]; then\n  psql \"$BUZZ_READ_DATABASE_URL\" -c 'select 1' || unset BUZZ_READ_DATABASE_URL\nfi","typeGuard":null,"tryCatchPattern":"// On boot failure matching \"Search DB connection failed\", retry only after\n// the replica accepts connections; otherwise drop BUZZ_READ_DATABASE_URL and\n// redeploy so search uses the writer.","preventionTips":["Healthcheck the read replica URL in CI before configuring it","Remember the search pool connects eagerly even though the main read pool is lazy — an unreachable replica still kills boot"],"tags":["postgres","search","read-replica","connection","startup","buzz-relay"],"backgroundTag":"database-connection-failed","analyzedSha":"ef2aa1ae38fadcc0bc22b8bf6ed96b35933146be","analyzedAt":"2026-09-20T04:38:17.397Z","contentChangedAt":"2026-09-20T04:38:17.397Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}