with_advisory_lock 0.0.7 → 0.0.8
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/README.md +55 -17
- data/lib/with_advisory_lock/concern.rb +1 -1
- data/lib/with_advisory_lock/mysql.rb +7 -3
- data/lib/with_advisory_lock/postgresql.rb +9 -3
- data/lib/with_advisory_lock/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzA3MTE5YzM5MzkwMjJmZjRjOWFhMTc1ZjA0ZGNmNWFlNTE1NjM4MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZGViZjU2NzczOGYyMTVkZDJjYWRmZjYwMTMwOWQwMDlkNDI1ZDc3Yw==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDNmYWE3ZmNhZGFlZDU3ODQzMzAyODdlNTAxZjdkZTFkYzk2NDM2N2I0Mjhm
|
10
|
+
YTE0OGM0MmZlNDY1ZWMyN2YzNmZmYTIzOWE5ODRhODFjZmE2MjY5MDNkNmY2
|
11
|
+
Y2M2ZmNhOWVkNGVjNzI3MDczZmVjN2IxZTI0NjcwYmUzYzI2MmE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NmY4MDdkOTM1NzkxODU1N2NiN2U0N2Q2MTNiYTk0YjU4NGFmZTY2NDA4NjEz
|
14
|
+
MWU5ODZhYTFkYWUwZGE4MmE4ZjZhNWU5ZGY5NDZkYzkxZjA3MGVmODgxOTEz
|
15
|
+
MDM3ZDYwNTk5YTg1NjNkY2M1MWI5MDYxYzkwM2E0MzI2ZjllMzA=
|
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# with_advisory_lock
|
1
|
+
# with_advisory_lock
|
2
2
|
|
3
|
-
Adds advisory locking to ActiveRecord 3.2.
|
3
|
+
Adds advisory locking (mutexes) to ActiveRecord 3.0, 3.1, 3.2, and 4.0.0 when used with
|
4
4
|
[MySQL](http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock)
|
5
|
-
|
6
|
-
|
5
|
+
or [PostgreSQL](http://www.postgresql.org/docs/9.1/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS).
|
6
|
+
SQLite resorts to file locking.
|
7
|
+
|
8
|
+
[](https://travis-ci.org/mceachen/with_advisory_lock)
|
7
9
|
|
8
10
|
## What's an "Advisory Lock"?
|
9
11
|
|
@@ -38,19 +40,6 @@ The return value of ```with_advisory_lock``` will be the result of the yielded b
|
|
38
40
|
if the lock was able to be acquired and the block yielded, or ```false```, if you provided
|
39
41
|
a timeout_seconds value and the lock was not able to be acquired in time.
|
40
42
|
|
41
|
-
### Transactions and Advisory Locks
|
42
|
-
|
43
|
-
Advisory locks with MySQL and PostgreSQL ignore database transaction boundaries.
|
44
|
-
|
45
|
-
You will want to wrap your block within a transaction to ensure consistency.
|
46
|
-
|
47
|
-
### MySQL doesn't support nesting
|
48
|
-
|
49
|
-
With MySQL (at least <= v5.5), if you ask for a *different* advisory lock within a ```with_advisory_lock``` block,
|
50
|
-
you will be releasing the parent lock (!!!). A ```NestedAdvisoryLockError```will be raised
|
51
|
-
in this case. If you ask for the same lock name, ```with_advisory_lock``` won't ask for the
|
52
|
-
lock again, and the block given will be yielded to.
|
53
|
-
|
54
43
|
## Installation
|
55
44
|
|
56
45
|
Add this line to your application's Gemfile:
|
@@ -89,8 +78,57 @@ gem, these prevent concurrent access to **any instance of a model**. Their coars
|
|
89
78
|
aren't going to be commonly applicable, and they can be a source of
|
90
79
|
[deadlocks](http://en.wikipedia.org/wiki/Deadlock).
|
91
80
|
|
81
|
+
## FAQ
|
82
|
+
|
83
|
+
### Transactions and Advisory Locks
|
84
|
+
|
85
|
+
Advisory locks with MySQL and PostgreSQL ignore database transaction boundaries.
|
86
|
+
|
87
|
+
You will want to wrap your block within a transaction to ensure consistency.
|
88
|
+
|
89
|
+
### MySQL doesn't support nesting
|
90
|
+
|
91
|
+
With MySQL (at least <= v5.5), if you ask for a *different* advisory lock within a ```with_advisory_lock``` block,
|
92
|
+
you will be releasing the parent lock (!!!). A ```NestedAdvisoryLockError```will be raised
|
93
|
+
in this case. If you ask for the same lock name, ```with_advisory_lock``` won't ask for the
|
94
|
+
lock again, and the block given will be yielded to.
|
95
|
+
|
96
|
+
### There are many ```lock-*``` files in my project directory after test runs
|
97
|
+
|
98
|
+
This is expected if you aren't using MySQL or Postgresql for your tests.
|
99
|
+
See [issue 3](https://github.com/mceachen/with_advisory_lock/issues/3).
|
100
|
+
|
101
|
+
SQLite doesn't have advisory locks, so we resort to file locking, which will only work
|
102
|
+
if the ```FLOCK_DIR``` is set consistently for all ruby processes.
|
103
|
+
|
104
|
+
In your ```spec_helper.rb``` or ```minitest_helper.rb```, add a ```before``` and ```after``` block:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
before do
|
108
|
+
ENV['FLOCK_DIR'] = Dir.mktmpdir
|
109
|
+
end
|
110
|
+
|
111
|
+
after do
|
112
|
+
FileUtils.remove_entry_secure ENV['FLOCK_DIR']
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
92
116
|
## Changelog
|
93
117
|
|
118
|
+
### 0.0.8
|
119
|
+
|
120
|
+
* Addressed [issue 5](https://github.com/mceachen/with_advisory_lock/issues/5) by
|
121
|
+
using a deterministic hash for Postgresql + MRI >= 1.9.
|
122
|
+
Thanks for the pull request, [Joel Turkel](https://github.com/jturkel)!
|
123
|
+
* Addressed [issue 2](https://github.com/mceachen/with_advisory_lock/issues/2) by
|
124
|
+
using a cache-busting query for MySQL and Postgres to deal with AR value caching bug.
|
125
|
+
Thanks for the pull request, [Jaime Giraldo](https://github.com/sposmen)!
|
126
|
+
* Addressed [issue 4](https://github.com/mceachen/with_advisory_lock/issues/4) by
|
127
|
+
adding support for ```em-postgresql-adapter```.
|
128
|
+
Thanks, [lestercsp](https://github.com/lestercsp)!
|
129
|
+
|
130
|
+
(Hey, github—your notifications are WAY too easy to ignore!)
|
131
|
+
|
94
132
|
### 0.0.7
|
95
133
|
|
96
134
|
* Added Travis tests for Rails 3.0, 3.1, 3.2, and 4.0
|
@@ -21,7 +21,7 @@ module WithAdvisoryLock
|
|
21
21
|
|
22
22
|
def with_advisory_lock(lock_name, timeout_seconds=nil, &block)
|
23
23
|
impl_class = case (connection.adapter_name.downcase)
|
24
|
-
when "postgresql"
|
24
|
+
when "postgresql", "empostgresql"
|
25
25
|
WithAdvisoryLock::PostgreSQL
|
26
26
|
when "mysql", "mysql2"
|
27
27
|
WithAdvisoryLock::MySQL
|
@@ -14,15 +14,19 @@ module WithAdvisoryLock
|
|
14
14
|
# 0 if the attempt timed out (for example, because another client has
|
15
15
|
# previously locked the name), or NULL if an error occurred
|
16
16
|
# (such as running out of memory or the thread was killed with mysqladmin kill).
|
17
|
-
|
17
|
+
# The timestamp prevents AR from caching the result improperly, and is ignored.
|
18
|
+
sql = "SELECT GET_LOCK(#{quoted_lock_name}, 0), #{Time.now.to_f}"
|
19
|
+
1 == connection.select_value(sql).to_i
|
18
20
|
end
|
19
21
|
|
20
22
|
def release_lock
|
21
|
-
# Returns
|
23
|
+
# Returns > 0 if the lock was released,
|
22
24
|
# 0 if the lock was not established by this thread (
|
23
25
|
# in which case the lock is not released), and
|
24
26
|
# NULL if the named lock did not exist.
|
25
|
-
|
27
|
+
# The timestamp prevents AR from caching the result improperly, and is ignored.
|
28
|
+
sql = "SELECT RELEASE_LOCK(#{quoted_lock_name}), #{Time.now.to_f}"
|
29
|
+
1 == connection.select_value(sql).to_i
|
26
30
|
end
|
27
31
|
|
28
32
|
def already_locked?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
1
3
|
module WithAdvisoryLock
|
2
4
|
class PostgreSQL < Base
|
3
5
|
|
@@ -6,11 +8,13 @@ module WithAdvisoryLock
|
|
6
8
|
def try_lock
|
7
9
|
# pg_try_advisory_lock will either obtain the lock immediately
|
8
10
|
# and return true, or return false if the lock cannot be acquired immediately
|
9
|
-
|
11
|
+
sql = "SELECT pg_try_advisory_lock(#{numeric_lock}), #{Time.now.to_f}"
|
12
|
+
"t" == connection.select_value(sql).to_s
|
10
13
|
end
|
11
14
|
|
12
15
|
def release_lock
|
13
|
-
|
16
|
+
sql = "SELECT pg_advisory_unlock(#{numeric_lock}), #{Time.now.to_f}"
|
17
|
+
"t" == connection.select_value(sql).to_s
|
14
18
|
end
|
15
19
|
|
16
20
|
def numeric_lock
|
@@ -18,7 +22,9 @@ module WithAdvisoryLock
|
|
18
22
|
if lock_name.is_a? Numeric
|
19
23
|
lock_name.to_i
|
20
24
|
else
|
21
|
-
|
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)
|
22
28
|
end
|
23
29
|
end
|
24
30
|
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.8
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|