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:install
This 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 = false
And 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 = 5
For dynamic timeouts, use:
config.slowpoke.timeout = lambda do |env|
request = Rack::Request.new(env)
request.path.start_with?("/admin") ? 15 : 5
end
Subscribe to timeouts with:
ActiveSupport::Notifications.subscribe "timeout.slowpoke" do |name, start, finish, id, payload|
# report timeout
end
To 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
end
Note: To access env["action_dispatch.exception"]
in development, temporarily add to config/environments/development.rb
:
config.consider_all_requests_local = false
It’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: 5s
If you set the timeout with:
Slowpoke.timeout = 5
Remove it and add to config/environments/production.rb
:
config.slowpoke.timeout = 5
If 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