A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts below:

ankane/the-ultimate-guide-to-ruby-timeouts: Timeouts for popular Ruby gems

The Ultimate Guide to Ruby Timeouts

An unresponsive service can be worse than a down one. It can tie up your entire system if not handled properly. All network requests should have a timeout.

Here’s how to add timeouts for popular Ruby gems. All have been tested. You should avoid Ruby’s Timeout module. The default is no timeout, unless otherwise specified. Enjoy!

Also available for Python, Node, Go, PHP, and Rust

For many apps, the single most important thing to do (if you use a relational database)

Standard Library

Data Stores

HTTP Clients

Commands

Web Servers

Rack Middleware

Solvers

Distributed Locks

3rd Party Services

Other

Prevent single queries from taking up all of your database’s resources.

If you use Rails, add to your config/database.yml

production:
  variables:
    statement_timeout: 5s # or ms, min, etc

or set it on your database role

ALTER ROLE myuser SET statement_timeout = '5s';

Test with

To set for a single transaction, use

BEGIN;
SET LOCAL statement_timeout = '5s';
...
COMMIT;

For migrations, you likely want to set a longer statement timeout. You can do this with

production:
  variables:
    statement_timeout: <%= ENV["STATEMENT_TIMEOUT"] || "5s" %>

And use

STATEMENT_TIMEOUT=90s rails db:migrate

Note: Only applies to read-only SELECT statements (more info)

If you use Rails, add to your config/database.yml

production:
  variables:
    max_execution_time: 5000 # ms

or set it directly on each connection

SET SESSION max_execution_time = 5000;

Test with

SELECT 1 FROM information_schema.tables WHERE sleep(6);

To set for a single statement, use an optimizer hint

SELECT /*+ MAX_EXECUTION_TIME(5000) */ ...

If you use Rails, add to your config/database.yml

production:
  variables:
    max_statement_time: 5 # sec

or set it directly on each connection

SET SESSION max_statement_time = 5;

Test with

SELECT 1 FROM information_schema.tables WHERE sleep(6);

To set for a single statement, use

SET STATEMENT max_statement_time=5 FOR
  SELECT ...

For migrations, you likely want to set a longer statement timeout. You can do this with

production:
  variables:
    max_statement_time: <%= ENV['MAX_STATEMENT_TIME'] || 5 %>

And use

MAX_STATEMENT_TIME=90 rails db:migrate

Official docs

Note: Requires Ruby 3.2+

Raises IO::TimeoutError

Net::FTP.new(host, open_timeout: 1, read_timeout: 1)

Raises

Net::HTTP.start(host, port, open_timeout: 1, read_timeout: 1, write_timeout: 1) do
  # ...
end

or

http = Net::HTTP.new(host, port)
http.open_timeout = 1
http.read_timeout = 1
http.write_timeout = 1

Raises

Default: 60s connect timeout, 60s read timeout, 60s write timeout

Read timeouts are retried once automatically for idempotent methods like GET. You can set the max number of retries with http.max_retries = 1.

Net::IMAP.new(host, open_timeout: 1)

Read timeout is not configurable at the moment

Raises Net::OpenTimeout on connect timeout

pop = Net::POP.new(host)
pop.open_timeout = 1
pop.read_timeout = 1

Raises

smtp = Net::SMTP.new(host, 25)
smtp.open_timeout = 1
smtp.read_timeout = 1

Raises

URI.parse(url).open(open_timeout: 1, read_timeout: 1)

Raises

Note: Requires Ruby 3.2+

Regexp.timeout = 1
# or
Regexp.new(regexp, timeout: 1)

Raises Regexp::TimeoutError

Bunny.new(connection_timeout: 1, read_timeout: 1, ...)

Raises

Cassandra.cluster(connect_timeout: 1, timeout: 1)

Default: 10s connect timeout, 12s read timeout

Raises

ConnectionPool.new(timeout: 1) { ... }

Raises ConnectionPool::TimeoutError

CouchRest.new(url, open_timeout: 1, read_timeout: 1, timeout: 1)

Raises

Dalli::Client.new(host, socket_timeout: 1, ...)

Default: 1s

Raises Dalli::RingError

Drill.new(url: url, open_timeout: 1, read_timeout: 1)

Default: 3s connect timeout, no read timeout

Raises

Elasticsearch::Client.new(transport_options: {request: {timeout: 1}}, ...)

Raises Elastic::Transport::Transport::Error

conn = Hiredis::Connection.new
conn.timeout = 1_000_000 # microseconds

Raises

Immudb::Client.new(host, timeout: 1)

Raises GRPC::DeadlineExceeded

