activerecord-bogacs 0.5.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.
Files changed (32) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +82 -0
  3. data/.travis.yml +32 -34
  4. data/Gemfile +2 -15
  5. data/LICENSE.txt +1 -1
  6. data/README.md +45 -21
  7. data/Rakefile +8 -6
  8. data/activerecord-bogacs.gemspec +6 -6
  9. data/lib/active_record/bogacs/autoload.rb +10 -0
  10. data/lib/active_record/bogacs/connection_handler.rb +36 -0
  11. data/lib/active_record/bogacs/default_pool.rb +278 -129
  12. data/lib/active_record/bogacs/false_pool.rb +95 -73
  13. data/lib/active_record/bogacs/pool_support.rb +26 -9
  14. data/lib/active_record/bogacs/railtie.rb +17 -0
  15. data/lib/active_record/bogacs/reaper.rb +4 -6
  16. data/lib/active_record/bogacs/shareable_pool.rb +12 -16
  17. data/lib/active_record/bogacs/thread_safe/synchronized.rb +18 -22
  18. data/lib/active_record/bogacs/thread_safe.rb +3 -67
  19. data/lib/active_record/bogacs/validator.rb +21 -26
  20. data/lib/active_record/bogacs/version.rb +2 -2
  21. data/lib/active_record/bogacs.rb +7 -54
  22. data/lib/active_record/connection_adapters/adapter_compat.rb +63 -17
  23. data/lib/active_record/connection_adapters/pool_class.rb +75 -0
  24. data/lib/activerecord-bogacs.rb +1 -0
  25. data/test/active_record/bogacs/false_pool_test.rb +66 -78
  26. data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +6 -3
  27. data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +3 -2
  28. data/test/active_record/bogacs/shareable_pool_helper.rb +1 -1
  29. data/test/active_record/bogacs/validator_test.rb +22 -28
  30. data/test/active_record/connection_pool_test_methods.rb +24 -20
  31. data/test/test_helper.rb +42 -25
  32. metadata +35 -17
@@ -1,3 +1,8 @@
1
+ require 'active_record/version'
2
+
3
+ require 'concurrent/atomic/atomic_boolean'
4
+
5
+ require 'active_record/connection_adapters/adapter_compat'
1
6
  require 'active_record/bogacs/pool_support'
2
7
  require 'active_record/bogacs/thread_safe'
3
8
 
@@ -14,17 +19,17 @@ module ActiveRecord
14
19
  attr_reader :size, :spec
15
20
 
16
21
  def initialize(spec)
17
- @connected = nil
22
+ super()
18
23
 
19
24
  @spec = spec
20
25
  @size = nil
21
26
  @automatic_reconnect = nil
27
+ @lock_thread = false
22
28
 
23
- @reserved_connections = ThreadSafe::Map.new #:initial_capacity => @size
24
- end
29
+ @thread_cached_conns = ThreadSafe::Map.new
25
30
 
26
- # @private replacement for attr_reader :connections
27
- def connections; @reserved_connections.values end
31
+ @connected = ::Concurrent::AtomicBoolean.new
32
+ end
28
33
 
29
34
  # @private attr_reader :reaper
30
35
  def reaper; end
@@ -38,20 +43,21 @@ module ActiveRecord
38
43
  # #connection can be called any number of times; the connection is
39
44
  # held in a hash keyed by the thread id.
40
45
  def connection
41
- @reserved_connections[current_connection_id] ||= checkout
46
+ connection_id = current_connection_id(current_thread)
47
+ @thread_cached_conns[connection_id] ||= checkout
42
48
  end
43
49
 
44
50
  # Is there an open connection that is being used for the current thread?
45
51
  def active_connection?
46
- conn = @reserved_connections[current_connection_id]
47
- conn ? conn.in_use? : false
52
+ connection_id = current_connection_id(current_thread)
53
+ @thread_cached_conns[connection_id]
48
54
  end
49
55
 
50
56
  # Signal that the thread is finished with the current connection.
51
57
  # #release_connection releases the connection-thread association
52
58
  # and returns the connection to the pool.
53
- def release_connection(with_id = current_connection_id)
54
- conn = @reserved_connections.delete(with_id)
59
+ def release_connection(owner_thread = Thread.current)
60
+ conn = @thread_cached_conns.delete(current_connection_id(owner_thread))
55
61
  checkin conn if conn
56
62
  end
57
63
 
@@ -60,40 +66,63 @@ module ActiveRecord
60
66
  # connection when finished.
61
67
  def with_connection
62
68
  connection_id = current_connection_id
63
- fresh_connection = true unless active_connection?
64
- yield connection
69
+ unless conn = @thread_cached_conns[connection_id]
70
+ conn = connection
71
+ fresh_connection = true
72
+ end
73
+ yield conn
65
74
  ensure
66
- release_connection(connection_id) if fresh_connection
75
+ release_connection if fresh_connection
67
76
  end
68
77
 
69
78
  # Returns true if a connection has already been opened.
70
- 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
71
83
 
72
84
  # Disconnects all connections in the pool, and clears the pool.
73
85
  def disconnect!
74
86
  synchronize do
75
- @connected = false
76
-
77
- connections = @reserved_connections.values
78
- @reserved_connections.clear
87
+ @connected.make_false
79
88
 
89
+ connections = @thread_cached_conns.values
90
+ @thread_cached_conns.clear
80
91
  connections.each do |conn|
81
- checkin conn
92
+ if conn.in_use?
93
+ conn.steal!
94
+ checkin conn
95
+ end
82
96
  conn.disconnect!
83
97
  end
84
98
  end
85
99
  end
86
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
+
87
113
  # Clears the cache which maps classes.
88
114
  def clear_reloadable_connections!
89
115
  synchronize do
90
- @connected = false
116
+ @connected.make_false
91
117
 
92
- connections = @reserved_connections.values
93
- @reserved_connections.clear
118
+ connections = @thread_cached_conns.values
119
+ @thread_cached_conns.clear
94
120
 
95
121
  connections.each do |conn|
96
- checkin conn
122
+ if conn.in_use?
123
+ conn.steal!
124
+ checkin conn
125
+ end
97
126
  conn.disconnect! if conn.requires_reloading?
98
127
  end
99
128
  end
@@ -105,7 +134,7 @@ module ActiveRecord
105
134
  def verify_active_connections!
106
135
  synchronize do
107
136
  clear_stale_cached_connections!
108
- @reserved_connections.values.each(&:verify!)
137
+ @thread_cached_conns.values.each(&:verify!)
109
138
  end
110
139
  end if ActiveRecord::VERSION::MAJOR < 4
111
140
 
@@ -113,12 +142,12 @@ module ActiveRecord
113
142
  # are no longer alive.
114
143
  # @private AR 3.2 compatibility
115
144
  def clear_stale_cached_connections!
116
- keys = Thread.list.find_all { |t| t.alive? }.map(&:object_id)
117
- keys = @reserved_connections.keys - keys
145
+ keys = Thread.list.find_all { |t| t.alive? }.map { |t| current_connection_id(t) }
146
+ keys = @thread_cached_conns.keys - keys
118
147
  keys.each do |key|
119
- if conn = @reserved_connections[key]
120
- checkin conn, false # no release
121
- @reserved_connections.delete(key)
148
+ if conn = @thread_cached_conns[key]
149
+ checkin conn, true # no release
150
+ @thread_cached_conns.delete(key)
122
151
  end
123
152
  end
124
153
  end if ActiveRecord::VERSION::MAJOR < 4
@@ -126,38 +155,26 @@ module ActiveRecord
126
155
  # Check-out a database connection from the pool, indicating that you want
127
156
  # to use it. You should call #checkin when you no longer need this.
128
157
  #
129
- # This is done by either returning and leasing existing connection, or by
130
- # creating a new connection and leasing it.
131
- #
132
- # If all connections are leased and the pool is at capacity (meaning the
133
- # number of currently leased connections is greater than or equal to the
134
- # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
135
- #
136
- # Returns: an AbstractAdapter object.
137
- #
138
- # Raises:
139
- # - ConnectionTimeoutError: no connection can be obtained from the pool.
158
+ # @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
159
+ # @raise [ActiveRecord::ConnectionTimeoutError] no connection can be obtained from the pool
140
160
  def checkout
141
- #synchronize do
142
- conn = checkout_new_connection # acquire_connection
161
+ conn = checkout_new_connection # acquire_connection
162
+ synchronize do
143
163
  conn.lease
144
164
  _run_checkout_callbacks(conn) # checkout_and_verify(conn)
145
- conn
146
- #end
165
+ end
166
+ conn
147
167
  end
148
168
 
149
169
  # Check-in a database connection back into the pool, indicating that you
150
170
  # no longer need this connection.
151
171
  #
152
- # +conn+: an AbstractAdapter object, which was obtained by earlier by
153
- # calling +checkout+ on this pool.
154
- def checkin(conn, do_release = true)
155
- release(conn) if do_release
156
- #synchronize do
157
- _run_checkin_callbacks(conn)
158
- #release conn
159
- #@available.add conn
160
- #end
172
+ # @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter] connection
173
+ # object, which was obtained earlier by calling #checkout on this pool
174
+ # @see #checkout
175
+ def checkin(conn, released = nil)
176
+ release(conn) unless released
177
+ _run_checkin_callbacks(conn)
161
178
  end
162
179
 
163
180
  # Remove a connection from the connection pool. The connection will
@@ -171,15 +188,24 @@ module ActiveRecord
171
188
  # we do not really manage the connection pool - nothing to do ...
172
189
  end
173
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
+
174
206
  private
175
207
 
176
- # Acquire a connection by one of 1) immediately removing one
177
- # from the queue of available connections, 2) creating a new
178
- # connection if the pool is not at capacity, 3) waiting on the
179
- # queue for a connection to become available.
180
- #
181
- # Raises:
182
- # - ConnectionTimeoutError if a connection could not be acquired
208
+ # @raise [ActiveRecord::ConnectionTimeoutError]
183
209
  def acquire_connection
184
210
  # underlying pool will poll and block if "empty" (all checked-out)
185
211
  #if conn = @available.poll
@@ -193,34 +219,30 @@ module ActiveRecord
193
219
  checkout_new_connection
194
220
  end
195
221
 
196
- def release(conn, owner = nil)
197
- thread_id = owner.object_id unless owner.nil?
222
+ def release(conn, owner = conn.owner)
223
+ thread_id = current_connection_id(owner) unless owner.nil?
198
224
 
199
225
  thread_id ||=
200
- if @reserved_connections[conn_id = current_connection_id] == conn
226
+ if @thread_cached_conns[conn_id = current_connection_id].equal?(conn)
201
227
  conn_id
202
228
  else
203
- connections = @reserved_connections
204
- connections.keys.find { |k| connections[k] == conn }
229
+ connections = @thread_cached_conns
230
+ connections.keys.find { |k| connections[k].equal?(conn) }
205
231
  end
206
232
 
207
- @reserved_connections.delete thread_id if thread_id
233
+ @thread_cached_conns.delete_pair(thread_id, conn) if thread_id
208
234
  end
209
235
 
210
236
  def checkout_new_connection
211
- # NOTE: automatic reconnect seems to make no sense for us!
212
- #raise ConnectionNotEstablished unless @automatic_reconnect
213
-
237
+ # NOTE: automatic reconnect makes no sense for us!
214
238
  begin
215
239
  conn = new_connection
216
- rescue ConnectionTimeoutError => e
217
- raise e
218
240
  rescue => e
219
- raise ConnectionTimeoutError, e.message if timeout_error?(e)
241
+ raise ConnectionTimeoutError, e.message if timeout_error?(e) && !e.is_a?(ConnectionTimeoutError)
220
242
  raise e
221
243
  end
244
+ @connected.make_true
222
245
  conn.pool = self
223
- synchronize { @connected = true } if @connected != true
224
246
  conn
225
247
  end
226
248
 
@@ -1,21 +1,34 @@
1
- #require 'thread_safe'
1
+ require 'active_record/connection_adapters/abstract/query_cache'
2
2
 
3
3
  module ActiveRecord
4
4
  module Bogacs
5
5
  module PoolSupport
6
6
 
7
- #def self.included(base)
8
- #base.send :include, ThreadSafe::Synchronized
9
- #end
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
10
20
 
11
21
  def new_connection
12
- 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
13
25
  end
14
26
 
15
- def current_connection_id
16
- # NOTE: possible fiber work-around on JRuby ?!
17
- Base.connection_id ||= Thread.current.object_id
27
+ # @private
28
+ def current_connection_id(owner_thread = Thread.current)
29
+ owner_thread.object_id
18
30
  end
31
+ alias_method :connection_cache_key, :current_connection_id # for AR (5.2) compatibility
19
32
 
20
33
  # @note Method not part of the pre 4.0 API (does no exist).
21
34
  def remove(conn)
@@ -38,6 +51,10 @@ module ActiveRecord
38
51
 
39
52
  private
40
53
 
54
+ def current_thread
55
+ @lock_thread || Thread.current
56
+ end
57
+
41
58
  if ActiveRecord::VERSION::STRING > '4.2'
42
59
 
43
60
  def _run_checkin_callbacks(conn)
@@ -90,4 +107,4 @@ module ActiveRecord
90
107
 
91
108
  end
92
109
  end
