with_advisory_lock 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjRiZmQ3MDg3MTEwZDYwNzkzMDQ1YWFjNjc0Y2FmYmJlNTkyN2NjZQ==
4
+ YzA3MTE5YzM5MzkwMjJmZjRjOWFhMTc1ZjA0ZGNmNWFlNTE1NjM4MQ==
5
5
  data.tar.gz: !binary |-
6
- ODk2NDU3MzZkMjk1YTIyZmU0Y2UzYmM1OGQzYjJmMWNmNTkwZTg5Mw==
6
+ ZGViZjU2NzczOGYyMTVkZDJjYWRmZjYwMTMwOWQwMDlkNDI1ZDc3Yw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- OTU4OGVjMDQyNzI4Njg5N2RhZmYzOTk5ZmNjMGRjZDg4M2MyZWJhYTJjNDcx
10
- OTYzMGU4Nzc5Y2Y3NWMxMTk5ZTUzNjU5M2M5OWI4MGRmMjQ1OTJlNDY4YTQ4
11
- NGY3MmI5ZjRhODZmYjM5NTMyZDRiZjFiZTE4ZDYwMzZiMDBjMmQ=
9
+ NDNmYWE3ZmNhZGFlZDU3ODQzMzAyODdlNTAxZjdkZTFkYzk2NDM2N2I0Mjhm
10
+ YTE0OGM0MmZlNDY1ZWMyN2YzNmZmYTIzOWE5ODRhODFjZmE2MjY5MDNkNmY2
11
+ Y2M2ZmNhOWVkNGVjNzI3MDczZmVjN2IxZTI0NjcwYmUzYzI2MmE=
12
12
  data.tar.gz: !binary |-
13
- ZWQ1NzY2N2QwM2JlNmRkMDg1N2NiZWVkOTU4MjUxMDA2NmQwZWRkZmQ0OWJj
14
- ZDE4Yzg0ZDBkODQwYzI2M2JjZGQ0YmZhNzFlZGVhYWFiMDViNDA1ZjU0MGI4
15
- Y2RkNTRlOWFiMDZmMzQ0N2RlOGE0ZWEyNjNmMjQ1ODg2N2IwNTY=
13
+ NmY4MDdkOTM1NzkxODU1N2NiN2U0N2Q2MTNiYTk0YjU4NGFmZTY2NDA4NjEz
14
+ MWU5ODZhYTFkYWUwZGE4MmE4ZjZhNWU5ZGY5NDZkYzkxZjA3MGVmODgxOTEz
15
+ MDM3ZDYwNTk5YTg1NjNkY2M1MWI5MDYxYzkwM2E0MzI2ZjllMzA=
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # with_advisory_lock [![Build Status](https://api.travis-ci.org/mceachen/with_advisory_lock.png?branch=master)](https://travis-ci.org/mceachen/with_advisory_lock)
1
+ # with_advisory_lock
2
2
 
3
- Adds advisory locking to ActiveRecord 3.2.x.
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
- and [PostgreSQL](http://www.postgresql.org/docs/9.1/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS)
6
- are supported natively. SQLite resorts to file locking (which won't span hosts, of course!).
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
+ [![Build Status](https://api.travis-ci.org/mceachen/with_advisory_lock.png?branch=master)](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
- 1 == connection.select_value("SELECT GET_LOCK(#{quoted_lock_name}, 0)").to_i
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 1 if the lock was released,
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
- 1 == connection.select_value("SELECT RELEASE_LOCK(#{quoted_lock_name})").to_i
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
- "t" == connection.select_value("SELECT pg_try_advisory_lock(#{numeric_lock})").to_s
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
- "t" == connection.select_value("SELECT pg_advisory_unlock(#{numeric_lock})").to_s
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
- lock_name.to_s.hash
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
@@ -1,3 +1,3 @@
1
1
  module WithAdvisoryLock
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  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.7
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-21 00:00:00.000000000 Z
11
+ date: 2013-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord