Class: ODBA::ConnectionPool

Inherits:
Object show all
Defined in:
lib/odba/connection_pool.rb

Constant Summary collapse

POOL_SIZE =
5
SETUP_RETRIES =
3
CONNECTION_LOST_MESSAGES =

ydbd-pg maps every PG::Error - including connection level failures - onto DBI::ProgrammingError, so for that class the message is the only thing that tells a dead connection apart from a malformed statement. Without this list a restart of the database server leaves every pooled connection permanently broken, because the reconnect below never runs.

/
  no\ connection\ to\ the\ server
  | PQsocket
  | server\ closed\ the\ connection
  | connection\ not\ open
  | terminating\ connection
  | SSL\ connection\ has\ been\ closed
  | could\ not\ connect\ to\ server
/xi

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*dbi_args) ⇒ ConnectionPool

All connections are delegated to DBI. The constructor simply records the DBI-arguments and reuses them to setup connections when needed.



30
31
32
33
34
35
36
# File 'lib/odba/connection_pool.rb', line 30

def initialize(*dbi_args)
  @dbi_args = dbi_args
  @opts = @dbi_args.last.is_a?(Hash) ? @dbi_args.pop : {}
  @connections = []
  @mutex = Mutex.new
  connect
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

:nodoc:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/odba/connection_pool.rb', line 50

def method_missing(method, *args, &block) # :nodoc:
  tries = SETUP_RETRIES
  begin
    next_connection { |conn|
      conn.send(method, *args, &block)
    }
  rescue NoMethodError, DBI::Error => e
    warn e
    if tries > 0 && retryable?(e)
      sleep(SETUP_RETRIES - tries)
      tries -= 1
      reconnect
      retry
    else
      raise
    end
  end
end

Instance Attribute Details

#connectionsObject (readonly)

attr_reader :connections



27
28
29
# File 'lib/odba/connection_pool.rb', line 27

def connections
  @connections
end

#dbi_argsObject (readonly)

attr_reader :connections



27
28
29
# File 'lib/odba/connection_pool.rb', line 27

def dbi_args
  @dbi_args
end

Instance Method Details

#_connectObject

:nodoc:



85
86
87
88
89
90
91
92
93
# File 'lib/odba/connection_pool.rb', line 85

def _connect # :nodoc:
  POOL_SIZE.times {
    conn = DBI.connect(*@dbi_args)
    if encoding = @opts[:client_encoding]
      conn.execute "SET CLIENT_ENCODING TO '#{encoding}'"
    end
    @connections.push(conn)
  }
end

#_disconnectObject

:nodoc:



99
100
101
102
103
104
105
106
107
108
# File 'lib/odba/connection_pool.rb', line 99

def _disconnect # :nodoc:
  while (conn = @connections.shift)
    begin
      conn.disconnect
    rescue DBI::InterfaceError, Exception
      ## we're not interested, since we are disconnecting anyway
      nil
    end
  end
end

#connectObject

:nodoc:



81
82
83
# File 'lib/odba/connection_pool.rb', line 81

def connect # :nodoc:
  @mutex.synchronize { _connect }
end

#disconnectObject

:nodoc:



95
96
97
# File 'lib/odba/connection_pool.rb', line 95

def disconnect # :nodoc:
  @mutex.synchronize { _disconnect }
end

#next_connectionObject

:nodoc:



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/odba/connection_pool.rb', line 38

def next_connection # :nodoc:
  conn = nil
  @mutex.synchronize {
    conn = @connections.shift
  }
  yield(conn)
ensure
  @mutex.synchronize {
    @connections.push(conn)
  }
end

#reconnectObject

:nodoc:



110
111
112
113
114
115
# File 'lib/odba/connection_pool.rb', line 110

def reconnect # :nodoc:
  @mutex.synchronize {
    _disconnect
    _connect
  }
end

#retryable?(error) ⇒ Boolean

A statement that failed because the connection is gone is worth retrying on a fresh one. A statement that failed on its own merits is not - retrying it would only repeat the same error three times.

Returns:

  • (Boolean)


72
73
74
75
# File 'lib/odba/connection_pool.rb', line 72

def retryable?(error) # :nodoc:
  !error.is_a?(DBI::ProgrammingError) ||
    CONNECTION_LOST_MESSAGES.match?(error.message.to_s)
end

#sizeObject Also known as: pool_size



77
78
79
# File 'lib/odba/connection_pool.rb', line 77

def size
  @connections.size
end