with_advisory_lock 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -0
- data/lib/with_advisory_lock/base.rb +3 -1
- data/lib/with_advisory_lock/concern.rb +20 -2
- data/lib/with_advisory_lock/version.rb +1 -1
- data/test/concern_test.rb +11 -0
- data/test/minitest_helper.rb +1 -0
- data/test/mysql_nesting_test.rb +11 -0
- data/test/{with_advisory_lock_test.rb → parallelism_test.rb} +2 -10
- data/with_advisory_lock.gemspec +1 -0
- metadata +26 -6
data/README.md
CHANGED
@@ -66,6 +66,15 @@ The return value of ```with_advisory_lock``` will be the result of the yielded b
|
|
66
66
|
if the lock was able to be acquired and the block yielded, or ```false```, if you provided
|
67
67
|
a timeout_seconds value and the lock was not able to be acquired in time.
|
68
68
|
|
69
|
+
### Gotchas
|
70
|
+
|
71
|
+
**MySQL doesn't support nesting advisory locks.** If you ask for another advisory lock within
|
72
|
+
a ```with_advisory_lock``` block, you will be releasing the parent lock.
|
73
|
+
|
74
|
+
An warning message will be emitted to the rails logger in this case, because you
|
75
|
+
probably didn't mean to lose the first lock. (Raising an exception would be safer. I'm open to
|
76
|
+
suggestions on how to handle this dangerous case).
|
77
|
+
|
69
78
|
## Installation
|
70
79
|
|
71
80
|
Add this line to your application's Gemfile:
|
@@ -80,6 +89,11 @@ And then execute:
|
|
80
89
|
|
81
90
|
## Changelog
|
82
91
|
|
92
|
+
### 0.0.2
|
93
|
+
|
94
|
+
* Added warning log message for nested MySQL lock calls
|
95
|
+
* Randomized lock wait time, which can help ameliorate lock contention
|
96
|
+
|
83
97
|
### 0.0.1
|
84
98
|
|
85
99
|
* First whack
|
@@ -18,16 +18,34 @@ module WithAdvisoryLock
|
|
18
18
|
end
|
19
19
|
|
20
20
|
module ClassMethods
|
21
|
+
|
21
22
|
def with_advisory_lock(lock_name, timeout_seconds=nil, &block)
|
22
|
-
|
23
|
+
lock_stack = Thread.current[:with_advisory_lock_stack] ||= []
|
24
|
+
impl = case (connection.adapter_name.downcase)
|
23
25
|
when "postgresql"
|
24
26
|
WithAdvisoryLock::PostgreSQL
|
25
27
|
when "mysql", "mysql2"
|
28
|
+
unless lock_stack.empty?
|
29
|
+
wal_log("with_advisory_lock: MySQL doesn't support nested advisory locks, and will now release lock '#{lock_stack.last}'")
|
30
|
+
end
|
26
31
|
WithAdvisoryLock::MySQL
|
27
32
|
else
|
28
33
|
WithAdvisoryLock::Flock
|
29
|
-
end
|
34
|
+
end
|
35
|
+
lock_stack.push(lock_name)
|
36
|
+
impl.new(connection, lock_name, timeout_seconds).with_advisory_lock(&block)
|
37
|
+
ensure
|
38
|
+
lock_stack.pop
|
39
|
+
end
|
40
|
+
|
41
|
+
def wal_log(msg)
|
42
|
+
if respond_to?(:logger) && logger
|
43
|
+
logger.warn(msg)
|
44
|
+
else
|
45
|
+
$stderr.puts(msg)
|
46
|
+
end
|
30
47
|
end
|
48
|
+
private :wal_log
|
31
49
|
end
|
32
50
|
end
|
33
51
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe "with_advisory_lock.concern" do
|
4
|
+
it "adds with_advisory_lock to ActiveRecord classes" do
|
5
|
+
assert Tag.respond_to?(:with_advisory_lock)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "adds with_advisory_lock to ActiveRecord instances" do
|
9
|
+
assert Tag.new.respond_to?(:with_advisory_lock)
|
10
|
+
end
|
11
|
+
end
|
data/test/minitest_helper.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
|
3
|
-
describe "
|
4
|
-
it "adds with_advisory_lock to ActiveRecord classes" do
|
5
|
-
assert Tag.respond_to?(:with_advisory_lock)
|
6
|
-
end
|
7
|
-
|
8
|
-
it "adds with_advisory_lock to ActiveRecord instances" do
|
9
|
-
assert Tag.new.respond_to?(:with_advisory_lock)
|
10
|
-
end
|
11
|
-
|
3
|
+
describe "parallelism" do
|
12
4
|
def find_or_create_at_even_second(run_at, with_advisory_lock)
|
13
5
|
sleep(run_at - Time.now.to_f)
|
14
6
|
ActiveRecord::Base.connection.reconnect!
|
@@ -36,7 +28,7 @@ describe "with_advisory_lock" do
|
|
36
28
|
|
37
29
|
before :each do
|
38
30
|
@iterations = 5
|
39
|
-
@workers =
|
31
|
+
@workers = 10
|
40
32
|
end
|
41
33
|
|
42
34
|
it "parallel threads create multiple duplicate rows" do
|
data/with_advisory_lock.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_development_dependency 'yard'
|
24
24
|
gem.add_development_dependency 'minitest'
|
25
25
|
gem.add_development_dependency 'minitest-great_expectations'
|
26
|
+
gem.add_development_dependency 'mocha'
|
26
27
|
gem.add_development_dependency 'mysql2'
|
27
28
|
gem.add_development_dependency 'pg'
|
28
29
|
gem.add_development_dependency 'sqlite3'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: with_advisory_lock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -91,6 +91,22 @@ dependencies:
|
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
111
|
name: mysql2
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,10 +191,12 @@ files:
|
|
175
191
|
- lib/with_advisory_lock/mysql.rb
|
176
192
|
- lib/with_advisory_lock/postgresql.rb
|
177
193
|
- lib/with_advisory_lock/version.rb
|
194
|
+
- test/concern_test.rb
|
178
195
|
- test/database.yml
|
179
196
|
- test/minitest_helper.rb
|
197
|
+
- test/mysql_nesting_test.rb
|
198
|
+
- test/parallelism_test.rb
|
180
199
|
- test/test_models.rb
|
181
|
-
- test/with_advisory_lock_test.rb
|
182
200
|
- with_advisory_lock.gemspec
|
183
201
|
homepage: ''
|
184
202
|
licenses: []
|
@@ -194,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
212
|
version: '0'
|
195
213
|
segments:
|
196
214
|
- 0
|
197
|
-
hash:
|
215
|
+
hash: 1854060024724922654
|
198
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
217
|
none: false
|
200
218
|
requirements:
|
@@ -203,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
221
|
version: '0'
|
204
222
|
segments:
|
205
223
|
- 0
|
206
|
-
hash:
|
224
|
+
hash: 1854060024724922654
|
207
225
|
requirements: []
|
208
226
|
rubyforge_project:
|
209
227
|
rubygems_version: 1.8.23
|
@@ -211,8 +229,10 @@ signing_key:
|
|
211
229
|
specification_version: 3
|
212
230
|
summary: Advisory locking for ActiveRecord
|
213
231
|
test_files:
|
232
|
+
- test/concern_test.rb
|
214
233
|
- test/database.yml
|
215
234
|
- test/minitest_helper.rb
|
235
|
+
- test/mysql_nesting_test.rb
|
236
|
+
- test/parallelism_test.rb
|
216
237
|
- test/test_models.rb
|
217
|
-
- test/with_advisory_lock_test.rb
|
218
238
|
has_rdoc:
|