93
- end
110
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module Bogacs
3
+ class Railtie < Rails::Railtie
4
+
5
+ initializer 'active_record.bogacs', :before => 'active_record.initialize_database' do |_|
6
+ ActiveSupport.on_load :active_record do
7
+ require 'active_record/bogacs'
8
+
9
+ # support for auto-configuring FalsePool (when config[:pool] set to false) :
10
+ require 'active_record/bogacs/connection_handler'
11
+ ActiveRecord::Base.default_connection_handler = ConnectionHandler.new
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -3,22 +3,20 @@ require 'thread'
3
3
  module ActiveRecord
4
4
  module Bogacs
5
5
 
6
- # Every +frequency+ seconds, the reaper will "reap" a pool it belongs to.
6
+ # Every *frequency* seconds, the reaper will "reap" a pool it belongs to.
7
7
  # Reaping means detecting stale cached connections in the pool - those that
8
8
  # were checked-out by a thread but never checked back in after the thread
9
9
  # finished/died.
10
10
  #
11
- # @note This version is fail safe - raised errors won't stop the reaping process.
12
- # Instead the thread will be restarted and reaping continues at the next tick.
13
- #
14
- # Configure the frequency by setting `:reaping_frequency` in your database yaml file.
11
+ # Configure the frequency by setting `:reaping_frequency` in your database.yml.
15
12
  #
13
+ # @note This version is fail safe - raised errors won't stop the reaping process.
16
14
  class Reaper
17
15
 
18
16
  attr_reader :pool, :frequency
19
17
  attr_accessor :retry_error
20
18
 
21
- # Reaper.new(self, spec.config[:reaping_frequency]).run
19
+ # `Reaper.new(pool, spec.config[:reaping_frequency]).run`
22
20
  # @private
23
21
  def initialize(pool, frequency)
24
22
  @pool = pool;
@@ -1,9 +1,10 @@
1
+ require 'active_record/version'
1
2
  require 'active_record/connection_adapters/abstract/connection_pool'
2
3
 
3
- require 'thread'
4
+ require 'concurrent/atomic/atomic_reference'
5
+ require 'concurrent/thread_safe/util/cheap_lockable.rb'
4
6
 
5
7
  require 'active_record/bogacs/thread_safe'
6
-
7
8
  require 'active_record/bogacs/pool_support'
8
9
 
9
10
  # NOTE: needs explicit configuration - before connection gets established e.g.
@@ -16,14 +17,9 @@ module ActiveRecord
16
17
  class ShareablePool < ConnectionAdapters::ConnectionPool # NOTE: maybe do not override?!
17
18
  include PoolSupport
18
19
 
19
- if ActiveRecord::Bogacs::ThreadSafe.load_cheap_lockable(false)
20
- include ThreadSafe::CheapLockable
21
- else
22
- alias_method :cheap_synchronize, :synchronize
23
- end
20
+ include ThreadSafe::Synchronized
24
21
 
25
- ActiveRecord::Bogacs::ThreadSafe.load_atomic_reference
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
- cheap_synchronize do # lock due #get_shared_connection ... not needed ?!
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
- cheap_synchronize { @shared_connections.clear; super }
74
+ synchronize { @shared_connections.clear; super }
79
75
  end
80
76
 
81
77
  # @override
82
78
  def clear_reloadable_connections!
83
- cheap_synchronize { @shared_connections.clear; super }
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
- cheap_synchronize { @shared_connections.delete(conn); super }
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
- cheap_synchronize do
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
- cheap_synchronize do # TODO although this likely might be avoided ...
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
- cheap_synchronize do # give it back to the pool
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
- engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
5
- if engine == 'rbx'
6
+ if defined? JRUBY_VERSION
6
7
  module Synchronized
7
- # Making use of the Rubinius' ability to lock via object headers to avoid
8
- # the overhead of the extra Mutex objects.
9
- def synchronize
10
- ::Rubinius.lock(self)
11
- begin
12
- yield
13
- ensure
14
- ::Rubinius.unlock(self)
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
- elsif engine == 'jruby'
19
+ else
20
+ require 'concurrent/thread_safe/util/cheap_lockable.rb'
21
+
19
22
  module Synchronized
20
- require 'jruby'
21
- # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid
22
- # the overhead of the extra Mutex objects
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,75 +2,11 @@ module ActiveRecord
2
2
  module Bogacs
3
3
  module ThreadSafe
4
4
 
5
- #def self.load_map
6
- begin
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
10
  end
75
11
  end
76
- end
12
+ end