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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +76 -0
  3. data/.github/workflows/release.yml +17 -0
  4. data/.gitignore +2 -0
  5. data/.release-please-manifest.json +1 -0
  6. data/.ruby-version +2 -0
  7. data/.tool-versions +1 -1
  8. data/CHANGELOG.md +89 -0
  9. data/Gemfile +22 -3
  10. data/LICENSE.txt +4 -4
  11. data/Makefile +10 -0
  12. data/README.md +22 -39
  13. data/Rakefile +5 -2
  14. data/bin/console +11 -0
  15. data/bin/rails +15 -0
  16. data/bin/sanity +20 -0
  17. data/bin/sanity_check +86 -0
  18. data/bin/setup +8 -0
  19. data/bin/setup_test_db +59 -0
  20. data/bin/test_connections +22 -0
  21. data/docker-compose.yml +19 -0
  22. data/lib/with_advisory_lock/concern.rb +37 -30
  23. data/lib/with_advisory_lock/core_advisory.rb +110 -0
  24. data/lib/with_advisory_lock/failed_to_acquire_lock.rb +9 -0
  25. data/lib/with_advisory_lock/jruby_adapter.rb +29 -0
  26. data/lib/with_advisory_lock/lock_stack_item.rb +6 -0
  27. data/lib/with_advisory_lock/mysql_advisory.rb +71 -0
  28. data/lib/with_advisory_lock/postgresql_advisory.rb +112 -0
  29. data/lib/with_advisory_lock/result.rb +14 -0
  30. data/lib/with_advisory_lock/version.rb +3 -1
  31. data/lib/with_advisory_lock.rb +38 -11
  32. data/release-please-config.json +9 -0
  33. data/test/dummy/Rakefile +8 -0
  34. data/test/dummy/app/controllers/application_controller.rb +7 -0
  35. data/test/dummy/app/models/application_record.rb +6 -0
  36. data/test/dummy/app/models/label.rb +4 -0
  37. data/test/dummy/app/models/mysql_label.rb +5 -0
  38. data/test/dummy/app/models/mysql_record.rb +6 -0
  39. data/test/dummy/app/models/mysql_tag.rb +10 -0
  40. data/test/dummy/app/models/mysql_tag_audit.rb +5 -0
  41. data/test/dummy/app/models/tag.rb +8 -0
  42. data/test/dummy/app/models/tag_audit.rb +4 -0
  43. data/test/dummy/config/application.rb +31 -0
  44. data/test/dummy/config/boot.rb +3 -0
  45. data/test/dummy/config/database.yml +13 -0
  46. data/test/dummy/config/environment.rb +7 -0
  47. data/test/dummy/config/routes.rb +4 -0
  48. data/test/dummy/config.ru +6 -0
  49. data/test/dummy/db/schema.rb +15 -0
  50. data/test/dummy/db/secondary_schema.rb +15 -0
  51. data/test/dummy/lib/tasks/db.rake +40 -0
  52. data/test/sanity_check_test.rb +63 -0
  53. data/test/test_helper.rb +33 -0
  54. data/test/with_advisory_lock/concern_test.rb +79 -0
  55. data/test/with_advisory_lock/lock_test.rb +197 -0
  56. data/test/with_advisory_lock/multi_adapter_test.rb +17 -0
  57. data/test/with_advisory_lock/mysql_release_lock_test.rb +119 -0
  58. data/test/with_advisory_lock/parallelism_test.rb +101 -0
  59. data/test/with_advisory_lock/postgresql_race_condition_test.rb +118 -0
  60. data/test/with_advisory_lock/shared_test.rb +129 -0
  61. data/test/with_advisory_lock/thread_test.rb +83 -0
  62. data/test/with_advisory_lock/transaction_test.rb +83 -0
  63. data/with_advisory_lock.gemspec +54 -28
  64. metadata +83 -69
  65. data/.travis.yml +0 -38
  66. data/Appraisals +0 -29
  67. data/gemfiles/activerecord_4.2.gemfile +0 -19
  68. data/gemfiles/activerecord_5.0.gemfile +0 -19
  69. data/gemfiles/activerecord_5.1.gemfile +0 -19
  70. data/gemfiles/activerecord_5.2.gemfile +0 -19
  71. data/gemfiles/activerecord_6.0.gemfile +0 -19
  72. data/lib/with_advisory_lock/base.rb +0 -104
  73. data/lib/with_advisory_lock/database_adapter_support.rb +0 -63
  74. data/lib/with_advisory_lock/flock.rb +0 -32
  75. data/lib/with_advisory_lock/mysql.rb +0 -27
  76. data/lib/with_advisory_lock/mysql_no_nesting.rb +0 -20
  77. data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
  78. data/lib/with_advisory_lock/postgresql.rb +0 -41
  79. data/test/concern_test.rb +0 -20
  80. data/test/database.yml +0 -17
  81. data/test/lock_test.rb +0 -47
  82. data/test/minitest_helper.rb +0 -40
  83. data/test/nesting_test.rb +0 -93
  84. data/test/options_test.rb +0 -64
  85. data/test/parallelism_test.rb +0 -77
  86. data/test/shared_test.rb +0 -131
  87. data/test/test_models.rb +0 -24
  88. data/test/thread_test.rb +0 -60
  89. data/test/transaction_test.rb +0 -70
  90. 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: f594bf1ed9b20028febee227bd33eddf70f2c3deb42ae7e8a4d83d794125281d
4
+ data.tar.gz: 6882cea1e84d517bdf847b8f66b7dd6bb7b6c93de09c156b459019aab324ace0
5
5
  SHA512:
6
- metadata.gz: be5a906b9740506447bfcdb1928cd5473add67e0a48acd32f8a26ca4671be49b34ca5eca987df2f8e6f29a0a3dbb0bccc3321061d9f1e5827a0ca4ef44341124
7
- data.tar.gz: 124358b251ffd9cc066d54aebfeeca97fb0db88ae18293fb7e0169ceb13960d298e72c66e9d5fbd79f0cbda5360d2fdb2e609866ac82eeb26b1eab6ead103daf
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
@@ -18,3 +18,5 @@ test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
20
  *.iml
21
+ .env
22
+ test/dummy/log/
@@ -0,0 +1 @@
1
+ {".":"7.0.1"}
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/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
- gem 'activerecord-jdbcmysql-adapter'
13
- gem 'activerecord-jdbcpostgresql-adapter'
14
- gem 'activerecord-jdbcsqlite3-adapter'
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
- 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,10 @@
1
+ .PHONY: test
2
+
3
+ test: setup-db
4
+ bin/rails test
5
+
6
+ setup-db:
7
+ docker compose up -d
8
+ sleep 2
9
+ bundle
10
+ bin/setup_test_db
data/README.md CHANGED
@@ -1,20 +1,22 @@
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 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
- [![Build Status](https://api.travis-ci.org/ClosureTree/with_advisory_lock.svg?branch=master)](https://travis-ci.org/ClosureTree/with_advisory_lock)
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
  [![Gem Version](https://badge.fury.io/rb/with_advisory_lock.svg)](https://badge.fury.io/rb/with_advisory_lock)
13
+ [![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
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, as long as it isn't SQLite,
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 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.
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
- require "bundler/gem_tasks"
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
- task :default => :test
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+ bin/rails db:setup
8
+ bin/rails db:migrate
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}"
@@ -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"