with_advisory_lock 0.0.8 → 0.0.9
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 +8 -8
- data/.travis.yml +4 -5
- data/README.md +12 -0
- data/ci/Gemfile.rails-4.0.x +1 -1
- data/lib/with_advisory_lock/base.rb +20 -0
- data/lib/with_advisory_lock/concern.rb +11 -13
- data/lib/with_advisory_lock/database_adapter_support.rb +19 -0
- data/lib/with_advisory_lock/flock.rb +2 -2
- data/lib/with_advisory_lock/postgresql.rb +1 -10
- data/lib/with_advisory_lock/version.rb +1 -1
- data/test/nesting_test.rb +10 -1
- data/test/{simplest_test.rb → simple_parallel_test.rb} +12 -4
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ODk5MWVhZjFhZmQxMzJmZjhhYWUzNTdkYWUxODNkYjM3OTRlZmFkOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MWI3YWVkNzI0ZDc5YzdkMmE3MDg1NGU5YjkyNjNmYjZiYjU4OWIzYg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MzBjOTlmZTVlNWRhZmM4NzMwODliNTFkODNiNDk1OGYzNzlhOWU2YjVhYTBj
|
10
|
+
OWM1M2IwOTljMWE5YTQzOWFjNWQ3M2Y1YWFiMWNjZDk0ZDA3YWRiZWRiYWNm
|
11
|
+
MGFkZTBlOTgxMzcwMWIzNjQyMGIwNzA2NjYxODJlYzJjZTQ3YWQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MDIwM2Q2YTFlNmI5NjQyMWMwZjRhZjlmZWZiOTU5YzQwN2VjMjZjZjEwMDlm
|
14
|
+
N2FjMzcxY2U1ODgwMWFkZWM3ZDBlMmEwYTgwM2MwOTcyZjUzZjExZjExNWM4
|
15
|
+
ZGYxNDMyMjBhZjQyYTgwZDYyNmM3ZGIxMzkzMjY4ZjM5NGIxMTc=
|
data/.travis.yml
CHANGED
@@ -2,22 +2,21 @@ language: ruby
|
|
2
2
|
|
3
3
|
rvm:
|
4
4
|
- 2.0.0
|
5
|
-
- 1.8.7
|
5
|
+
# - 1.8.7
|
6
6
|
- 1.9.3
|
7
7
|
|
8
8
|
gemfile:
|
9
|
-
# TODO: reenable once tests pass with 4.0
|
10
9
|
- ci/Gemfile.rails-4.0.x
|
11
10
|
- ci/Gemfile.rails-3.2.x
|
12
|
-
- ci/Gemfile.rails-3.1.x
|
13
|
-
- ci/Gemfile.rails-3.0.x
|
11
|
+
# - ci/Gemfile.rails-3.1.x
|
12
|
+
# - ci/Gemfile.rails-3.0.x
|
14
13
|
|
15
14
|
env:
|
16
15
|
- DB=sqlite
|
17
16
|
- DB=mysql
|
18
17
|
- DB=postgresql
|
19
18
|
|
20
|
-
script: bundle exec rake
|
19
|
+
script: WITH_ADVISORY_LOCK_PREFIX=$TRAVIS_JOB_ID bundle exec rake --trace
|
21
20
|
|
22
21
|
before_script:
|
23
22
|
- mysql -e 'create database with_advisory_lock_test'
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@ or [PostgreSQL](http://www.postgresql.org/docs/9.1/static/functions-admin.html#F
|
|
6
6
|
SQLite resorts to file locking.
|
7
7
|
|
8
8
|
[](https://travis-ci.org/mceachen/with_advisory_lock)
|
9
|
+
[](http://rubygems.org/gems/with_advisory_lock)
|
10
|
+
[](https://codeclimate.com/github/mceachen/with_advisory_lock)
|
9
11
|
|
10
12
|
## What's an "Advisory Lock"?
|
11
13
|
|
@@ -115,6 +117,16 @@ end
|
|
115
117
|
|
116
118
|
## Changelog
|
117
119
|
|
120
|
+
### 0.0.9
|
121
|
+
|
122
|
+
* Merged in Postgis Adapter Support to address [issue 7](https://github.com/mceachen/with_advisory_lock/issues/7)
|
123
|
+
Thanks for the pull request, [Abdelkader Boudih](https://github.com/seuros)!
|
124
|
+
* The database switching code had to be duplicated by [Closure Tree](https://github.com/mceachen/closure_tree),
|
125
|
+
so I extracted a new ```WithAdvisoryLock::DatabaseAdapterSupport``` one-trick pony.
|
126
|
+
* Builds were failing on Travis, so I introduced a global lock prefix that can be set with the
|
127
|
+
```WITH_ADVISORY_LOCK_PREFIX``` environment variable. I'm not going to advertise this feature yet.
|
128
|
+
It's a secret. Only you and I know, now. *shhh*
|
129
|
+
|
118
130
|
### 0.0.8
|
119
131
|
|
120
132
|
* Addressed [issue 5](https://github.com/mceachen/with_advisory_lock/issues/5) by
|
data/ci/Gemfile.rails-4.0.x
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
1
3
|
module WithAdvisoryLock
|
2
4
|
class Base
|
3
5
|
attr_reader :connection, :lock_name, :timeout_seconds
|
@@ -5,6 +7,14 @@ module WithAdvisoryLock
|
|
5
7
|
def initialize(connection, lock_name, timeout_seconds)
|
6
8
|
@connection = connection
|
7
9
|
@lock_name = lock_name
|
10
|
+
lock_name_prefix = ENV['WITH_ADVISORY_LOCK_PREFIX']
|
11
|
+
if lock_name_prefix
|
12
|
+
@lock_name = if lock_name.is_a? Numeric
|
13
|
+
"#{lock_name_prefix.to_i}#{lock_name}".to_i
|
14
|
+
else
|
15
|
+
"#{lock_name_prefix}#{lock_name}"
|
16
|
+
end
|
17
|
+
end
|
8
18
|
@timeout_seconds = timeout_seconds
|
9
19
|
end
|
10
20
|
|
@@ -28,6 +38,16 @@ module WithAdvisoryLock
|
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
41
|
+
def stable_hashcode(input)
|
42
|
+
if input.is_a? Numeric
|
43
|
+
input.to_i
|
44
|
+
else
|
45
|
+
# Ruby MRI's String#hash is randomly seeded as of Ruby 1.9 so
|
46
|
+
# make sure we use a deterministic hash.
|
47
|
+
Zlib.crc32(input.to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
31
51
|
def yield_with_lock
|
32
52
|
give_up_at = Time.now + @timeout_seconds if @timeout_seconds
|
33
53
|
while @timeout_seconds.nil? || Time.now < give_up_at do
|
@@ -1,13 +1,12 @@
|
|
1
1
|
# Tried desperately to monkeypatch the polymorphic connection object,
|
2
2
|
# but rails autoloading is too clever by half. Pull requests are welcome.
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require 'active_support/concern'
|
6
5
|
require 'with_advisory_lock/base'
|
6
|
+
require 'with_advisory_lock/database_adapter_support'
|
7
|
+
require 'with_advisory_lock/flock'
|
7
8
|
require 'with_advisory_lock/mysql'
|
8
9
|
require 'with_advisory_lock/postgresql'
|
9
|
-
require 'with_advisory_lock/flock'
|
10
|
-
require 'active_support/concern'
|
11
10
|
|
12
11
|
module WithAdvisoryLock
|
13
12
|
module Concern
|
@@ -18,19 +17,18 @@ module WithAdvisoryLock
|
|
18
17
|
end
|
19
18
|
|
20
19
|
module ClassMethods
|
21
|
-
|
22
20
|
def with_advisory_lock(lock_name, timeout_seconds=nil, &block)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
das = WithAdvisoryLock::DatabaseAdapterSupport.new(connection)
|
22
|
+
impl_class = if das.postgresql?
|
23
|
+
WithAdvisoryLock::PostgreSQL
|
24
|
+
elsif das.mysql?
|
25
|
+
WithAdvisoryLock::MySQL
|
26
|
+
else
|
27
|
+
WithAdvisoryLock::Flock
|
30
28
|
end
|
31
29
|
impl = impl_class.new(connection, lock_name, timeout_seconds)
|
32
30
|
impl.with_advisory_lock_if_needed(&block)
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
36
|
-
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module WithAdvisoryLock
|
2
|
+
class DatabaseAdapterSupport
|
3
|
+
def initialize(connection)
|
4
|
+
@sym_name = connection.adapter_name.downcase.to_sym
|
5
|
+
end
|
6
|
+
|
7
|
+
def mysql?
|
8
|
+
[:mysql, :mysql2].include? @sym_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def postgresql?
|
12
|
+
[:postgresql, :empostgresql, :postgis].include? @sym_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def sqlite?
|
16
|
+
:sqlite3 == @sym_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -5,8 +5,8 @@ module WithAdvisoryLock
|
|
5
5
|
|
6
6
|
def filename
|
7
7
|
@filename ||= begin
|
8
|
-
safe = @lock_name.gsub(/[^a-z0-9]/i, '')
|
9
|
-
fn = ".lock-#{safe}-#{@lock_name
|
8
|
+
safe = @lock_name.to_s.gsub(/[^a-z0-9]/i, '')
|
9
|
+
fn = ".lock-#{safe}-#{stable_hashcode(@lock_name)}"
|
10
10
|
# Let the user specify a directory besides CWD.
|
11
11
|
ENV['FLOCK_DIR'] ? File.expand_path(fn, ENV['FLOCK_DIR']) : fn
|
12
12
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'zlib'
|
2
1
|
|
3
2
|
module WithAdvisoryLock
|
4
3
|
class PostgreSQL < Base
|
@@ -18,15 +17,7 @@ module WithAdvisoryLock
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def numeric_lock
|
21
|
-
@numeric_lock ||=
|
22
|
-
if lock_name.is_a? Numeric
|
23
|
-
lock_name.to_i
|
24
|
-
else
|
25
|
-
# Ruby MRI's String#hash is randomly seeded as of Ruby 1.9 so
|
26
|
-
# make sure we use a deterministic hash.
|
27
|
-
Zlib.crc32(lock_name.to_s)
|
28
|
-
end
|
29
|
-
end
|
20
|
+
@numeric_lock ||= stable_hashcode(lock_name)
|
30
21
|
end
|
31
22
|
end
|
32
23
|
end
|
data/test/nesting_test.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
|
3
3
|
describe "lock nesting" do
|
4
|
+
before :each do
|
5
|
+
@prior_prefix = ENV['WITH_ADVISORY_LOCK_PREFIX']
|
6
|
+
ENV['WITH_ADVISORY_LOCK_PREFIX'] = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
after :each do
|
10
|
+
ENV['WITH_ADVISORY_LOCK_PREFIX'] = @prior_prefix
|
11
|
+
end
|
12
|
+
|
4
13
|
it "doesn't request the same lock twice" do
|
5
14
|
impl = WithAdvisoryLock::Base.new(nil, nil, nil)
|
6
15
|
impl.lock_stack.must_be_empty
|
@@ -16,7 +25,7 @@ describe "lock nesting" do
|
|
16
25
|
end
|
17
26
|
|
18
27
|
it "raises errors with MySQL when acquiring nested lock" do
|
19
|
-
skip
|
28
|
+
skip unless env_db == 'mysql'
|
20
29
|
exc = proc {
|
21
30
|
Tag.with_advisory_lock("first") do
|
22
31
|
Tag.with_advisory_lock("second") do
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'minitest_helper'
|
2
2
|
|
3
|
-
describe "
|
4
|
-
|
3
|
+
describe "prevents threads from accessing a resource concurrently" do
|
4
|
+
def assert_correct_parallel_behavior(lock_name)
|
5
5
|
times = ActiveSupport::OrderedHash.new
|
6
6
|
ActiveRecord::Base.connection_pool.disconnect!
|
7
7
|
t1 = Thread.new do
|
8
8
|
ActiveRecord::Base.connection.reconnect!
|
9
|
-
ActiveRecord::Base.with_advisory_lock(
|
9
|
+
ActiveRecord::Base.with_advisory_lock(lock_name) do
|
10
10
|
times[:t1_acquire] = Time.now
|
11
11
|
sleep 0.5
|
12
12
|
end
|
@@ -15,7 +15,7 @@ describe "simplest" do
|
|
15
15
|
sleep 0.1
|
16
16
|
t2 = Thread.new do
|
17
17
|
ActiveRecord::Base.connection.reconnect!
|
18
|
-
ActiveRecord::Base.with_advisory_lock(
|
18
|
+
ActiveRecord::Base.with_advisory_lock(lock_name) do
|
19
19
|
times[:t2_acquire] = Time.now
|
20
20
|
sleep 1
|
21
21
|
end
|
@@ -26,4 +26,12 @@ describe "simplest" do
|
|
26
26
|
times.keys.must_equal [:t1_acquire, :t1_release, :t2_acquire, :t2_release]
|
27
27
|
times[:t2_acquire].must_be :>, times[:t1_release]
|
28
28
|
end
|
29
|
+
|
30
|
+
it "with a string lock name" do
|
31
|
+
assert_correct_parallel_behavior("example lock name")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "with a numeric lock name" do
|
35
|
+
assert_correct_parallel_behavior(1234)
|
36
|
+
end
|
29
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew McEachen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -156,6 +156,7 @@ files:
|
|
156
156
|
- lib/with_advisory_lock.rb
|
157
157
|
- lib/with_advisory_lock/base.rb
|
158
158
|
- lib/with_advisory_lock/concern.rb
|
159
|
+
- lib/with_advisory_lock/database_adapter_support.rb
|
159
160
|
- lib/with_advisory_lock/flock.rb
|
160
161
|
- lib/with_advisory_lock/mysql.rb
|
161
162
|
- lib/with_advisory_lock/nested_advisory_lock_error.rb
|
@@ -166,7 +167,7 @@ files:
|
|
166
167
|
- test/minitest_helper.rb
|
167
168
|
- test/nesting_test.rb
|
168
169
|
- test/parallelism_test.rb
|
169
|
-
- test/
|
170
|
+
- test/simple_parallel_test.rb
|
170
171
|
- test/test_models.rb
|
171
172
|
- tests.sh
|
172
173
|
- with_advisory_lock.gemspec
|
@@ -199,6 +200,6 @@ test_files:
|
|
199
200
|
- test/minitest_helper.rb
|
200
201
|
- test/nesting_test.rb
|
201
202
|
- test/parallelism_test.rb
|
202
|
-
- test/
|
203
|
+
- test/simple_parallel_test.rb
|
203
204
|
- test/test_models.rb
|
204
205
|
has_rdoc:
|