with_advisory_lock 4.6.0 → 5.3.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci-mysql5.yml +61 -0
  3. data/.github/workflows/ci-mysql8.yml +62 -0
  4. data/.github/workflows/ci-postgresql.yml +64 -0
  5. data/.github/workflows/ci-sqlite3.yml +54 -0
  6. data/.github/workflows/release.yml +17 -0
  7. data/.gitignore +2 -0
  8. data/.release-please-manifest.json +1 -0
  9. data/.ruby-version +2 -0
  10. data/.tool-versions +1 -1
  11. data/Appraisals +34 -18
  12. data/CHANGELOG.md +50 -0
  13. data/Gemfile +0 -12
  14. data/LICENSE.txt +4 -4
  15. data/Makefile +14 -0
  16. data/README.md +17 -6
  17. data/docker-compose.yml +20 -0
  18. data/gemfiles/{activerecord_6.0.gemfile → activerecord_6.1.gemfile} +4 -2
  19. data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.0.gemfile} +4 -2
  20. data/gemfiles/activerecord_7.1.gemfile +14 -0
  21. data/lib/with_advisory_lock/base.rb +18 -4
  22. data/lib/with_advisory_lock/concern.rb +17 -17
  23. data/lib/with_advisory_lock/database_adapter_support.rb +7 -47
  24. data/lib/with_advisory_lock/failed_to_acquire_lock.rb +9 -0
  25. data/lib/with_advisory_lock/flock.rb +4 -3
  26. data/lib/with_advisory_lock/mysql.rb +12 -7
  27. data/lib/with_advisory_lock/postgresql.rb +43 -18
  28. data/lib/with_advisory_lock/version.rb +3 -1
  29. data/lib/with_advisory_lock.rb +9 -10
  30. data/release-please-config.json +9 -0
  31. data/test/test_helper.rb +66 -0
  32. data/test/test_models.rb +16 -10
  33. data/test/with_advisory_lock/base_test.rb +9 -0
  34. data/test/with_advisory_lock/concern_test.rb +33 -0
  35. data/test/with_advisory_lock/lock_test.rb +111 -0
  36. data/test/with_advisory_lock/nesting_test.rb +28 -0
  37. data/test/with_advisory_lock/options_test.rb +66 -0
  38. data/test/with_advisory_lock/parallelism_test.rb +75 -0
  39. data/test/with_advisory_lock/shared_test.rb +134 -0
  40. data/test/with_advisory_lock/thread_test.rb +61 -0
  41. data/test/with_advisory_lock/transaction_test.rb +68 -0
  42. data/with_advisory_lock.gemspec +29 -23
  43. metadata +56 -47
  44. data/.travis.yml +0 -38
  45. data/gemfiles/activerecord_4.2.gemfile +0 -19
  46. data/gemfiles/activerecord_5.0.gemfile +0 -19
  47. data/gemfiles/activerecord_5.1.gemfile +0 -19
  48. data/lib/with_advisory_lock/mysql_no_nesting.rb +0 -20
  49. data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
  50. data/test/concern_test.rb +0 -20
  51. data/test/database.yml +0 -17
  52. data/test/lock_test.rb +0 -47
  53. data/test/minitest_helper.rb +0 -40
  54. data/test/nesting_test.rb +0 -93
  55. data/test/options_test.rb +0 -64
  56. data/test/parallelism_test.rb +0 -77
  57. data/test/shared_test.rb +0 -131
  58. data/test/thread_test.rb +0 -60
  59. data/test/transaction_test.rb +0 -70
  60. data/tests.sh +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc4719c3f44e4cf1f219b582d21cf9757274ca1fcca6128c227798f32d4b589e
4
- data.tar.gz: c63b8be1ee91b25a519b5a4d7d090ff98172d6d3a476899b55c6cd03bf77eda6
3
+ metadata.gz: d65989d7502660ac5cbebd51a589fc49fb76bbeae5abb13814cb79a711fdaae8
4
+ data.tar.gz: 2eea1e741e2fdb100d3170ddce8c38b1d0fc59dc98f6340be51d6d5bde0591a4
5
5
  SHA512:
6
- metadata.gz: be5a906b9740506447bfcdb1928cd5473add67e0a48acd32f8a26ca4671be49b34ca5eca987df2f8e6f29a0a3dbb0bccc3321061d9f1e5827a0ca4ef44341124
7
- data.tar.gz: 124358b251ffd9cc066d54aebfeeca97fb0db88ae18293fb7e0169ceb13960d298e72c66e9d5fbd79f0cbda5360d2fdb2e609866ac82eeb26b1eab6ead103daf
6
+ metadata.gz: 8358a8783df5bcf9bd49507fc6e59d56aeaec8febd3978a4f9178950c88b254ec76925e3caa9f9f2ee18d7bc15d186512b3790dd1ba602a38fa6d1a877570887
7
+ data.tar.gz: d179cc20743f15a671ad822fd21e04e808336382d2eed318439e7b9c5ae9283ff4abf0576e5b0c274946863033f403d27280dfec84b3e3e4ff20f832ca62bcb5
@@ -0,0 +1,61 @@
1
+ name: CI Mysql 5.7
2
+ on:
3
+ pull_request:
4
+ branches:
5
+ - master
6
+ concurrency:
7
+ group: ci-mysql5-${{ github.head_ref }}
8
+ cancel-in-progress: true
9
+
10
+ jobs:
11
+ minitest:
12
+ runs-on: ubuntu-latest
13
+ name: CI Mysql 5.7 Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / Adapter ${{ matrix.adapter }}
14
+ services:
15
+ mysql:
16
+ image: mysql/mysql-server:5.7
17
+ ports:
18
+ - 3306
19
+ env:
20
+ MYSQL_USER: with_advisory
21
+ MYSQL_PASSWORD: with_advisory_pass
22
+ MYSQL_DATABASE: with_advisory_lock_test
23
+ MYSQL_ROOT_HOST: '%'
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ ruby:
28
+ # - '3.2'
29
+ # - '3.1'
30
+ # - '3.0'
31
+ # - '2.7'
32
+ - '3.3'
33
+ - 'truffleruby'
34
+ rails:
35
+ - 7.1
36
+ - "7.0"
37
+ - 6.1
38
+ adapter:
39
+ - mysql2
40
+ - trilogy
41
+ include:
42
+ - ruby: jruby
43
+ rails: 6.1
44
+ adapter: jdbcmysql
45
+ steps:
46
+ - name: Checkout
47
+ uses: actions/checkout@v4
48
+ - name: Setup Ruby
49
+ uses: ruby/setup-ruby@v1
50
+ with:
51
+ ruby-version: ${{ matrix.ruby }}
52
+ bundler-cache: true
53
+ rubygems: latest
54
+ env:
55
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
56
+ - name: Test
57
+ env:
58
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
59
+ DATABASE_URL: ${{ matrix.adapter }}://with_advisory:with_advisory_pass@0:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
60
+ WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
61
+ run: bundle exec rake
@@ -0,0 +1,62 @@
1
+ name: CI Mysql 8.0
2
+ on:
3
+ pull_request:
4
+ branches:
5
+ - master
6
+
7
+ concurrency:
8
+ group: ci-mysql8-${{ github.head_ref }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ minitest:
13
+ runs-on: ubuntu-latest
14
+ name: CI Mysql 8.0 Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / Adapter ${{ matrix.adapter }}
15
+ services:
16
+ mysql:
17
+ image: mysql/mysql-server
18
+ ports:
19
+ - 3306
20
+ env:
21
+ MYSQL_USER: with_advisory
22
+ MYSQL_PASSWORD: with_advisory_pass
23
+ MYSQL_DATABASE: with_advisory_lock_test
24
+ MYSQL_ROOT_HOST: '%'
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ ruby:
29
+ # - '3.2'
30
+ # - '3.1'
31
+ # - '3.0'
32
+ # - '2.7'
33
+ - '3.3'
34
+ - 'truffleruby'
35
+ rails:
36
+ - 7.1
37
+ - "7.0"
38
+ - 6.1
39
+ adapter:
40
+ - mysql2
41
+ # - trilogy://with_advisory:with_advisory_pass@0/with_advisory_lock_test Trilogy is not supported by mysql 8 with new encryption
42
+ include:
43
+ - ruby: jruby
44
+ rails: 6.1
45
+ adapter: jdbcmysql
46
+ steps:
47
+ - name: Checkout
48
+ uses: actions/checkout@v4
49
+ - name: Setup Ruby
50
+ uses: ruby/setup-ruby@v1
51
+ with:
52
+ ruby-version: ${{ matrix.ruby }}
53
+ bundler-cache: true
54
+ rubygems: latest
55
+ env:
56
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
57
+ - name: Test
58
+ env:
59
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
60
+ DATABASE_URL: ${{ matrix.adapter }}://with_advisory:with_advisory_pass@0:${{ job.services.mysql.ports[3306] }}/with_advisory_lock_test
61
+ WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
62
+ run: bundle exec rake
@@ -0,0 +1,64 @@
1
+ name: CI Postgresql
2
+ on:
3
+ pull_request:
4
+ branches:
5
+ - master
6
+ concurrency:
7
+ group: ci-postgresql-${{ github.head_ref }}
8
+ cancel-in-progress: true
9
+
10
+ jobs:
11
+ minitest:
12
+ runs-on: ubuntu-latest
13
+ name: CI Postgresql Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / Adapter ${{ matrix.adapter }}
14
+ services:
15
+ postgres:
16
+ image: 'postgres:16-alpine'
17
+ ports:
18
+ - '5432'
19
+ env:
20
+ POSTGRES_USER: with_advisory
21
+ POSTGRES_PASSWORD: with_advisory_pass
22
+ POSTGRES_DB: with_advisory_lock_test
23
+ options: >-
24
+ --health-cmd pg_isready
25
+ --health-interval 10s
26
+ --health-timeout 5s
27
+ --health-retries 5
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ ruby:
32
+ # - '3.2'
33
+ # - '3.1'
34
+ # - '3.0'
35
+ # - '2.7'
36
+ - '3.3'
37
+ - 'truffleruby'
38
+ rails:
39
+ - 7.1
40
+ - "7.0"
41
+ - 6.1
42
+ adapter:
43
+ - postgres
44
+ include:
45
+ - ruby: jruby
46
+ rails: 6.1
47
+ adapter: jdbcpostgresql
48
+ steps:
49
+ - name: Checkout
50
+ uses: actions/checkout@v4
51
+ - name: Setup Ruby
52
+ uses: ruby/setup-ruby@v1
53
+ with:
54
+ ruby-version: ${{ matrix.ruby }}
55
+ bundler-cache: true
56
+ rubygems: latest
57
+ env:
58
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
59
+ - name: Test
60
+ env:
61
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
62
+ DATABASE_URL: ${{ matrix.adapter }}://with_advisory:with_advisory_pass@localhost:${{ job.services.postgres.ports[5432] }}/with_advisory_lock_test
63
+ WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
64
+ run: bundle exec rake
@@ -0,0 +1,54 @@
1
+ name: CI Sqlite3
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+
8
+ concurrency:
9
+ group: ci-sqlite3-${{ github.head_ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ minitest:
14
+ runs-on: ubuntu-latest
15
+ name: CI Sqlite3 Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / Adapter ${{ matrix.adapter }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby:
20
+ # - '3.2'
21
+ # - '3.1'
22
+ # - '3.0'
23
+ # - '2.7'
24
+ - '3.3'
25
+ - 'truffleruby'
26
+ rails:
27
+ - 7.1
28
+ - "7.0"
29
+ - 6.1
30
+ adapter:
31
+ - sqlite3
32
+ include:
33
+ - ruby: jruby
34
+ rails: 6.1
35
+ adapter: jdbcsqlite3
36
+ steps:
37
+ - name: Checkout
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Setup Ruby
41
+ uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: ${{ matrix.ruby }}
44
+ bundler-cache: true
45
+ rubygems: latest
46
+ env:
47
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
48
+
49
+ - name: Test
50
+ env:
51
+ BUNDLE_GEMFILE: gemfiles/activerecord_${{ matrix.rails }}.gemfile
52
+ DATABASE_URL: ${{ matrix.adapter }}:///tmp/test.sqlite3
53
+ WITH_ADVISORY_LOCK_PREFIX: ${{ github.run_id }}
54
+ run: bundle exec rake
@@ -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
@@ -18,3 +18,5 @@ test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
20
  *.iml
21
+ test/sqlite.db
22
+ .env
@@ -0,0 +1 @@
1
+ {".":"5.3.0"}
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ 3.4.4
2
+
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.6.4
1
+ ruby 3.4.4
data/Appraisals CHANGED
@@ -1,29 +1,45 @@
1
- appraise "activerecord-4.2" do
2
- gem "activerecord", "~> 4.2.0"
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'activerecord-7.1' do
4
+ gem 'activerecord', '~> 7.1.0'
3
5
  platforms :ruby do
4
- gem "pg", "~> 0.21"
5
- gem "mysql2", "< 0.5"
6
- gem "sqlite3", "~> 1.3.6"
6
+ gem 'sqlite3'
7
+ gem 'mysql2'
8
+ gem 'trilogy'
9
+ gem 'pg'
7
10
  end
8
11
  end
9
12
 
10
- appraise "activerecord-5.0" do
11
- gem "activerecord", "~> 5.0.0"
13
+ appraise 'activerecord-7.0' do
14
+ gem 'activerecord', '~> 7.0.0'
12
15
  platforms :ruby do
13
- gem "sqlite3", "~> 1.3.6"
16
+ gem 'sqlite3'
17
+ gem 'mysql2'
18
+ gem 'trilogy'
19
+ gem "activerecord-trilogy-adapter"
20
+ gem 'pg'
21
+ end
22
+ platforms :jruby do
23
+ gem "activerecord-jdbcmysql-adapter"
24
+ gem "activerecord-jdbcpostgresql-adapter"
25
+ gem "activerecord-jdbcsqlite3-adapter"
14
26
  end
15
27
  end
16
28
 
17
- appraise "activerecord-5.1" do
18
- gem "activerecord", "~> 5.1.0"
19
- gem "sqlite3", "~> 1.3.6"
20
- end
29
+ appraise 'activerecord-6.1' do
30
+ gem 'activerecord', '~> 6.1.0'
21
31
 
22
- appraise "activerecord-5.2" do
23
- gem "activerecord", "~> 5.1.0"
24
- gem "sqlite3", "~> 1.3.6"
32
+ platforms :ruby do
33
+ gem 'sqlite3'
34
+ gem 'mysql2'
35
+ gem 'trilogy'
36
+ gem "activerecord-trilogy-adapter"
37
+ gem 'pg'
38
+ end
39
+ platforms :jruby do
40
+ gem "activerecord-jdbcmysql-adapter"
41
+ gem "activerecord-jdbcpostgresql-adapter"
42
+ gem "activerecord-jdbcsqlite3-adapter"
43
+ end
25
44
  end
26
45
 
27
- appraise "activerecord-6.0" do
28
- gem "activerecord", "~> 6.0.0"
29
- end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  ## Changelog
2
2
 
3
+ ## [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)
4
+
5
+
6
+ ### Features
7
+
8
+ * 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))
9
+
10
+ ## [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)
11
+
12
+
13
+ ### Features
14
+
15
+ * 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))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * 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))
21
+
22
+ ## [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)
23
+
24
+
25
+ ### Features
26
+
27
+ * use zeitwerk loader instead of ActiveSupport::Autoload ([b5082fd](https://github.com/ClosureTree/with_advisory_lock/commit/b5082fddacacacff48139f5bf509601a37945a0e))
28
+
29
+ ## 5.0.1 (2024-01-21)
30
+
31
+
32
+ ### Features
33
+
34
+ * add release workflow ([5d32520](https://github.com/ClosureTree/with_advisory_lock/commit/5d325201c82974991381a9fbc4d1714c9739dc4f))
35
+ * 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))
36
+ * 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))
37
+ * add truffleruby support ([#62](https://github.com/ClosureTree/with_advisory_lock/issues/62)) ([ec34bd4](https://github.com/ClosureTree/with_advisory_lock/commit/ec34bd448e3505e5df631daaf47bb83f2f5316dc))
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * 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))
43
+
44
+ ### 5.0.0
45
+ - Drop support for EOL rubies and activerecord (ruby below 2.7 and activerecord below 6.1).
46
+ - Allow lock name to be integer
47
+ - Jruby support
48
+ - Truffleruby support
49
+ - Add `with_advisory_lock!`, which raises an error if the lock acquisition fails
50
+ - Add `disable_query_cache` option to `with_advisory_lock`
51
+ - Drop support for mysql < 5.7.5
52
+
3
53
  ### 4.6.0
4
54
 
5
55
  - Support for ActiveRecord 6
data/Gemfile CHANGED
@@ -1,15 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- platforms :ruby do
6
- gem 'mysql2'
7
- gem 'pg'
8
- gem 'sqlite3'
9
- end
10
-
11
- platforms :jruby do
12
- gem 'activerecord-jdbcmysql-adapter'
13
- gem 'activerecord-jdbcpostgresql-adapter'
14
- gem 'activerecord-jdbcsqlite3-adapter'
15
- end
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
- Copyright (c) 2013 Matthew McEachen
2
-
3
- MIT License
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
@@ -0,0 +1,14 @@
1
+ .PHONY: test-pg test-mysql
2
+
3
+ test-pg:
4
+ docker compose up -d pg
5
+ sleep 10 # give some time for the service to start
6
+ DATABASE_URL=postgres://with_advisory:with_advisory_pass@localhost/with_advisory_lock_test appraisal rake test
7
+
8
+ test-mysql:
9
+ docker compose up -d mysql
10
+ sleep 10 # give some time for the service to start
11
+ DATABASE_URL=mysql2://with_advisory:with_advisory_pass@0.0.0.0:3306/with_advisory_lock_test appraisal rake test
12
+
13
+
14
+ test: test-pg test-mysql
data/README.md CHANGED
@@ -1,14 +1,13 @@
1
1
  # with_advisory_lock
