• Stars
    star
    2,212
  • Rank 19,960 (Top 0.5 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 9 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

Build Status

Timeout Types

  • connect (or open) - time to open the connection
  • read (or receive) - time to receive data after connected
  • write (or send) - time to send data after connected
  • checkout - time to checkout a connection from the pool
  • statement - time to execute a database statement
  • lock (or acquisition) - time to acquire a lock
  • request (or service) - time to process a request
  • wait - time to start processing a queued request
  • command - time to run a command
  • solve - time to solve an optimization problem

Statement Timeouts

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

Gems

Standard Library

Data Stores

HTTP Clients

Commands

Web Servers

Rack Middleware

Solvers

Distributed Locks

3rd Party Services

Other

Statement Timeouts

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

PostgreSQL

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

SELECT pg_sleep(6);

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

MySQL

Note: Requires MySQL 5.7.8 or higher, and 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) */ ...

MariaDB

Note: Requires MariaDB 10.1.1 or higher

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);

As of MariaDB 10.1.2, you can set single statement timeouts with

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

Standard Library

io

Note: Requires Ruby 3.2+

STDIN.timeout = 1

Raises IO::TimeoutError

net/ftp

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

net/http

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

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout
  • Net::WriteTimeout on write timeout

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

Write timeout can be set in Ruby 2.6+. Read timeouts are retried once automatically for idempotent methods like GET. In Ruby 2.5+, you can set the number of retries with http.max_retries = 1.

net/imap

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

Read timeout is not configurable at the moment

Raises Net::OpenTimeout on connect timeout

net/pop

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

net/smtp

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

open-uri

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

regexp

Note: Requires Ruby 3.2+

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

Raises Regexp::TimeoutError

Data Stores

activerecord

  • postgres adapter

    ActiveRecord::Base.establish_connection(connect_timeout: 1, checkout_timeout: 1, ...)

    or in config/database.yml

    production:
      connect_timeout: 1
      checkout_timeout: 1

    Raises

    • ActiveRecord::ConnectionNotEstablished on connect and read timeouts for Active Record 6.1+
    • PG::ConnectionBad on connect and read timeouts for Active Record < 6.1
    • ActiveRecord::ConnectionTimeoutError on checkout timeout

    See also PostgreSQL statement timeouts

  • mysql2 adapter

    ActiveRecord::Base.establish_connection(connect_timeout: 1, read_timeout: 1, write_timeout: 1, checkout_timeout: 1, ...)

    or in config/database.yml

    production:
      connect_timeout: 1
      read_timeout: 1
      write_timeout: 1
      checkout_timeout: 1

    Raises

    • ActiveRecord::ConnectionNotEstablished on connect and read timeouts for Active Record 6.1+
    • Mysql2::Error on connect and read timeouts for Active Record < 6.1
    • ActiveRecord::ConnectionTimeoutError on checkout timeout

    See also MySQL statement timeouts

bunny

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

Raises

  • Bunny::TCPConnectionFailedForAllHosts on connect timeout
  • Bunny::NetworkFailure on read timeout

cassandra-driver

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

Default: 10s connect timeout, 12s read timeout

Raises

  • Cassandra::Errors::NoHostsAvailable on connect timeout
  • Cassandra::Errors::TimeoutError on read timeout

connection_pool

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

Raises ConnectionPool::TimeoutError

couchrest

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

Raises

  • HTTPClient::ConnectTimeoutError on connect timeout
  • HTTPClient::ReceiveTimeoutError on read timeout

dalli

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

Default: 1s

Raises Dalli::RingError

drill-sergeant

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

Default: 3s connect timeout, no read timeout

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

elasticsearch

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

Raises Elastic::Transport::Transport::Error

hiredis

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

Raises

  • Errno::ETIMEDOUT on connect timeout
  • Errno::EAGAIN on read timeout

immudb

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

Raises GRPC::DeadlineExceeded

influxdb

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

Raises InfluxDB::ConnectionError

influxdb-client

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

Raises InfluxDB2::InfluxError

meilisearch

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

Raises MeiliSearch::TimeoutError

mongo

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

Raises Mongo::Error::NoServerAvailable

mongoid

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

Raises Mongo::Error::NoServerAvailable

mysql2

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

Raises Mysql2::Error

neo4j

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

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

Raises PG::ConnectionBad

presto-client

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

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

redis

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

Default: 5s

Raises

  • Redis::CannotConnectError on connect timeout
  • Redis::TimeoutError on read timeout

redis-client

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

Default: 1s

Raises RedisClient::CannotConnectError

riddle

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

Raises Riddle::ResponseError

rsolr

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

Raises

  • RSolr::Error::ConnectionRefused on connect timeout
  • RSolr::Error::Timeout on read timeout

ruby-druid

Not configurable at the moment

Default: 10s connect timeout, no read timeout

ruby-kafka

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

Raises Kafka::ConnectionError

searchkick

Searchkick.timeout = 1
Searchkick.search_timeout = 1

Default: 10s

Raises same exceptions as elasticsearch

sequel

  • postgres adapter

    Sequel.connect(connect_timeout: 1, pool_timeout: 1, ...)

    Raises

    • Sequel::DatabaseConnectionError on connect and read timeouts
    • Sequel::PoolTimeout on checkout timeout
  • mysql2 adapter

    Sequel.connect(timeout: 1, read_timeout: 1, connect_timeout: 1, pool_timeout: 1, ...)

    Raises

    • Sequel::DatabaseConnectionError on connect and read timeouts
    • Sequel::PoolTimeout on checkout timeout

trino-client

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

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

typesense

Typesense::Client.new(connection_timeout_seconds: 1)

Raises Typesense::Error::TimeoutError

HTTP Clients

curb

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

Raises Curl::Err::TimeoutError

down

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

Raises Down::TimeoutError

em-http-client

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

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

Raises Excon::Errors::Timeout

faraday

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

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

http

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

Raises

  • HTTP::ConnectTimeoutError on connect timeout
  • HTTP::TimeoutError on read timeout

httparty

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

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

httpclient

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

Raises

  • HTTPClient::ConnectTimeoutError on connect timeout
  • HTTPClient::ReceiveTimeoutError on read timeout

httpi

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

Raises same errors as underlying client

patron

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

Raises Patron::TimeoutError

rest-client

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

  • RestClient::Exceptions::OpenTimeout on connect timeout
  • RestClient::Exceptions::ReadTimeout on read timeout

Default: 60s connect timeout, 60s read timeout

typhoeus

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

No exception is raised. Check for a timeout with

response.timed_out?

unirest

Unirest.timeout(1)

Connect timeout is not configurable

Default: 10s read timeout, no connect timeout

Raises RuntimeError

Commands

frontkick

Frontkick.exec(command, timeout: 1)

Raises Frontkick::Timeout

mixlib-shellout

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

Raises Mixlib::ShellOut::CommandTimeout

posix-spawn

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

Raises POSIX::Spawn::TimeoutExceeded

tty-command

TTY::Command.new(timeout: 1)

or

cmd.run(command, timeout: 1)

Raises TTY::Command::TimeoutExceeded

Web Servers

puma

# 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.

unicorn

# 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.

Rack Middleware

rack-timeout

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.

slowpoke

Slowpoke.timeout = 5

Default: 15s

Raises same exceptions as rack-timeout

Solvers

or-tools

routing.solve(time_limit: 1)

osqp

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

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

ruby-cbc

problem.set_time_limit(1)

or

problem.solve(sec: 1)

Check for a timeout with

problem.time_limit_reached?

scs

solver.solve(data, cone, time_limit_secs: 1)

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

Distributed Locks

activerecord

ActiveRecord::Base.connection.get_advisory_lock(123)

