Database circuit breaker — prevent cascading failures when DB is down.
1class CircuitBreaker {2 constructor(fn, options = {}) {3 this.fn = fn;4 this.state = "CLOSED";5 this.failures = 0;6 this.successes = 0;7 this.threshold = options.threshold || 5;8 this.timeout = options.timeout || 30000;9 this.halfOpenAfter = options.halfOpenAfter || 5000;10 this.nextAttempt = Date.now();11 }1213 async call(...args) {14 if (this.state === "OPEN") {15 if (Date.now() < this.nextAttempt) {16 throw new Error("Circuit breaker is OPEN");17 }18 this.state = "HALF_OPEN";19 }2021 try {22 const result = await Promise.race([23 this.fn(...args),24 new Promise((_, reject) =>25 setTimeout(() => reject(new Error("Timeout")), this.timeout)26 ),27 ]);28 this.onSuccess();29 return result;30 } catch (err) {31 this.onFailure();32 throw err;33 }34 }3536 onSuccess() {37 this.failures = 0;38 this.state = "CLOSED";39 }4041 onFailure() {42 this.failures++;43 if (this.failures >= this.threshold) {44 this.state = "OPEN";45 this.nextAttempt = Date.now() + this.halfOpenAfter;46 }47 }48}4950// Usage51const dbBreaker = new CircuitBreaker(52 (query) => db.query(query),53 { threshold: 5, timeout: 5000 }54);5556try {57 const result = await dbBreaker.call("SELECT * FROM users");58} catch (err) {59 // Return cached data or default60}