with_advisory_lock 4.0.0 → 4.6.0

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,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 82b0119f2158391cace745c0fa71a65497198de1
4
- data.tar.gz: eb7f626ed7db658000ad0db260a2692035ee4dc1
2
+ SHA256:
3
+ metadata.gz: bc4719c3f44e4cf1f219b582d21cf9757274ca1fcca6128c227798f32d4b589e
4
+ data.tar.gz: c63b8be1ee91b25a519b5a4d7d090ff98172d6d3a476899b55c6cd03bf77eda6
5
5
  SHA512:
6
- metadata.gz: efe96bf02dc09cf167ed2058c164405c4f6d6ae9e31442b46d9e9f5ceebc7add1850861d0b6bd311087a056cab5928ad27afa680a4b11884d1474bee4ae25269
7
- data.tar.gz: f9f753d910d24f6cb98fc91a47aed8dcccb82bf99f55ea193aceb8ffedf3c2732eef1c7945c64d1a5ac72aa27d89a0205307ddab72cce7113ca6c37c22496ac9
6
+ metadata.gz: be5a906b9740506447bfcdb1928cd5473add67e0a48acd32f8a26ca4671be49b34ca5eca987df2f8e6f29a0a3dbb0bccc3321061d9f1e5827a0ca4ef44341124
7
+ data.tar.gz: 124358b251ffd9cc066d54aebfeeca97fb0db88ae18293fb7e0169ceb13960d298e72c66e9d5fbd79f0cbda5360d2fdb2e609866ac82eeb26b1eab6ead103daf
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ spec/reports
17
17
  test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
+ *.iml
@@ -0,0 +1 @@
1
+ ruby 2.6.4
@@ -1,32 +1,38 @@
1
1
  language: ruby
2
2
 
3
- dist: trusty
4
- sudo: required
3
+ services:
4
+ - postgresql
5
+ - mysql
6
+
7
+ addons:
8
+ postgresql: "10"
5
9
 
6
10
  rvm:
7
- - 2.5.1
8
- - 2.4.0
9
- - 2.3.3
10
- - 2.2.10
11
+ - 2.6.4
12
+ - 2.5.6
13
+ - 2.4.7
11
14
 
12
15
  gemfile:
16
+ - gemfiles/activerecord_6.0.gemfile
13
17
  - gemfiles/activerecord_5.2.gemfile
14
18
  - gemfiles/activerecord_5.1.gemfile
15
19
  - gemfiles/activerecord_5.0.gemfile
16
20
  - gemfiles/activerecord_4.2.gemfile
17
21
 
18
22
  env:
19
- - DB=postgresql
20
- - DB=mysql
21
- - DB=mysql MYSQL_VERSION=5.7
22
- - DB=sqlite
23
+ global:
24
+ - WITH_ADVISORY_LOCK_PREFIX=$TRAVIS_JOB_ID
25
+ matrix:
26
+ - DB=postgresql
27
+ - DB=mysql MYSQL_VERSION=5.7
28
+ - DB=sqlite
29
+ matrix:
30
+ exclude:
31
+ - rvm: 2.4.7
32
+ gemfile: gemfiles/activerecord_6.0.gemfile
23
33
 
24
34
  before_install:
25
- - gem install bundler
26
-
27
- script: WITH_ADVISORY_LOCK_PREFIX=$TRAVIS_JOB_ID bundle exec rake --trace
28
-
29
- before_script:
30
- - bash .travis.install-mysql-5.7.sh
31
35
  - mysql -e 'create database with_advisory_lock_test'
32
- - psql -c 'create database with_advisory_lock_test' -U postgres
36
+ - psql -c 'create database with_advisory_lock_test' -U postgres
37
+
38
+ script: bundle exec rake --trace
data/Appraisals CHANGED
@@ -3,17 +3,27 @@ appraise "activerecord-4.2" do
3
3
  platforms :ruby do
4
4
  gem "pg", "~> 0.21"
5
5
  gem "mysql2", "< 0.5"
6
+ gem "sqlite3", "~> 1.3.6"
6
7
  end
7
8
  end
8
9
 
9
10
  appraise "activerecord-5.0" do
10
11
  gem "activerecord", "~> 5.0.0"
12
+ platforms :ruby do
13
+ gem "sqlite3", "~> 1.3.6"
14
+ end
11
15
  end
12
16
 
13
17
  appraise "activerecord-5.1" do
14
18
  gem "activerecord", "~> 5.1.0"
19
+ gem "sqlite3", "~> 1.3.6"
15
20
  end
16
21
 
17
22
  appraise "activerecord-5.2" do
18
23
  gem "activerecord", "~> 5.1.0"
