Skip to content

Database Circuit Breaker — Complete Implementation Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Database Circuit Breaker. We cover key concepts, practical examples, and best practices to help you master this topic.

Database circuit breaker protects application databases by monitoring query failures and opening the circuit when the database becomes unresponsive, preventing Connection Pool exhaustion.

What You'll Learn

By the end of this tutorial, you will implement circuit breakers around database queries, integrate with connection pools, and provide fallback read queries.

Real-World Use

DodaTech's services use database circuit breakers that open after 5 query timeouts, preventing connection pool exhaustion and allowing the database time to recover.

Database Circuit Breaker Implementation

const { Pool } = require("pg");

class DatabaseCircuitBreaker {
  constructor(options = {}) {
    this.threshold = options.threshold || 5;
    this.resetTimeout = options.resetTimeout || 30000;
    this.timeout = options.timeout || 10000;
    this.state = "closed";
    this.failureCount = 0;
    this.nextAttempt = Date.now();
  }

  async query(pool, text, params) {
    if (this.state === "open") {
      if (Date.now() < this.nextAttempt) {
        throw new Error("Database circuit open");
      }
      this.state = "half-open";
    }

    try {
      const result = await Promise.race([
        pool.query(text, params),
        new Promise((_, reject) =>
          setTimeout(() => reject(new Error("Query timeout")), this.timeout)
        )
      ]);

      this.onSuccess();
      return result;
    } catch (err) {
      this.onFailure(err);
      throw err;
    }
  }

  onSuccess() {
    this.failureCount = 0;
    if (this.state === "half-open") this.state = "closed";
  }

  onFailure(err) {
    const isTransient = err.message.includes("timeout") ||
      err.code === "08006" || err.code === "08003";

    if (!isTransient) return;

    this.failureCount++;
    if (this.state === "half-open" || this.failureCount >= this.threshold) {
      this.state = "open";
      this.nextAttempt = Date.now() + this.resetTimeout;
    }
  }
}

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const dbCircuitBreaker = new DatabaseCircuitBreaker();

async function queryWithBreaker(text, params) {
  try {
    return await dbCircuitBreaker.query(pool, text, params);
  } catch (err) {
    if (err.message === "Database circuit open") {
      return fallbackQuery(text, params);
    }
    throw err;
  }
}

async function fallbackQuery(text, params) {
  if (text.trim().toUpperCase().startsWith("SELECT")) {
    return { rows: [], fallback: true };
  }
  throw new Error("Database unavailable");
}

Connection Pool Protection

Circuit breakers protect the connection pool by rejecting queries early, preventing connection acquisition attempts that would exhaust the pool.

class PoolProtectingBreaker {
  constructor(pool, options = {}) {
    this.pool = pool;
    this.state = "closed";
    this.failures = 0;
    this.threshold = options.threshold || 5;
    this.resetTimeout = options.resetTimeout || 30000;
  }

  async query(text, params) {
    if (this.state === "open") {
      if (Date.now() < this.nextAttempt) {
        return { error: "Circuit open", rows: [] };
      }
      this.state = "half-open";
    }

    const client = await this.pool.connect();
    try {
      const result = await client.query(text, params);
      this.onSuccess();
      return result;
    } catch (err) {
      this.onFailure(err);
      throw err;
    } finally {
      client.release();
    }
  }

  onSuccess() {
    this.failures = 0;
    if (this.state === "half-open") this.state = "closed";
  }

  onFailure(err) {
    if (err.code === "08006" || err.code === "08003" || err.message.includes("timeout")) {
      this.failures++;
      if (this.state === "half-open" || this.failures >= this.threshold) {
        this.state = "open";
        this.nextAttempt = Date.now() + this.resetTimeout;
      }
    }
  }

  async healthCheck() {
    try {
      await this.pool.query("SELECT 1");
      return true;
    } catch {
      return false;
    }
  }
}

Common Mistakes

  1. Counting non-transient errors -- Syntax errors and constraint violations should not open the circuit. Only connection and timeout errors.

  2. Not releasing connections on error -- When a query fails, the client must be released back to the pool. Use try/finally.

  3. Opening circuit on slow queries -- Slow queries need optimization, not circuit breaking. Set separate thresholds for timeouts.

  4. No fallback for read queries -- Read queries can often return cached or empty data. Write queries may need to fail.

  5. Not monitoring database circuit state -- An open database circuit is a critical event. Always alert when it happens.

Practice Questions

  1. Which database errors should trigger the circuit breaker? Connection failures (08006, 08003) and query timeouts. Syntax errors and constraint violations should not.

  2. How does the circuit breaker protect the connection pool? By rejecting queries early without acquiring a connection from the pool, preserving connections for healthy queries.

  3. What fallback should read queries use during an open circuit? Cached data, an empty result set, or a read replica if available.

  4. Challenge: Implement a circuit breaker that uses a read replica as fallback.

class ReadReplicaBreaker {
  async query(text, params) {
    try {
      return await primary.query(text, params);
    } catch (err) {
      if (circuit.state === "open") {
        return await replica.query(text, params);
      }
      throw err;
    }
  }
}

FAQ

Should I use a circuit breaker for every database query?

No. Use it for database connections and transaction boundaries. Individual query errors should use retry instead.

How does the circuit breaker interact with connection pooling?

The circuit breaker should check state before acquiring a connection, preserving pool capacity for healthy paths.

Can I use circuit breakers with Redis?

Yes. Redis connections can also be protected by circuit breakers, especially for caching layers.

What happens to in-flight queries when the circuit opens?

They complete normally. Only new queries are affected. This prevents abrupt termination of valid operations.

How do I test database circuit breakers?

Use a test database that can be made unreachable. Verify queries fail fast when the circuit is open.

Mini Project

Build a database circuit breaker with read replica fallback, connection pool protection, and health checking.

class DatabaseProtector {
  constructor(primary, replica = null) {
    this.primary = primary;
    this.replica = replica;
    this.state = "closed";
    this.failures = 0;
    this.threshold = 5;
    this.resetTimeout = 30000;
    this.nextAttempt = Date.now();
  }

  async query(text, params) {
    if (this.state === "open") {
      if (Date.now() < this.nextAttempt) {
        return this.fallback(text, params);
      }
      this.state = "half-open";
    }

    try {
      const result = await this.primary.query(text, params);
      this.failures = 0;
      if (this.state === "half-open") this.state = "closed";
      return result;
    } catch (err) {
      this.failures++;
      if (this.state === "half-open" || this.failures >= this.threshold) {
        this.state = "open";
        this.nextAttempt = Date.now() + this.resetTimeout;
      }

      if (this.isRetryable(err) && this.replica) {
        return this.replica.query(text, params);
      }
      throw err;
    }
  }

  isRetryable(err) {
    return err.code === "08006" || err.code === "08003" || err.message.includes("timeout");
  }

  fallback(text, params) {
    if (this.replica && text.trim().toUpperCase().startsWith("SELECT")) {
      return this.replica.query(text, params);
    }
    return { rows: [] };
  }

  async healthCheck() {
    try {
      await this.primary.query("SELECT 1");
      return "healthy";
    } catch {
      return "unhealthy";
    }
  }
}

What's Next

Now that you understand database circuit breakers, explore circuit breakers in microservice architectures. Then learn about advanced circuit breaker patterns.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro