Circuit Breaker for Automatic Failover Protection
HeliosProxy protects applications from a degraded HeliosDB Lite node with a per-node circuit breaker: after a configured number of consecutive failures it opens the node's circuit and stops routing to it, sends a half-open probe after a cool-down period, and closes the circuit once enough probes succeed, so retries stop piling onto a failing backend while healthy nodes keep serving reads.
Problem Being Solved
Core Problem Statement
Traditional database connection pools and load balancers lack intelligent failure detection and isolation mechanisms, treating all connection errors identically and employing naive retry logic that amplifies problems. When a backend degrades (slow queries, high CPU, storage saturation), connection pools continue sending traffic, timeouts accumulate, retry storms begin, and the load redistributes to healthy backends in an uncontrolled manner—often overwhelming them and creating a cascading failure. By the time operations teams detect and respond, multiple systems are impacted and recovery requires manual intervention.
Concrete Examples with Code, Config & Architecture
Example 1: Embedded Configuration for Circuit Breaker
Configuration: helios_circuit_breaker.toml
[proxy]
listen_address = "0.0.0.0:5432"
protocol = "postgresql"
circuit_breaker_enabled = true
[circuit_breaker]
# Core circuit breaker behavior
enabled = true
mode = "adaptive" # "adaptive" | "static" | "predictive"
# Failure detection thresholds
failure_threshold_percentage = 5.0 # 5% error rate triggers circuit open
latency_threshold_p95_ms = 500
latency_threshold_p99_ms = 2000
connection_saturation_threshold = 0.95 # 95% pool utilization triggers circuit open
# Sliding window for metrics aggregation
metrics_window_size = "10s" # Evaluate metrics over 10-second window
metrics_bucket_size = "1s" # 1-second granularity for aggregation
minimum_requests = 20 # Minimum requests before circuit can trip
# Circuit state transition timing
open_duration = "30s" # How long to wait before testing recovery
half_open_max_concurrent = 10 # Max concurrent probe requests in HALF_OPEN
half_open_success_threshold = 80 # 80% success rate to close circuit
half_open_probe_interval = "5s" # Interval between probe attempts
# Recovery behavior
gradual_recovery_enabled = true
recovery_ramp_up_duration = "120s" # 2 minutes to fully restore traffic
recovery_step_percentage = 10 # Increase traffic by 10% per step
# Advanced: Query-level circuit breakers
query_level_breakers_enabled = true
slow_query_threshold_ms = 5000
query_pattern_detection = true # Automatically detect problematic query patterns
# Advanced: User-level circuit breakers (prevent noisy neighbor)
user_level_breakers_enabled = true
per_user_rate_limit = 1000 # Max 1000 queries/sec per user
user_isolation_on_abuse = true # Auto-isolate abusive users
[backends]
# Backend 1: Primary
[[backends.instances]]
name = "primary-1"
host = "db-primary-1.internal"
port = 5432
priority = 100
weight = 1.0
# Circuit breaker overrides for this backend
circuit_breaker_latency_threshold_p95_ms = 300 # Stricter for primary
circuit_breaker_failure_threshold = 3.0 # Lower tolerance
# Backend 2: Primary
[[backends.instances]]
name = "primary-2"
host = "db-primary-2.internal"
port = 5432
priority = 100
weight = 1.0
# Backend 3: Analytics replica (more tolerant)
[[backends.instances]]
name = "analytics-1"
host = "db-analytics-1.internal"
port = 5432
priority = 50
weight = 0.5
circuit_breaker_latency_threshold_p95_ms = 2000 # Analytics can be slower
circuit_breaker_failure_threshold = 10.0 # Higher tolerance
allowed_query_types = ["SELECT"] # Read-only
[health_checks]
# Active health check configuration
enabled = true
interval = "1s" # Check every second
timeout = "500ms"
check_query = "SELECT 1" # Simple liveness check
# Comprehensive health metrics
collect_latency_percentiles = true
collect_connection_metrics = true
collect_replication_lag = true
collect_query_queue_depth = true
[observability]
# Monitoring and alerting
metrics_enabled = true
metrics_port = 9090
log_circuit_state_changes = true
log_level = "info"
# Prometheus metrics exposed:
# - helios_circuit_breaker_state{backend} (0=closed, 1=open, 2=half_open)
# - helios_circuit_breaker_failures_total{backend}
# - helios_circuit_breaker_trip_duration_seconds{backend}
# - helios_backend_latency_p95_milliseconds{backend}
# - helios_backend_error_rate{backend}
# - helios_backend_connection_saturation{backend}
[alerts]
# Automatic alerting (optional integration)
slack_webhook_url = "${SLACK_WEBHOOK_URL}"
pagerduty_integration_key = "${PAGERDUTY_KEY}"
alert_on_circuit_open = true
alert_on_multiple_circuits_open = true
alert_on_recovery_failure = true
Example 3: Infrastructure & Container Deployment
Docker Compose with Circuit Breaker Configuration:
version: '3.9'
services:
# HeliosProxy with circuit breaker (front-end)
heliosproxy:
image: ghcr.io/heliosdatabase/hdb-heliosdb-proxy:<version>
container_name: heliosproxy
hostname: proxy.internal
environment:
HELIOS_CONFIG: /etc/helios/helios_circuit_breaker.toml
HELIOS_LOG_LEVEL: info
RUST_BACKTRACE: 1
volumes:
- ./helios_circuit_breaker.toml:/etc/helios/helios_circuit_breaker.toml:ro
- helios-proxy-logs:/var/log/helios
networks:
- helios-network
ports:
- "5432:5432" # PostgreSQL protocol
- "9090:9090" # Prometheus metrics
- "8080:8080" # Circuit breaker status API
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 3
deploy:
resources:
limits:
memory: 4G
cpus: '2.0'
reservations:
memory: 2G
cpus: '1.0'
# Backend 1: Primary database
heliosdb-backend-1:
image: registry.example.com/heliosdb-lite:<version> # licensed image from HeliosDB sales, or built from the Lite Dockerfile
container_name: heliosdb-backend-1
hostname: db-backend-1.internal
environment:
HELIOS_MODE: primary
POSTGRES_USER: helios
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ecommerce
volumes:
- helios-backend-1-data:/var/lib/postgresql/data
networks:
- helios-network
healthcheck:
test: ["CMD", "pg_isready", "-U", "helios"]
interval: 1s
timeout: 500ms
retries: 3
# Backend 2: Primary database
heliosdb-backend-2:
image: registry.example.com/heliosdb-lite:<version>
container_name: heliosdb-backend-2
hostname: db-backend-2.internal
environment:
HELIOS_MODE: primary
POSTGRES_USER: helios
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ecommerce
volumes:
- helios-backend-2-data:/var/lib/postgresql/data
networks:
- helios-network
healthcheck:
test: ["CMD", "pg_isready", "-U", "helios"]
interval: 1s
timeout: 500ms
retries: 3
# Backend 3: Primary database
heliosdb-backend-3:
image: registry.example.com/heliosdb-lite:<version>
container_name: heliosdb-backend-3
hostname: db-backend-3.internal
environment:
HELIOS_MODE: primary
POSTGRES_USER: helios
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ecommerce
volumes:
- helios-backend-3-data:/var/lib/postgresql/data
networks:
- helios-network
healthcheck:
test: ["CMD", "pg_isready", "-U", "helios"]
interval: 1s
timeout: 500ms
retries: 3
# Application: E-commerce checkout service
checkout-service:
build:
context: ./checkout-service
dockerfile: Dockerfile
container_name: checkout-service
environment:
DATABASE_URL: postgresql://helios:${DB_PASSWORD}@proxy.internal:5432/ecommerce
PORT: 3000
networks:
- helios-network
ports:
- "3000:3000"
depends_on:
heliosproxy:
condition: service_healthy
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 10s
# Chaos engineering: Simulate backend failures
chaos-monkey:
image: chaos-engineering/chaos-monkey:latest
container_name: chaos-monkey
environment:
TARGETS: heliosdb-backend-1,heliosdb-backend-2,heliosdb-backend-3
FAILURE_MODE: latency # Inject latency spikes
FAILURE_INTERVAL: 300s # Every 5 minutes
FAILURE_DURATION: 120s # 2 minutes of degradation
networks:
- helios-network
depends_on:
- heliosdb-backend-1
- heliosdb-backend-2
- heliosdb-backend-3
# Monitoring: Prometheus
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
networks:
- helios-network
ports:
- "9091:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
# Monitoring: Grafana with circuit breaker dashboard
grafana:
image: grafana/grafana:latest
container_name: grafana
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
GF_INSTALL_PLUGINS: grafana-piechart-panel,grafana-clock-panel
volumes:
- grafana-data:/var/lib/grafana
- ./grafana-dashboards/circuit-breaker.json:/etc/grafana/provisioning/dashboards/circuit-breaker.json:ro
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro
networks:
- helios-network
ports:
- "3001:3000"
depends_on:
- prometheus
networks:
helios-network:
driver: bridge
volumes:
helios-backend-1-data:
helios-backend-2-data:
helios-backend-3-data:
helios-proxy-logs:
prometheus-data:
grafana-data:
Kubernetes Deployment with Circuit Breaker:
apiVersion: v1
kind: ConfigMap
metadata:
name: heliosproxy-circuit-breaker-config
namespace: production
data:
helios_circuit_breaker.toml: |
[proxy]
listen_address = "0.0.0.0:5432"
circuit_breaker_enabled = true
[circuit_breaker]
failure_threshold_percentage = 5.0
latency_threshold_p95_ms = 500
open_duration = "30s"
gradual_recovery_enabled = true
[backends]
[[backends.instances]]
name = "backend-1"
host = "heliosdb-backend-1.production.svc.cluster.local"
port = 5432
priority = 100
[[backends.instances]]
name = "backend-2"
host = "heliosdb-backend-2.production.svc.cluster.local"
port = 5432
priority = 100
[[backends.instances]]
name = "backend-3"
host = "heliosdb-backend-3.production.svc.cluster.local"
port = 5432
priority = 100
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: heliosproxy
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: heliosproxy
template:
metadata:
labels:
app: heliosproxy
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
containers:
- name: heliosproxy
image: ghcr.io/heliosdatabase/hdb-heliosdb-proxy:<version>
ports:
- containerPort: 5432
name: postgres
- containerPort: 9090
name: metrics
- containerPort: 8080
name: status-api
volumeMounts:
- name: config
mountPath: /etc/helios/helios_circuit_breaker.toml
subPath: helios_circuit_breaker.toml
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
volumes:
- name: config
configMap:
name: heliosproxy-circuit-breaker-config
---
apiVersion: v1
kind: Service
metadata:
name: heliosproxy
namespace: production
spec:
selector:
app: heliosproxy
ports:
- name: postgres
port: 5432
targetPort: 5432
- name: metrics
port: 9090
targetPort: 9090
type: ClusterIP
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: heliosproxy-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: heliosproxy
Example 4: Microservices Integration (Go/Rust)
Go Microservice with Circuit Breaker Awareness:
package main
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
type OrderService struct {
db *sql.DB
}
type CircuitBreakerStatus struct {
Backend string `json:"backend"`
CircuitState string `json:"circuit_state"`
LatencyP95Ms float64 `json:"latency_p95_ms"`
ErrorRate float64 `json:"error_rate"`
ConnectionSaturation float64 `json:"connection_saturation"`
}
func main() {
// Connect to HeliosDB-Lite via HeliosProxy
// Circuit breaker operates transparently at proxy level
dsn := "postgres://order_service:password@heliosproxy:5432/ecommerce?sslmode=disable"
db, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer db.Close()
// Configure connection pool
db.SetMaxOpenConns(50)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)
service := &OrderService{db: db}
// Initialize Gin router
router := gin.Default()
// API endpoints
router.POST("/api/v1/orders", service.CreateOrder)
router.GET("/api/v1/orders/:id", service.GetOrder)
router.GET("/api/v1/health", service.HealthCheck)
router.GET("/api/v1/circuit-breaker/status", service.GetCircuitBreakerStatus)
log.Println("Order Service starting on :8080")
log.Println("Circuit breaker protection: active (via HeliosProxy)")
router.Run(":8080")
}
func (s *OrderService) CreateOrder(c *gin.Context) {
var request struct {
UserID string `json:"user_id"`
Items []struct {
ProductID string `json:"product_id"`
Quantity int `json:"quantity"`
Price float64 `json:"price"`
} `json:"items"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
startTime := time.Now()
// Start transaction
// If backend fails during this transaction, circuit breaker will:
// 2. Open circuit for failed backend
// 3. Route subsequent queries to healthy backend
// 4. Return retryable error to client
ctx := context.Background()
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
log.Printf("Failed to begin transaction: %v", err)
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database temporarily unavailable",
"retryable": true, // Circuit breaker makes retries safe
})
return
}
defer tx.Rollback()
// Calculate total
var totalAmount float64
for _, item := range request.Items {
totalAmount += item.Price * float64(item.Quantity)
}
// Insert order
var orderID string
err = tx.QueryRowContext(ctx, `
INSERT INTO orders (user_id, total_amount, status, created_at)
VALUES ($1, $2, 'PENDING', NOW())
RETURNING order_id
`, request.UserID, totalAmount).Scan(&orderID)
if err != nil {
log.Printf("Failed to create order: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to create order",
"retryable": true,
})
return
}
// Insert order items
for _, item := range request.Items {
_, err = tx.ExecContext(ctx, `
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES ($1, $2, $3, $4)
`, orderID, item.ProductID, item.Quantity, item.Price)
if err != nil {
log.Printf("Failed to insert order item: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to create order items",
"retryable": true,
})
return
}
}
// Commit transaction
if err = tx.Commit(); err != nil {
log.Printf("Failed to commit transaction: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to commit order",
"retryable": true,
})
return
}
elapsed := time.Since(startTime)
log.Printf("Order created: order_id=%s, amount=%.2f, items=%d, time=%dms",
orderID, totalAmount, len(request.Items), elapsed.Milliseconds())
c.JSON(http.StatusCreated, gin.H{
"order_id": orderID,
"total_amount": totalAmount,
"status": "PENDING",
"processing_time_ms": elapsed.Milliseconds(),
})
}
func (s *OrderService) GetOrder(c *gin.Context) {
orderID := c.Param("id")
var order struct {
OrderID string `json:"order_id"`
UserID string `json:"user_id"`
TotalAmount float64 `json:"total_amount"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
err := s.db.QueryRow(`
SELECT order_id, user_id, total_amount, status, created_at
FROM orders
WHERE order_id = $1
`, orderID).Scan(
&order.OrderID,
&order.UserID,
&order.TotalAmount,
&order.Status,
&order.CreatedAt,
)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
return
}
if err != nil {
log.Printf("Failed to fetch order: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to fetch order",
"retryable": true,
})
return
}
c.JSON(http.StatusOK, order)
}
func (s *OrderService) HealthCheck(c *gin.Context) {
// Check database connectivity
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err := s.db.PingContext(ctx)
if err != nil {
c.JSON(http.StatusServiceUnavailable, gin.H{
"status": "unhealthy",
"message": "Database unavailable",
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"message": "Service operational",
})
}
func (s *OrderService) GetCircuitBreakerStatus(c *gin.Context) {
// Query circuit breaker status from HeliosProxy
rows, err := s.db.Query(`
SELECT
backend_name,
circuit_state,
latency_p95_ms,
error_rate,
connection_saturation
FROM helios_proxy.circuit_breaker_status
ORDER BY backend_name
`)
if err != nil {
log.Printf("Failed to fetch circuit breaker status: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch status"})
return
}
defer rows.Close()
var statuses []CircuitBreakerStatus
for rows.Next() {
var status CircuitBreakerStatus
err := rows.Scan(
&status.Backend,
&status.CircuitState,
&status.LatencyP95Ms,
&status.ErrorRate,
&status.ConnectionSaturation,
)
if err != nil {
log.Printf("Failed to scan row: %v", err)
continue
}
statuses = append(statuses, status)
}
c.JSON(http.StatusOK, gin.H{
"backends": statuses,
"timestamp": time.Now(),
})
}
Example 5: Edge Computing & IoT Deployment
Edge Device with Circuit Breaker (Manufacturing Sensor Network):
# Circuit breaker protects against local database failures
[helios]
mode = "edge"
data_dir = "/opt/helios/data"
[proxy]
listen_address = "127.0.0.1:5432"
circuit_breaker_enabled = true
[circuit_breaker]
# Edge-optimized thresholds
failure_threshold_percentage = 10.0 # More tolerant for edge
latency_threshold_p95_ms = 1000 # Edge can be slower
open_duration = "60s" # Longer recovery window
# Edge-specific: Handle intermittent connectivity
connection_failure_threshold = 3 # Requires 3 consecutive failures
recovery_probe_timeout = "5s" # More patient with edge networks
[edge]
local_processing = true
cloud_sync_enabled = true
offline_mode_enabled = true
# Circuit breaker for cloud connectivity
cloud_circuit_breaker_enabled = true
cloud_failure_threshold = 5
cloud_open_duration = "300s" # 5 minutes before retry
[backends]
# Local embedded instance (primary)
[[backends.instances]]
name = "local-primary"
host = "localhost"
port = 5433
priority = 100
# Local standby instance
[[backends.instances]]
name = "local-standby"
host = "localhost"
port = 5434
priority = 50
# Cloud instance (optional, for analytics)
[[backends.instances]]
name = "cloud-analytics"
host = "cloud.manufacturing.example.com"
port = 5432
priority = 10
allowed_query_types = ["SELECT"] # Read-only queries to cloud
circuit_breaker_latency_threshold_p95_ms = 5000 # Very tolerant
Adoption Strategy
Phase 1: Pilot with Non-Critical Workload (Weeks 1-2)
Objective: Validate circuit breaker behavior in production with low-risk application
Steps: 1. Deploy HeliosProxy with circuit breaker enabled for staging environment 2. Configure conservative thresholds (high tolerance for latency/errors) 3. Simulate backend failures using chaos engineering tools 4. Observe circuit breaker behavior: detection speed, isolation, recovery 5. Tune thresholds based on workload characteristics
Phase 2: Production Rollout for Critical Services (Weeks 3-6)
Objective: Deploy circuit breaker protection for customer-facing applications
Steps: Monitor circuit breaker metrics alongside existing observability tools Conduct controlled failure tests during low-traffic periods 5. Create runbooks and alerting for circuit breaker events
Phase 3: Advanced Features & Optimization
Objective: Maximize value from circuit breaker; enable predictive capabilities
Steps: 1. Enable query-level and user-level circuit breakers for surgical isolation 2. Implement predictive failure detection (machine learning models) 3. Integrate circuit breaker with incident management (PagerDuty, Slack) 4. Tune gradual recovery parameters for optimal performance 5. Document cost savings and operational improvements for business case
Conclusion
The circuit breaker pattern, when implemented correctly at the database proxy layer, represents a fundamental shift from reactive incident response to proactive failure prevention.
The business case is compelling and immediate. E-commerce platforms protect revenue during peak periods. SaaS platforms reduce customer impact and SLA credits. Financial services meet regulatory resilience requirements. The common thread is risk reduction: the elimination of manual intervention from the critical path of failure recovery, and the prevention of localized failures from becoming system-wide outages.
Deep PostgreSQL protocol integration enables query-level circuit breakers and session migration integration that external proxies cannot replicate. Multi-dimensional health scoring and adaptive load redistribution prevent both false positives (unnecessary failovers) and false negatives (undetected degradation).
Performance depends on workload and hardware. Published figures are measured on representative fixtures; see the benchmarks page and reproduce them on your own workload.