Rack::Timeout enhancements for Rails
- safer service timeouts
- dynamic timeouts
- custom error pages
Add this line to your application’s Gemfile:
gem "slowpoke"And run:
rails generate slowpoke:installThis creates a public/503.html you can customize.
To try out custom error pages in development, temporarily add to config/environments/development.rb:
config.slowpoke.timeout = 1
config.consider_all_requests_local = falseAnd add a sleep call to one of your actions:
sleep(2)The custom error page should appear.
The default timeout is 15 seconds. You can change this in config/environments/production.rb with:
config.slowpoke.timeout = 5For dynamic timeouts, use:
config.slowpoke.timeout = lambda do |env|
request = Rack::Request.new(env)
request.path.start_with?("/admin") ? 15 : 5
endSubscribe to timeouts with:
ActiveSupport::Notifications.subscribe "timeout.slowpoke" do |name, start, finish, id, payload|
# report timeout
endTo learn more, see the Rack::Timeout documentation.
Rack::Timeout can raise an exception at any point in the code, which can leave your app in an unclean state. The safest way to recover from a request timeout is to spawn a new process. This is the default behavior for Slowpoke.
For threaded servers like Puma, this means killing all threads when any one of them times out. This can have a significant impact on performance.
You can customize this behavior with:
Slowpoke.on_timeout do |env|
next if Rails.env.development? || Rails.env.test?
exception = env["action_dispatch.exception"]
if exception && exception.backtrace.first.include?("/active_record/")
Slowpoke.kill
end
endNote: To access env["action_dispatch.exception"] in development, temporarily add to config/environments/development.rb:
config.consider_all_requests_local = falseIt’s a good idea to set a statement timeout and a connect timeout. For Postgres, your config/database.yml should include something like:
production:
connect_timeout: 3 # sec
variables:
statement_timeout: 5sIf you set the timeout with:
Slowpoke.timeout = 5Remove it and add to config/environments/production.rb:
config.slowpoke.timeout = 5If you use migration timeouts, check out this guide for how to configure them directly in config/database.yml.
0.1.0 removes database timeouts, since Rails supports them by default. To restore the previous behavior, use:
production:
variables:
statement_timeout: <%= Slowpoke.timeout * 1000 %>View the changelog
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development:
git clone https://github.com/ankane/slowpoke.git
cd slowpoke
bundle install
bundle exec rake test