active_record_mutex 3.1.0 → 3.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 337ab2cfb637dc992dda05b67bc12098b0d8d7266591dc741d4918dbb3238aee
4
- data.tar.gz: e812c787de8cb82696027803eafa426793af334d4c1d847c9956473a7d1faf20
3
+ metadata.gz: 34e4fb422fbe0336eee934df7fdeb49843c4b0335fe4222db9d8df1728b7d477
4
+ data.tar.gz: c3dc8c6fe86409160ded37ed5941cef3033d03b82e60c630c950ade50fc1d901
5
5
  SHA512:
6
- metadata.gz: f8cb37ec99b0a52267f964b032b8f061801bef3000a11d355af5cddef3d5f075c471bd51e3fa79310136cbc53b3cd0376c293a501363b4f48f979fc774f03efc
7
- data.tar.gz: 1418b58e067e151bb33e92cdd7beb215cb3fdce4ea9f7439e34ae6315b452c80d9e0cafa5f368f399ec2abb7d48c0479a1a233b1fc01f4ff364043042647faa1
6
+ metadata.gz: bd1437072b603f1dd5641d70970fe3bd2a24e77862a88d035222b4b1c0c360eb99ce14a573be87fc2e331e85955283262380be09c709e3bad936944a36b6615a
7
+ data.tar.gz: 2f4528e9a7d63ce3e2f9584f0b0099c783b07bf518174fd50c107237e0ad994fed77519f408ef27daa3aa752d3df1452fdc7a0139a312c21fe6d708d77cc5f0c
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: active_record_mutex 3.1.0 ruby lib
2
+ # stub: active_record_mutex 3.2.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "active_record_mutex".freeze
6
- s.version = "3.1.0".freeze
6
+ s.version = "3.2.0".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -20,7 +20,8 @@ module ActiveRecord
20
20
  #
21
21
  # @raise [ ArgumentError ] if no **name** option is provided in the options hash.
22
22
  def initialize(opts = {})
23
- @name = opts[:name] or raise ArgumentError, "mutex requires a :name argument"
23
+ @name = opts[:name].to_s
24
+ @name.size != 0 or raise ArgumentError, "mutex requires a nonempty :name argument"
24
25
  internal_name # create/check internal_name
25
26
  end
26
27
 
@@ -43,38 +44,48 @@ module ActiveRecord
43
44
  end
44
45
  end
45
46
 
46
- # The synchronize method attempts to acquire a mutex lock for the given name
47
- # and executes the block passed to it. If the lock is already held by another
48
- # database connection, this method will return nil instead of raising an
49
- # exception and not execute the block. #
50
- #
51
- # This method provides a convenient way to ensure that critical sections of code
52
- # are executed while holding the mutex lock. It attempts to acquire the lock using
53
- # the underlying locking mechanisms (such as {lock} and {unlock}) and executes
54
- # the block passed to it.
55
- #
56
- # The **block** and **timeout** options are passed to the {lock} method
57
- # and configure the way the lock is acquired.
58
- #
59
- # The **force** option is passed to the {unlock} method, which will force the
60
- # lock to open if true.
61
- #
62
- # @example
63
- # foo.mutex.synchronize { do_something_with foo } # wait forever and never give up
64
- #
65
- # @example
66
- # foo.mutex.synchronize(timeout: 5) { do_something_with foo } # wait 5s and give up
67
- #
68
- # @example
69
- # unless foo.mutex.synchronize(block: false) { do_something_with foo }
70
- # # try again later
71
- # end
72
- #
73
- # @param opts [ Hash ] Options hash containing the **block**, **timeout**, or **force** keys
74
- #
75
- # @yield [ Result ] The block to be executed while holding the mutex lock
76
- #
77
- # @return [ Nil or result of yielded block ] depending on whether the lock was acquired
47
+ # The lock_name method generates the name for the mutex's internal lock
48
+ # variable based on its class and {name} attributes, prefixing it with a
49
+ # truncated version of the name that only includes printable characters.
50
+ #
51
+ # @return [ String ] the generated lock name
52
+ def lock_name
53
+ prefix_name = name.gsub(/[^[:print:]]/, '')[0, 32]
54
+ prefix_name + ?= + internal_name
55
+ end
56
+
57
+ # The synchronize method attempts to acquire a mutex lock for the given name
58
+ # and executes the block passed to it. If the lock is already held by another
59
+ # database connection, this method will return nil instead of raising an
60
+ # exception and not execute the block. #
61
+ #
62
+ # This method provides a convenient way to ensure that critical sections of code
63
+ # are executed while holding the mutex lock. It attempts to acquire the lock using
64
+ # the underlying locking mechanisms (such as {lock} and {unlock}) and executes
65
+ # the block passed to it.
66
+ #
67
+ # The **block** and **timeout** options are passed to the {lock} method
68
+ # and configure the way the lock is acquired.
69
+ #
70
+ # The **force** option is passed to the {unlock} method, which will force the
71
+ # lock to open if true.
72
+ #
73
+ # @example
74
+ # foo.mutex.synchronize { do_something_with foo } # wait forever and never give up
75
+ #
76
+ # @example
77
+ # foo.mutex.synchronize(timeout: 5) { do_something_with foo } # wait 5s and give up
78
+ #
79
+ # @example
80
+ # unless foo.mutex.synchronize(block: false) { do_something_with foo }
81
+ # # try again later
82
+ # end
83
+ #
84
+ # @param opts [ Hash ] Options hash containing the **block**, **timeout**, or **force** keys
85
+ #
86
+ # @yield [ Result ] The block to be executed while holding the mutex lock
87
+ #
88
+ # @return [ Nil or result of yielded block ] depending on whether the lock was acquired
78
89
  def synchronize(opts = {})
79
90
  locked = lock(opts.slice(:block, :timeout)) or return
80
91
  yield
@@ -147,7 +158,7 @@ module ActiveRecord
147
158
  decrement_counter
148
159
  end
149
160
  if counter_zero?
150
- case query("SELECT RELEASE_LOCK(#{quote(internal_name)})")
161
+ case query("SELECT RELEASE_LOCK(#{quote(lock_name)})")
151
162
  when 1
152
163
  true
153
164
  when 0, nil
@@ -181,7 +192,7 @@ module ActiveRecord
181
192
  #
182
193
  # @return [ true, false ] true if the mutex is unlocked, false otherwise
183
194
  def unlocked?
184
- query("SELECT IS_FREE_LOCK(#{quote(internal_name)})") == 1
195
+ query("SELECT IS_FREE_LOCK(#{quote(lock_name)})") == 1
185
196
  end
186
197
 
187
198
  # The locked? method returns true if this mutex is currently locked by
@@ -194,7 +205,7 @@ module ActiveRecord
194
205
 
195
206
  # Returns true if the mutex is was acquired on this database connection.
196
207
  def owned?
197
- query("SELECT CONNECTION_ID() = IS_USED_LOCK(#{quote(internal_name)})") == 1
208
+ query("SELECT CONNECTION_ID() = IS_USED_LOCK(#{quote(lock_name)})") == 1
198
209
  end
199
210
 
200
211
  # Returns true if this mutex was not acquired on this database connection,
@@ -225,13 +236,15 @@ module ActiveRecord
225
236
  ActiveRecord::Base.connection.quote(value)
226
237
  end
227
238
 
239
+ alias counter_name internal_name
240
+
228
241
  # The counter method generates a unique name for the mutex's internal
229
242
  # counter variable. This name is used as part of the SQL query to set and
230
243
  # retrieve the counter value.
231
244
  #
232
245
  # @return [String] the unique name for the mutex's internal counter variable.
233
246
  def counter
234
- "@#{internal_name}"
247
+ "@#{counter_name}"
235
248
  end
236
249
 
237
250
  # The increment_counter method increments the internal counter value for
@@ -290,7 +303,7 @@ module ActiveRecord
290
303
  increment_counter
291
304
  true
292
305
  else
293
- case query("SELECT GET_LOCK(#{quote(internal_name)}, #{timeout})")
306
+ case query("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})")
294
307
  when 1
295
308
  increment_counter
296
309
  true
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord::DatabaseMutex
2
2
  # ActiveRecord::DatabaseMutex version
3
- VERSION = '3.1.0'
3
+ VERSION = '3.2.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
  connection.select_all(<<~EOT).map { MutexInfo.new(_1) }
77
77
  SELECT * FROM performance_schema.metadata_locks
78
78
  WHERE OBJECT_TYPE = 'USER LEVEL LOCK'
79
- AND OBJECT_NAME LIKE "$%"
79
+ AND OBJECT_NAME LIKE "%=$%"
80
80
  EOT
81
81
  end
82
82
  end
@@ -55,6 +55,22 @@ class DatabaseMutexTest < Test::Unit::TestCase
55
55
  assert_kind_of ActiveRecord::DatabaseMutex::Implementation, mutex
56
56
  end
57
57
 
58
+ private def lock_exists?(string)
59
+ ActiveRecord::Base.all_mutexes.map(&:OBJECT_NAME).any? {
60
+ _1.include?(string)
61
+ }
62
+ end
63
+
64
+ def test_all_mutexes
65
+ string = SecureRandom.hex(16)
66
+ mutex = ActiveRecord::DatabaseMutex.for(string)
67
+ assert_false lock_exists?(string)
68
+ mutex.lock
69
+ assert lock_exists?(string)
70
+ ensure
71
+ mutex.unlock force: true
72
+ end
73
+
58
74
  def test_create
59
75
  mutex = Implementation.new(:name => 'Create')
60
76
  assert_equal 'Create', mutex.name
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_mutex
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank