philiprehberger-circuit_board
Health check framework with dependency aggregation and Rack endpoint
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-circuit_board"Or install directly:
gem install philiprehberger-circuit_boardUsage
require "philiprehberger/circuit_board"
Philiprehberger::CircuitBoard.configure do
check(:database) { ActiveRecord::Base.connection.active? }
check(:redis, timeout: 2) { Redis.current.ping == 'PONG' }
end
status = Philiprehberger::CircuitBoard.check
status.healthy? # => true
status.degraded? # => false
status.to_h # => { status: 'healthy', checks: [...] }Single Check
result = Philiprehberger::CircuitBoard.check_one(:database)
# => { name: :database, healthy: true, duration: 0.0012 }Raises Philiprehberger::CircuitBoard::Error if the named check does not exist.
Rack Middleware
# config.ru
require "philiprehberger/circuit_board"
Philiprehberger::CircuitBoard.configure do
check(:database) { DB.connected? }
end
use Philiprehberger::CircuitBoard::Rack::Middleware
run MyAppExposes three endpoints:
-
GET /health- full health check with all dependency results -
GET /health/ready- readiness probe (all checks must pass) -
GET /health/live- liveness probe (always returns 200)
Critical and Non-Critical Checks
Philiprehberger::CircuitBoard.configure do
check(:database) { ActiveRecord::Base.connection.active? } # critical (default)
check(:cache, critical: false) { Redis.current.ping == 'PONG' } # non-critical
end
status = Philiprehberger::CircuitBoard.check
# If only cache fails: status is "degraded"
# If database fails: status is "unhealthy"Parallel Execution
Run all checks concurrently for lower latency:
status = Philiprehberger::CircuitBoard.check(parallel: true)
status.healthy? # => true
status.duration # => wall-clock time of the slowest check
status.unhealthy_checks # => []
status.to_json # => '{"status":"healthy","checks":[...]}'Caching Expensive Checks
Philiprehberger::CircuitBoard.configure do
# Cache successful database probe for 30 seconds
check(:database, cache: 30) { ActiveRecord::Base.connection.active? }
end
# First call hits the DB; subsequent calls within 30s return the cached result.
# Failed checks are never cached — failures re-run on every probe.Check Timeouts
Philiprehberger::CircuitBoard.configure do
check(:fast_service, timeout: 1) { FastService.ping }
check(:slow_service, timeout: 10) { SlowService.ping }
endState Change Callback
require "philiprehberger/circuit_board"
Philiprehberger::CircuitBoard.configure do |c|
c.check("database") { ActiveRecord::Base.connection.active? }
c.on_change do |from, to|
puts "Health changed: #{from} -> #{to}"
end
endAPI
| Method | Description |
|---|---|
.configure { ... } |
Define health checks using the DSL (check(name, timeout:, critical:, cache:)) |
.check(parallel: false) |
Run all checks and return a Status; parallel: true runs concurrently |
.check_one(name) |
Run a single named check and return its result hash |
.reset! |
Remove all configured checks |
on_change(&block) |
Callback invoked on health status transitions |
Status#healthy? |
Whether all checks passed |
Status#degraded? |
Whether some checks passed but not all |
Status#to_h |
Hash with :status and :checks keys |
Status#results |
Array of individual check results |
Status#unhealthy_checks |
Failed check results |
Status#healthy_checks |
Passed check results |
Status#duration |
Wall-clock duration of slowest check |
Status#to_json |
JSON string of to_h
|
Rack::Middleware.new(app) |
Rack middleware for health endpoints |
Development
bundle install
bundle exec rspec
bundle exec rubocopSupport
If you find this project useful: