with_advisory_lock 4.6.0 → 7.0.1
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 +4 -4
- data/.github/workflows/ci.yml +76 -0
- data/.github/workflows/release.yml +17 -0
- data/.gitignore +2 -0
- data/.release-please-manifest.json +1 -0
- data/.ruby-version +2 -0
- data/.tool-versions +1 -1
- data/CHANGELOG.md +89 -0
- data/Gemfile +22 -3
- data/LICENSE.txt +4 -4
- data/Makefile +10 -0
- data/README.md +22 -39
- data/Rakefile +5 -2
- data/bin/console +11 -0
- data/bin/rails +15 -0
- data/bin/sanity +20 -0
- data/bin/sanity_check +86 -0
- data/bin/setup +8 -0
- data/bin/setup_test_db +59 -0
- data/bin/test_connections +22 -0
- data/docker-compose.yml +19 -0
- data/lib/with_advisory_lock/concern.rb +37 -30
- data/lib/with_advisory_lock/core_advisory.rb +110 -0
- data/lib/with_advisory_lock/failed_to_acquire_lock.rb +9 -0
- data/lib/with_advisory_lock/jruby_adapter.rb +29 -0
- data/lib/with_advisory_lock/lock_stack_item.rb +6 -0
- data/lib/with_advisory_lock/mysql_advisory.rb +71 -0
- data/lib/with_advisory_lock/postgresql_advisory.rb +112 -0
- data/lib/with_advisory_lock/result.rb +14 -0
- data/lib/with_advisory_lock/version.rb +3 -1
- data/lib/with_advisory_lock.rb +38 -11
- data/release-please-config.json +9 -0
- data/test/dummy/Rakefile +8 -0
- data/test/dummy/app/controllers/application_controller.rb +7 -0
- data/test/dummy/app/models/application_record.rb +6 -0
- data/test/dummy/app/models/label.rb +4 -0
- data/test/dummy/app/models/mysql_label.rb +5 -0
- data/test/dummy/app/models/mysql_record.rb +6 -0
- data/test/dummy/app/models/mysql_tag.rb +10 -0
- data/test/dummy/app/models/mysql_tag_audit.rb +5 -0
- data/test/dummy/app/models/tag.rb +8 -0
- data/test/dummy/app/models/tag_audit.rb +4 -0
- data/test/dummy/config/application.rb +31 -0
- data/test/dummy/config/boot.rb +3 -0
- data/test/dummy/config/database.yml +13 -0
- data/test/dummy/config/environment.rb +7 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/db/schema.rb +15 -0
- data/test/dummy/db/secondary_schema.rb +15 -0
- data/test/dummy/lib/tasks/db.rake +40 -0
- data/test/sanity_check_test.rb +63 -0
- data/test/test_helper.rb +33 -0
- data/test/with_advisory_lock/concern_test.rb +79 -0
- data/test/with_advisory_lock/lock_test.rb +197 -0
- data/test/with_advisory_lock/multi_adapter_test.rb +17 -0
- data/test/with_advisory_lock/mysql_release_lock_test.rb +119 -0
- data/test/with_advisory_lock/parallelism_test.rb +101 -0
- data/test/with_advisory_lock/postgresql_race_condition_test.rb +118 -0
- data/test/with_advisory_lock/shared_test.rb +129 -0
- data/test/with_advisory_lock/thread_test.rb +83 -0
- data/test/with_advisory_lock/transaction_test.rb +83 -0
- data/with_advisory_lock.gemspec +54 -28
- metadata +83 -69
- data/.travis.yml +0 -38
- data/Appraisals +0 -29
- data/gemfiles/activerecord_4.2.gemfile +0 -19
- data/gemfiles/activerecord_5.0.gemfile +0 -19
- data/gemfiles/activerecord_5.1.gemfile +0 -19
- data/gemfiles/activerecord_5.2.gemfile +0 -19
- data/gemfiles/activerecord_6.0.gemfile +0 -19
- data/lib/with_advisory_lock/base.rb +0 -104
- data/lib/with_advisory_lock/database_adapter_support.rb +0 -63
- data/lib/with_advisory_lock/flock.rb +0 -32
- data/lib/with_advisory_lock/mysql.rb +0 -27
- data/lib/with_advisory_lock/mysql_no_nesting.rb +0 -20
- data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
- data/lib/with_advisory_lock/postgresql.rb +0 -41
- data/test/concern_test.rb +0 -20
- data/test/database.yml +0 -17
- data/test/lock_test.rb +0 -47
- data/test/minitest_helper.rb +0 -40
- data/test/nesting_test.rb +0 -93
- data/test/options_test.rb +0 -64
- data/test/parallelism_test.rb +0 -77
- data/test/shared_test.rb +0 -131
- data/test/test_models.rb +0 -24
- data/test/thread_test.rb +0 -60
- data/test/transaction_test.rb +0 -70
- data/tests.sh +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f594bf1ed9b20028febee227bd33eddf70f2c3deb42ae7e8a4d83d794125281d
|
4
|
+
data.tar.gz: 6882cea1e84d517bdf847b8f66b7dd6bb7b6c93de09c156b459019aab324ace0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a89eff2b9fccadd2f64ac7467c9daf19156fbac5196358b9ce7089c2d743c353e4fbb8b993d2c0440b0f253c78a56620a603cd9d41dd0e3f027e02b5f901f72c
|
7
|
+
data.tar.gz: 02b5903b081175e8f7ce07450738fd88d8f0f92dfc52fbb6901e9738aa90312313c3313c70ccfcdab998aa799399a173215edf913145b72d56958042681fb4e6
|
@@ -0,0 +1,76 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
|
8
|
+
concurrency:
|
9
|
+
group: ci-${{ github.head_ref }}
|
10
|
+
cancel-in-progress: true
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
minitest:
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
name: CI Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }}
|
16
|
+
services:
|
17
|
+
postgres:
|
18
|
+
image: 'postgres:17-alpine'
|
19
|
+
ports:
|
20
|
+
- '5432'
|
21
|
+
env:
|
22
|
+
POSTGRES_USER: with_advisory
|
23
|
+
POSTGRES_PASSWORD: with_advisory_pass
|
24
|
+
POSTGRES_DB: with_advisory_lock_test
|
25
|
+
options: >-
|
26
|
+
--health-cmd pg_isready
|
27
|
+
--health-interval 10s
|
28
|
+
--health-timeout 5s
|
29
|
+
--health-retries 5
|
30
|
+
mysql:
|
31
|
+
image: mysql/mysql-server
|
32
|
+
ports:
|
33
|
+
- 3306
|
34
|
+
env:
|
35
|
+
MYSQL_USER: with_advisory
|
36
|
+
MYSQL_PASSWORD: with_advisory_pass
|
37
|
+
MYSQL_DATABASE: with_advisory_lock_test
|
38
|
+
MYSQL_ROOT_HOST: '%'
|
39
|
+
strategy:
|
40
|
+
fail-fast: false
|
41
|
+
matrix:
|
42
|
+
ruby:
|
43
|
+
- '3.3'
|
44
|
+
- '3.4'
|
45
|
+
- 'truffleruby'
|
46
|
+
rails:
|
47
|
+
- 7.2
|
48
|
+
- "8.0"
|
49
|
+
env:
|
50
|
+
ACTIVERECORD_VERSION: ${{ matrix.rails }}
|
51
|
+
RAILS_ENV: test
|
52
|
+
steps:
|
53
|
+
- name: Checkout
|
54
|
+
uses: actions/checkout@v4
|
55
|
+
|
56
|
+
- name: Setup Ruby
|
57
|
+
uses: ruby/setup-ruby@v1
|
58
|
+
with:
|
59
|
+
ruby-version: ${{ matrix.ruby }}
|
60
|
+
bundler-cache: true
|
61
|
+
rubygems: latest
|
62
|
+
|
63
|
+
- name: Setup test databases
|
64
|
+
env:
|
65
|
+
DATABASE_URL_PG: postgres://with_advisory:with_advisory_pass@localhost:${{ job.services.postgres.ports[5432] }}/with_advisory_lock_test
|
66
|
+
DATABASE_URL_MYSQL: mysql2://with_advisory:with_advisory_pass@127.0.0.1:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
|
67
|
+
run: |
|
68
|
+
cd test/dummy
|
69
|
+
bundle exec rake db:test:prepare
|
70
|
+
|
71
|
+
- name: Test
|
72
|
+
env:
|
73
|
+
DATABASE_URL_PG: postgres://with_advisory:with_advisory_pass@localhost:${{ job.services.postgres.ports[5432] }}/with_advisory_lock_test
|
74
|
+
DATABASE_URL_MYSQL: mysql2://with_advisory:with_advisory_pass@127.0.0.1:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
|
75
|
+
WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
|
76
|
+
run: bin/rails test
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: release-please
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
workflow_dispatch:
|
8
|
+
|
9
|
+
permissions:
|
10
|
+
contents: write
|
11
|
+
pull-requests: write
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
release-please:
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
steps:
|
17
|
+
- uses: googleapis/release-please-action@v4
|
data/.gitignore
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{".":"7.0.1"}
|
data/.ruby-version
ADDED
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby
|
1
|
+
ruby 3.4.4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,94 @@
|
|
1
1
|
## Changelog
|
2
2
|
|
3
|
+
## [7.0.1](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v7.0.0...with_advisory_lock/v7.0.1) (2025-07-21)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* handle ActiveRecord's release_advisory_lock signature for Rails 7.2+ ([#127](https://github.com/ClosureTree/with_advisory_lock/issues/127)) ([94253ca](https://github.com/ClosureTree/with_advisory_lock/commit/94253ca2af7f684a3c99645765853546c3da8e02)), closes [#126](https://github.com/ClosureTree/with_advisory_lock/issues/126)
|
9
|
+
|
10
|
+
## [7.0.0](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v6.0.0...with_advisory_lock/v7.0.0) (2025-07-05)
|
11
|
+
|
12
|
+
|
13
|
+
### ⚠ BREAKING CHANGES
|
14
|
+
|
15
|
+
* require Rails 7.2+ as minimum version
|
16
|
+
|
17
|
+
### Features
|
18
|
+
|
19
|
+
* fire Ruby from its second job checking locks and let PostgreSQL do what it's paid for ([#124](https://github.com/ClosureTree/with_advisory_lock/issues/124)) ([f7f8dbc](https://github.com/ClosureTree/with_advisory_lock/commit/f7f8dbcd69842a358d0d70227bcc52ba3183c098))
|
20
|
+
* handle connection disconnection gracefully ([1944e98](https://github.com/ClosureTree/with_advisory_lock/commit/1944e98877917e234bfe597f9358c0b74643a045))
|
21
|
+
* handle connection disconnection gracefully ([77046a9](https://github.com/ClosureTree/with_advisory_lock/commit/77046a94c7504f77a59fae6fbcd75e73ed41bf23))
|
22
|
+
* implement MySQL native timeout support ([#123](https://github.com/ClosureTree/with_advisory_lock/issues/123)) ([387dedd](https://github.com/ClosureTree/with_advisory_lock/commit/387dedd133c897f7a3da13ed2ebbd9223b81317d))
|
23
|
+
* require Rails 7.2+ as minimum version ([d4e7826](https://github.com/ClosureTree/with_advisory_lock/commit/d4e7826ddc216c103cd666674068cb7f512fc32d))
|
24
|
+
* validate transaction-level locks require active transaction ([#122](https://github.com/ClosureTree/with_advisory_lock/issues/122)) ([e4bc6c1](https://github.com/ClosureTree/with_advisory_lock/commit/e4bc6c10666e02c560f18629df0106c39bb85e19))
|
25
|
+
|
26
|
+
## [6.0.0](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v5.3.0...with_advisory_lock/v6.0.0) (2025-05-28)
|
27
|
+
|
28
|
+
|
29
|
+
### ⚠ BREAKING CHANGES
|
30
|
+
|
31
|
+
* Remove private APIs (Base, DatabaseAdapterSupport). Add full mixed adapter support for PostgreSQL/MySQL in same app. Add JRuby compatibility.
|
32
|
+
* drop support for sqlite3
|
33
|
+
* drop legacy version of ruby/rails ([#113](https://github.com/ClosureTree/with_advisory_lock/issues/113))
|
34
|
+
|
35
|
+
### Features
|
36
|
+
|
37
|
+
* drop legacy version of ruby/rails ([#113](https://github.com/ClosureTree/with_advisory_lock/issues/113)) ([26fd427](https://github.com/ClosureTree/with_advisory_lock/commit/26fd4278f9fa155974e6f86df7cd92dd2b7d9154))
|
38
|
+
* drop support for sqlite3 ([26fd427](https://github.com/ClosureTree/with_advisory_lock/commit/26fd4278f9fa155974e6f86df7cd92dd2b7d9154))
|
39
|
+
* move to rails dummy app to test multidb setup ([#115](https://github.com/ClosureTree/with_advisory_lock/issues/115)) ([71a3431](https://github.com/ClosureTree/with_advisory_lock/commit/71a34316b365a0f3be0e8a046db14289e69efc9c))
|
40
|
+
* support of multidb ([#116](https://github.com/ClosureTree/with_advisory_lock/issues/116)) ([935e7e5](https://github.com/ClosureTree/with_advisory_lock/commit/935e7e5fb05dad2eba034745d2ef49e11c163f7d))
|
41
|
+
|
42
|
+
## [5.3.0](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v5.2.0...with_advisory_lock/v5.3.0) (2025-04-25)
|
43
|
+
|
44
|
+
|
45
|
+
### Features
|
46
|
+
|
47
|
+
* add #current_advisory_locks method ([#111](https://github.com/ClosureTree/with_advisory_lock/issues/111)) ([ccbd3b2](https://github.com/ClosureTree/with_advisory_lock/commit/ccbd3b23465f7fa1fc3800334159986c31d5c351))
|
48
|
+
|
49
|
+
## [5.2.0](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v5.1.0...with_advisory_lock/v5.2.0) (2025-04-24)
|
50
|
+
|
51
|
+
|
52
|
+
### Features
|
53
|
+
|
54
|
+
* use current connnection instead of the one in ActiveRecord::Base ([#90](https://github.com/ClosureTree/with_advisory_lock/issues/90)) ([c28a172](https://github.com/ClosureTree/with_advisory_lock/commit/c28a172a5a64594448b6090501fc0b8cbace06f6))
|
55
|
+
|
56
|
+
|
57
|
+
### Bug Fixes
|
58
|
+
|
59
|
+
* Removed MySQL unused lock variable and broaden SQLite detection. ([#94](https://github.com/ClosureTree/with_advisory_lock/issues/94)) ([f818a18](https://github.com/ClosureTree/with_advisory_lock/commit/f818a181dde6711c8439c4cbf67c4525a09d346e))
|
60
|
+
|
61
|
+
## [5.1.0](https://github.com/ClosureTree/with_advisory_lock/compare/with_advisory_lock/v5.0.1...with_advisory_lock/v5.1.0) (2024-01-21)
|
62
|
+
|
63
|
+
|
64
|
+
### Features
|
65
|
+
|
66
|
+
* use zeitwerk loader instead of ActiveSupport::Autoload ([b5082fd](https://github.com/ClosureTree/with_advisory_lock/commit/b5082fddacacacff48139f5bf509601a37945a0e))
|
67
|
+
|
68
|
+
## 5.0.1 (2024-01-21)
|
69
|
+
|
70
|
+
|
71
|
+
### Features
|
72
|
+
|
73
|
+
* add release workflow ([5d32520](https://github.com/ClosureTree/with_advisory_lock/commit/5d325201c82974991381a9fbc4d1714c9739dc4f))
|
74
|
+
* add ruby 3.1 test/support ([#60](https://github.com/ClosureTree/with_advisory_lock/issues/60)) ([514f042](https://github.com/ClosureTree/with_advisory_lock/commit/514f0420d957ef30911a00d54685385bec5867c3))
|
75
|
+
* Add testing for activerecord 7.1 and support for trilogy adapter ([#77](https://github.com/ClosureTree/with_advisory_lock/issues/77)) ([69c23fe](https://github.com/ClosureTree/with_advisory_lock/commit/69c23fe09887fc5d97ac7b0194825c21efe244a5))
|
76
|
+
* add truffleruby support ([#62](https://github.com/ClosureTree/with_advisory_lock/issues/62)) ([ec34bd4](https://github.com/ClosureTree/with_advisory_lock/commit/ec34bd448e3505e5df631daaf47bb83f2f5316dc))
|
77
|
+
|
78
|
+
|
79
|
+
### Bug Fixes
|
80
|
+
|
81
|
+
* User may sometimes pass in non-strings, such as integers ([#55](https://github.com/ClosureTree/with_advisory_lock/issues/55)) ([9885597](https://github.com/ClosureTree/with_advisory_lock/commit/988559747363ef00958fcf782317e76c40ffa2a3))
|
82
|
+
|
83
|
+
### 5.0.0
|
84
|
+
- Drop support for EOL rubies and activerecord (ruby below 2.7 and activerecord below 6.1).
|
85
|
+
- Allow lock name to be integer
|
86
|
+
- Jruby support
|
87
|
+
- Truffleruby support
|
88
|
+
- Add `with_advisory_lock!`, which raises an error if the lock acquisition fails
|
89
|
+
- Add `disable_query_cache` option to `with_advisory_lock`
|
90
|
+
- Drop support for mysql < 5.7.5
|
91
|
+
|
3
92
|
### 4.6.0
|
4
93
|
|
5
94
|
- Support for ActiveRecord 6
|
data/Gemfile
CHANGED
@@ -1,15 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
gemspec
|
4
6
|
|
7
|
+
gem 'rake'
|
8
|
+
|
9
|
+
# Gems that will be removed from default gems in Ruby 3.5.0
|
10
|
+
gem 'benchmark'
|
11
|
+
gem 'logger'
|
12
|
+
gem 'ostruct'
|
13
|
+
|
14
|
+
activerecord_version = ENV.fetch('ACTIVERECORD_VERSION', '7.2')
|
15
|
+
|
16
|
+
gem 'activerecord', "~> #{activerecord_version}.0"
|
17
|
+
|
18
|
+
gem 'dotenv'
|
19
|
+
gem 'railties'
|
20
|
+
|
5
21
|
platforms :ruby do
|
6
22
|
gem 'mysql2'
|
7
23
|
gem 'pg'
|
8
24
|
gem 'sqlite3'
|
25
|
+
gem 'trilogy'
|
9
26
|
end
|
10
27
|
|
11
28
|
platforms :jruby do
|
12
|
-
|
13
|
-
|
14
|
-
|
29
|
+
# JRuby JDBC adapters support Rails 7.2+
|
30
|
+
if activerecord_version >= '7.2'
|
31
|
+
gem 'activerecord-jdbcmysql-adapter', '~> 72.0'
|
32
|
+
gem 'activerecord-jdbcpostgresql-adapter', '~> 72.0'
|
33
|
+
end
|
15
34
|
end
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
SPDX-License-Identifier: MIT
|
2
|
+
SPDX-FileCopyrightText: 2013 Matthew McEachen
|
3
|
+
SPDX-FileCopyrightText: 2013-2025 Abdelkader Boudih
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
6
6
|
a copy of this software and associated documentation files (the
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Makefile
ADDED
data/README.md
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
# with_advisory_lock
|
2
2
|
|
3
|
-
Adds advisory locking (mutexes) to ActiveRecord
|
4
|
-
2.4, 2.5 and 2.6, when used with
|
3
|
+
Adds advisory locking (mutexes) to ActiveRecord 7.2+, with ruby 3.3+, jruby or truffleruby, when used with
|
5
4
|
[MySQL](https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_get-lock)
|
6
5
|
or
|
7
6
|
[PostgreSQL](https://www.postgresql.org/docs/current/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS).
|
8
|
-
SQLite resorts to file locking.
|
9
7
|
|
10
|
-
|
8
|
+
**Note:** SQLite support has been removed. For single-node SQLite deployments,
|
9
|
+
consider using a Ruby mutex instead. Support for MySQL 5.7 has also been
|
10
|
+
dropped; please use MySQL 8 or PostgreSQL.
|
11
|
+
|
11
12
|
[](https://badge.fury.io/rb/with_advisory_lock)
|
13
|
+
[](https://github.com/ClosureTree/with_advisory_lock/actions/workflows/ci.yml)
|
12
14
|
|
13
15
|
## What's an "Advisory Lock"?
|
14
16
|
|
15
17
|
An advisory lock is a [mutex](https://en.wikipedia.org/wiki/Mutual_exclusion)
|
16
18
|
used to ensure no two processes run some process at the same time. When the
|
17
|
-
advisory lock is powered by your database server,
|
19
|
+
advisory lock is powered by your database server,
|
18
20
|
your mutex spans hosts.
|
19
21
|
|
20
22
|
## Usage
|
@@ -45,8 +47,10 @@ A value of zero will try the lock only once. If the lock is acquired, the block
|
|
45
47
|
will be yielded to. If the lock is currently being held, the block will not be
|
46
48
|
called.
|
47
49
|
|
48
|
-
Note
|
49
|
-
|
50
|
+
> **Note**
|
51
|
+
>
|
52
|
+
> If a non-nil value is provided for `timeout_seconds`, the block will
|
53
|
+
*not* be invoked if the lock cannot be acquired within that time-frame. In this case, `with_advisory_lock` will return `false`, while `with_advisory_lock!` will raise a `WithAdvisoryLock::FailedToAcquireLock` error.
|
50
54
|
|
51
55
|
For backwards compatability, the timeout value can be specified directly as the
|
52
56
|
second parameter.
|
@@ -80,6 +84,8 @@ block, if the lock was able to be acquired and the block yielded, or `false`, if
|
|
80
84
|
you provided a timeout_seconds value and the lock was not able to be acquired in
|
81
85
|
time.
|
82
86
|
|
87
|
+
`with_advisory_lock!` is similar to `with_advisory_lock`, but raises a `WithAdvisoryLock::FailedToAcquireLock` error if the lock was not able to be acquired in time.
|
88
|
+
|
83
89
|
### Testing for the current lock status
|
84
90
|
|
85
91
|
If you needed to check if the advisory lock is currently being held, you can
|
@@ -91,6 +97,14 @@ If you want to see if the current Thread is holding a lock, you can call
|
|
91
97
|
`Tag.current_advisory_lock` which will return the name of the current lock. If
|
92
98
|
no lock is currently held, `.current_advisory_lock` returns `nil`.
|
93
99
|
|
100
|
+
### ActiveRecord Query Cache
|
101
|
+
|
102
|
+
You can optionally pass `disable_query_cache: true` to the options hash of
|
103
|
+
`with_advisory_lock` in order to disable ActiveRecord's query cache. This can
|
104
|
+
prevent problems when you query the database from within the lock and it returns
|
105
|
+
stale results. More info on why this can be a problem can be
|
106
|
+
[found here](https://github.com/ClosureTree/with_advisory_lock/issues/52)
|
107
|
+
|
94
108
|
## Installation
|
95
109
|
|
96
110
|
Add this line to your application's Gemfile:
|
@@ -123,7 +137,7 @@ row-level locks prevent concurrent modification to a given model.
|
|
123
137
|
|
124
138
|
**If you're building a
|
125
139
|
[CRUD](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
|
126
|
-
application, this will be your most commonly used lock.**
|
140
|
+
application, this will be 2.4, 2.5 and your most commonly used lock.**
|
127
141
|
|
128
142
|
### Table-level locks
|
129
143
|
|
@@ -141,38 +155,7 @@ Advisory locks with MySQL and PostgreSQL ignore database transaction boundaries.
|
|
141
155
|
|
142
156
|
You will want to wrap your block within a transaction to ensure consistency.
|
143
157
|
|
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.
|
151
|
-
|
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.
|
155
|
-
|
156
158
|
### Is clustered MySQL supported?
|
157
159
|
|
158
160
|
[No.](https://github.com/ClosureTree/with_advisory_lock/issues/16)
|
159
161
|
|
160
|
-
### There are many `lock-*` files in my project directory after test runs
|
161
|
-
|
162
|
-
This is expected if you aren't using MySQL or Postgresql for your tests.
|
163
|
-
See [issue 3](https://github.com/ClosureTree/with_advisory_lock/issues/3).
|
164
|
-
|
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.
|
167
|
-
|
168
|
-
In your `spec_helper.rb` or `minitest_helper.rb`, add a `before` and `after` block:
|
169
|
-
|
170
|
-
```ruby
|
171
|
-
before do
|
172
|
-
ENV['FLOCK_DIR'] = Dir.mktmpdir
|
173
|
-
end
|
174
|
-
|
175
|
-
after do
|
176
|
-
FileUtils.remove_entry_secure ENV['FLOCK_DIR']
|
177
|
-
end
|
178
|
-
```
|
data/Rakefile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
2
4
|
|
3
5
|
require 'yard'
|
4
6
|
YARD::Rake::YardocTask.new do |t|
|
@@ -14,4 +16,5 @@ Rake::TestTask.new do |t|
|
|
14
16
|
t.verbose = true
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
|
20
|
+
task default: :test
|
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'no_fly_list'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require 'irb'
|
11
|
+
IRB.start(__FILE__)
|
data/bin/rails
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
5
|
+
# installed from the root of your application.
|
6
|
+
|
7
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
8
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
9
|
+
|
10
|
+
# Set up gems listed in the Gemfile.
|
11
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
12
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
13
|
+
|
14
|
+
require 'rails/all'
|
15
|
+
require 'rails/engine/commands'
|
data/bin/sanity
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -euo pipefail
|
3
|
+
|
4
|
+
check_port() {
|
5
|
+
local port="$1"
|
6
|
+
local name="$2"
|
7
|
+
if nc -z localhost "$port" >/dev/null 2>&1; then
|
8
|
+
echo "$name running on port $port"
|
9
|
+
else
|
10
|
+
echo "ERROR: $name is not running on port $port" >&2
|
11
|
+
return 1
|
12
|
+
fi
|
13
|
+
}
|
14
|
+
|
15
|
+
main() {
|
16
|
+
check_port 5433 "Postgresql"
|
17
|
+
check_port 3366 "Mysql"
|
18
|
+
}
|
19
|
+
|
20
|
+
main "$@"
|
data/bin/sanity_check
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
ENV['RAILS_ENV'] = 'test'
|
5
|
+
ENV['DATABASE_URL_PG'] ||= 'postgres://with_advisory:with_advisory_pass@localhost:5433/with_advisory_lock_test'
|
6
|
+
ENV['DATABASE_URL_MYSQL'] ||= 'mysql2://with_advisory:with_advisory_pass@0.0.0.0:3366/with_advisory_lock_test'
|
7
|
+
|
8
|
+
require_relative '../test/dummy/config/environment'
|
9
|
+
|
10
|
+
puts '=' * 80
|
11
|
+
puts 'WITH_ADVISORY_LOCK SANITY CHECK'
|
12
|
+
puts '=' * 80
|
13
|
+
puts
|
14
|
+
|
15
|
+
# Check Rails environment
|
16
|
+
puts "Rails Environment: #{Rails.env}"
|
17
|
+
puts "Rails Root: #{Rails.root}"
|
18
|
+
puts
|
19
|
+
|
20
|
+
# Check PostgreSQL connection
|
21
|
+
puts 'PostgreSQL Connection (ApplicationRecord):'
|
22
|
+
begin
|
23
|
+
ApplicationRecord.connection.execute('SELECT 1')
|
24
|
+
puts ' ✓ Connected to PostgreSQL'
|
25
|
+
puts " Database: #{ApplicationRecord.connection.current_database}"
|
26
|
+
puts " Adapter: #{ApplicationRecord.connection.adapter_name}"
|
27
|
+
puts " Tables: #{ApplicationRecord.connection.tables.sort.join(', ')}"
|
28
|
+
|
29
|
+
# Test creating a record
|
30
|
+
tag = Tag.create!(name: "test-pg-#{Time.now.to_i}")
|
31
|
+
puts " ✓ Created Tag record with id: #{tag.id}"
|
32
|
+
tag.destroy
|
33
|
+
puts ' ✓ Deleted Tag record'
|
34
|
+
rescue StandardError => e
|
35
|
+
puts " ✗ ERROR: #{e.message}"
|
36
|
+
puts " #{e.backtrace.first}"
|
37
|
+
end
|
38
|
+
puts
|
39
|
+
|
40
|
+
# Check MySQL connection
|
41
|
+
puts 'MySQL Connection (MysqlRecord):'
|
42
|
+
begin
|
43
|
+
MysqlRecord.connection.execute('SELECT 1')
|
44
|
+
puts ' ✓ Connected to MySQL'
|
45
|
+
puts " Database: #{MysqlRecord.connection.current_database}"
|
46
|
+
puts " Adapter: #{MysqlRecord.connection.adapter_name}"
|
47
|
+
puts " Tables: #{MysqlRecord.connection.tables.sort.join(', ')}"
|
48
|
+
|
49
|
+
# Test creating a record
|
50
|
+
mysql_tag = MysqlTag.create!(name: "test-mysql-#{Time.now.to_i}")
|
51
|
+
puts " ✓ Created MysqlTag record with id: #{mysql_tag.id}"
|
52
|
+
mysql_tag.destroy
|
53
|
+
puts ' ✓ Deleted MysqlTag record'
|
54
|
+
rescue StandardError => e
|
55
|
+
puts " ✗ ERROR: #{e.message}"
|
56
|
+
puts " #{e.backtrace.first}"
|
57
|
+
end
|
58
|
+
puts
|
59
|
+
|
60
|
+
# Check model associations
|
61
|
+
puts 'Model Configuration:'
|
62
|
+
puts ' PostgreSQL Models:'
|
63
|
+
puts " - Tag -> #{Tag.connection.adapter_name}"
|
64
|
+
puts " - TagAudit -> #{TagAudit.connection.adapter_name}"
|
65
|
+
puts " - Label -> #{Label.connection.adapter_name}"
|
66
|
+
puts ' MySQL Models:'
|
67
|
+
puts " - MysqlTag -> #{MysqlTag.connection.adapter_name}"
|
68
|
+
puts " - MysqlTagAudit -> #{MysqlTagAudit.connection.adapter_name}"
|
69
|
+
puts " - MysqlLabel -> #{MysqlLabel.connection.adapter_name}"
|
70
|
+
puts
|
71
|
+
|
72
|
+
# Check if WithAdvisoryLock is loaded
|
73
|
+
puts 'WithAdvisoryLock Status:'
|
74
|
+
puts " Module loaded: #{defined?(WithAdvisoryLock) ? 'Yes' : 'No'}"
|
75
|
+
puts " Concern loaded: #{defined?(WithAdvisoryLock::Concern) ? 'Yes' : 'No'}"
|
76
|
+
puts " PostgreSQL adapter loaded: #{defined?(WithAdvisoryLock::PostgreSQL) ? 'Yes' : 'No'}"
|
77
|
+
puts " MySQL adapter loaded: #{defined?(WithAdvisoryLock::MySQL) ? 'Yes' : 'No'}"
|
78
|
+
|
79
|
+
# Check if models have advisory lock methods
|
80
|
+
puts "\nModel Methods:"
|
81
|
+
puts " Tag.with_advisory_lock available: #{Tag.respond_to?(:with_advisory_lock)}"
|
82
|
+
puts " MysqlTag.with_advisory_lock available: #{MysqlTag.respond_to?(:with_advisory_lock)}"
|
83
|
+
|
84
|
+
puts "\n#{'=' * 80}"
|
85
|
+
puts 'SANITY CHECK COMPLETE'
|
86
|
+
puts '=' * 80
|
data/bin/setup
ADDED
data/bin/setup_test_db
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
# Setup PostgreSQL database
|
8
|
+
puts 'Setting up PostgreSQL test database...'
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
adapter: 'postgresql',
|
11
|
+
host: 'localhost',
|
12
|
+
port: 5433,
|
13
|
+
database: 'with_advisory_lock_test',
|
14
|
+
username: 'with_advisory',
|
15
|
+
password: 'with_advisory_pass'
|
16
|
+
)
|
17
|
+
|
18
|
+
ActiveRecord::Schema.define(version: 1) do
|
19
|
+
create_table 'tags', force: true do |t|
|
20
|
+
t.string 'name'
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table 'tag_audits', id: false, force: true do |t|
|
24
|
+
t.string 'tag_name'
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table 'labels', id: false, force: true do |t|
|
28
|
+
t.string 'name'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
puts 'PostgreSQL tables created!'
|
32
|
+
|
33
|
+
# Setup MySQL database
|
34
|
+
puts "\nSetting up MySQL test database..."
|
35
|
+
ActiveRecord::Base.establish_connection(
|
36
|
+
adapter: 'mysql2',
|
37
|
+
host: '127.0.0.1',
|
38
|
+
port: 3366,
|
39
|
+
database: 'with_advisory_lock_test',
|
40
|
+
username: 'with_advisory',
|
41
|
+
password: 'with_advisory_pass'
|
42
|
+
)
|
43
|
+
|
44
|
+
ActiveRecord::Schema.define(version: 1) do
|
45
|
+
create_table 'mysql_tags', force: true do |t|
|
46
|
+
t.string 'name'
|
47
|
+
end
|
48
|
+
|
49
|
+
create_table 'mysql_tag_audits', id: false, force: true do |t|
|
50
|
+
t.string 'tag_name'
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table 'mysql_labels', id: false, force: true do |t|
|
54
|
+
t.string 'name'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
puts 'MySQL tables created!'
|
58
|
+
|
59
|
+
puts "\nTest databases setup complete!"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
ENV['RAILS_ENV'] = 'test'
|
5
|
+
ENV['DATABASE_URL_PG'] ||= 'postgres://with_advisory:with_advisory_pass@localhost:5433/with_advisory_lock_test'
|
6
|
+
ENV['DATABASE_URL_MYSQL'] ||= 'mysql2://with_advisory:with_advisory_pass@0.0.0.0:3366/with_advisory_lock_test'
|
7
|
+
|
8
|
+
require_relative '../test/dummy/config/environment'
|
9
|
+
|
10
|
+
puts 'Testing database connections...'
|
11
|
+
|
12
|
+
puts "\nPostgreSQL (ApplicationRecord):"
|
13
|
+
puts " Connected: #{ApplicationRecord.connected?}"
|
14
|
+
puts " Tables: #{ApplicationRecord.connection.tables.sort.join(', ')}"
|
15
|
+
|
16
|
+
puts "\nMySQL (MysqlRecord):"
|
17
|
+
puts " Connected: #{MysqlRecord.connected?}"
|
18
|
+
puts " Tables: #{MysqlRecord.connection.tables.sort.join(', ')}"
|
19
|
+
|
20
|
+
puts "\nModel connections:"
|
21
|
+
puts " Tag uses: #{Tag.connection.adapter_name}"
|
22
|
+
puts " MysqlTag uses: #{MysqlTag.connection.adapter_name}"
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
services:
|
2
|
+
pg:
|
3
|
+
image: postgres:17-alpine
|
4
|
+
environment:
|
5
|
+
POSTGRES_USER: with_advisory
|
6
|
+
POSTGRES_PASSWORD: with_advisory_pass
|
7
|
+
POSTGRES_DB: with_advisory_lock_test
|
8
|
+
ports:
|
9
|
+
- "5433:5432"
|
10
|
+
mysql:
|
11
|
+
image: mysql:8
|
12
|
+
environment:
|
13
|
+
MYSQL_USER: with_advisory
|
14
|
+
MYSQL_PASSWORD: with_advisory_pass
|
15
|
+
MYSQL_DATABASE: with_advisory_lock_test
|
16
|
+
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
17
|
+
MYSQL_ROOT_HOST: '%'
|
18
|
+
ports:
|
19
|
+
- "3366:3306"
|