Class: Authorize::Redis::ConnectionManager

Inherits:
Object
  • Object
show all
Defined in:
lib/authorize/redis/connection_manager.rb

Overview

This class arbitrates access to a Redis server ensuring that threads don’t concurrently access the same connection yehudakatz.com/2010/08/14/threads-in-ruby-enough-already/ blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html Inspired by the ConnectionPool class in Rails 2.3 Notes on thread-safety: Because instances of this class are expected to be shared across threads, access to all non-local variables and “constants” need to be synchronized. We assume that Hash read/assign operations are thread-safe. We also require that the value returned by #current_connection_id not be shared across threads.

Defined Under Namespace

Classes: ConnectionError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(specification, options = {}) ⇒ ConnectionManager

Creates a new ConnectionPool object. specification is a ConnectionSpecification object which describes database connection parameters



21
22
23
24
25
26
# File 'lib/authorize/redis/connection_manager.rb', line 21

def initialize(specification, options = {})
  @options = {:size => 5}.merge(options)
  @pool = ResourcePool.new(@options[:size], lambda {specification.connect!})
  @connection_map = {} # Connections mapped to threads
  @mutex = Monitor.new
end

Instance Attribute Details

#poolObject (readonly)

Returns the value of attribute pool.



17
18
19
# File 'lib/authorize/redis/connection_manager.rb', line 17

def pool
  @pool
end

Instance Method Details

#acquire_connectionObject Also known as: connection

Retrieve the connection associated with the current thread, or checkout one from the pool as required. #connection can be called any number of times; the connection is held in a hash with a thread-specific key.



30
31
32
# File 'lib/authorize/redis/connection_manager.rb', line 30

def acquire_connection
  @connection_map[current_connection_id] ||= @pool.checkout(10)
end

#expire_stale_connections!Object

Expire stale connections



66
67
68
69
70
# File 'lib/authorize/redis/connection_manager.rb', line 66

def expire_stale_connections!
  @pool.expire do |connection, reserved_flag|
    !connection.client.connected?
  end
end

#recover_unused_connectionsObject

Identifies connections in the death grip of defunct threads, removes them from the map and checks them back into the pool Because this method operates across connections for multiple threads (not just the current thread), concurrent execution needs to be synchronized to be thread-safe.



54
55
56
57
58
59
60
61
62
63
# File 'lib/authorize/redis/connection_manager.rb', line 54

def recover_unused_connections
  tids = Thread.list.select{|t| t.alive?}.map(&:object_id)
  @mutex.synchronize do
    cids = @connection_map.keys
    (cids - tids).each do |sid|
      c = @connection_map.delete(sid)
      @pool.checkin(c)
    end
  end
end

#release_connectionObject

Signal that the thread is finished with the current connection. #release_connection releases the connection-thread association and returns the connection to the pool.



38
39
40
41
# File 'lib/authorize/redis/connection_manager.rb', line 38

def release_connection
  c = @connection_map.delete(current_connection_id)
  @pool.checkin(c) if c
end

#reset!Object

Revert to a freshly initialized state



73
74
75
76
77
78
79
# File 'lib/authorize/redis/connection_manager.rb', line 73

def reset!
  @mutex.synchronize do
    @pool.clear!
    @connection_map.clear
  end
  self
end

#with_connectionObject

Checks out a connection from the pool, yields it to a block and checks it back into the pool when the block finishes.



44
45
46
47
48
49
# File 'lib/authorize/redis/connection_manager.rb', line 44

def with_connection
  c = @pool.checkout
  yield c
ensure
  @pool.checkin(c)
end