Skip to content

Instantly share code, notes, and snippets.

@hden
Created July 27, 2023 03:34
Show Gist options
  • Save hden/f361ec9d5133d21c58187e08a0264251 to your computer and use it in GitHub Desktop.
Save hden/f361ec9d5133d21c58187e08a0264251 to your computer and use it in GitHub Desktop.
BayesianCircuitBreaker
class BayesianCircuitBreaker {
constructor(windowSize, thresholdHealthy, thresholdStruggling, credibleLevel, gcInterval = 100) {
this.windowSize = windowSize
this.thresholdHealthy = thresholdHealthy
this.thresholdStruggling = thresholdStruggling
this.credibleLevel = credibleLevel
this.successCount = 0
this.failureCount = 0
this.samples = []
this.gcTimer = setInterval((self) => {
const t = Date.now()
// Remove events that are outside the window
while (self.samples.length > 0) {
const oldestEventTime = self.samples[0].timestamp
if (t - oldestEventTime <= gcInterval) {
break
}
// Remove the oldest event
self.shift()
}
}, gcInterval, this)
}
// Method to record the result of a request (success: true, failure: false)
push(success) {
if (success) {
this.successCount++
} else {
this.failureCount++
}
this.samples.push(success)
if (this.samples.length > this.windowSize) {
// Remove the oldest event
this.shift()
}
}
shift() {
const removedEvent = this.samples.shift()
if (removedEvent) {
this.successCount--
} else {
this.failureCount--
}
return removedEvent
}
// Method to estimate the Highest Posterior Density (HPD) interval of the posteriors (Beta distribution)
estimateHPDInterval(alpha, beta) {
alpha += 5
beta += 1
// Calculate the quantiles for the Beta distribution using percentiles approach
const lowerQuantile = jStat.beta.inv(this.credibleLevel / 2, alpha, beta)
const upperQuantile = jStat.beta.inv(1 - this.credibleLevel / 2, alpha, beta)
return [lowerQuantile, upperQuantile]
}
// Method to get the current circuit status ('open', 'closed', or 'half-open')
getStatus() {
const [lowerBoundHealthy, upperBoundHealthy] = this.estimateHPDInterval(this.successCount, this.failureCount)
const [lowerBoundStruggling, upperBoundStruggling] = this.estimateHPDInterval(this.failureCount, this.successCount)
if (upperBoundHealthy < this.thresholdHealthy && upperBoundStruggling < this.thresholdStruggling) {
return 'half-open' // Both posteriors are below the HPD interval, HALF-OPEN state
} else if (lowerBoundHealthy >= this.thresholdHealthy) {
return 'closed' // Server is healthy, CLOSE the circuit
} else if (upperBoundStruggling >= this.thresholdStruggling) {
return 'open' // Server is struggling, OPEN the circuit
} else {
return 'half-open' // Otherwise, HALF-OPEN the circuit to explore more data
}
}
}
// Example usage:
const cb = new BayesianCircuitBreaker(100, 0.85, 0.4, 0.95)
const range = (n) => Array.from(Array(n), (_, k) => k + 1)
console.log('Circuit Status 0:', cb.getStatus())
// Simulate some requests and record results (~1% error rate)
range(10).forEach((n) => {
const event = Math.random() >= 0.01
cb.push(event)
// Get the current circuit status
console.log(`Circuit Status ${n} (${event}): ${cb.getStatus()}`)
})
range(10).forEach((n) => {
const event = true
cb.push(event)
// Get the current circuit status
console.log(`Circuit Status ${10 + n} (${event}): ${cb.getStatus()}`)
})
range(10).forEach((n) => {
const event = false
cb.push(event)
// Get the current circuit status
console.log(`Circuit Status ${20 + n} (${event}): ${cb.getStatus()}`)
})
setTimeout(() => {
// Get the current circuit status
console.log(`Circuit Status (1 sec later):`, cb.getStatus())
}, 1000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment