activerecord-bogacs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +26 -0
- data/Gemfile +33 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +167 -0
- data/activerecord-bogacs.gemspec +26 -0
- data/lib/active_record/bogacs.rb +55 -0
- data/lib/active_record/bogacs/default_pool.rb +672 -0
- data/lib/active_record/bogacs/false_pool.rb +259 -0
- data/lib/active_record/bogacs/pool_support.rb +21 -0
- data/lib/active_record/bogacs/shareable_pool.rb +255 -0
- data/lib/active_record/bogacs/version.rb +5 -0
- data/lib/active_record/connection_adapters/adapter_compat.rb +57 -0
- data/lib/active_record/shared_connection.rb +24 -0
- data/test/active_record/bogacs/default_pool_test.rb +34 -0
- data/test/active_record/bogacs/false_pool_test.rb +200 -0
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +186 -0
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +429 -0
- data/test/active_record/bogacs/shareable_pool_helper.rb +81 -0
- data/test/active_record/builtin_pool_test.rb +18 -0
- data/test/active_record/connection_pool_test_methods.rb +336 -0
- data/test/test_helper.rb +304 -0
- metadata +130 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
require 'active_record/bogacs/pool_support'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module Bogacs
|
6
|
+
class FalsePool
|
7
|
+
|
8
|
+
include PoolSupport
|
9
|
+
|
10
|
+
include ThreadSafe::Util::CheapLockable
|
11
|
+
alias_method :synchronize, :cheap_synchronize
|
12
|
+
|
13
|
+
attr_accessor :automatic_reconnect
|
14
|
+
|
15
|
+
attr_reader :size, :spec
|
16
|
+
|
17
|
+
def initialize(spec)
|
18
|
+
@connected = nil
|
19
|
+
|
20
|
+
@spec = spec
|
21
|
+
@size = nil
|
22
|
+
#@automatic_reconnect = true
|
23
|
+
|
24
|
+
@reserved_connections = ThreadSafe::Cache.new #:initial_capacity => @size
|
25
|
+
end
|
26
|
+
|
27
|
+
# @private replacement for attr_reader :connections
|
28
|
+
def connections; @reserved_connections.values end
|
29
|
+
|
30
|
+
# @private attr_reader :reaper
|
31
|
+
def reaper; end
|
32
|
+
|
33
|
+
# @private
|
34
|
+
def checkout_timeout; end
|
35
|
+
|
36
|
+
# Retrieve the connection associated with the current thread, or call
|
37
|
+
# #checkout to obtain one if necessary.
|
38
|
+
#
|
39
|
+
# #connection can be called any number of times; the connection is
|
40
|
+
# held in a hash keyed by the thread id.
|
41
|
+
def connection
|
42
|
+
@reserved_connections[current_connection_id] ||= checkout
|
43
|
+
end
|
44
|
+
|
45
|
+
# Is there an open connection that is being used for the current thread?
|
46
|
+
def active_connection?
|
47
|
+
conn = @reserved_connections[current_connection_id]
|
48
|
+
conn ? conn.in_use? : false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Signal that the thread is finished with the current connection.
|
52
|
+
# #release_connection releases the connection-thread association
|
53
|
+
# and returns the connection to the pool.
|
54
|
+
def release_connection(with_id = current_connection_id)
|
55
|
+
conn = @reserved_connections.delete(with_id)
|
56
|
+
checkin conn if conn
|
57
|
+
end
|
58
|
+
|
59
|
+
# If a connection already exists yield it to the block. If no connection
|
60
|
+
# exists checkout a connection, yield it to the block, and checkin the
|
61
|
+
# connection when finished.
|
62
|
+
def with_connection
|
63
|
+
connection_id = current_connection_id
|
64
|
+
fresh_connection = true unless active_connection?
|
65
|
+
yield connection
|
66
|
+
ensure
|
67
|
+
release_connection(connection_id) if fresh_connection
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns true if a connection has already been opened.
|
71
|
+
def connected?; @connected end
|
72
|
+
|
73
|
+
# Disconnects all connections in the pool, and clears the pool.
|
74
|
+
def disconnect!
|
75
|
+
synchronize do
|
76
|
+
@connected = false
|
77
|
+
|
78
|
+
connections = @reserved_connections.values
|
79
|
+
@reserved_connections.clear
|
80
|
+
|
81
|
+
connections.each do |conn|
|
82
|
+
checkin conn
|
83
|
+
conn.disconnect!
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Clears the cache which maps classes.
|
89
|
+
def clear_reloadable_connections!
|
90
|
+
synchronize do
|
91
|
+
@connected = false
|
92
|
+
|
93
|
+
connections = @reserved_connections.values
|
94
|
+
@reserved_connections.clear
|
95
|
+
|
96
|
+
connections.each do |conn|
|
97
|
+
checkin conn
|
98
|
+
conn.disconnect! if conn.requires_reloading?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Verify active connections and remove and disconnect connections
|
104
|
+
# associated with stale threads.
|
105
|
+
# @private AR 3.2 compatibility
|
106
|
+
def verify_active_connections!
|
107
|
+
synchronize do
|
108
|
+
clear_stale_cached_connections!
|
109
|
+
connections = @reserved_connections.values
|
110
|
+
connections.each do |connection|
|
111
|
+
connection.verify!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end if ActiveRecord::VERSION::MAJOR < 4
|
115
|
+
|
116
|
+
# Return any checked-out connections back to the pool by threads that
|
117
|
+
# are no longer alive.
|
118
|
+
# @private AR 3.2 compatibility
|
119
|
+
def clear_stale_cached_connections!
|
120
|
+
keys = Thread.list.find_all { |t| t.alive? }.map(&:object_id)
|
121
|
+
keys = @reserved_connections.keys - keys
|
122
|
+
keys.each do |key|
|
123
|
+
if conn = @reserved_connections[key]
|
124
|
+
checkin conn, false # no release
|
125
|
+
@reserved_connections.delete(key)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end if ActiveRecord::VERSION::MAJOR < 4
|
129
|
+
|
130
|
+
# Check-out a database connection from the pool, indicating that you want
|
131
|
+
# to use it. You should call #checkin when you no longer need this.
|
132
|
+
#
|
133
|
+
# This is done by either returning and leasing existing connection, or by
|
134
|
+
# creating a new connection and leasing it.
|
135
|
+
#
|
136
|
+
# If all connections are leased and the pool is at capacity (meaning the
|
137
|
+
# number of currently leased connections is greater than or equal to the
|
138
|
+
# size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
|
139
|
+
#
|
140
|
+
# Returns: an AbstractAdapter object.
|
141
|
+
#
|
142
|
+
# Raises:
|
143
|
+
# - ConnectionTimeoutError: no connection can be obtained from the pool.
|
144
|
+
def checkout
|
145
|
+
#synchronize do
|
146
|
+
conn = checkout_new_connection # acquire_connection
|
147
|
+
conn.lease
|
148
|
+
conn.run_callbacks(:checkout) { conn.verify! } # checkout_and_verify(conn)
|
149
|
+
conn
|
150
|
+
#end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Check-in a database connection back into the pool, indicating that you
|
154
|
+
# no longer need this connection.
|
155
|
+
#
|
156
|
+
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
157
|
+
# calling +checkout+ on this pool.
|
158
|
+
def checkin(conn, do_release = true)
|
159
|
+
release(conn) if do_release
|
160
|
+
#synchronize do
|
161
|
+
conn.run_callbacks(:checkin) { conn.expire }
|
162
|
+
#release conn
|
163
|
+
#@available.add conn
|
164
|
+
#end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Remove a connection from the connection pool. The connection will
|
168
|
+
# remain open and active but will no longer be managed by this pool.
|
169
|
+
def remove(conn)
|
170
|
+
release(conn)
|
171
|
+
end
|
172
|
+
|
173
|
+
# @private
|
174
|
+
def reap
|
175
|
+
# we do not really manage the connection pool - nothing to do ...
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
# Acquire a connection by one of 1) immediately removing one
|
181
|
+
# from the queue of available connections, 2) creating a new
|
182
|
+
# connection if the pool is not at capacity, 3) waiting on the
|
183
|
+
# queue for a connection to become available.
|
184
|
+
#
|
185
|
+
# Raises:
|
186
|
+
# - ConnectionTimeoutError if a connection could not be acquired
|
187
|
+
def acquire_connection
|
188
|
+
# underlying pool will poll and block if "empty" (all checked-out)
|
189
|
+
#if conn = @available.poll
|
190
|
+
#conn
|
191
|
+
#elsif @connections.size < @size
|
192
|
+
#checkout_new_connection
|
193
|
+
#else
|
194
|
+
#reap
|
195
|
+
#@available.poll(@checkout_timeout)
|
196
|
+
#end
|
197
|
+
checkout_new_connection
|
198
|
+
end
|
199
|
+
|
200
|
+
def release(conn, owner = nil)
|
201
|
+
thread_id = owner.object_id unless owner.nil?
|
202
|
+
|
203
|
+
thread_id ||=
|
204
|
+
if @reserved_connections[conn_id = current_connection_id] == conn
|
205
|
+
conn_id
|
206
|
+
else
|
207
|
+
connections = @reserved_connections
|
208
|
+
connections.keys.find { |k| connections[k] == conn }
|
209
|
+
end
|
210
|
+
|
211
|
+
@reserved_connections.delete thread_id if thread_id
|
212
|
+
end
|
213
|
+
|
214
|
+
def checkout_new_connection
|
215
|
+
# NOTE: automatic reconnect seems to make no sense for us!
|
216
|
+
#raise ConnectionNotEstablished unless @automatic_reconnect
|
217
|
+
|
218
|
+
begin
|
219
|
+
conn = new_connection
|
220
|
+
rescue ConnectionTimeoutError => e
|
221
|
+
raise e
|
222
|
+
rescue => e
|
223
|
+
raise ConnectionTimeoutError, e.message if _timeout_error?(e)
|
224
|
+
raise e
|
225
|
+
end
|
226
|
+
conn.pool = self
|
227
|
+
synchronize { @connected = true } if @connected != true
|
228
|
+
conn
|
229
|
+
end
|
230
|
+
|
231
|
+
#def checkout_and_verify(conn)
|
232
|
+
# conn.run_callbacks(:checkout) { conn.verify! }
|
233
|
+
# conn
|
234
|
+
#end
|
235
|
+
|
236
|
+
# sample on JRuby + Tomcat JDBC :
|
237
|
+
# ActiveRecord::JDBCError(<The driver encountered an unknown error:
|
238
|
+
# org.apache.tomcat.jdbc.pool.PoolExhaustedException:
|
239
|
+
# [main] Timeout: Pool empty. Unable to fetch a connection in 2 seconds,
|
240
|
+
# none available[size:10; busy:10; idle:0; lastwait:2500].>
|
241
|
+
# )
|
242
|
+
|
243
|
+
def _timeout_error?(error)
|
244
|
+
error.inspect =~ /timeout/i
|
245
|
+
end
|
246
|
+
|
247
|
+
# def _timeout_error?(error); end # TODO: not sure what to do on MRI and friends
|
248
|
+
#
|
249
|
+
# def _timeout_error?(error)
|
250
|
+
# if error.is_a?(JDBCError)
|
251
|
+
# if sql_exception = error.sql_exception
|
252
|
+
# return true if sql_exception.to_s =~ /timeout/i
|
253
|
+
# end
|
254
|
+
# end
|
255
|
+
# end if defined? ArJdbc
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#require 'thread_safe'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Bogacs
|
5
|
+
module PoolSupport
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
#base.send :include, ThreadSafe::Util::CheapLockable
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_connection
|
12
|
+
Base.send(spec.adapter_method, spec.config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_connection_id
|
16
|
+
Base.connection_id ||= Thread.current.object_id # TODO
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/connection_pool'
|
2
|
+
require 'thread'
|
3
|
+
require 'thread_safe'
|
4
|
+
require 'atomic'
|
5
|
+
|
6
|
+
# NOTE: needs explicit configuration - before connection gets established e.g.
|
7
|
+
#
|
8
|
+
# pool_class = ActiveRecord::ConnectionAdapters::ShareableConnectionPool
|
9
|
+
# ActiveRecord::ConnectionAdapters::ConnectionHandler.connection_pool_class = pool_class
|
10
|
+
#
|
11
|
+
module ActiveRecord
|
12
|
+
module Bogacs
|
13
|
+
class ShareablePool < ConnectionAdapters::ConnectionPool # TODO do not override?!
|
14
|
+
include ThreadSafe::Util::CheapLockable
|
15
|
+
|
16
|
+
DEFAULT_SHARED_POOL = 0.25 # only allow 25% of the pool size to be shared
|
17
|
+
MAX_THREAD_SHARING = 5 # not really a strict limit but should hold
|
18
|
+
|
19
|
+
attr_reader :shared_size
|
20
|
+
|
21
|
+
# @override
|
22
|
+
def initialize(spec)
|
23
|
+
super(spec) # ConnectionPool#initialize
|
24
|
+
shared_size = spec.config[:shared_pool]
|
25
|
+
shared_size = shared_size ? shared_size.to_f : DEFAULT_SHARED_POOL
|
26
|
+
# size 0.0 - 1.0 assumes percentage of the pool size
|
27
|
+
shared_size = ( @size * shared_size ).round if shared_size <= 1.0
|
28
|
+
@shared_size = shared_size.to_i
|
29
|
+
@shared_connections = ThreadSafe::Cache.new # :initial_capacity => @shared_size, :concurrency_level => 20
|
30
|
+
end
|
31
|
+
|
32
|
+
# @override
|
33
|
+
def connection
|
34
|
+
# TODO we assume here a single pool - multiple pool support not implemented!
|
35
|
+
Thread.current[:shared_pool_connection] || begin # super - simplified :
|
36
|
+
super # @reserved_connections.compute_if_absent(current_connection_id) { checkout }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @override
|
41
|
+
def active_connection?
|
42
|
+
if shared_conn = Thread.current[:shared_pool_connection]
|
43
|
+
return shared_conn.in_use?
|
44
|
+
end
|
45
|
+
super_active_connection? current_connection_id
|
46
|
+
end
|
47
|
+
|
48
|
+
# @override called from ConnectionManagement middle-ware (when finished)
|
49
|
+
def release_connection(with_id = current_connection_id)
|
50
|
+
if reserved_conn = @reserved_connections[with_id]
|
51
|
+
if shared_count = @shared_connections[reserved_conn]
|
52
|
+
cheap_synchronize do # lock due #get_shared_connection ... not needed ?!
|
53
|
+
# NOTE: the other option is to not care about shared here at all ...
|
54
|
+
if shared_count.get == 0 # releasing a shared connection
|
55
|
+
release_shared_connection(reserved_conn)
|
56
|
+
#else return false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
else # check back-in non-shared connections
|
60
|
+
checkin reserved_conn # (what super does)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @override
|
66
|
+
def disconnect!
|
67
|
+
cheap_synchronize { @shared_connections.clear; super }
|
68
|
+
end
|
69
|
+
|
70
|
+
# @override
|
71
|
+
def clear_reloadable_connections!
|
72
|
+
cheap_synchronize { @shared_connections.clear; super }
|
73
|
+
end
|
74
|
+
|
75
|
+
# @override
|
76
|
+
# @note called from #reap thus the pool should work with reaper
|
77
|
+
def remove(conn)
|
78
|
+
cheap_synchronize { @shared_connections.delete(conn); super }
|
79
|
+
end
|
80
|
+
|
81
|
+
# # Return any checked-out connections back to the pool by threads that
|
82
|
+
# # are no longer alive.
|
83
|
+
# # @private AR 3.2 compatibility
|
84
|
+
# def clear_stale_cached_connections!
|
85
|
+
# keys = Thread.list.find_all { |t| t.alive? }.map(&:object_id)
|
86
|
+
# keys = @reserved_connections.keys - keys
|
87
|
+
# keys.each do |key|
|
88
|
+
# release_connection(key)
|
89
|
+
# @reserved_connections.delete(key)
|
90
|
+
# end
|
91
|
+
# end if ActiveRecord::VERSION::MAJOR < 4
|
92
|
+
|
93
|
+
# TODO take care of explicit connection.close (`pool.checkin self`) ?
|
94
|
+
|
95
|
+
# Custom API :
|
96
|
+
|
97
|
+
def release_shared_connection(connection)
|
98
|
+
if connection == Thread.current[:shared_pool_connection]
|
99
|
+
Thread.current[:shared_pool_connection] = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
@shared_connections.delete(connection)
|
103
|
+
checkin connection
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_shared_connection
|
107
|
+
# with_shared_connection call nested in the same thread
|
108
|
+
if connection = Thread.current[:shared_pool_connection]
|
109
|
+
emulated_checkout(connection)
|
110
|
+
return yield connection
|
111
|
+
end
|
112
|
+
|
113
|
+
start = Time.now if DEBUG
|
114
|
+
begin
|
115
|
+
connection_id = current_connection_id
|
116
|
+
# if there's a 'regular' connection on the thread use it as super
|
117
|
+
if super_active_connection?(connection_id) # for current thread
|
118
|
+
connection = self.connection # do not mark as shared
|
119
|
+
DEBUG && debug("with_shared_conn 10 got active = #{connection.to_s}")
|
120
|
+
# otherwise if we have a shared connection - use that one :
|
121
|
+
elsif connection = get_shared_connection
|
122
|
+
emulated_checkout(connection); shared = true
|
123
|
+
DEBUG && debug("with_shared_conn 20 got shared = #{connection.to_s}")
|
124
|
+
else
|
125
|
+
cheap_synchronize do
|
126
|
+
# check shared again as/if threads end up sync-ing up here :
|
127
|
+
if connection = get_shared_connection
|
128
|
+
emulated_checkout(connection)
|
129
|
+
DEBUG && debug("with_shared_conn 21 got shared = #{connection.to_s}")
|
130
|
+
end # here we acquire but a connection from the pool
|
131
|
+
# TODO the bottle-neck for concurrency doing sync { checkout } :
|
132
|
+
unless connection # here we acquire a connection from the pool
|
133
|
+
connection = self.checkout # might block if pool fully used
|
134
|
+
add_shared_connection(connection)
|
135
|
+
DEBUG && debug("with_shared_conn 30 acq shared = #{connection.to_s}")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
shared = true
|
139
|
+
end
|
140
|
+
|
141
|
+
Thread.current[:shared_pool_connection] = connection if shared
|
142
|
+
|
143
|
+
DEBUG && debug("with_shared_conn obtaining a connection took #{(Time.now - start) * 1000}ms")
|
144
|
+
yield connection
|
145
|
+
ensure
|
146
|
+
Thread.current[:shared_pool_connection] = nil # if shared
|
147
|
+
rem_shared_connection(connection) if shared
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def super_active_connection?(connection_id = current_connection_id)
|
154
|
+
(@reserved_connections.get(connection_id) || ( return false )).in_use?
|
155
|
+
end
|
156
|
+
|
157
|
+
def super_active_connection?(connection_id = current_connection_id)
|
158
|
+
synchronize do
|
159
|
+
(@reserved_connections[connection_id] || ( return false )).in_use?
|
160
|
+
end
|
161
|
+
end if ActiveRecord::VERSION::MAJOR < 4
|
162
|
+
|
163
|
+
def acquire_connection_no_wait?
|
164
|
+
synchronize do
|
165
|
+
@available.send(:can_remove_no_wait?) || @connections.size < @size
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# get a (shared) connection that is least shared among threads (or nil)
|
170
|
+
# nil gets returned if it's 'better' to checkout a new one to be shared
|
171
|
+
# ... to better utilize shared connection reuse among multiple threads
|
172
|
+
def get_shared_connection # (lock = nil)
|
173
|
+
least_count = MAX_THREAD_SHARING + 1; least_shared = nil
|
174
|
+
shared_connections_size = @shared_connections.size
|
175
|
+
|
176
|
+
@shared_connections.each_pair do |connection, shared_count|
|
177
|
+
next if shared_count.get >= MAX_THREAD_SHARING
|
178
|
+
if ( shared_count = shared_count.get ) < least_count
|
179
|
+
DEBUG && debug(" get_shared_conn loop : #{connection.to_s} shared #{shared_count}-time(s)")
|
180
|
+
# ! DO NOT return connection if shared_count == 0
|
181
|
+
least_count = shared_count; least_shared = connection
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if least_count > 0
|
186
|
+
if shared_connections_size < @shared_connections.size
|
187
|
+
DEBUG && debug(" get_shared_conn retry (shared connection added)")
|
188
|
+
return get_shared_connection # someone else added something re-try
|
189
|
+
end
|
190
|
+
if ( @shared_connections.size < @shared_size ) && acquire_connection_no_wait?
|
191
|
+
DEBUG && debug(" get_shared_conn return none - acquire from pool")
|
192
|
+
return nil # we should rather 'get' a new shared one from the pool
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# we did as much as could without a lock - now sync due possible release
|
197
|
+
cheap_synchronize do # TODO although this likely might be avoided ...
|
198
|
+
# should try again if possibly the same connection got released :
|
199
|
+
unless least_count = @shared_connections[least_shared]
|
200
|
+
DEBUG && debug(" get_shared_conn retry (connection got released)")
|
201
|
+
return get_shared_connection
|
202
|
+
end
|
203
|
+
least_count.update { |v| v + 1 }
|
204
|
+
end if least_shared
|
205
|
+
|
206
|
+
DEBUG && debug(" get_shared_conn least shared = #{least_shared.to_s}")
|
207
|
+
least_shared # might be nil in that case we'll likely wait (as super)
|
208
|
+
end
|
209
|
+
|
210
|
+
def add_shared_connection(connection)
|
211
|
+
@shared_connections[connection] = Atomic.new(1)
|
212
|
+
end
|
213
|
+
|
214
|
+
def rem_shared_connection(connection)
|
215
|
+
if shared_count = @shared_connections[connection]
|
216
|
+
# shared_count.update { |v| v - 1 } # NOTE: likely fine without lock!
|
217
|
+
cheap_synchronize do # give it back to the pool
|
218
|
+
shared_count.update { |v| v - 1 } # might give it back if :
|
219
|
+
release_shared_connection(connection) if shared_count.get == 0
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def emulated_checkin(connection)
|
225
|
+
# NOTE: not sure we'd like to run `run_callbacks :checkin {}` here ...
|
226
|
+
connection.expire
|
227
|
+
end
|
228
|
+
|
229
|
+
def emulated_checkout(connection)
|
230
|
+
# NOTE: not sure we'd like to run `run_callbacks :checkout {}` here ...
|
231
|
+
connection.lease; # connection.verify! auto-reconnect should do this
|
232
|
+
end
|
233
|
+
|
234
|
+
DEBUG = begin
|
235
|
+
debug = ENV['DB_POOL_DEBUG'].to_s
|
236
|
+
if debug.to_s == 'false' then false
|
237
|
+
elsif ! debug.empty?
|
238
|
+
log_dev = case debug
|
239
|
+
when 'STDOUT', 'stdout' then STDOUT
|
240
|
+
when 'STDERR', 'stderr' then STDERR
|
241
|
+
when 'true' then ActiveRecord::Base.logger
|
242
|
+
else File.expand_path(debug)
|
243
|
+
end
|
244
|
+
require 'logger'; Logger.new log_dev
|
245
|
+
else nil
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def debug(msg); DEBUG.debug msg end
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|