19
- end
24
+ gem "sqlite3", "~> 1.3.6"
25
+ end
26
+
27
+ appraise "activerecord-6.0" do
28
+ gem "activerecord", "~> 6.0.0"
29
+ end
@@ -0,0 +1,122 @@
1
+ ## Changelog
2
+
3
+ ### 4.6.0
4
+
5
+ - Support for ActiveRecord 6
6
+ - Add Support for nested locks in MySQL
7
+
8
+ ### 4.0.0
9
+
10
+ - Drop support for unsupported versions of activerecord
11
+ - Drop support for unsupported versions of ruby
12
+
13
+ ### 3.2.0
14
+
15
+ - [Joshua Flanagan](https://github.com/joshuaflanagan) [added a SQL comment to the lock query for PostgreSQL](https://github.com/ClosureTree/with_advisory_lock/pull/28). Thanks!
16
+ - [Fernando Luizão](https://github.com/fernandoluizao) found a spurious requirement for `thread_safe`. Thanks for the [fix](https://github.com/ClosureTree/with_advisory_lock/pull/27)!
17
+
18
+ ### 3.1.1
19
+
20
+ - [Joel Turkel](https://github.com/jturkel) added `require 'active_support'` (it was required, but relied on downstream gems to pull in active_support before pulling in with_advisory_lock). Thanks!
21
+
22
+ ### 3.1.0
23
+
24
+ - [Jason Weathered](https://github.com/jasoncodes) Added new shared and transaction-level lock options ([Pull request 21](https://github.com/ClosureTree/with_advisory_lock/pull/21)). Thanks!
25
+ - Added ActiveRecord 5.0 to build matrix. Dropped 3.2, 4.0, and 4.1 (which no longer get security updates: http://rubyonrails.org/security/)
26
+ - Replaced ruby 1.9 and 2.0 (both EOL) with ruby 2.2 and 2.3 (see https://www.ruby-lang.org/en/downloads/)
27
+
28
+ ### 3.0.0
29
+
30
+ - Added jruby/PostgreSQL support for Rails 4.x
31
+ - Reworked threaded tests to allow jruby tests to pass
32
+
33
+ #### API changes
34
+
35
+ - `yield_with_lock_and_timeout` and `yield_with_lock` now return instances of
36
+ `WithAdvisoryLock::Result`, so blocks that return `false` are not misinterpreted
37
+ as a failure to lock. As this changes the interface (albeit internal methods), the major version
38
+ number was incremented.
39
+ - `with_advisory_lock_result` was introduced, which clarifies whether the lock was acquired
40
+ versus the yielded block returned false.
41
+
42
+ ### 2.0.0
43
+
44
+ - Lock timeouts of 0 now attempt the lock once, as per suggested by
45
+ [Jon Leighton](https://github.com/jonleighton) and implemented by
46
+ [Abdelkader Boudih](https://github.com/seuros). Thanks to both of you!
47
+ - [Pull request 11](https://github.com/ClosureTree/with_advisory_lock/pull/11)
48
+ fixed a downstream issue with jruby support! Thanks, [Aaron Todd](https://github.com/ozzyaaron)!
49
+ - Added Travis tests for jruby
50
+ - Dropped support for Rails 3.0, 3.1, and Ruby 1.8.7, as they are no longer
51
+ receiving security patches. See http://rubyonrails.org/security/ for more information.
52
+ This required the major version bump.
53
+ - Refactored `advisory_lock_exists?` to use existing functionality
54
+ - Fixed sqlite's implementation so parallel tests could be run against it
55
+
56
+ ### 1.0.0
57
+
58
+ - Releasing 1.0.0. The interface will be stable.
59
+ - Added `advisory_lock_exists?`. Thanks, [Sean Devine](https://github.com/barelyknown), for the
60
+ great pull request!
61
+ - Added Travis test for Rails 4.1
62
+
63
+ ### 0.0.10
64
+
65
+ - Explicitly added MIT licensing to the gemspec.
66
+
67
+ ### 0.0.9
68
+
69
+ - Merged in Postgis Adapter Support to address [issue 7](https://github.com/ClosureTree/with_advisory_lock/issues/7)
70
+ Thanks for the pull request, [Abdelkader Boudih](https://github.com/seuros)!
71
+ - The database switching code had to be duplicated by [Closure Tree](https://github.com/ClosureTree/closure_tree),
72
+ so I extracted a new `WithAdvisoryLock::DatabaseAdapterSupport` one-trick pony.
73
+ - Builds were failing on Travis, so I introduced a global lock prefix that can be set with the
74
+ `WITH_ADVISORY_LOCK_PREFIX` environment variable. I'm not going to advertise this feature yet.
75
+ It's a secret. Only you and I know, now. _shhh_
76
+
77
+ ### 0.0.8
78
+
79
+ - Addressed [issue 5](https://github.com/ClosureTree/with_advisory_lock/issues/5) by
80
+ using a deterministic hash for Postgresql + MRI >= 1.9.
81
+ Thanks for the pull request, [Joel Turkel](https://github.com/jturkel)!
82
+ - Addressed [issue 2](https://github.com/ClosureTree/with_advisory_lock/issues/2) by
83
+ using a cache-busting query for MySQL and Postgres to deal with AR value caching bug.
84
+ Thanks for the pull request, [Jaime Giraldo](https://github.com/sposmen)!
85
+ - Addressed [issue 4](https://github.com/ClosureTree/with_advisory_lock/issues/4) by
86
+ adding support for `em-postgresql-adapter`.
87
+ Thanks, [lestercsp](https://github.com/lestercsp)!
88
+
89
+ (Hey, github—your notifications are WAY too easy to ignore!)
90
+
91
+ ### 0.0.7
92
+
93
+ - Added Travis tests for Rails 3.0, 3.1, 3.2, and 4.0
94
+ - Fixed MySQL bug with select_value returning a string instead of an integer when using AR 3.0.x
95
+
96
+ ### 0.0.6
97
+
98
+ - Only require ActiveRecord >= 3.0.x
99
+ - Fixed MySQL error reporting
100
+
101
+ ### 0.0.5
102
+
103
+ - Asking for the currently acquired advisory lock doesn't re-ask for the lock now.
104
+ - Introduced NestedAdvisoryLockError when asking for different, nested advisory locksMySQL
105
+
106
+ ### 0.0.4
107
+
108
+ - Moved require into on_load, which should speed loading when AR doesn't have to spin up
109
+
110
+ ### 0.0.3
111
+
112
+ - Fought with ActiveRecord 3.0.x and 3.1.x. You don't want them if you use threads—they fail
113
+ predictably.
114
+
115
+ ### 0.0.2
116
+
117
+ - Added warning log message for nested MySQL lock calls
118
+ - Randomized lock wait time, which can help ameliorate lock contention
119
+
120
+ ### 0.0.1
121
+
122
+ - First whack
data/README.md CHANGED
@@ -1,22 +1,27 @@
1
1
  # with_advisory_lock
2
2
 
3
- Adds advisory locking (mutexes) to ActiveRecord 4.2 and 5.0, with ruby 2.4, 2.3 or 2.2, when used with
4
- [MySQL](http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock)
5
- or [PostgreSQL](http://www.postgresql.org/docs/9.3/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS).
3
+ Adds advisory locking (mutexes) to ActiveRecord 4.2, 5.x and 6.0, with ruby
4
+ 2.4, 2.5 and 2.6, when used with
5
+ [MySQL](https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_get-lock)
6
+ or
7
+ [PostgreSQL](https://www.postgresql.org/docs/current/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS).
6
8
  SQLite resorts to file locking.
7
9
 
8
- [![Build Status](https://api.travis-ci.org/ClosureTree/with_advisory_lock.svg?branch=master)](http://travis-ci.org/ClosureTree/with_advisory_lock)
10
+ [![Build Status](https://api.travis-ci.org/ClosureTree/with_advisory_lock.svg?branch=master)](https://travis-ci.org/ClosureTree/with_advisory_lock)
9
11
  [![Gem Version](https://badge.fury.io/rb/with_advisory_lock.svg)](https://badge.fury.io/rb/with_advisory_lock)
10
12
 
11
13
  ## What's an "Advisory Lock"?
12
14
 
13
- An advisory lock is a [mutex](http://en.wikipedia.org/wiki/Mutual_exclusion) used to ensure no two
14
- processes run some process at the same time. When the advisory lock is powered by your database
15
- server, as long as it isn't SQLite, your mutex spans hosts.
15
+ An advisory lock is a [mutex](https://en.wikipedia.org/wiki/Mutual_exclusion)
16
+ used to ensure no two processes run some process at the same time. When the
17
+ advisory lock is powered by your database server, as long as it isn't SQLite,
18
+ your mutex spans hosts.
16
19
 
17
20
  ## Usage
18
21
 
19
- Where ```User``` is an ActiveRecord model, and ```lock_name``` is some string:
22
+ This gem automatically includes the `WithAdvisoryLock` module in all of your
23
+ ActiveRecord models. Here's an example of how to use it where `User` is an
24
+ ActiveRecord model, and `lock_name` is some string:
20
25
 
21
26
  ```ruby
22
27
  User.with_advisory_lock(lock_name) do
@@ -32,58 +37,65 @@ end
32
37
 
33
38
  ### Lock wait timeouts
34
39
 
35
- ```with_advisory_lock``` takes an options hash as the second parameter.
36
- The ```timeout_seconds``` option defaults to ```nil```, which means wait indefinitely for the lock.
40
+ `with_advisory_lock` takes an options hash as the second parameter. The
41
+ `timeout_seconds` option defaults to `nil`, which means wait indefinitely for
42
+ the lock.
37
43
 
38
44
  A value of zero will try the lock only once. If the lock is acquired, the block
39
- will be yielded to. If the lock is currently being held, the block will not be called.
45
+ will be yielded to. If the lock is currently being held, the block will not be
46
+ called.
40
47
 
41
- Note that if a non-nil value is provided for `timeout_seconds`, the block will not be invoked if
42
- the lock cannot be acquired within that time-frame.
48
+ Note that if a non-nil value is provided for `timeout_seconds`, the block will
49
+ not be invoked if the lock cannot be acquired within that time-frame.
43
50
 
44
- For backwards compatability, the timeout value can be specified directly as the second parameter.
51
+ For backwards compatability, the timeout value can be specified directly as the
52
+ second parameter.
45
53
 
46
54
  ### Shared locks
47
55
 
48
- The ```shared``` option defaults to ```false``` which means an exclusive lock will be obtained.
49
- Setting ```shared``` to ```true``` will allow locks to be obtained by multiple actors
50
- as long as they are all shared locks.
56
+ The `shared` option defaults to `false` which means an exclusive lock will be
57
+ obtained. Setting `shared` to `true` will allow locks to be obtained by multiple
58
+ actors as long as they are all shared locks.
51
59
 
52
60
  Note: MySQL does not support shared locks.
53
61
 
54
62
  ### Transaction-level locks
55
63
 
56
- PostgreSQL supports transaction-level locks which remain held until the transaction completes.
57
- You can enable this by setting the ```transaction``` option to ```true```.
64
+ PostgreSQL supports transaction-level locks which remain held until the
65
+ transaction completes. You can enable this by setting the `transaction` option
66
+ to `true`.
58
67
 
59
- Note: transaction-level locks will not be reflected by `.current_advisory_lock` when the block has returned.
68
+ Note: transaction-level locks will not be reflected by `.current_advisory_lock`
69
+ when the block has returned.
60
70
 
61
71
  ### Return values
62
72
 
63
- The return value of `with_advisory_lock_result` is a `WithAdvisoryLock::Result` instance,
64
- which has a `lock_was_acquired?` method and a `result` accessor method, which is
65
- the returned value of the given block. If your block may validly return false, you should use
66
- this method.
67
-
68
- The return value of ```with_advisory_lock``` will be the result of the yielded block,
69
- if the lock was able to be acquired and the block yielded, or ```false```, if you provided
70
- a timeout_seconds value and the lock was not able to be acquired in time.
73
+ The return value of `with_advisory_lock_result` is a `WithAdvisoryLock::Result`
74
+ instance, which has a `lock_was_acquired?` method and a `result` accessor
75
+ method, which is the returned value of the given block. If your block may
76
+ validly return false, you should use this method.
77
+
78
+ The return value of `with_advisory_lock` will be the result of the yielded
79
+ block, if the lock was able to be acquired and the block yielded, or `false`, if
80
+ you provided a timeout_seconds value and the lock was not able to be acquired in
81
+ time.
71
82
 
72
83
  ### Testing for the current lock status
73
84
 
74
- If you needed to check if the advisory lock is currently being held, you can call
75
- ```Tag.advisory_lock_exists?("foo")```, but realize the lock can be acquired between the time you
76
- test for the lock, and the time you try to acquire the lock.
85
+ If you needed to check if the advisory lock is currently being held, you can
86
+ call `Tag.advisory_lock_exists?("foo")`, but realize the lock can be acquired
87
+ between the time you test for the lock, and the time you try to acquire the
88
+ lock.
77
89
 
78
- If you want to see if the current Thread is holding a lock, you can call ```Tag.current_advisory_lock```
79
- which will return the name of the current lock. If no lock is currently held,
80
- ```.current_advisory_lock``` returns ```nil```.
90
+ If you want to see if the current Thread is holding a lock, you can call
91
+ `Tag.current_advisory_lock` which will return the name of the current lock. If
92
+ no lock is currently held, `.current_advisory_lock` returns `nil`.
81
93
 
82
94
  ## Installation
83
95
 
84
96
  Add this line to your application's Gemfile:
85
97
 
86
- ``` ruby
98
+ ```ruby
87
99
  gem 'with_advisory_lock'
88
100
  ```
89
101
 
@@ -93,27 +105,31 @@ And then execute:
93
105
 
94
106
  ## Lock Types
95
107
 
96
- First off, know that there are **lots** of different kinds of locks available to you. **Pick the
97
- finest-grain lock that ensures correctness.** If you choose a lock that is too coarse, you are
98
- unnecessarily blocking other processes.
108
+ First off, know that there are **lots** of different kinds of locks available to
109
+ you. **Pick the finest-grain lock that ensures correctness.** If you choose a
110
+ lock that is too coarse, you are unnecessarily blocking other processes.
99
111
 
100
112
  ### Advisory locks
101
- These are named mutexes that are inherently "application level"—it is up to the application
102
- to acquire, run a critical code section, and release the advisory lock.
113
+
114
+ These are named mutexes that are inherently "application level"—it is up to the
115
+ application to acquire, run a critical code section, and release the advisory
116
+ lock.
103
117
 
104
118
  ### Row-level locks
119
+
105
120
  Whether [optimistic](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html)
106
121
  or [pessimistic](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html),
107
122
  row-level locks prevent concurrent modification to a given model.
108
123
 
109
124
  **If you're building a
110
- [CRUD](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete) application, this will be your
111
- most commonly used lock.**
125
+ [CRUD](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
126
+ application, this will be your most commonly used lock.**
112
127
 
113
128
  ### Table-level locks
114
129
 
115
- Provided through something like the [monogamy](https://github.com/ClosureTree/monogamy)
116
- gem, these prevent concurrent access to **any instance of a model**. Their coarseness means they
130
+ Provided through something like the
131
+ [monogamy](https://github.com/ClosureTree/monogamy) gem, these prevent
132
+ concurrent access to **any instance of a model**. Their coarseness means they
117
133
  aren't going to be commonly applicable, and they can be a source of
118
134
  [deadlocks](http://en.wikipedia.org/wiki/Deadlock).
119
135
 
@@ -125,26 +141,31 @@ Advisory locks with MySQL and PostgreSQL ignore database transaction boundaries.
125
141
 
126
142
  You will want to wrap your block within a transaction to ensure consistency.
127
143
 
128
- ### MySQL doesn't support nesting
144
+ ### MySQL < 5.7.5 doesn't support nesting
145
+
146
+ With MySQL < 5.7.5, if you ask for a _different_ advisory lock within
147
+ a `with_advisory_lock` block, you will be releasing the parent lock (!!!). A
148
+ `NestedAdvisoryLockError`will be raised in this case. If you ask for the same
149
+ lock name, `with_advisory_lock` won't ask for the lock again, and the block
150
+ given will be yielded to.
129
151
 
130
- With MySQL (at least <= v5.5), if you ask for a *different* advisory lock within a ```with_advisory_lock``` block,
131
- you will be releasing the parent lock (!!!). A ```NestedAdvisoryLockError```will be raised
132
- in this case. If you ask for the same lock name, ```with_advisory_lock``` won't ask for the
133
- lock again, and the block given will be yielded to.
152
+ This is not an issue in MySQL >= 5.7.5, and no error will be raised for nested
153
+ lock usage. You can override this by passing `force_nested_lock_support: true`
154
+ or `force_nested_lock_support: false` to the `with_advisory_lock` options.
134
155
 
135
156
  ### Is clustered MySQL supported?
136
157
 
137
158
  [No.](https://github.com/ClosureTree/with_advisory_lock/issues/16)
138
159
 
139
- ### There are many ```lock-*``` files in my project directory after test runs
160
+ ### There are many `lock-*` files in my project directory after test runs
140
161
 
141
162
  This is expected if you aren't using MySQL or Postgresql for your tests.
142
163
  See [issue 3](https://github.com/ClosureTree/with_advisory_lock/issues/3).
143
164
 
144
- SQLite doesn't have advisory locks, so we resort to file locking, which will only work
145
- if the ```FLOCK_DIR``` is set consistently for all ruby processes.
165
+ SQLite doesn't have advisory locks, so we resort to file locking, which will
166
+ only work if the `FLOCK_DIR` is set consistently for all ruby processes.
146
167
 
147
- In your ```spec_helper.rb``` or ```minitest_helper.rb```, add a ```before``` and ```after``` block:
168
+ In your `spec_helper.rb` or `minitest_helper.rb`, add a `before` and `after` block:
148
169
 
149
170
  ```ruby
150
171
  before do
@@ -155,121 +176,3 @@ after do
155
176
  FileUtils.remove_entry_secure ENV['FLOCK_DIR']
156
177
  end
157
178
  ```
158
-
159
- ## Changelog
160
-
161
- ### 4.0.0
162
-
163
- * Drop support for unsupported versions of activerecord
164
- * Drop support for unsupported versions of ruby
165
-
166
- ### 3.2.0
167
-
168
- * [Joshua Flanagan](https://github.com/joshuaflanagan) [added a SQL comment to the lock query for PostgreSQL](https://github.com/ClosureTree/with_advisory_lock/pull/28). Thanks!
169
- * [Fernando Luizão](https://github.com/fernandoluizao) found a spurious requirement for `thread_safe`. Thanks for the [fix](https://github.com/ClosureTree/with_advisory_lock/pull/27)!
170
-
171
- ### 3.1.1
172
-
173
- * [Joel Turkel](https://github.com/jturkel) added `require 'active_support'` (it was required, but relied on downstream gems to pull in active_support before pulling in with_advisory_lock). Thanks!
174
-
175
- ### 3.1.0
176
-
177
- * [Jason Weathered](https://github.com/jasoncodes) Added new shared and transaction-level lock options ([Pull request 21](https://github.com/ClosureTree/with_advisory_lock/pull/21)). Thanks!
178
- * Added ActiveRecord 5.0 to build matrix. Dropped 3.2, 4.0, and 4.1 (which no longer get security updates: http://rubyonrails.org/security/)
179
- * Replaced ruby 1.9 and 2.0 (both EOL) with ruby 2.2 and 2.3 (see https://www.ruby-lang.org/en/downloads/)
180
-
181
- ### 3.0.0
182
-
183
- * Added jruby/PostgreSQL support for Rails 4.x
184
- * Reworked threaded tests to allow jruby tests to pass
185
-
186
- #### API changes
187
-
188
- * `yield_with_lock_and_timeout` and `yield_with_lock` now return instances of
189
- `WithAdvisoryLock::Result`, so blocks that return `false` are not misinterpreted
190
- as a failure to lock. As this changes the interface (albeit internal methods), the major version
191
- number was incremented.
192
- * `with_advisory_lock_result` was introduced, which clarifies whether the lock was acquired
193
- versus the yielded block returned false.
194
-
195
- ### 2.0.0
196
-
197
- * Lock timeouts of 0 now attempt the lock once, as per suggested by
198
- [Jon Leighton](https://github.com/jonleighton) and implemented by
199
- [Abdelkader Boudih](https://github.com/seuros). Thanks to both of you!
200
- * [Pull request 11](https://github.com/ClosureTree/with_advisory_lock/pull/11)
201
- fixed a downstream issue with jruby support! Thanks, [Aaron Todd](https://github.com/ozzyaaron)!
202
- * Added Travis tests for jruby
203
- * Dropped support for Rails 3.0, 3.1, and Ruby 1.8.7, as they are no longer
204
- receiving security patches. See http://rubyonrails.org/security/ for more information.
205
- This required the major version bump.
206
- * Refactored `advisory_lock_exists?` to use existing functionality
207
- * Fixed sqlite's implementation so parallel tests could be run against it
208
-
209
- ### 1.0.0
210
-
211
- * Releasing 1.0.0. The interface will be stable.
212
- * Added ```advisory_lock_exists?```. Thanks, [Sean Devine](https://github.com/barelyknown), for the
213
- great pull request!
214
- * Added Travis test for Rails 4.1
215
-
216
- ### 0.0.10
217
-
218
- * Explicitly added MIT licensing to the gemspec.
219
-
220
- ### 0.0.9
221
-
222
- * Merged in Postgis Adapter Support to address [issue 7](https://github.com/ClosureTree/with_advisory_lock/issues/7)
223
- Thanks for the pull request, [Abdelkader Boudih](https://github.com/seuros)!
224
- * The database switching code had to be duplicated by [Closure Tree](https://github.com/ClosureTree/closure_tree),
225
- so I extracted a new ```WithAdvisoryLock::DatabaseAdapterSupport``` one-trick pony.
226
- * Builds were failing on Travis, so I introduced a global lock prefix that can be set with the
227
- ```WITH_ADVISORY_LOCK_PREFIX``` environment variable. I'm not going to advertise this feature yet.
228
- It's a secret. Only you and I know, now. *shhh*
229
-
230
- ### 0.0.8
231
-
232
- * Addressed [issue 5](https://github.com/ClosureTree/with_advisory_lock/issues/5) by
233
- using a deterministic hash for Postgresql + MRI >= 1.9.
234
- Thanks for the pull request, [Joel Turkel](https://github.com/jturkel)!
235
- * Addressed [issue 2](https://github.com/ClosureTree/with_advisory_lock/issues/2) by
236
- using a cache-busting query for MySQL and Postgres to deal with AR value caching bug.
237
- Thanks for the pull request, [Jaime Giraldo](https://github.com/sposmen)!
238
- * Addressed [issue 4](https://github.com/ClosureTree/with_advisory_lock/issues/4) by
239
- adding support for ```em-postgresql-adapter```.
240
- Thanks, [lestercsp](https://github.com/lestercsp)!
241
-
242
- (Hey, github—your notifications are WAY too easy to ignore!)
243
-
244
- ### 0.0.7
245
-
246
- * Added Travis tests for Rails 3.0, 3.1, 3.2, and 4.0
247
- * Fixed MySQL bug with select_value returning a string instead of an integer when using AR 3.0.x
248
-
249
- ### 0.0.6
250
-
251
- * Only require ActiveRecord >= 3.0.x
252
- * Fixed MySQL error reporting
253
-
254
- ### 0.0.5
255
-
256
- * Asking for the currently acquired advisory lock doesn't re-ask for the lock now.
257
- * Introduced NestedAdvisoryLockError when asking for different, nested advisory locksMySQL
258
-
259
- ### 0.0.4
260
-
261
- * Moved require into on_load, which should speed loading when AR doesn't have to spin up
262
-
263
- ### 0.0.3
264
-
265
- * Fought with ActiveRecord 3.0.x and 3.1.x. You don't want them if you use threads—they fail
266
- predictably.
267
-
268
- ### 0.0.2
269
-
270
- * Added warning log message for nested MySQL lock calls
271
- * Randomized lock wait time, which can help ameliorate lock contention
272
-
273
- ### 0.0.1
274
-
275
- * First whack
@@ -7,7 +7,7 @@ gem "activerecord", "~> 4.2.0"
7
7
  platforms :ruby do
8
8
  gem "mysql2", "< 0.5"
9
9
  gem "pg", "~> 0.21"
10
- gem "sqlite3"
10
+ gem "sqlite3", "~> 1.3.6"
11
11
  end
12
12
 
13
13
  platforms :jruby do
@@ -7,7 +7,7 @@ gem "activerecord", "~> 5.0.0"
7
7
  platforms :ruby do
8
8
  gem "mysql2"
9
9
  gem "pg"
10
- gem "sqlite3"
10
+ gem "sqlite3", "~> 1.3.6"
11
11
  end
12
12
 
13
13
  platforms :jruby do
@@ -7,7 +7,7 @@ gem "activerecord", "~> 5.1.0"
7
7
  platforms :ruby do
8
8
  gem "mysql2"
9
9
  gem "pg"
10
- gem "sqlite3"
10
+ gem "sqlite3", "~> 1.3.6"
11
11
  end
12
12
 
13
13
  platforms :jruby do
@@ -7,7 +7,7 @@ gem "activerecord", "~> 5.1.0"
7
7
  platforms :ruby do
8
8
  gem "mysql2"
9
9
  gem "pg"
10
- gem "sqlite3"
10
+ gem "sqlite3", "~> 1.3.6"
11
11
  end
12
12
 
13
13
  platforms :jruby do
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 6.0.0"
6
+
7
+ platforms :ruby do
8
+ gem "mysql2"
9
+ gem "pg"
10
+ gem "sqlite3"
11
+ end
12
+
13
+ platforms :jruby do
14
+ gem "activerecord-jdbcmysql-adapter"
15
+ gem "activerecord-jdbcpostgresql-adapter"
16
+ gem "activerecord-jdbcsqlite3-adapter"
17
+ end
18
+
19
+ gemspec path: "../"
@@ -9,6 +9,7 @@ module WithAdvisoryLock
9
9
  autoload :DatabaseAdapterSupport
10
10
  autoload :Flock
11
11
  autoload :MySQL, 'with_advisory_lock/mysql'
12
+ autoload :MySQLNoNesting, 'with_advisory_lock/mysql_no_nesting'
12
13
  autoload :NestedAdvisoryLockError
13
14
  autoload :PostgreSQL, 'with_advisory_lock/postgresql'
14
15
  end
@@ -12,7 +12,8 @@ module WithAdvisoryLock
12
12
  end
13
13
 
14
14
  def with_advisory_lock_result(lock_name, options = {}, &block)
15
- impl = impl_class.new(connection, lock_name, options)
15
+ class_options = options.extract!(:force_nested_lock_support) if options.respond_to?(:fetch)
16
+ impl = impl_class(class_options).new(connection, lock_name, options)
16
17
  impl.with_advisory_lock_if_needed(&block)
17
18
  end
18
19
 
@@ -28,12 +29,22 @@ module WithAdvisoryLock
28
29
 
29
30
  private
30
31
 
31
- def impl_class
32
+ def impl_class(options = nil)
32
33
  adapter = WithAdvisoryLock::DatabaseAdapterSupport.new(connection)
33
34
  if adapter.postgresql?
34
35
  WithAdvisoryLock::PostgreSQL
35
36
  elsif adapter.mysql?
36
- WithAdvisoryLock::MySQL
37
+ nested_lock = if options.respond_to?(:fetch) && [true, false].include?(options.fetch(:force_nested_lock_support, nil))
38
+ options.fetch(:force_nested_lock_support)
39
+ else
40
+ adapter.mysql_nested_lock_support?
41
+ end
42
+
43
+ if nested_lock
44
+ WithAdvisoryLock::MySQL
45
+ else
46
+ WithAdvisoryLock::MySQLNoNesting
47
+ end
37
48
  else
38
49
  WithAdvisoryLock::Flock
39
50
  end
@@ -1,13 +1,57 @@
1
1
  module WithAdvisoryLock
2
2
  class DatabaseAdapterSupport
3
+ # Caches nested lock support by MySQL reported version
4
+ @@mysql_nl_cache = {}
5
+ @@mysql_nl_cache_mutex = Mutex.new
6
+
3
7
  def initialize(connection)
4
- @sym_name = connection.adapter_name.downcase.to_sym
8
+ @connection = connection
9
+ @sym_name = connection.adapter_name.downcase.to_sym
5
10
  end
6
11
 
7
12
  def mysql?
8
13
  %i[mysql mysql2].include? @sym_name
9
14
  end
10
15
 
16
+ # Nested lock support for MySQL was introduced in 5.7.5
17
+ # Checking by version number is complicated by MySQL compatible DBs (like MariaDB) having their own versioning schemes
18
+ # Therefore, we check for nested lock support by simply trying a nested lock, then testing and caching the outcome
19
+ def mysql_nested_lock_support?
20
+ return false unless mysql?
21
+
22
+ # We select the MySQL version this way and cache on it, as MySQL will report versions like "5.7.5", and MariaDB will
23
+ # report versions like "10.3.8-MariaDB", which allow us to cache on features without introducing problems.
24
+ version = @connection.select_value("SELECT version()")
25
+
26
+ @@mysql_nl_cache_mutex.synchronize do
27
+ return @@mysql_nl_cache[version] if @@mysql_nl_cache.keys.include?(version)
28
+
29
+ lock_1 = "\"nested-test-1-#{SecureRandom.hex}\""
30
+ lock_2 = "\"nested-test-2-#{SecureRandom.hex}\""
31
+
32
+ get_1 = @connection.select_value("SELECT GET_LOCK(#{lock_1}, 0) AS t#{SecureRandom.hex}")
33
+ get_2 = @connection.select_value("SELECT GET_LOCK(#{lock_2}, 0) AS t#{SecureRandom.hex}")
34
+
35
+ # Both locks should succeed in old and new MySQL versions with "1"
36
+ raise RuntimeError, "Unexpected nested lock acquire result #{get_1}, #{get_2}" unless [get_1, get_2] == [1, 1]
37
+
38
+ release_1 = @connection.select_value("SELECT RELEASE_LOCK(#{lock_1}) AS t#{SecureRandom.hex}")
39
+ release_2 = @connection.select_value("SELECT RELEASE_LOCK(#{lock_2}) AS t#{SecureRandom.hex}")
40
+
41
+ # In MySQL < 5.7.5 release_1 will return nil (not currently locked) and release_2 will return 1 (successfully unlocked)
42
+ # In MySQL >= 5.7.5 release_1 and release_2 will return 1 (both successfully unlocked)
43
+ # See https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock for more
44
+ @@mysql_nl_cache[version] = case [release_1, release_2]
45
+ when [1, 1]
46
+ true
47
+ when [nil, 1]
48
+ false
49
+ else
50
+ raise RuntimeError, "Unexpected nested lock release result #{release_1}, #{release_2}"
51
+ end
52
+ end
53
+ end
54
+
11
55
  def postgresql?
12
56
  %i[postgresql empostgresql postgis].include? @sym_name
13
57
  end
@@ -1,13 +1,8 @@
1
1
  module WithAdvisoryLock
2
+ # MySQL > 5.7.5 supports nested locks
2
3
  class MySQL < Base
3
- # See http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock
4
+ # See https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
4
5
  def try_lock
5
- unless lock_stack.empty?
6
- raise NestedAdvisoryLockError.new(
7
- "MySQL doesn't support nested Advisory Locks",
8
- lock_stack.dup
9
- )
10
- end
11
6
  raise ArgumentError, 'shared locks are not supported on MySQL' if shared
12
7
  if transaction
13
8
  raise ArgumentError, 'transaction level locks are not supported on MySQL'
@@ -24,11 +19,6 @@ module WithAdvisoryLock
24
19
  connection.select_value(sql).to_i > 0
25
20
  end
26
21
 
27
- # MySQL doesn't support nested locks:
28
- def already_locked?
29
- lock_stack.last == lock_stack_item
30
- end
31
-
32
22
  # MySQL wants a string as the lock key.
33
23
  def quoted_lock_str
34
24
  connection.quote(lock_str)
@@ -0,0 +1,20 @@
1
+ module WithAdvisoryLock
2
+ # For MySQL < 5.7.5 that does not support nested locks
3
+ class MySQLNoNesting < MySQL
4
+ # See http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock
5
+ def try_lock
6
+ unless lock_stack.empty?
7
+ raise NestedAdvisoryLockError.new(
8
+ "MySQL < 5.7.5 doesn't support nested Advisory Locks",
9
+ lock_stack.dup
10
+ )
11
+ end
12
+ super
13
+ end
14
+
15
+ # MySQL doesn't support nested locks:
16
+ def already_locked?
17
+ lock_stack.last == lock_stack_item
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module WithAdvisoryLock
2
- VERSION = Gem::Version.new('4.0.0')
2
+ VERSION = Gem::Version.new('4.6.0')
3
3
  end
@@ -25,8 +25,8 @@ describe "lock nesting" do
25
25
  impl.lock_stack.must_be_empty
26
26
  end
27
27
 
28
- it "raises errors with MySQL when acquiring nested lock" do
29
- skip unless env_db == :mysql
28
+ it "raises errors with MySQL < 5.7.5 when acquiring nested lock" do
29
+ skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
30
30
  exc = proc {
31
31
  Tag.with_advisory_lock("first") do
32
32
  Tag.with_advisory_lock("second") do
@@ -36,8 +36,30 @@ describe "lock nesting" do
36
36
  exc.lock_stack.map(&:name).must_equal %w(first)
37
37
  end
38
38
 
39
- it "supports nested advisory locks with !MySQL" do
40
- skip if env_db == :mysql
39
+ it "does not raise errors with MySQL < 5.7.5 when acquiring nested error force enabled" do
40
+ skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
41
+ impl = WithAdvisoryLock::Base.new(nil, nil, nil)
42
+ impl.lock_stack.must_be_empty
43
+ Tag.with_advisory_lock("first", force_nested_lock_support: true) do
44
+ impl.lock_stack.map(&:name).must_equal %w(first)
45
+ Tag.with_advisory_lock("second", force_nested_lock_support: true) do
46
+ impl.lock_stack.map(&:name).must_equal %w(first second)
47
+ Tag.with_advisory_lock("first", force_nested_lock_support: true) do
48
+ # Shouldn't ask for another lock:
49
+ impl.lock_stack.map(&:name).must_equal %w(first second)
50
+ Tag.with_advisory_lock("second", force_nested_lock_support: true) do
51
+ # Shouldn't ask for another lock:
52
+ impl.lock_stack.map(&:name).must_equal %w(first second)
53
+ end
54
+ end
55
+ end
56
+ impl.lock_stack.map(&:name).must_equal %w(first)
57
+ end
58
+ impl.lock_stack.must_be_empty
59
+ end
60
+
61
+ it "supports nested advisory locks with !MySQL 5.6" do
62
+ skip if env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
41
63
  impl = WithAdvisoryLock::Base.new(nil, nil, nil)
42
64
  impl.lock_stack.must_be_empty
43
65
  Tag.with_advisory_lock("first") do
@@ -57,4 +79,15 @@ describe "lock nesting" do
57
79
  end
58
80
  impl.lock_stack.must_be_empty
59
81
  end
82
+
83
+ it "raises with !MySQL 5.6 and nested error force disabled" do
84
+ skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
85
+ exc = proc {
86
+ Tag.with_advisory_lock("first", force_nested_lock_support: false) do
87
+ Tag.with_advisory_lock("second", force_nested_lock_support: false) do
88
+ end
89
+ end
90
+ }.must_raise WithAdvisoryLock::NestedAdvisoryLockError
91
+ exc.lock_stack.map(&:name).must_equal %w(first)
92
+ end
60
93
  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: 4.0.0
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew McEachen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-04 00:00:00.000000000 Z
11
+ date: 2019-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -116,9 +116,10 @@ extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
118
  - ".gitignore"
119
- - ".travis.install-mysql-5.7.sh"
119
+ - ".tool-versions"
120
120
  - ".travis.yml"
121
121
  - Appraisals
122
+ - CHANGELOG.md
122
123
  - Gemfile
123
124
  - LICENSE.txt
124
125
  - README.md
@@ -127,12 +128,14 @@ files:
127
128
  - gemfiles/activerecord_5.0.gemfile
128
129
  - gemfiles/activerecord_5.1.gemfile
129
130
  - gemfiles/activerecord_5.2.gemfile
131
+ - gemfiles/activerecord_6.0.gemfile
130
132
  - lib/with_advisory_lock.rb
131
133
  - lib/with_advisory_lock/base.rb
132
134
  - lib/with_advisory_lock/concern.rb
133
135
  - lib/with_advisory_lock/database_adapter_support.rb
134
136
  - lib/with_advisory_lock/flock.rb
135
137
  - lib/with_advisory_lock/mysql.rb
138
+ - lib/with_advisory_lock/mysql_no_nesting.rb
136
139
  - lib/with_advisory_lock/nested_advisory_lock_error.rb
137
140
  - lib/with_advisory_lock/postgresql.rb
138
141
  - lib/with_advisory_lock/version.rb
@@ -168,8 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
171
  - !ruby/object:Gem::Version
169
172
  version: '0'
170
173
  requirements: []
171
- rubyforge_project:
172
- rubygems_version: 2.6.11
174
+ rubygems_version: 3.0.3
173
175
  signing_key:
174
176
  specification_version: 4
175
177
  summary: Advisory locking for ActiveRecord
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env bash
2
- if [[ "${MYSQL_VERSION}" == "5.7" ]]; then
3
- sudo service mysql stop || echo "mysql not stopped"
4
- sudo stop mysql-5.6 || echo "mysql-5.6 not stopped"
5
- echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections
6
- wget http://dev.mysql.com/get/mysql-apt-config_0.7.3-1_all.deb
7
- sudo dpkg --install mysql-apt-config_0.7.3-1_all.deb
8
- sudo apt-get update -q
9
- sudo apt-get install -q -y --allow-unauthenticated -o Dpkg::Options::=--force-confnew mysql-server
10
- sudo mysql_upgrade
11
- fi