2
2
 
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
3
+ Adds advisory locking (mutexes) to ActiveRecord 6.0+, with ruby 2.7+, 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
7
  SQLite resorts to file locking.
9
8
 
10
- [![Build Status](https://api.travis-ci.org/ClosureTree/with_advisory_lock.svg?branch=master)](https://travis-ci.org/ClosureTree/with_advisory_lock)
11
9
  [![Gem Version](https://badge.fury.io/rb/with_advisory_lock.svg)](https://badge.fury.io/rb/with_advisory_lock)
10
+ [![CI](https://github.com/ClosureTree/with_advisory_lock/actions/workflows/ci.yml/badge.svg)](https://github.com/ClosureTree/with_advisory_lock/actions/workflows/ci.yml)
12
11
 
13
12
  ## What's an "Advisory Lock"?
14
13
 
@@ -45,8 +44,10 @@ A value of zero will try the lock only once. If the lock is acquired, the block
45
44
  will be yielded to. If the lock is currently being held, the block will not be
46
45
  called.
47
46
 
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.
47
+ > **Note**
48
+ >
49
+ > If a non-nil value is provided for `timeout_seconds`, the block will
50
+ *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
51
 
51
52
  For backwards compatability, the timeout value can be specified directly as the
52
53
  second parameter.
@@ -80,6 +81,8 @@ block, if the lock was able to be acquired and the block yielded, or `false`, if
80
81
  you provided a timeout_seconds value and the lock was not able to be acquired in
81
82
  time.
82
83
 
84
+ `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.
85
+
83
86
  ### Testing for the current lock status
84
87
 
85
88
  If you needed to check if the advisory lock is currently being held, you can
@@ -91,6 +94,14 @@ If you want to see if the current Thread is holding a lock, you can call
91
94
  `Tag.current_advisory_lock` which will return the name of the current lock. If
92
95
  no lock is currently held, `.current_advisory_lock` returns `nil`.
93
96
 
97
+ ### ActiveRecord Query Cache
98
+
99
+ You can optionally pass `disable_query_cache: true` to the options hash of
100
+ `with_advisory_lock` in order to disable ActiveRecord's query cache. This can
101
+ prevent problems when you query the database from within the lock and it returns
102
+ stale results. More info on why this can be a problem can be
103
+ [found here](https://github.com/ClosureTree/with_advisory_lock/issues/52)
104
+
94
105
  ## Installation
95
106
 
96
107
  Add this line to your application's Gemfile:
@@ -123,7 +134,7 @@ row-level locks prevent concurrent modification to a given model.
123
134
 
124
135
  **If you're building a
125
136
  [CRUD](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
126
- application, this will be your most commonly used lock.**
137
+ application, this will be 2.4, 2.5 and your most commonly used lock.**
127
138
 
128
139
  ### Table-level locks
129
140
 
@@ -0,0 +1,20 @@
1
+ version: "3.9"
2
+ services:
3
+ pg:
4
+ image: postgres:16
5
+ environment:
6
+ POSTGRES_USER: with_advisory
7
+ POSTGRES_PASSWORD: with_advisory_pass
8
+ POSTGRES_DB: with_advisory_lock_test
9
+ ports:
10
+ - "5432:5432"
11
+ mysql:
12
+ image: mysql:8
13
+ environment:
14
+ MYSQL_USER: with_advisory
15
+ MYSQL_PASSWORD: with_advisory_pass
16
+ MYSQL_DATABASE: with_advisory_lock_test
17
+ MYSQL_RANDOM_ROOT_PASSWORD: "yes"
18
+ MYSQL_ROOT_HOST: '%'
19
+ ports:
20
+ - "3306:3306"
@@ -2,12 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 6.0.0"
5
+ gem "activerecord", "~> 6.1.0"
6
6
 
7
7
  platforms :ruby do
8
+ gem "sqlite3"
8
9
  gem "mysql2"
10
+ gem "trilogy"
11
+ gem "activerecord-trilogy-adapter"
9
12
  gem "pg"
10
- gem "sqlite3"
11
13
  end
12
14
 
13
15
  platforms :jruby do
@@ -2,12 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 5.1.0"
5
+ gem "activerecord", "~> 7.0.0"
6
6
 
7
7
  platforms :ruby do
8
+ gem "sqlite3"
8
9
  gem "mysql2"
10
+ gem "trilogy"
11
+ gem "activerecord-trilogy-adapter"
9
12
  gem "pg"
10
- gem "sqlite3", "~> 1.3.6"
11
13
  end
12
14
 
13
15
  platforms :jruby do
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 7.1.0"
6
+
7
+ platforms :ruby do
8
+ gem "sqlite3"
9
+ gem "mysql2"
10
+ gem "trilogy"
11
+ gem "pg"
12
+ end
13
+
14
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
 
3
5
  module WithAdvisoryLock
@@ -19,21 +21,22 @@ module WithAdvisoryLock
19
21
  LockStackItem = Struct.new(:name, :shared)
20
22
 
21
23
  class Base
22
- attr_reader :connection, :lock_name, :timeout_seconds, :shared, :transaction
24
+ attr_reader :connection, :lock_name, :timeout_seconds, :shared, :transaction, :disable_query_cache
23
25
 
24
26
  def initialize(connection, lock_name, options)
25
27
  options = { timeout_seconds: options } unless options.respond_to?(:fetch)
26
- options.assert_valid_keys :timeout_seconds, :shared, :transaction
28
+ options.assert_valid_keys :timeout_seconds, :shared, :transaction, :disable_query_cache
27
29
 
28
30
  @connection = connection
29
31
  @lock_name = lock_name
30
32
  @timeout_seconds = options.fetch(:timeout_seconds, nil)
31
33
  @shared = options.fetch(:shared, false)
32
34
  @transaction = options.fetch(:transaction, false)
35
+ @disable_query_cache = options.fetch(:disable_query_cache, false)
33
36
  end
34
37
 
35
38
  def lock_str
36
- @lock_str ||= "#{ENV['WITH_ADVISORY_LOCK_PREFIX']}#{lock_name}"
39
+ @lock_str ||= "#{ENV[LOCK_PREFIX_ENV]}#{lock_name}"
37
40
  end
38
41
 
39
42
  def lock_stack_item
@@ -51,6 +54,16 @@ module WithAdvisoryLock
51
54
  end
52
55
 
53
56
  def with_advisory_lock_if_needed(&block)
57
+ if disable_query_cache
58
+ return lock_and_yield do
59
+ connection.uncached(&block)
60
+ end
61
+ end
62
+
63
+ lock_and_yield(&block)
64
+ end
65
+
66
+ def lock_and_yield(&block)
54
67
  if already_locked?
55
68
  Result.new(true, yield)
56
69
  elsif timeout_seconds == 0
@@ -66,7 +79,7 @@ module WithAdvisoryLock
66
79
  else
67
80
  # Ruby MRI's String#hash is randomly seeded as of Ruby 1.9 so
68
81
  # make sure we use a deterministic hash.
69
- Zlib.crc32(input.to_s)
82
+ Zlib.crc32(input.to_s, 0)
70
83
  end
71
84
  end
72
85
 
@@ -75,6 +88,7 @@ module WithAdvisoryLock
75
88
  while @timeout_seconds.nil? || Time.now < give_up_at
76
89
  r = yield_with_lock(&block)
77
90
  return r if r.lock_was_acquired?
91
+
78
92
  # Randomizing sleep time may help reduce contention.
79
93
  sleep(rand(0.05..0.15))
80
94
  end