activerecord-bogacs 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +82 -0
- data/Gemfile +2 -15
- data/Rakefile +8 -6
- data/activerecord-bogacs.gemspec +6 -6
- data/lib/active_record/bogacs/connection_handler.rb +1 -1
- data/lib/active_record/bogacs/default_pool.rb +250 -90
- data/lib/active_record/bogacs/false_pool.rb +87 -53
- data/lib/active_record/bogacs/pool_support.rb +26 -8
- data/lib/active_record/bogacs/shareable_pool.rb +11 -15
- data/lib/active_record/bogacs/thread_safe/synchronized.rb +18 -22
- data/lib/active_record/bogacs/thread_safe.rb +3 -90
- data/lib/active_record/bogacs/validator.rb +14 -19
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/connection_adapters/adapter_compat.rb +41 -25
- data/lib/active_record/connection_adapters/pool_class.rb +33 -2
- data/test/active_record/bogacs/false_pool_test.rb +66 -78
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +6 -3
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +3 -2
- data/test/active_record/bogacs/shareable_pool_helper.rb +1 -1
- data/test/active_record/bogacs/validator_test.rb +22 -28
- data/test/active_record/connection_pool_test_methods.rb +24 -20
- data/test/test_helper.rb +40 -25
- metadata +30 -17
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'active_record/version'
|
2
2
|
|
3
|
+
require 'concurrent/atomic/atomic_boolean'
|
4
|
+
|
5
|
+
require 'active_record/connection_adapters/adapter_compat'
|
3
6
|
require 'active_record/bogacs/pool_support'
|
4
7
|
require 'active_record/bogacs/thread_safe'
|
5
8
|
|
@@ -16,17 +19,17 @@ module ActiveRecord
|
|
16
19
|
attr_reader :size, :spec
|
17
20
|
|
18
21
|
def initialize(spec)
|
19
|
-
|
22
|
+
super()
|
20
23
|
|
21
24
|
@spec = spec
|
22
25
|
@size = nil
|
23
26
|
@automatic_reconnect = nil
|
27
|
+
@lock_thread = false
|
24
28
|
|
25
|
-
@
|
26
|
-
end
|
29
|
+
@thread_cached_conns = ThreadSafe::Map.new
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
@connected = ::Concurrent::AtomicBoolean.new
|
32
|
+
end
|
30
33
|
|
31
34
|
# @private attr_reader :reaper
|
32
35
|
def reaper; end
|
@@ -40,20 +43,21 @@ module ActiveRecord
|
|
40
43
|
# #connection can be called any number of times; the connection is
|
41
44
|
# held in a hash keyed by the thread id.
|
42
45
|
def connection
|
43
|
-
|
46
|
+
connection_id = current_connection_id(current_thread)
|
47
|
+
@thread_cached_conns[connection_id] ||= checkout
|
44
48
|
end
|
45
49
|
|
46
50
|
# Is there an open connection that is being used for the current thread?
|
47
51
|
def active_connection?
|
48
|
-
|
49
|
-
|
52
|
+
connection_id = current_connection_id(current_thread)
|
53
|
+
@thread_cached_conns[connection_id]
|
50
54
|
end
|
51
55
|
|
52
56
|
# Signal that the thread is finished with the current connection.
|
53
57
|
# #release_connection releases the connection-thread association
|
54
58
|
# and returns the connection to the pool.
|
55
|
-
def release_connection(
|
56
|
-
conn = @
|
59
|
+
def release_connection(owner_thread = Thread.current)
|
60
|
+
conn = @thread_cached_conns.delete(current_connection_id(owner_thread))
|
57
61
|
checkin conn if conn
|
58
62
|
end
|
59
63
|
|
@@ -62,40 +66,63 @@ module ActiveRecord
|
|
62
66
|
# connection when finished.
|
63
67
|
def with_connection
|
64
68
|
connection_id = current_connection_id
|
65
|
-
|
66
|
-
|
69
|
+
unless conn = @thread_cached_conns[connection_id]
|
70
|
+
conn = connection
|
71
|
+
fresh_connection = true
|
72
|
+
end
|
73
|
+
yield conn
|
67
74
|
ensure
|
68
|
-
release_connection
|
75
|
+
release_connection if fresh_connection
|
69
76
|
end
|
70
77
|
|
71
78
|
# Returns true if a connection has already been opened.
|
72
|
-
def connected?; @connected end
|
79
|
+
def connected?; @connected.true? end
|
80
|
+
|
81
|
+
# @private replacement for attr_reader :connections
|
82
|
+
def connections; @thread_cached_conns.values end
|
73
83
|
|
74
84
|
# Disconnects all connections in the pool, and clears the pool.
|
75
85
|
def disconnect!
|
76
86
|
synchronize do
|
77
|
-
@connected
|
78
|
-
|
79
|
-
connections = @reserved_connections.values
|
80
|
-
@reserved_connections.clear
|
87
|
+
@connected.make_false
|
81
88
|
|
89
|
+
connections = @thread_cached_conns.values
|
90
|
+
@thread_cached_conns.clear
|
82
91
|
connections.each do |conn|
|
83
|
-
|
92
|
+
if conn.in_use?
|
93
|
+
conn.steal!
|
94
|
+
checkin conn
|
95
|
+
end
|
84
96
|
conn.disconnect!
|
85
97
|
end
|
86
98
|
end
|
87
99
|
end
|
88
100
|
|
101
|
+
def discard! # :nodoc:
|
102
|
+
synchronize do
|
103
|
+
return if @thread_cached_conns.nil? # already discarded
|
104
|
+
@connected.make_false
|
105
|
+
|
106
|
+
connections.each do |conn|
|
107
|
+
conn.discard!
|
108
|
+
end
|
109
|
+
@thread_cached_conns = nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
89
113
|
# Clears the cache which maps classes.
|
90
114
|
def clear_reloadable_connections!
|
91
115
|
synchronize do
|
92
|
-
@connected
|
116
|
+
@connected.make_false
|
93
117
|
|
94
|
-
connections = @
|
95
|
-
@
|
118
|
+
connections = @thread_cached_conns.values
|
119
|
+
@thread_cached_conns.clear
|
96
120
|
|
97
121
|
connections.each do |conn|
|
98
|
-
|
122
|
+
if conn.in_use?
|
123
|
+
conn.steal!
|
124
|
+
checkin conn
|
125
|
+
end
|
99
126
|
conn.disconnect! if conn.requires_reloading?
|
100
127
|
end
|
101
128
|
end
|
@@ -107,7 +134,7 @@ module ActiveRecord
|
|
107
134
|
def verify_active_connections!
|
108
135
|
synchronize do
|
109
136
|
clear_stale_cached_connections!
|
110
|
-
@
|
137
|
+
@thread_cached_conns.values.each(&:verify!)
|
111
138
|
end
|
112
139
|
end if ActiveRecord::VERSION::MAJOR < 4
|
113
140
|
|
@@ -115,12 +142,12 @@ module ActiveRecord
|
|
115
142
|
# are no longer alive.
|
116
143
|
# @private AR 3.2 compatibility
|
117
144
|
def clear_stale_cached_connections!
|
118
|
-
keys = Thread.list.find_all { |t| t.alive? }.map(
|
119
|
-
keys = @
|
145
|
+
keys = Thread.list.find_all { |t| t.alive? }.map { |t| current_connection_id(t) }
|
146
|
+
keys = @thread_cached_conns.keys - keys
|
120
147
|
keys.each do |key|
|
121
|
-
if conn = @
|
122
|
-
checkin conn,
|
123
|
-
@
|
148
|
+
if conn = @thread_cached_conns[key]
|
149
|
+
checkin conn, true # no release
|
150
|
+
@thread_cached_conns.delete(key)
|
124
151
|
end
|
125
152
|
end
|
126
153
|
end if ActiveRecord::VERSION::MAJOR < 4
|
@@ -131,12 +158,12 @@ module ActiveRecord
|
|
131
158
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
132
159
|
# @raise [ActiveRecord::ConnectionTimeoutError] no connection can be obtained from the pool
|
133
160
|
def checkout
|
134
|
-
#
|
135
|
-
|
161
|
+
conn = checkout_new_connection # acquire_connection
|
162
|
+
synchronize do
|
136
163
|
conn.lease
|
137
164
|
_run_checkout_callbacks(conn) # checkout_and_verify(conn)
|
138
|
-
|
139
|
-
|
165
|
+
end
|
166
|
+
conn
|
140
167
|
end
|
141
168
|
|
142
169
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -145,13 +172,9 @@ module ActiveRecord
|
|
145
172
|
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter] connection
|
146
173
|
# object, which was obtained earlier by calling #checkout on this pool
|
147
174
|
# @see #checkout
|
148
|
-
def checkin(conn,
|
149
|
-
release(conn)
|
150
|
-
|
151
|
-
_run_checkin_callbacks(conn)
|
152
|
-
#release conn
|
153
|
-
#@available.add conn
|
154
|
-
#end
|
175
|
+
def checkin(conn, released = nil)
|
176
|
+
release(conn) unless released
|
177
|
+
_run_checkin_callbacks(conn)
|
155
178
|
end
|
156
179
|
|
157
180
|
# Remove a connection from the connection pool. The connection will
|
@@ -165,6 +188,21 @@ module ActiveRecord
|
|
165
188
|
# we do not really manage the connection pool - nothing to do ...
|
166
189
|
end
|
167
190
|
|
191
|
+
def flush(minimum_idle = nil)
|
192
|
+
# we do not really manage the connection pool
|
193
|
+
end
|
194
|
+
|
195
|
+
def flush!
|
196
|
+
reap
|
197
|
+
flush(-1)
|
198
|
+
end
|
199
|
+
|
200
|
+
def stat
|
201
|
+
{
|
202
|
+
connections: connections.size
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
168
206
|
private
|
169
207
|
|
170
208
|
# @raise [ActiveRecord::ConnectionTimeoutError]
|
@@ -181,34 +219,30 @@ module ActiveRecord
|
|
181
219
|
checkout_new_connection
|
182
220
|
end
|
183
221
|
|
184
|
-
def release(conn, owner =
|
185
|
-
thread_id = owner
|
222
|
+
def release(conn, owner = conn.owner)
|
223
|
+
thread_id = current_connection_id(owner) unless owner.nil?
|
186
224
|
|
187
225
|
thread_id ||=
|
188
|
-
if @
|
226
|
+
if @thread_cached_conns[conn_id = current_connection_id].equal?(conn)
|
189
227
|
conn_id
|
190
228
|
else
|
191
|
-
connections = @
|
192
|
-
connections.keys.find { |k| connections[k]
|
229
|
+
connections = @thread_cached_conns
|
230
|
+
connections.keys.find { |k| connections[k].equal?(conn) }
|
193
231
|
end
|
194
232
|
|
195
|
-
@
|
233
|
+
@thread_cached_conns.delete_pair(thread_id, conn) if thread_id
|
196
234
|
end
|
197
235
|
|
198
236
|
def checkout_new_connection
|
199
|
-
# NOTE: automatic reconnect
|
200
|
-
#raise ConnectionNotEstablished unless @automatic_reconnect
|
201
|
-
|
237
|
+
# NOTE: automatic reconnect makes no sense for us!
|
202
238
|
begin
|
203
239
|
conn = new_connection
|
204
|
-
rescue ConnectionTimeoutError => e
|
205
|
-
raise e
|
206
240
|
rescue => e
|
207
|
-
raise ConnectionTimeoutError, e.message if timeout_error?(e)
|
241
|
+
raise ConnectionTimeoutError, e.message if timeout_error?(e) && !e.is_a?(ConnectionTimeoutError)
|
208
242
|
raise e
|
209
243
|
end
|
244
|
+
@connected.make_true
|
210
245
|
conn.pool = self
|
211
|
-
synchronize { @connected = true } if @connected != true
|
212
246
|
conn
|
213
247
|
end
|
214
248
|
|
@@ -1,20 +1,34 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/query_cache'
|
1
2
|
|
2
3
|
module ActiveRecord
|
3
4
|
module Bogacs
|
4
5
|
module PoolSupport
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def self.included(base)
|
8
|
+
base.send :include, ActiveRecord::ConnectionAdapters::QueryCache::ConnectionPoolConfiguration
|
9
|
+
end if ActiveRecord::ConnectionAdapters::QueryCache.const_defined? :ConnectionPoolConfiguration
|
10
|
+
|
11
|
+
attr_accessor :schema_cache
|
12
|
+
|
13
|
+
def lock_thread=(lock_thread)
|
14
|
+
if lock_thread
|
15
|
+
@lock_thread = Thread.current
|
16
|
+
else
|
17
|
+
@lock_thread = nil
|
18
|
+
end
|
19
|
+
end if ActiveRecord::VERSION::MAJOR > 4
|
9
20
|
|
10
21
|
def new_connection
|
11
|
-
Base.send(spec.adapter_method, spec.config)
|
22
|
+
conn = Base.send(spec.adapter_method, spec.config)
|
23
|
+
conn.schema_cache = schema_cache.dup if schema_cache && conn.respond_to?(:schema_cache=)
|
24
|
+
conn
|
12
25
|
end
|
13
26
|
|
14
|
-
|
15
|
-
|
16
|
-
|
27
|
+
# @private
|
28
|
+
def current_connection_id(owner_thread = Thread.current)
|
29
|
+
owner_thread.object_id
|
17
30
|
end
|
31
|
+
alias_method :connection_cache_key, :current_connection_id # for AR (5.2) compatibility
|
18
32
|
|
19
33
|
# @note Method not part of the pre 4.0 API (does no exist).
|
20
34
|
def remove(conn)
|
@@ -37,6 +51,10 @@ module ActiveRecord
|
|
37
51
|
|
38
52
|
private
|
39
53
|
|
54
|
+
def current_thread
|
55
|
+
@lock_thread || Thread.current
|
56
|
+
end
|
57
|
+
|
40
58
|
if ActiveRecord::VERSION::STRING > '4.2'
|
41
59
|
|
42
60
|
def _run_checkin_callbacks(conn)
|
@@ -89,4 +107,4 @@ module ActiveRecord
|
|
89
107
|
|
90
108
|
end
|
91
109
|
end
|
92
|
-
end
|
110
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'active_record/version'
|
2
2
|
require 'active_record/connection_adapters/abstract/connection_pool'
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'concurrent/atomic/atomic_reference'
|
5
|
+
require 'concurrent/thread_safe/util/cheap_lockable.rb'
|
5
6
|
|
6
7
|
require 'active_record/bogacs/thread_safe'
|
7
8
|
require 'active_record/bogacs/pool_support'
|
@@ -16,14 +17,9 @@ module ActiveRecord
|
|
16
17
|
class ShareablePool < ConnectionAdapters::ConnectionPool # NOTE: maybe do not override?!
|
17
18
|
include PoolSupport
|
18
19
|
|
19
|
-
|
20
|
-
include ThreadSafe::CheapLockable
|
21
|
-
else
|
22
|
-
alias_method :cheap_synchronize, :synchronize
|
23
|
-
end
|
20
|
+
include ThreadSafe::Synchronized
|
24
21
|
|
25
|
-
|
26
|
-
AtomicReference = ThreadSafe::AtomicReference
|
22
|
+
AtomicReference = ::Concurrent::AtomicReference
|
27
23
|
|
28
24
|
DEFAULT_SHARED_POOL = 0.25 # only allow 25% of the pool size to be shared
|
29
25
|
MAX_THREAD_SHARING = 5 # not really a strict limit but should hold
|
@@ -60,7 +56,7 @@ module ActiveRecord
|
|
60
56
|
def release_connection(with_id = current_connection_id)
|
61
57
|
if reserved_conn = @reserved_connections[with_id]
|
62
58
|
if shared_count = @shared_connections[reserved_conn]
|
63
|
-
|
59
|
+
synchronize do # lock due #get_shared_connection ... not needed ?!
|
64
60
|
# NOTE: the other option is to not care about shared here at all ...
|
65
61
|
if shared_count.get == 0 # releasing a shared connection
|
66
62
|
release_shared_connection(reserved_conn)
|
@@ -75,18 +71,18 @@ module ActiveRecord
|
|
75
71
|
|
76
72
|
# @override
|
77
73
|
def disconnect!
|
78
|
-
|
74
|
+
synchronize { @shared_connections.clear; super }
|
79
75
|
end
|
80
76
|
|
81
77
|
# @override
|
82
78
|
def clear_reloadable_connections!
|
83
|
-
|
79
|
+
synchronize { @shared_connections.clear; super }
|
84
80
|
end
|
85
81
|
|
86
82
|
# @override
|
87
83
|
# @note called from #reap thus the pool should work with reaper
|
88
84
|
def remove(conn)
|
89
|
-
|
85
|
+
synchronize { @shared_connections.delete(conn); super }
|
90
86
|
end
|
91
87
|
|
92
88
|
# # Return any checked-out connections back to the pool by threads that
|
@@ -135,7 +131,7 @@ module ActiveRecord
|
|
135
131
|
emulated_checkout(connection); shared = true
|
136
132
|
DEBUG && debug("with_shared_conn 20 got shared = #{connection.to_s}")
|
137
133
|
else
|
138
|
-
|
134
|
+
synchronize do
|
139
135
|
# check shared again as/if threads end up sync-ing up here :
|
140
136
|
if connection = get_shared_connection
|
141
137
|
emulated_checkout(connection)
|
@@ -218,7 +214,7 @@ module ActiveRecord
|
|
218
214
|
end
|
219
215
|
|
220
216
|
# we did as much as could without a lock - now sync due possible release
|
221
|
-
|
217
|
+
synchronize do # TODO although this likely might be avoided ...
|
222
218
|
# should try again if possibly the same connection got released :
|
223
219
|
unless least_count = @shared_connections[least_shared]
|
224
220
|
DEBUG && debug(" get_shared_conn retry (connection got released)")
|
@@ -238,7 +234,7 @@ module ActiveRecord
|
|
238
234
|
def rem_shared_connection(connection)
|
239
235
|
if shared_count = @shared_connections[connection]
|
240
236
|
# shared_count.update { |v| v - 1 } # NOTE: likely fine without lock!
|
241
|
-
|
237
|
+
synchronize do # give it back to the pool
|
242
238
|
shared_count.update { |v| v - 1 } # might give it back if :
|
243
239
|
release_shared_connection(connection) if shared_count.get == 0
|
244
240
|
end
|
@@ -1,33 +1,29 @@
|
|
1
|
+
require 'concurrent/thread_safe/util/cheap_lockable.rb'
|
2
|
+
|
1
3
|
# @note Inpspired by thread_safe gem's Util::CheapLockable mixin.
|
2
4
|
module ActiveRecord::Bogacs
|
3
5
|
module ThreadSafe
|
4
|
-
|
5
|
-
if engine == 'rbx'
|
6
|
+
if defined? JRUBY_VERSION
|
6
7
|
module Synchronized
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
::
|
8
|
+
if defined? ::JRuby::Util.synchronized # since JRuby 9.3
|
9
|
+
def synchronize
|
10
|
+
::JRuby::Util.synchronized(self) { yield }
|
11
|
+
end
|
12
|
+
else
|
13
|
+
require 'jruby'
|
14
|
+
def synchronize
|
15
|
+
::JRuby.reference0(self).synchronized { yield }
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
18
|
-
|
19
|
+
else
|
20
|
+
require 'concurrent/thread_safe/util/cheap_lockable.rb'
|
21
|
+
|
19
22
|
module Synchronized
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def synchronize
|
24
|
-
::JRuby.reference0(self).synchronized { yield }
|
25
|
-
end
|
23
|
+
include ::Concurrent::ThreadSafe::Util::CheapLockable
|
24
|
+
alias_method :synchronize, :cheap_synchronize
|
25
|
+
public :synchronize
|
26
26
|
end
|
27
|
-
else
|
28
|
-
require 'thread'; require 'monitor'
|
29
|
-
# on MRI fallback to built-in MonitorMixin
|
30
|
-
Synchronized = MonitorMixin
|
31
27
|
end
|
32
28
|
end
|
33
|
-
end
|
29
|
+
end
|
@@ -2,98 +2,11 @@ module ActiveRecord
|
|
2
2
|
module Bogacs
|
3
3
|
module ThreadSafe
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
require 'concurrent/map.rb'
|
8
|
-
rescue LoadError => e
|
9
|
-
begin
|
10
|
-
require 'thread_safe'
|
11
|
-
rescue
|
12
|
-
warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem)" +
|
13
|
-
" please install or add it to your Gemfile"
|
14
|
-
raise e
|
15
|
-
end
|
16
|
-
end
|
17
|
-
#end
|
18
|
-
|
19
|
-
#load_map # always pre-load thread_safe
|
20
|
-
|
21
|
-
if defined? ::Concurrent::Map
|
22
|
-
Map = ::Concurrent::Map
|
23
|
-
else
|
24
|
-
Map = ::ThreadSafe::Cache
|
25
|
-
end
|
5
|
+
require 'concurrent/map.rb'
|
6
|
+
Map = ::Concurrent::Map
|
26
7
|
|
27
8
|
autoload :Synchronized, 'active_record/bogacs/thread_safe/synchronized'
|
28
9
|
|
29
|
-
def self.load_atomic_reference
|
30
|
-
return const_get :AtomicReference if const_defined? :AtomicReference
|
31
|
-
|
32
|
-
begin
|
33
|
-
require 'concurrent/atomic/atomic_reference.rb'
|
34
|
-
rescue LoadError => e
|
35
|
-
begin
|
36
|
-
require 'atomic'
|
37
|
-
rescue LoadError
|
38
|
-
warn "shareable pool needs gem 'concurrent-ruby', '>= 0.9.1' (or the old 'atomic' gem) " <<
|
39
|
-
"please install or add it to your Gemfile"
|
40
|
-
raise e
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
if defined? ::Concurrent::AtomicReference
|
45
|
-
const_set :AtomicReference, ::Concurrent::AtomicReference
|
46
|
-
else
|
47
|
-
const_set :AtomicReference, ::Atomic
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.load_cheap_lockable(required = true)
|
52
|
-
return const_get :CheapLockable if const_defined? :CheapLockable
|
53
|
-
|
54
|
-
begin
|
55
|
-
require 'concurrent/thread_safe/util/cheap_lockable.rb'
|
56
|
-
rescue LoadError => e
|
57
|
-
begin
|
58
|
-
require 'thread_safe'
|
59
|
-
rescue
|
60
|
-
return nil unless required
|
61
|
-
warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem)" +
|
62
|
-
" please install or add it to your Gemfile"
|
63
|
-
raise e
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
if defined? ::Concurrent::ThreadSafe::Util::CheapLockable
|
68
|
-
const_set :CheapLockable, ::Concurrent::ThreadSafe::Util::CheapLockable
|
69
|
-
else
|
70
|
-
const_set :CheapLockable, ::ThreadSafe::Util::CheapLockable
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
def self.load_monotonic_clock(required = true)
|
76
|
-
return const_get :MONOTONIC_CLOCK if const_defined? :MONOTONIC_CLOCK
|
77
|
-
|
78
|
-
begin
|
79
|
-
require 'concurrent/utility/monotonic_time.rb'
|
80
|
-
rescue LoadError => e
|
81
|
-
return nil unless required
|
82
|
-
warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0'" +
|
83
|
-
" please install or add it to your Gemfile"
|
84
|
-
raise e
|
85
|
-
end
|
86
|
-
|
87
|
-
if defined? ::Concurrent.monotonic_time
|
88
|
-
const_set :MONOTONIC_CLOCK, ::Concurrent.const_get(:GLOBAL_MONOTONIC_CLOCK)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# @note Only works when {@link #load_monotonic_time} succeeds.
|
93
|
-
#def self.monotonic_time
|
94
|
-
# MONOTONIC_CLOCK.get_time # java.lang.System.nanoTime() / 1_000_000_000.0
|
95
|
-
#end
|
96
|
-
|
97
10
|
end
|
98
11
|
end
|
99
|
-
end
|
12
|
+
end
|
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'concurrent/timer_task'
|
4
|
-
rescue LoadError => e
|
5
|
-
warn "activerecord-bogacs' validator feature needs gem 'concurrent-ruby', please install or add it to your Gemfile"
|
6
|
-
raise e
|
7
|
-
end
|
1
|
+
require 'concurrent/executors'
|
2
|
+
require 'concurrent/timer_task'
|
8
3
|
|
9
4
|
require 'active_record/connection_adapters/adapter_compat'
|
10
5
|
require 'active_record/bogacs/thread_safe'
|
@@ -81,20 +76,19 @@ module ActiveRecord
|
|
81
76
|
connections = pool.connections.dup
|
82
77
|
connections.map! do |conn|
|
83
78
|
if conn
|
84
|
-
owner = conn.owner
|
85
|
-
|
86
|
-
|
79
|
+
if owner = conn.owner
|
80
|
+
if owner.alive?
|
81
|
+
nil # owner.alive? ... do not touch
|
82
|
+
else # stale-conn (reaping)
|
87
83
|
pool.remove conn # remove is synchronized
|
88
84
|
conn.disconnect! rescue nil
|
89
85
|
nil
|
90
|
-
elsif ! owner # NOTE: this is likely a nasty bug
|
91
|
-
logger && logger.warn("[validator] found in-use connection ##{conn.object_id} without owner - removing from pool")
|
92
|
-
pool.remove_without_owner conn # synchronized
|
93
|
-
conn.disconnect! rescue nil
|
94
|
-
nil
|
95
|
-
else
|
96
|
-
nil # owner.alive? ... do not touch
|
97
86
|
end
|
87
|
+
elsif conn.in_use? # no owner? (likely a nasty bug)
|
88
|
+
logger && logger.warn("[validator] found in-use connection ##{conn.object_id} without owner - removing from pool")
|
89
|
+
pool.remove_without_owner conn # synchronized
|
90
|
+
conn.disconnect! rescue nil
|
91
|
+
nil
|
98
92
|
else
|
99
93
|
conn # conn not in-use - candidate for validation
|
100
94
|
end
|
@@ -170,7 +164,8 @@ module ActiveRecord
|
|
170
164
|
|
171
165
|
def release_without_owner(conn)
|
172
166
|
if owner_id = cached_conn_owner_id(conn)
|
173
|
-
thread_cached_conns.delete owner_id
|
167
|
+
thread_cached_conns.delete owner_id
|
168
|
+
return true
|
174
169
|
end
|
175
170
|
end
|
176
171
|
|
@@ -179,4 +174,4 @@ module ActiveRecord
|
|
179
174
|
end
|
180
175
|
|
181
176
|
end
|
182
|
-
end
|
177
|
+
end
|