InfluxDB::Client.new(open_timeout: 1, read_timeout: 1)

Raises InfluxDB::ConnectionError

InfluxDB2::Client.new(url, token, open_timeout: 1, read_timeout: 1)

Raises InfluxDB2::InfluxError

Meilisearch::Client.new(url, api_key, timeout: 1)

Raises Meilisearch::TimeoutError

Mongo::Client.new([host], connect_timeout: 1, socket_timeout: 1, server_selection_timeout: 1, ...)

Raises Mongo::Error::NoServerAvailable

production:
  clients:
    default:
      options:
        connect_timeout: 1
        socket_timeout: 1
        server_selection_timeout: 1

Raises Mongo::Error::NoServerAvailable

Mysql2::Client.new(connect_timeout: 1, read_timeout: 1, write_timeout: 1, ...)

Raises Mysql2::Error

config.neo4j.session.options = {
  faraday_configurator: lambda do |faraday|
    faraday.adapter :typhoeus
    faraday.options[:open_timeout] = 5
    faraday.options[:timeout] = 65
  end
}

Raises Faraday::TimeoutError

PG.connect(connect_timeout: 1, ...)

Raises PG::ConnectionBad

Presto::Client.new(http_open_timeout: 1, http_timeout: 1)

Raises

Redis.new(connect_timeout: 1, timeout: 1, ...)

Default: 1s after 5.0, 5s before

Raises

RedisClient.config(timeout: 1, ...)
# or
RedisClient.config(connect_timeout: 1, read_timeout: 1, write_timeout: 1, ...)

Default: 1s

Raises RedisClient::CannotConnectError

client = Riddle::Client.new
client.timeout = 1

Raises Riddle::ResponseError

RSolr.connect(open_timeout: 1, timeout: 1)

Raises

Not configurable at the moment

Default: 10s connect timeout, no read timeout

Kafka.new(connect_timeout: 1, socket_timeout: 1)

Raises Kafka::ConnectionError

Searchkick.timeout = 1
Searchkick.search_timeout = 1

Default: 10s

Raises same exceptions as elasticsearch

Trino::Client.new(http_open_timeout: 1, http_timeout: 1)

Raises

Typesense::Client.new(connection_timeout_seconds: 1)

Raises Typesense::Error::TimeoutError

curl = Curl::Easy.new(url)
curl.connect_timeout = 1
curl.timeout = 1
curl.perform

Raises Curl::Err::TimeoutError

Down::NetHttp.download(connect_url, open_timeout: 1, read_timeout: 1)

Raises Down::TimeoutError

EventMachine.run do
  http = EventMachine::HttpRequest.new(url, connect_timeout: 1, inactivity_timeout: 1).get
  http.errback  { http.error }
end

No exception is raised, but http.error is set to Errno::ETIMEDOUT in http.errback.

Excon.get(url, connect_timeout: 1, read_timeout: 1, write_timeout: 1)

Raises Excon::Errors::Timeout

Faraday.get(url) do |req|
  req.options.open_timeout = 1
  req.options.timeout = 1
end

or

Faraday.new(url, request: {open_timeout: 1, timeout: 1}) do |faraday|
  # ...
end

Raises

HTTP.timeout(connect: 1, read: 1, write: 1).get(url)

Raises

HTTParty.get(url, timeout: 1)

or

class Resource
  include HTTParty

  default_timeout 1
  # or
  open_timeout 1
  read_timeout 1
  write_timeout 1
end

Raises

client = HTTPClient.new
client.connect_timeout = 1
client.receive_timeout = 1
client.send_timeout = 1
client.get(url)

Raises

HTTPI::Request.new(url: url, open_timeout: 1)

Raises same errors as underlying client

sess = Patron::Session.new
sess.connect_timeout = 1
sess.timeout = 1

Raises Patron::TimeoutError

RestClient::Request.execute(method: :get, url: url, open_timeout: 1, read_timeout: 1)

# shorthand to set open_timeout = read_timeout = 1
RestClient::Request.execute(method: :get, url: url, timeout: 1)

Same options also work with RestClient::Resource.

Raises

Default: 60s connect timeout, 60s read timeout

response = Typhoeus.get(url, connecttimeout: 1, timeout: 1)

No exception is raised. Check for a timeout with

Connect timeout is not configurable

Default: 10s read timeout, no connect timeout

Raises RuntimeError

Mixlib::ShellOut.new(command, timeout: 1)

Raises Mixlib::ShellOut::CommandTimeout

POSIX::Spawn::Child.new(command, timeout: 1)

Raises POSIX::Spawn::TimeoutExceeded

TTY::Command.new(timeout: 1)

or

cmd.run(command, timeout: 1)

Raises TTY::Command::TimeoutExceeded

# config/puma.rb
worker_timeout 15

Default: 60s

This kills and respawns the worker process. Note that this is for the worker and not threads. This isn’t a request timeout either. Use Rack middleware for request timeouts.

# config/puma.rb
worker_shutdown_timeout 8

Default: 30s

This causes Puma to send a SIGKILL signal to a worker if it hasn’t shutdown within the specified time period after having received a SIGTERM signal.

# config/unicorn.rb
timeout 15

Default: 60s

This kills and respawns the worker process.

It’s recommended to use this in addition to Rack middleware.

use Rack::Timeout,
  service_timeout:   15,     # ENV["RACK_TIMEOUT_SERVICE_TIMEOUT"]
  wait_timeout:      30,     # ENV["RACK_TIMEOUT_WAIT_TIMEOUT"]
  wait_overtime:     60,     # ENV["RACK_TIMEOUT_WAIT_OVERTIME"]
  service_past_wait: false,  # ENV["RACK_TIMEOUT_SERVICE_PAST_WAIT"]
  term_on_timeout:   false   # ENV["RACK_TIMEOUT_TERM_ON_TIMEOUT"]

Default: 15s service timeout, 30s wait timeout

Raises Rack::Timeout::RequestTimeoutError or Rack::Timeout::RequestExpiryError

Read more here

Note: The approach used by Rack::Timeout can leave your application in an inconsistent state, as described here. You can use term on timeout to avoid this.

Default: 15s

Raises same exceptions as rack-timeout

routing.solve(time_limit: 1)
solver.solve(p, q, a, l, u, time_limit: 1)

Check for a status of run time limit reached for a timeout

problem.set_time_limit(1)

or

Check for a timeout with

problem.time_limit_reached?
solver.solve(data, cone, time_limit_secs: 1)

Check for a status of solved (inaccurate - reached time_limit_secs) for a timeout

ActiveRecord::Base.connection.get_advisory_lock(123)

Returns false if lock cannot be immediately acquired

redis.lock(key, life: 1, acquire: 1) do |lock|
  # ...
end

Default: 10s acquisition timeout

Raises Redis::Lock::LockNotAcquired

lock_manager.lock!(key, 1000) do
  # ...
end

Default: 200ms acquisition timeout with 3 retries

Raises Redlock::LockError

Suo::Client::Memcached.new(key, acquisition_timeout: 1)

or

Suo::Client::Redis.new(key, acquisition_timeout: 1)

Default: 0.1s acquisition timeout with 10 retries

The lock method returns nil on timeout

ActiveRecord::Base.with_advisory_lock("123", timeout_seconds: 1) do
  # ...
end

Returns false on acquisition timeout

Not configurable at the moment, and no timeout by default

Airtable::Resource.default_timeout 1

Raises

Algolia.init(
  connect_timeout: 1,
  send_timeout: 1,
  receive_timeout: 1,
  batch_timeout: 1,
  search_timeout: 1
)

Raises Algolia::AlgoliaProtocolError

Aws.config = {
  http_open_timeout: 1,
  http_read_timeout: 1
}

Or with a client

Aws::S3::Client.new(
  http_open_timeout: 1,
  http_read_timeout: 1
)

Raises Seahorse::Client::NetworkingError

Not configurable at the moment, and no timeout by default

adapter = Bitly::HTTP::Adapters::NetHTTP.new(request_opts: {
  open_timeout: 1,
  read_timeout: 1
})
http_client = Bitly::HTTP::Client.new(adapter)
client = Bitly::API::Client.new(token: token, http: http_client)

Raises

Boxr::BOX_CLIENT.connect_timeout = 1
Boxr::BOX_CLIENT.receive_timeout = 1
Boxr::BOX_CLIENT.send_timeout = 1

Raises

Default: 30s connect timeout, 60s read timeout

Not configurable at the moment

Clearbit::Resource.options = {timeout: 1}

Raises Nestful::TimeoutError

timeout = 1
Dogapi::Client.new(api_key, nil, nil, nil, false, timeout)

Raises

Not configurable at the moment

Default: No connect timeout, 600s read timeout

DropletKit::Client.new(open_timeout: 1, timeout: 1)

Raises

Not configurable at the moment, and no timeout by default

firebase = Firebase::Client.new(url)
firebase.request.connect_timeout = 1
firebase.request.receive_timeout = 1
firebase.request.send_timeout = 1

Raises

Not configurable at the moment

Gibbon::Request.new(open_timeout: 1, timeout: 1, ...)

Raises Gibbon::MailChimpError

