active_record_mutex 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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