Skip to content

Instantly share code, notes, and snippets.

@reidmorrison
Last active August 29, 2015 14:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reidmorrison/07b9e009925415ace98d to your computer and use it in GitHub Desktop.
Save reidmorrison/07b9e009925415ace98d to your computer and use it in GitHub Desktop.
Implement a connection pool for Redis
# Implement a Redis Connection Pool
#
# Convenience methods
# Instead of having to write:
# redis_pool.perform {|redis| redis.get('key')}
# The following can be used
# redis_pool.get('key')
#
# This applies to all Redis methods except quit. RedisPool#quit calls quit
# on all instances of Redis already in the pool
#
# Dependencies: gene_pool
#
require 'gene_pool'
class RedisPool
# Each thread can request a Redis Connection and then return it once it is no
# longer needed by that thread. Rather than requesting a connection directly using
# Redis.new, pass a block so that the Redis connection
# is automatically returned to the pool. For example:
# redis_pool.perform do |redis|
# .. do something with the redis connection here
# end
#
# Parameters:
# see regular session parameters from: Redis.new
#
# Additional parameters for controlling the session pool itself
# :pool_name Name of the pool as it shows up in the logger.
# Default: 'RedisPool'
# :pool_size Maximum Pool Size. Default: 10
# The pool only grows as needed and will never exceed
# :pool_size
# :pool_warn_timeout Amount of time to wait before logging a warning when a
# session in the pool is not available. Measured in seconds
# Default: 5.0
# :pool_logger Supply a logger that responds to #debug, #info, #warn and #debug?
# For example: Rails.logger
# Default: None.
# Example:
# redis_pool = RedisPool.new(config)
#
# # Perform a single call to redis
# value = redis_pool.get('some_key')
#
# # Perform multiple calls using the same connection:
# redis_pool.perform do |redis|
# redis.set('some_key', 'Hello World')
# value = redis.get('some_key')
# end
def initialize(parms={})
# Save Session parms since it will be used every time a new session is
# created in the pool
config = parms.dup
config[:logger] = SemanticLogger['Redis::Client']
@pool = GenePool.new(
:name => parms[:pool_name] || self.class.name,
:pool_size => parms[:pool_size] || 10,
:warn_timeout => parms[:pool_warn_timeout] || 5,
:logger => parms[:pool_logger]) do
redis = Redis.new(config)
# Remove locking overhead since GenePool takes care of locking
redis.instance_eval("def synchronize\n yield(@client)\n end")
redis
end
end
# Obtain a Redis instance from the pool and pass it to the supplied block
# The session is automatically returned to the pool once the block completes
def perform(&block)
@pool.with_connection_auto_remove &block
end
# Quit all active Redis connections
def quit
@pool.each do |r|
r.quit
end
end
# Manually implement type since it is already implemented as a deprecated method
def type(key)
@pool.with_connection_auto_remove {|r| r.type(key)}
end
# Convenience methods
# Use with Care, multiple calls should rather be grouped into a single
# block using RedisPool#perform
#
# Obtain a Redis instance from the pool, call the matching redis method
# Return the Redis instance to the pool on completion
#
# Example:
# redis_pool = RedisPool.new(config)
#
# # Since we are only setting one key, we can use the shortcut
# redis_pool.set('some_key', 'Hello World')
# .....
# value = redis_pool.get('some_key')
# ....
# # Release connections to Redis
# redis_pool.quit
(Redis.instance_methods - Object.methods).each do |method_name|
unless self.instance_methods.include? method_name
eval "def #{method_name}(*args)\n @pool.with_connection_auto_remove {|r| r.#{method_name}(*args)}\nend"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment