block/buzz · critical · anyhow::Error

Audit DB connection failed

Error message

Audit DB connection failed: {e}

What it means

Fatal startup error: BUZZ_AUDIT_ENABLED=true, and the small dedicated audit pool (max 5 connections) failed to connect to config.database_url. The audit service keeps a hash-chain audit log in Postgres; without it the relay refuses to start because the operator has explicitly required auditing.

Solutions

  1. Verify connectivity again with the same URL: psql "$DATABASE_URL" -c 'select 1'
  2. Check Postgres max_connections headroom — the audit pool adds up to 5 more eager connections
  3. Restart once Postgres is stable
  4. If auditing is not actually required in this environment, set BUZZ_AUDIT_ENABLED=false (then audit is skipped at boot)
Defensive patterns

Strategy: retry

Validate before calling

# Reserve headroom: keep (relay pools + audit 5 + search 1) below Postgres
# max_connections; check current usage before deploy:
psql "$DATABASE_URL" -c 'show max_connections; select count(*) from pg_stat_activity;'

Try / catch

// Retry startup with backoff — the eager audit connect only fails when
// Postgres is momentarily unavailable or connections are exhausted.

Prevention

When it happens

Trigger: Postgres became unreachable between the main Db::new() connect and this one (e.g. failover, connection limit hit), or the URL allows the main pool's lazy dials but not additional eager connections (max_connections exhausted).

Common situations: Postgres connection budget consumed by other services so the extra eager pool connect fails; Postgres restarting mid-boot; audit enabled in an environment whose DB user has a hard connection cap.

Related errors


AI-assisted analysis of block/buzz@ef2aa1ae38 (2026-09-20). Data as JSON: /api/errors/371346879db0e5d8. Report an issue: GitHub.

Appendix: source

Thrown at crates/buzz-relay/src/main.rs:443

                        "Failed to bootstrap relay owner (non-fatal, membership not required): {e}"
                    );
                }
            }
        }
    }

    // NIP-33: backfill d_tag for any existing parameterized replaceable events
    // that predate the column addition. Idempotent — no-ops when fully populated.
    match db.backfill_d_tags().await {
        Ok(0) => {}
        Ok(n) => info!("Backfilled d_tag for {n} NIP-33 events"),
        Err(e) => error!("Failed to backfill d_tags: {e}"),
    }

    let (audit, audit_metrics_pool) = if config.audit_enabled {
        let audit_pool = connect_audit_pool(&db_config)
            .await
            .map_err(|e| anyhow::anyhow!("Audit DB connection failed: {e}"))?;
        info!("Audit service ready");
        let metrics_pool = audit_pool.clone();
        (Some(AuditService::new(audit_pool)), Some(metrics_pool))
    } else {
        info!("Audit logging disabled by BUZZ_AUDIT_ENABLED");
        (None, None)
    };

    let redis_pool = {
        let mut cfg = deadpool_redis::Config::from_url(&config.redis_url);
        cfg.pool = Some(deadpool_redis::PoolConfig::new(config.redis_pool_size));
        cfg.create_pool(Some(deadpool_redis::Runtime::Tokio1))
            .map_err(|e| anyhow::anyhow!("Redis pool creation failed: {e}"))?
    };
    let redis_health_pool = redis_pool.clone(); // cheap Arc clone — shared with readiness handler
    let pubsub = Arc::new(
        PubSubManager::new(&config.redis_url, redis_pool)
            .await

View on GitHub (pinned to ef2aa1ae38)