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 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