Returns false if lock cannot be immediately acquired

mlanett-redis-lock

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

Default: 10s acquisition timeout

Raises Redis::Lock::LockNotAcquired

redlock

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

Default: 200ms acquisition timeout with 3 retries

Raises Redlock::LockError

suo

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

with_advisory_lock

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

Returns false on acquisition timeout

3rd Party Services

airrecord

Not configurable at the moment, and no timeout by default

airtable

Airtable::Resource.default_timeout 1

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

algoliasearch

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

Raises Algolia::AlgoliaProtocolError

aws-sdk

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

azure

Not configurable at the moment, and no timeout by default

bitly

Available since version 3.0.0:

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

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

boxr

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

Raises

  • HTTPClient::ConnectTimeoutError on connect timeout
  • HTTPClient::ReceiveTimeoutError on read timeout

checkr-official

Default: 30s connect timeout, 60s read timeout

Not configurable at the moment

clearbit

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

Raises Nestful::TimeoutError

coinbase

Not configurable at the moment

dogapi

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

dropbox-sdk

Not configurable at the moment

Default: No connect timeout, 600s read timeout

droplet_kit

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

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

fastly

Not configurable at the moment, and no timeout by default

firebase

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

Raises

  • HTTPClient::ConnectTimeoutError on connect timeout
  • HTTPClient::ReceiveTimeoutError on read timeout

flickraw

Not configurable at the moment

gibbon

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

Raises Gibbon::MailChimpError

github_api

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

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

gitlab

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

google-api-client

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

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

Raises Google::Cloud::Error

hipchat

[HipChat::Client, HipChat::Room, HipChat::User].each { |c| c.default_timeout(1) }

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

intercom

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

Raises

  • Intercom::ServiceConnectionError on connect timeout (inherits from Intercom::IntercomError)
  • Intercom::ServiceUnavailableError on read timeout (inherits from Intercom::IntercomError)

jira-ruby

JIRA::Client.new(read_timeout: 1)

Connect timeout is not configurable at the moment

Raises Net::ReadTimeout on read timeout

koala

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

Raises Faraday::ConnectionFailed

linkedin

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

octokit

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

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

pinterest-api

Pinterest::Client.new(access_token, request: {open_timeout: 1, timeout: 1})

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

pusher

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

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

Raises Pwned::TimeoutError

restforce

Restforce.new(timeout: 1)

Raises

  • Faraday::ConnectionFailed on connect timeout
  • Faraday::TimeoutError on read timeout

rspotify

Not configurable at the moment, and no timeout by default

ruby-trello

Not configurable at the moment, and no timeout by default

shopify_api

ShopifyAPI::Base.timeout = 1

Raises ActiveResource::TimeoutError

sift

Sift::Client.new(timeout: 1)

Default: 2s

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

slack-notifier

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

slack-ruby-client

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

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

smartystreets_ruby_sdk

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

soda-ruby

SODA::Client.new(timeout: 1)

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

soundcloud

Not configurable at the moment, and no timeout by default

stripe

Stripe.open_timeout = 1
Stripe.read_timeout = 1

Default: 30s connect timeout, 80s read timeout

Raises Stripe::APIConnectionError

tamber

Tamber.open_timeout = 1
Tamber.read_timeout = 1

Raises Tamber::NetworkError

twilio-ruby

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

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.

yt

Not configurable at the moment, and no timeout by default

zendesk_api

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

Other

acme-client

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

Raises Acme::Client::Error::Timeout

actionmailer

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

activemerchant

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

Default: 60s

Raises ActiveMerchant::ConnectionError

activeresource

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

Raises ActiveResource::TimeoutError

active_shipping

client = ActiveShipping::USPS.new(login: "developer-key")
client.open_timeout = 1
client.read_timeout = 1

Default: 2s connect timeout, 10s read timeout

Raises ActiveUtils::ConnectionError

carrot2

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

docker-api

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

Raises Docker::Error::TimeoutError

etcd

client = Etcd.client(read_timeout: 1)

Connect timeout not configurable

Default: 60s read timeout

Raises

  • Net::ReadTimeout on read timeout

etcdv3

Etcdv3.new(command_timeout: 1)

or

conn.get(key, timeout: 1)

Raises GRPC::DeadlineExceeded

fastimage

FastImage.size(url, timeout: 1)

Returns nil on timeouts

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

geocoder

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

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

grpc

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

Raises GRPC::DeadlineExceeded

hexspace

Hexspace::Client.new(timeout: 1)

Raises Thrift::TransportException

ignite-client

Ignite::Client.new(connect_timeout: 1)

Read timeout is not configurable at the moment

Raises Ignite::TimeoutError on connect timeout

kubeclient

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

Raises KubeException

Default: 60s connect timeout, 60s read timeout

mail

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

mechanize

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

nats-pure

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

Raises NATS::IO::SocketTimeoutError

nestful

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

or

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

Raises Nestful::TimeoutError

net-dns

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

Default: 5s

Raises Net::DNS::Resolver::NoResponseError

net-ldap

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

net-ntp

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

Raises Timeout::Error

net-scp

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

Raises Net::SSH::ConnectionTimeout

net-sftp

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

Raises Net::SSH::ConnectionTimeout

net-ssh

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

Raises Net::SSH::ConnectionTimeout

net-telnet

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

Raises

  • Net::OpenTimeout on connect timeout
  • Net::ReadTimeout on read timeout

omniauth-oauth2

Not configurable at the moment, and no timeout by default

rbhive

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

Raises Thrift::TransportException

reversed

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

Returns nil on timeouts

savon

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

Raises

  • HTTPClient::ConnectTimeoutError on connect timeout
  • HTTPClient::ReceiveTimeoutError on read timeout

socket

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

Raises Errno::ETIMEDOUT

spydr

Spidr.open_timeout = 1
Spidr.read_timeout = 1

No exception is raised. Check for failures with

agent = Spidr.site(url)
agent.failures

spyke

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

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

Raises

  • Stomp::Error::StartTimeoutException on connect timeout
  • Stomp::Error::ReceiveTimeout on read timeout

thrift

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

Raises Thrift::TransportException

thrift_client

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

Raises

  • ThriftClient::NoServersAvailable on connect timeout
  • TODO: read timeout

vault

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

Whois::Client.new(timeout: 1)

Default: 10s

Raises Timeout::Error

zk

Not configurable at the moment

Default: 30s

Raises Zookeeper::Exceptions::ContinuationTimeoutError

zookeeper

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.

Rescuing Exceptions

Take advantage of inheritance. Instead of

rescue Net::OpenTimeout, Net::ReadTimeout

you can do

rescue Timeout::Error

Use

  • Timeout::Error for both Net::OpenTimeout and Net::ReadTimeout
  • Faraday::ClientError for both Faraday::ConnectionFailed and Faraday::TimeoutError
  • HTTPClient::TimeoutError for both HTTPClient::ConnectTimeoutError and HTTPClient::ReceiveTimeoutError
  • Redis::BaseConnectionError for both Redis::CannotConnectError and Redis::TimeoutError
  • Rack::Timeout::Error for both Rack::Timeout::RequestTimeoutError and Rack::Timeout::RequestExpiryError
  • RestClient::Exceptions::Timeout for both RestClient::Exceptions::OpenTimeout and RestClient::Exceptions::ReadTimeout

Existing Services

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

Running the Tests

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

And lastly...

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

🕓

More Repositories

1

pghero

A performance dashboard for Postgres
Ruby
7,123
star
2

searchkick

Intelligent search made easy
Ruby
6,257
star
3

chartkick

Create beautiful JavaScript charts with one line of Ruby
Ruby
6,157
star
4

blazer

Business intelligence made simple
Ruby
4,351
star
5

