Module: Exercism::RedisPool
- Defined in:
- lib/exercism/redis_pool.rb
Overview
A registry of pooled Redis clients, keyed by name.
Clients used to be built per call, which turned out to be expensive rather than merely wasteful. Redis::Cluster is eager: RedisClient::Cluster#initialize builds its router inline, and the router does topology discovery and opens node connections there and then. Because nothing held a reference to the client afterwards, every command paid a connect plus a full slot-map fetch and then threw the connection away - putting a handshake on the critical path of every tooling job state transition, and (because the orchestrator does the same on each idle poll) accounting for ~96% of our ElastiCache ECPU bill.
A single shared client would fix the churn, but Redis serialises every command through an internal Monitor, so sharing one across the orchestrator's and Sidekiq's threads would trade connection cost for contention. Hence a pool.
Constant Summary collapse
- DEFAULT_SIZE =
5- DEFAULT_TIMEOUT =
5
Class Method Summary collapse
-
.for(key) ⇒ Object
Returns the pool for
key, building it from the block on first use. - .size ⇒ Object
Class Method Details
.for(key) ⇒ Object
Returns the pool for key, building it from the block on first use.
The returned object quacks like a Redis client: ConnectionPool::Wrapper
checks a connection out for the duration of each method call.
29 30 31 32 33 34 35 36 37 |
# File 'lib/exercism/redis_pool.rb', line 29 def self.for(key, &) require 'connection_pool' @mutex.synchronize do reset_after_fork! @pools[key] ||= ConnectionPool::Wrapper.new(size:, timeout: DEFAULT_TIMEOUT, &) end end |
.size ⇒ Object
39 40 41 |
# File 'lib/exercism/redis_pool.rb', line 39 def self.size Integer(ENV.fetch('EXERCISM_REDIS_POOL_SIZE', DEFAULT_SIZE)) end |