with_advisory_lock 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://api.travis-ci.org/mceachen/with_advisory_lock.png?branch=master)](https://travis-ci.org/mceachen/with_advisory_lock)
|
9
|
+
[![Gem Version](https://badge.fury.io/rb/with_advisory_lock.png)](http://rubygems.org/gems/with_advisory_lock)
|
10
|
+
[![Code Climate](https://codeclimate.com/github/mceachen/with_advisory_lock.png)](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:
|