ahoy

Simple, powerful, first-party analytics for Rails
Ruby
3,872
star
6

strong_migrations

Catch unsafe migrations in development
Ruby
3,662
star
7

groupdate

The simplest way to group temporal data
Ruby
3,617
star
8

pgsync

Sync data from one Postgres database to another
Ruby
2,787
star
9

production_rails

Best practices for running Rails in production
1,975
star
10

dexter

The automatic indexer for Postgres
Ruby
1,491
star
11

lockbox

Modern encryption for Ruby and Rails
Ruby
1,290
star
12

chartkick.js

Create beautiful charts with one line of JavaScript
JavaScript
1,211
star
13

react-chartkick

Create beautiful JavaScript charts with one line of React
JavaScript
1,183
star
14

pretender

Log in as another user in Rails
Ruby
1,124
star
15

ahoy_email

First-party email analytics for Rails
Ruby
1,051
star
16

secure_rails

Rails security best practices
954
star
17

pgslice

Postgres partitioning as easy as pie
Ruby
953
star
18

mailkick

Email subscriptions for Rails
Ruby
847
star
19

vue-chartkick

Create beautiful JavaScript charts with one line of Vue
JavaScript
747
star
20

eps

Machine learning for Ruby
Ruby
609
star
21

awesome-legal

Awesome free legal documents for companies
589
star
22

searchjoy

Search analytics made easy
Ruby
579
star
23

polars-ruby

Blazingly fast DataFrames for Ruby
Ruby
563
star
24

torch.rb

Deep learning for Ruby, powered by LibTorch
Ruby
552
star
25

blind_index

Securely search encrypted database fields
Ruby
470
star
26

safely

Rescue and report exceptions in non-critical code
Ruby
470
star
27

authtrail

Track Devise login activity
Ruby
466
star
28

ahoy.js

Simple, powerful JavaScript analytics
JavaScript
463
star
29

multiverse

Multiple databases for Rails 🎉
Ruby
463
star
30

hightop

A nice shortcut for group count queries
Ruby
462
star
31

field_test

A/B testing for Rails
Ruby
460
star
32

s3tk

A security toolkit for Amazon S3
Python
439
star
33

disco

Recommendations for Ruby and Rails using collaborative filtering
Ruby
431
star
34

active_median

Median and percentile for Active Record, Mongoid, arrays, and hashes
Ruby
427
star
35

informers

State-of-the-art natural language processing for Ruby
Ruby
417
star
36

notable

Track notable requests and background jobs
Ruby
402
star
37

shorts

Short, random tutorials and posts
379
star
38

tensorflow-ruby

Deep learning for Ruby
Ruby
350
star
39

distribute_reads

Scale database reads to replicas in Rails
Ruby
328
star
40

slowpoke

Rack::Timeout enhancements for Rails
Ruby
327
star
41

prophet-ruby

Time series forecasting for Ruby
Ruby
321
star
42

rover

Simple, powerful data frames for Ruby
Ruby
311
star
43

groupdate.sql

The simplest way to group temporal data
PLpgSQL
280
star
44

kms_encrypted

Simple, secure key management for Lockbox and attr_encrypted
Ruby
235
star
45

jetpack

A friendly package manager for R
R
234
star
46

neighbor

Nearest neighbor search for Rails and Postgres
Ruby
230
star
47

rollup

Rollup time-series data in Rails
Ruby
230
star
48

hypershield

Shield sensitive data in Postgres and MySQL
Ruby
227
star
49

logstop

Keep personal data out of your logs
Ruby
218
star
50

pdscan

Scan your data stores for unencrypted personal data (PII)
Go
213
star
51

delete_in_batches

Fast batch deletes for Active Record and Postgres
Ruby
202
star
52

vega-ruby

Interactive charts for Ruby, powered by Vega and Vega-Lite
Ruby
192
star
53

mapkick

