activerecord-bogacs 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|