Class: Rack::NackMode
- Inherits:
-
Object
- Object
- Rack::NackMode
- Defined in:
- lib/rack/nack_mode.rb
Overview
Middleware that communicates impending shutdown to a load balancer via NACKing (negative acking) health checks. Your app needs to inform the middleware when it wants to shut down, and the middleware will call back when it’s safe to do so.
Responds to health checks on /admin (configurable via :path option).
Basic usage:
class MyApp < Sinatra::Base
use Rack::NackMode do |health_check|
# store the middleware instance for calling #shutdown below
@health_check = health_check
end
class << self
def shutdown
if @health_check
@health_check.shutdown { exit 0 }
else
exit 0
end
end
end
end
N.B. because Rack waits to initialise middleware until it receives an HTTP request, it’s possible to shut down before the middleware is initialised. That’s unlikely to be a problem, because having not received any HTTP requests, we’ve obviously not received any *health check* requests either, meaning the load balancer should already believe we’re down: so it should be safe to shutdown immediately, as in the above example.
Defined Under Namespace
Modules: Timer
Constant Summary collapse
- DEFAULT_NACKS_BEFORE_SHUTDOWN =
Default number of health checks we NACK before shutting down. This matches e.g. haproxy’s default for how many failed checks it needs before marking a backend as down.
3
- DEFAULT_HEALTHCHECK_TIMEOUT =
Default time (in seconds) during shutdown to wait for the first healthcheck request before concluding that the healthcheck is missing or misconfigured, and shutting down anyway.
15
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, options = {}) {|_self| ... } ⇒ NackMode
constructor
A new instance of NackMode.
- #shutdown(&block) ⇒ Object
Constructor Details
#initialize(app, options = {}) {|_self| ... } ⇒ NackMode
Returns a new instance of NackMode.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/rack/nack_mode.rb', line 46 def initialize(app, = {}) @app = app .assert_valid_keys :path, :healthy_if, :sick_if, :nacks_before_shutdown, :healthcheck_timeout, :logger @path = [:path] || '/admin' @health_callback = if [:healthy_if] && [:sick_if] raise ArgumentError, 'Please specify either :healthy_if or :sick_if, not both' elsif healthy_if = [:healthy_if] healthy_if elsif sick_if = [:sick_if] lambda { !sick_if.call } else lambda { true } end @nacks_before_shutdown = [:nacks_before_shutdown] || DEFAULT_NACKS_BEFORE_SHUTDOWN raise ArgumentError, ":nacks_before_shutdown must be at least 1" unless @nacks_before_shutdown >= 1 @healthcheck_timeout = [:healthcheck_timeout] || DEFAULT_HEALTHCHECK_TIMEOUT @logger = [:logger] yield self if block_given? end |
Instance Method Details
#call(env) ⇒ Object
73 74 75 76 77 78 79 80 |
# File 'lib/rack/nack_mode.rb', line 73 def call(env) if health_check?(env) clear_healthcheck_timeout health_check_response(env) else @app.call(env) end end |
#shutdown(&block) ⇒ Object
82 83 84 85 86 87 88 89 |
# File 'lib/rack/nack_mode.rb', line 82 def shutdown(&block) info "Shutting down after NACKing #@nacks_before_shutdown health checks" @shutdown_callback = block install_healthcheck_timeout { do_shutdown } nil end |