Github.new(connection_options: {request: {open_timeout: 1, timeout: 1}})

Raises

Gitlab.client(httparty: {timeout: 1})

Raises

client = Google::Apis::DriveV2::DriveService.new
client.client_options.open_timeout_sec = 1
client.client_options.read_timeout_sec = 1

Raise Google::Apis::TransmissionError

Google::Cloud::Storage.new(timeout: 1)

Raises Google::Cloud::Error

client = Intercom::Client.new(token: token)
client.options(Intercom::Client.set_timeouts(open_timeout: 1, read_timeout: 1))

Raises

JIRA::Client.new(read_timeout: 1)

Connect timeout is not configurable at the moment

Raises Net::ReadTimeout on read timeout

Koala.http_service.http_options = {request: {open_timeout: 1, timeout: 1}}

Raises Faraday::ConnectionFailed

Not configurable at the moment, and no timeout by default.

Octokit::Client.new(connection_options: {request: {open_timeout: 1, timeout: 1}})

Raises

client.timeout = 1
# or
client.connect_timeout = 1
client.send_timeout = 1
client.receive_timeout = 1
client.keep_alive_timeout = 1

Raises Pusher::HTTPError

Pwned::Password.new("password", open_timeout: 1, read_timeout: 1)

Raises Pwned::TimeoutError

Restforce.new(timeout: 1)

Raises

Not configurable at the moment, and no timeout by default

Not configurable at the moment, and no timeout by default

Sentry.init do |config|
  config.transport.open_timeout = 1
  config.transport.timeout = 1
end

Default: 1s connect timeout, 2s read/write timeout

Raises Sentry::ExternalError in some cases

Not configurable at the moment, and no timeout by default

Sift::Client.new(timeout: 1)

Default: 2s

Raises

Slack::Notifier.new(webhook_url, http_options: {open_timeout: 1, read_timeout: 1})

Raises

Slack::Web::Client.new(open_timeout: 1, timeout: 1)

Raises Slack::Web::Api::Errors::TimeoutError

SmartyStreets::ClientBuilder.new(credentials).with_max_timeout(1)

Raises

SODA::Client.new(timeout: 1)

Raises

Not configurable at the moment, and no timeout by default

Stripe.open_timeout = 1
Stripe.read_timeout = 1

Default: 30s connect timeout, 80s read timeout

Raises Stripe::APIConnectionError

Tamber.open_timeout = 1
Tamber.read_timeout = 1

Raises Tamber::NetworkError

http_client = Twilio::HTTP::Client.new(timeout: 1)
Twilio::REST::Client.new(account_sid, auth_token, nil, nil, http_client)

Default: 30s

Raises Twilio::REST::TwilioError

Twitter::REST::Client.new do |config|
  config.timeouts = {connect: 1, read: 1, write: 1}
end

Raises HTTP::TimeoutError

Note: All three timeouts must be set for any to take effect.

Not configurable at the moment, and no timeout by default

ZendeskAPI::Client.new do |config|
  config.client_options = {request: {open_timeout: 1, timeout: 1}}
end

Default: 10s connect timeout, no read timeout

Raises ZendeskAPI::Error::NetworkError

Acme::Client.new(connection_options: {request: {open_timeout: 1, timeout: 1}})

Raises Acme::Client::Error::Timeout

ActionMailer::Base.smtp_settings = {
  open_timeout: 1,
  read_timeout: 1
}

Raises

ActiveMerchant::Billing::Gateway.open_timeout = 1
ActiveMerchant::Billing::Gateway.read_timeout = 1

Default: 60s

Raises ActiveMerchant::ConnectionError

class Person < ActiveResource::Base
  self.open_timeout = 1
  self.read_timeout = 1
end

Raises ActiveResource::TimeoutError

Carrot2.new(open_timeout: 1, read_timeout: 1)

Raises

Docker.options = {
  connect_timeout: 1,
  read_timeout: 1
}

Raises Docker::Error::TimeoutError

client = Etcd.client(read_timeout: 1)

Connect timeout not configurable

Default: 60s read timeout

Raises

Etcdv3.new(command_timeout: 1)

or

conn.get(key, timeout: 1)

Raises GRPC::DeadlineExceeded

FastImage.size(url, timeout: 1)

Returns nil on timeouts

If you pass raise_on_failure: true, raises FastImage::ImageFetchFailure

Geocoder.configure(timeout: 1, ...)

No exception is raised by default. To raise exceptions, use

Geocoder.configure(timeout: 1, always_raise: :all, ...)

Raises Geocoder::LookupTimeout

GraphQL::Client::HTTP.new(url) do
  def connection
    conn = super
    conn.open_timeout = 1
    conn.read_timeout = 1
    conn
  end
end

Raises

RouteGuide::Stub.new(addr, :this_channel_is_insecure, timeout: 1)

Raises GRPC::DeadlineExceeded

Hexspace::Client.new(timeout: 1)

Raises Thrift::TransportException

Ignite::Client.new(connect_timeout: 1)

Read timeout is not configurable at the moment

Raises Ignite::TimeoutError on connect timeout

Kubeclient::Client.new(url, timeouts: {open: 1, read: 1})

Raises KubeException

Default: 60s connect timeout, 60s read timeout

Mail.defaults do
  delivery_method :smtp, open_timeout: 1, read_timeout: 1
end

Raises

agent = Mechanize.new
agent.open_timeout = 1
agent.read_timeout = 1

Raises

nats = NATS::IO::Client.new
nats.connect(connect_timeout: 1)

Raises NATS::IO::SocketTimeoutError

Nestful::Request.new(url, timeout: 1)

or

class Resource < Nestful::Resource
  options timeout: 1
end

Raises Nestful::TimeoutError

Net::DNS::Resolver.new(udp_timeout: 1)

Default: 5s

Raises Net::DNS::Resolver::NoResponseError

Net::LDAP.new(host: host, connect_timeout: 1)

Read timeout not configurable at the moment

Default: 5s connect timeout, no read timeout

Raises Net::LDAP::Error

timeout = 1
Net::NTP.get(host, port, timeout)

Raises Timeout::Error

Net::SCP.start(host, user, timeout: 1)

Raises Net::SSH::ConnectionTimeout

Net::SFTP.start(host, user, timeout: 1)

Raises Net::SSH::ConnectionTimeout

Net::SSH.start(host, user, timeout: 1)

Raises Net::SSH::ConnectionTimeout

Net::Telnet::new("Host" => host, "Timeout" => 1)

Raises

Not configurable at the moment, and no timeout by default

RBHive.tcli_connect(host, port, timeout: 1) do |connection|
  # ...
end

Raises Thrift::TransportException

Reversed.lookup("8.8.8.8", timeout: 1)

Returns nil on timeouts

Savon.client(wsdl: url, open_timeout: 1, read_timeout: 1)

Raises

Socket.tcp(host, 80, connect_timeout: 1) do |sock|
  # ...
end

Raises Errno::ETIMEDOUT

Spidr.open_timeout = 1
Spidr.read_timeout = 1

No exception is raised. Check for failures with

agent = Spidr.site(url)
agent.failures
Spyke::Base.connection = Faraday.new(url: url) do |c|
  c.adapter Faraday.default_adapter
  c.options[:open_timeout] = 1
  c.options[:timeout] = 1
end

Raises Spyke::ConnectionError

Stomp::Client.new(start_timeout: 1, connect_timeout: 1, connread_timeout: 1, parse_timeout: 1)

Raises

Thrift::Socket.new(host, port, 1)

Raises Thrift::TransportException

ThriftClient.new(client_class, servers, connect_timeout: 1, timeout: 1)

Raises

Vault.configure do |config|
  config.timeout = 1

  # or more granular
  config.ssl_timeout  = 1
  config.open_timeout = 1
  config.read_timeout = 1
end

Raises Vault::HTTPConnectionError

Whois::Client.new(timeout: 1)

Default: 10s

Raises Timeout::Error

Not configurable at the moment

Default: 30s

Raises Zookeeper::Exceptions::ContinuationTimeoutError

Not configurable at the moment

Default: 30s

Raises Zookeeper::Exceptions::ContinuationTimeoutError

Don’t see a library you use?

Let us know. Even better, create a pull request for it.

Take advantage of inheritance. Instead of

rescue Net::OpenTimeout, Net::ReadTimeout

you can do

Use

Adding timeouts to existing services can be a daunting task, but there’s a low risk way to do it.

  1. Select a timeout - say 5 seconds
  2. Log instances exceeding the proposed timeout
  3. Fix them
  4. Add the timeout
  5. Repeat this process with a lower timeout, until your target timeout is achieved
git clone https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts.git
cd the-ultimate-guide-to-ruby-timeouts
bundle install

To run all tests, use:

bundle exec appraisal rake test

To run individual tests, use:

bundle exec appraisal faraday rake test

To add a new gem:

  1. Add it to Appraisals and run bundle exec appraisal generate
  2. Run bundle exec appraisal new_gem bundle
  3. Create test/new_gem_test.rb and run bundle exec appraisal new_gem rake test
  4. Add it to the appropriate section of the readme

Because time is not going to go backwards, I think I better stop now. - Stephen Hawking

🕓


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4