Create beautiful JavaScript maps with one line of Ruby
Ruby
173
star
54

dbx

A fast, easy-to-use database library for R
R
171
star
55

fastText-ruby

Efficient text classification and representation learning for Ruby
Ruby
162
star
56

autosuggest

Autocomplete suggestions based on what your users search
Ruby
162
star
57

swipeout

Swipe-to-delete goodness for the mobile web
JavaScript
159
star
58

pghero.sql

Postgres insights made easy
PLpgSQL
154
star
59

mainstreet

Address verification for Ruby and Rails
Ruby
149
star
60

or-tools-ruby

Operations research tools for Ruby
Ruby
139
star
61

mapkick.js

Create beautiful, interactive maps with one line of JavaScript
JavaScript
138
star
62

trend-ruby

Anomaly detection and forecasting for Ruby
Ruby
128
star
63

mitie-ruby

Named-entity recognition for Ruby
Ruby
122
star
64

barkick

Barcodes made easy
Ruby
120
star
65

ownership

Code ownership for Rails
Ruby
111
star
66

anomaly

Easy-to-use anomaly detection for Ruby
Ruby
98
star
67

errbase

Common exception reporting for a variety of services
Ruby
87
star
68

tokenizers-ruby

Fast state-of-the-art tokenizers for Ruby
Rust
81
star
69

ip_anonymizer

IP address anonymizer for Ruby and Rails
Ruby
79
star
70

str_enum

String enums for Rails
Ruby
75
star
71

faiss-ruby

Efficient similarity search and clustering for Ruby
C++
73
star
72

trend-api

Anomaly detection and forecasting API
R
71
star
73

archer

Rails console history for Heroku, Docker, and more
Ruby
70
star
74

onnxruntime-ruby

Run ONNX models in Ruby
Ruby
70
star
75

xgboost-ruby

High performance gradient boosting for Ruby
Ruby
69
star
76

secure-spreadsheet

Encrypt and password protect sensitive CSV and XLSX files
JavaScript
66
star
77

active_hll

HyperLogLog for Rails and Postgres
Ruby
66
star
78

guess

Statistical gender detection for Ruby
Ruby
60
star
79

morph

An encrypted, in-memory, key-value store
C++
59
star
80

lightgbm

High performance gradient boosting for Ruby
Ruby
56
star
81

midas-ruby

Edge stream anomaly detection for Ruby
Ruby
54
star
82

moves

Ruby client for Moves
Ruby
54
star
83

blingfire-ruby

High speed text tokenization for Ruby
Ruby
54
star
84

vowpalwabbit-ruby

Fast online machine learning for Ruby
Ruby
52
star
85

xlearn-ruby

High performance factorization machines for Ruby
Ruby
51
star
86

tomoto-ruby

High performance topic modeling for Ruby
C++
51
star
87

trove

Deploy machine learning models in Ruby (and Rails)
Ruby
50
star
88

ahoy_events

Simple, powerful event tracking for Rails
Ruby
42
star
89

mapkick-static

Create beautiful static maps with one line of Ruby
Ruby
42
star
90

practical-search

Let’s make search a better experience for our users
40
star
91

breakout-ruby

Breakout detection for Ruby
Ruby
40
star
92

plu

Price look-up codes made easy
Ruby
40
star
93

ngt-ruby

High-speed approximate nearest neighbors for Ruby
Ruby
39
star
94

gindex

Concurrent index migrations for Rails
Ruby
39
star
95

clockwork_web

A web interface for Clockwork
Ruby
38
star
96

ahoy_guide

A foundation of knowledge and libraries for solid analytics
38
star
97

notable_web

A web interface for Notable
HTML
36
star
98

AnomalyDetection.rb

Time series anomaly detection for Ruby
Ruby
34
star
99

khiva-ruby

High-performance time series algorithms for Ruby
Ruby
34
star
100

immudb-ruby

Ruby client for immudb, the immutable database
Ruby
34
star