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.
- checksums.yaml +4 -4
- data/.github/workflows/ci-mysql5.yml +61 -0
- data/.github/workflows/ci-mysql8.yml +62 -0
- data/.github/workflows/ci-postgresql.yml +64 -0
- data/.github/workflows/ci-sqlite3.yml +54 -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/Appraisals +34 -18
- data/CHANGELOG.md +50 -0
- data/Gemfile +0 -12
- data/LICENSE.txt +4 -4
- data/Makefile +14 -0
- data/README.md +17 -6
- data/docker-compose.yml +20 -0
- data/gemfiles/{activerecord_6.0.gemfile → activerecord_6.1.gemfile} +4 -2
- data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.0.gemfile} +4 -2
- data/gemfiles/activerecord_7.1.gemfile +14 -0
- data/lib/with_advisory_lock/base.rb +18 -4
- data/lib/with_advisory_lock/concern.rb +17 -17
- data/lib/with_advisory_lock/database_adapter_support.rb +7 -47
- data/lib/with_advisory_lock/failed_to_acquire_lock.rb +9 -0
- data/lib/with_advisory_lock/flock.rb +4 -3
- data/lib/with_advisory_lock/mysql.rb +12 -7
- data/lib/with_advisory_lock/postgresql.rb +43 -18
- data/lib/with_advisory_lock/version.rb +3 -1
- data/lib/with_advisory_lock.rb +9 -10
- data/release-please-config.json +9 -0
- data/test/test_helper.rb +66 -0
- data/test/test_models.rb +16 -10
- data/test/with_advisory_lock/base_test.rb +9 -0
- data/test/with_advisory_lock/concern_test.rb +33 -0
- data/test/with_advisory_lock/lock_test.rb +111 -0
- data/test/with_advisory_lock/nesting_test.rb +28 -0
- data/test/with_advisory_lock/options_test.rb +66 -0
- data/test/with_advisory_lock/parallelism_test.rb +75 -0
- data/test/with_advisory_lock/shared_test.rb +134 -0
- data/test/with_advisory_lock/thread_test.rb +61 -0
- data/test/with_advisory_lock/transaction_test.rb +68 -0
- data/with_advisory_lock.gemspec +29 -23
- metadata +56 -47
- data/.travis.yml +0 -38
- 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/lib/with_advisory_lock/mysql_no_nesting.rb +0 -20
- data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
- 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/thread_test.rb +0 -60
- data/test/transaction_test.rb +0 -70
- data/tests.sh +0 -11
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class OptionsParsingTest < GemTestCase
|
|
6
|
+
def parse_options(options)
|
|
7
|
+
WithAdvisoryLock::Base.new(mock, mock, options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
test 'defaults (empty hash)' do
|
|
11
|
+
impl = parse_options({})
|
|
12
|
+
assert_nil(impl.timeout_seconds)
|
|
13
|
+
assert_not(impl.shared)
|
|
14
|
+
assert_not(impl.transaction)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test 'nil sets timeout to nil' do
|
|
18
|
+
impl = parse_options(nil)
|
|
19
|
+
assert_nil(impl.timeout_seconds)
|
|
20
|
+
assert_not(impl.shared)
|
|
21
|
+
assert_not(impl.transaction)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test 'integer sets timeout to value' do
|
|
25
|
+
impl = parse_options(42)
|
|
26
|
+
assert_equal(42, impl.timeout_seconds)
|
|
27
|
+
assert_not(impl.shared)
|
|
28
|
+
assert_not(impl.transaction)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test 'hash with invalid key errors' do
|
|
32
|
+
assert_raises(ArgumentError) do
|
|
33
|
+
parse_options(foo: 42)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test 'hash with timeout_seconds sets timeout to value' do
|
|
38
|
+
impl = parse_options(timeout_seconds: 123)
|
|
39
|
+
assert_equal(123, impl.timeout_seconds)
|
|
40
|
+
assert_not(impl.shared)
|
|
41
|
+
assert_not(impl.transaction)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test 'hash with shared option sets shared to true' do
|
|
45
|
+
impl = parse_options(shared: true)
|
|
46
|
+
assert_nil(impl.timeout_seconds)
|
|
47
|
+
assert(impl.shared)
|
|
48
|
+
assert_not(impl.transaction)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'hash with transaction option set transaction to true' do
|
|
52
|
+
impl = parse_options(transaction: true)
|
|
53
|
+
assert_nil(impl.timeout_seconds)
|
|
54
|
+
assert_not(impl.shared)
|
|
55
|
+
assert(impl.transaction)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test 'hash with multiple keys sets options' do
|
|
59
|
+
foo = mock
|
|
60
|
+
bar = mock
|
|
61
|
+
impl = parse_options(timeout_seconds: foo, shared: bar)
|
|
62
|
+
assert_equal(foo, impl.timeout_seconds)
|
|
63
|
+
assert_equal(bar, impl.shared)
|
|
64
|
+
assert_not(impl.transaction)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'forwardable'
|
|
5
|
+
|
|
6
|
+
class FindOrCreateWorker
|
|
7
|
+
extend Forwardable
|
|
8
|
+
def_delegators :@thread, :join, :wakeup, :status, :to_s
|
|
9
|
+
|
|
10
|
+
def initialize(name, use_advisory_lock)
|
|
11
|
+
@name = name
|
|
12
|
+
@use_advisory_lock = use_advisory_lock
|
|
13
|
+
@thread = Thread.new { work_later }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def work_later
|
|
17
|
+
sleep
|
|
18
|
+
ApplicationRecord.connection_pool.with_connection do
|
|
19
|
+
if @use_advisory_lock
|
|
20
|
+
Tag.with_advisory_lock(@name) { work }
|
|
21
|
+
else
|
|
22
|
+
work
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def work
|
|
28
|
+
Tag.transaction do
|
|
29
|
+
Tag.where(name: @name).first_or_create
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class ParallelismTest < GemTestCase
|
|
35
|
+
def run_workers
|
|
36
|
+
@names = @iterations.times.map { |iter| "iteration ##{iter}" }
|
|
37
|
+
@names.each do |name|
|
|
38
|
+
workers = @workers.times.map do
|
|
39
|
+
FindOrCreateWorker.new(name, @use_advisory_lock)
|
|
40
|
+
end
|
|
41
|
+
# Wait for all the threads to get ready:
|
|
42
|
+
sleep(0.1) until workers.all? { |ea| ea.status == 'sleep' }
|
|
43
|
+
# OK, GO!
|
|
44
|
+
workers.each(&:wakeup)
|
|
45
|
+
# Then wait for them to finish:
|
|
46
|
+
workers.each(&:join)
|
|
47
|
+
end
|
|
48
|
+
# Ensure we're still connected:
|
|
49
|
+
ApplicationRecord.connection_pool.connection
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
setup do
|
|
53
|
+
ApplicationRecord.connection.reconnect!
|
|
54
|
+
@workers = 10
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test 'creates multiple duplicate rows without advisory locks' do
|
|
58
|
+
skip if %i[sqlite3 jdbcsqlite3].include?(env_db)
|
|
59
|
+
@use_advisory_lock = false
|
|
60
|
+
@iterations = 1
|
|
61
|
+
run_workers
|
|
62
|
+
assert_operator(Tag.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
|
|
63
|
+
assert_operator(TagAudit.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
|
|
64
|
+
assert_operator(Label.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
test "doesn't create multiple duplicate rows with advisory locks" do
|
|
68
|
+
@use_advisory_lock = true
|
|
69
|
+
@iterations = 10
|
|
70
|
+
run_workers
|
|
71
|
+
assert_equal(@iterations, Tag.all.size) # <- any duplicated rows will NOT make me happy.
|
|
72
|
+
assert_equal(@iterations, TagAudit.all.size) # <- any duplicated rows will NOT make me happy.
|
|
73
|
+
assert_equal(@iterations, Label.all.size) # <- any duplicated rows will NOT make me happy.
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
class SharedTestWorker
|
|
5
|
+
def initialize(shared)
|
|
6
|
+
@shared = shared
|
|
7
|
+
|
|
8
|
+
@locked = nil
|
|
9
|
+
@cleanup = false
|
|
10
|
+
@thread = Thread.new { work }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def locked?
|
|
14
|
+
sleep 0.01 while @locked.nil? && @thread.alive?
|
|
15
|
+
@locked
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cleanup!
|
|
19
|
+
@cleanup = true
|
|
20
|
+
@thread.join
|
|
21
|
+
raise if @thread.status.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def work
|
|
27
|
+
Tag.connection_pool.with_connection do
|
|
28
|
+
Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
|
|
29
|
+
@locked = true
|
|
30
|
+
sleep 0.01 until @cleanup
|
|
31
|
+
end
|
|
32
|
+
@locked = false
|
|
33
|
+
sleep 0.01 until @cleanup
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class SharedLocksTest < GemTestCase
|
|
39
|
+
def supported?
|
|
40
|
+
%i[trilogy mysql2 jdbcmysql].exclude?(env_db)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test 'does not allow two exclusive locks' do
|
|
44
|
+
one = SharedTestWorker.new(false)
|
|
45
|
+
assert_predicate(one, :locked?)
|
|
46
|
+
|
|
47
|
+
two = SharedTestWorker.new(false)
|
|
48
|
+
refute(two.locked?)
|
|
49
|
+
|
|
50
|
+
one.cleanup!
|
|
51
|
+
two.cleanup!
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class NotSupportedEnvironmentTest < SharedLocksTest
|
|
56
|
+
setup do
|
|
57
|
+
skip if supported?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test 'raises an error when attempting to use a shared lock' do
|
|
61
|
+
one = SharedTestWorker.new(true)
|
|
62
|
+
assert_nil(one.locked?)
|
|
63
|
+
|
|
64
|
+
exception = assert_raises(ArgumentError) do
|
|
65
|
+
one.cleanup!
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
assert_match(/#{Regexp.escape('not supported')}/, exception.message)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class SupportedEnvironmentTest < SharedLocksTest
|
|
73
|
+
setup do
|
|
74
|
+
skip unless supported?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
test 'does allow two shared locks' do
|
|
78
|
+
one = SharedTestWorker.new(true)
|
|
79
|
+
assert_predicate(one, :locked?)
|
|
80
|
+
|
|
81
|
+
two = SharedTestWorker.new(true)
|
|
82
|
+
assert_predicate(two, :locked?)
|
|
83
|
+
|
|
84
|
+
one.cleanup!
|
|
85
|
+
two.cleanup!
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
test 'does not allow exclusive lock with shared lock' do
|
|
89
|
+
one = SharedTestWorker.new(true)
|
|
90
|
+
assert_predicate(one, :locked?)
|
|
91
|
+
|
|
92
|
+
two = SharedTestWorker.new(false)
|
|
93
|
+
refute(two.locked?)
|
|
94
|
+
|
|
95
|
+
three = SharedTestWorker.new(true)
|
|
96
|
+
assert_predicate(three, :locked?)
|
|
97
|
+
|
|
98
|
+
one.cleanup!
|
|
99
|
+
two.cleanup!
|
|
100
|
+
three.cleanup!
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
test 'does not allow shared lock with exclusive lock' do
|
|
104
|
+
one = SharedTestWorker.new(false)
|
|
105
|
+
assert_predicate(one, :locked?)
|
|
106
|
+
|
|
107
|
+
two = SharedTestWorker.new(true)
|
|
108
|
+
refute(two.locked?)
|
|
109
|
+
|
|
110
|
+
one.cleanup!
|
|
111
|
+
two.cleanup!
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class PostgreSQLTest < SupportedEnvironmentTest
|
|
115
|
+
setup do
|
|
116
|
+
skip unless env_db == :postgresql
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def pg_lock_modes
|
|
120
|
+
Tag.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
test 'allows shared lock to be upgraded to an exclusive lock' do
|
|
124
|
+
assert_empty(pg_lock_modes)
|
|
125
|
+
Tag.with_advisory_lock 'test', shared: true do
|
|
126
|
+
assert_equal(%w[ShareLock], pg_lock_modes)
|
|
127
|
+
Tag.with_advisory_lock 'test', shared: false do
|
|
128
|
+
assert_equal(%w[ShareLock ExclusiveLock], pg_lock_modes)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
assert_empty(pg_lock_modes)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class SeparateThreadTest < GemTestCase
|
|
6
|
+
setup do
|
|
7
|
+
@lock_name = 'testing 1,2,3' # OMG COMMAS
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
@t1_acquired_lock = false
|
|
10
|
+
@t1_return_value = nil
|
|
11
|
+
|
|
12
|
+
@t1 = Thread.new do
|
|
13
|
+
Label.connection_pool.with_connection do
|
|
14
|
+
@t1_return_value = Label.with_advisory_lock(@lock_name) do
|
|
15
|
+
@mutex.synchronize { @t1_acquired_lock = true }
|
|
16
|
+
sleep
|
|
17
|
+
't1 finished'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Wait for the thread to acquire the lock:
|
|
23
|
+
sleep(0.1) until @mutex.synchronize { @t1_acquired_lock }
|
|
24
|
+
Label.connection.reconnect!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
teardown do
|
|
28
|
+
@t1.wakeup if @t1.status == 'sleep'
|
|
29
|
+
@t1.join
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test '#with_advisory_lock with a 0 timeout returns false immediately' do
|
|
33
|
+
response = Label.with_advisory_lock(@lock_name, 0) do
|
|
34
|
+
raise 'should not be yielded to'
|
|
35
|
+
end
|
|
36
|
+
assert_not(response)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test '#with_advisory_lock yields to the provided block' do
|
|
40
|
+
assert(@t1_acquired_lock)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test '#advisory_lock_exists? returns true when another thread has the lock' do
|
|
44
|
+
assert(Tag.advisory_lock_exists?(@lock_name))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test 'can re-establish the lock after the other thread releases it' do
|
|
48
|
+
@t1.wakeup
|
|
49
|
+
@t1.join
|
|
50
|
+
assert_equal('t1 finished', @t1_return_value)
|
|
51
|
+
|
|
52
|
+
# We should now be able to acquire the lock immediately:
|
|
53
|
+
reacquired = false
|
|
54
|
+
lock_result = Label.with_advisory_lock(@lock_name, 0) do
|
|
55
|
+
reacquired = true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
assert(lock_result)
|
|
59
|
+
assert(reacquired)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class TransactionScopingTest < GemTestCase
|
|
6
|
+
def supported?
|
|
7
|
+
%i[postgresql jdbcpostgresql].include?(env_db)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
test 'raises an error when attempting to use transaction level locks if not supported' do
|
|
11
|
+
skip if supported?
|
|
12
|
+
|
|
13
|
+
Tag.transaction do
|
|
14
|
+
exception = assert_raises(ArgumentError) do
|
|
15
|
+
Tag.with_advisory_lock 'test', transaction: true do
|
|
16
|
+
raise 'should not get here'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
assert_match(/#{Regexp.escape('not supported')}/, exception.message)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class PostgresqlTest < TransactionScopingTest
|
|
25
|
+
setup do
|
|
26
|
+
skip unless env_db == :postgresql
|
|
27
|
+
@pg_lock_count = lambda do
|
|
28
|
+
ApplicationRecord.connection.select_value("SELECT COUNT(*) FROM pg_locks WHERE locktype = 'advisory';").to_i
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test 'session locks release after the block executes' do
|
|
33
|
+
Tag.transaction do
|
|
34
|
+
assert_equal(0, @pg_lock_count.call)
|
|
35
|
+
Tag.with_advisory_lock 'test' do
|
|
36
|
+
assert_equal(1, @pg_lock_count.call)
|
|
37
|
+
end
|
|
38
|
+
assert_equal(0, @pg_lock_count.call)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test 'session locks release when transaction fails inside block' do
|
|
43
|
+
Tag.transaction do
|
|
44
|
+
assert_equal(0, @pg_lock_count.call)
|
|
45
|
+
|
|
46
|
+
exception = assert_raises(ActiveRecord::StatementInvalid) do
|
|
47
|
+
Tag.with_advisory_lock 'test' do
|
|
48
|
+
Tag.connection.execute 'SELECT 1/0;'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
assert_match(/#{Regexp.escape('division by zero')}/, exception.message)
|
|
53
|
+
assert_equal(0, @pg_lock_count.call)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test 'transaction level locks hold until the transaction completes' do
|
|
58
|
+
Tag.transaction do
|
|
59
|
+
assert_equal(0, @pg_lock_count.call)
|
|
60
|
+
Tag.with_advisory_lock 'test', transaction: true do
|
|
61
|
+
assert_equal(1, @pg_lock_count.call)
|
|
62
|
+
end
|
|
63
|
+
assert_equal(1, @pg_lock_count.call)
|
|
64
|
+
end
|
|
65
|
+
assert_equal(0, @pg_lock_count.call)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/with_advisory_lock.gemspec
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
require 'with_advisory_lock/version'
|
|
1
|
+
# frozen_string_literal: true
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
gem.version = WithAdvisoryLock::VERSION
|
|
8
|
-
gem.authors = ['Matthew McEachen']
|
|
9
|
-
gem.email = %w(matthew+github@mceachen.org)
|
|
10
|
-
gem.homepage = 'https://github.com/mceachen/with_advisory_lock'
|
|
11
|
-
gem.summary = %q{Advisory locking for ActiveRecord}
|
|
12
|
-
gem.description = %q{Advisory locking for ActiveRecord}
|
|
13
|
-
gem.license = 'MIT'
|
|
3
|
+
require 'English'
|
|
4
|
+
require_relative 'lib/with_advisory_lock/version'
|
|
14
5
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'with_advisory_lock'
|
|
8
|
+
spec.version = WithAdvisoryLock::VERSION
|
|
9
|
+
spec.authors = ['Matthew McEachen', 'Abdelkader Boudih']
|
|
10
|
+
spec.email = %w[matthew+github@mceachen.org terminale@gmail.com]
|
|
11
|
+
spec.homepage = 'https://github.com/ClosureTree/with_advisory_lock'
|
|
12
|
+
spec.summary = 'Advisory locking for ActiveRecord'
|
|
13
|
+
spec.description = 'Advisory locking for ActiveRecord'
|
|
14
|
+
spec.license = 'MIT'
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
17
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
|
18
|
+
spec.require_paths = %w[lib]
|
|
19
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
|
20
|
+
spec.required_ruby_version = '>= 2.7.0'
|
|
21
|
+
spec.metadata['yard.run'] = 'yri'
|
|
21
22
|
|
|
23
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
24
|
+
spec.metadata['source_code_uri'] = 'https://github.com/ClosureTree/with_advisory_lock'
|
|
25
|
+
spec.metadata['changelog_uri'] = 'https://github.com/ClosureTree/with_advisory_lock/blob/master/CHANGELOG.md'
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
spec.add_runtime_dependency 'activerecord', '>= 6.1'
|
|
28
|
+
spec.add_runtime_dependency 'zeitwerk', '>= 2.6'
|
|
29
|
+
|
|
30
|
+
spec.add_development_dependency 'appraisal'
|
|
31
|
+
spec.add_development_dependency 'maxitest'
|
|
32
|
+
spec.add_development_dependency 'minitest-reporters'
|
|
33
|
+
spec.add_development_dependency 'mocha'
|
|
34
|
+
spec.add_development_dependency 'yard'
|
|
29
35
|
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
|
+
version: 5.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew McEachen
|
|
8
|
-
|
|
8
|
+
- Abdelkader Boudih
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -16,30 +16,30 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '6.1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '6.1'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: zeitwerk
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
34
|
-
type: :
|
|
33
|
+
version: '2.6'
|
|
34
|
+
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '2.6'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: appraisal
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
@@ -53,7 +53,7 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: maxitest
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
@@ -95,7 +95,7 @@ dependencies:
|
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
98
|
+
name: yard
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - ">="
|
|
@@ -111,52 +111,62 @@ dependencies:
|
|
|
111
111
|
description: Advisory locking for ActiveRecord
|
|
112
112
|
email:
|
|
113
113
|
- matthew+github@mceachen.org
|
|
114
|
+
- terminale@gmail.com
|
|
114
115
|
executables: []
|
|
115
116
|
extensions: []
|
|
116
117
|
extra_rdoc_files: []
|
|
117
118
|
files:
|
|
119
|
+
- ".github/workflows/ci-mysql5.yml"
|
|
120
|
+
- ".github/workflows/ci-mysql8.yml"
|
|
121
|
+
- ".github/workflows/ci-postgresql.yml"
|
|
122
|
+
- ".github/workflows/ci-sqlite3.yml"
|
|
123
|
+
- ".github/workflows/release.yml"
|
|
118
124
|
- ".gitignore"
|
|
125
|
+
- ".release-please-manifest.json"
|
|
126
|
+
- ".ruby-version"
|
|
119
127
|
- ".tool-versions"
|
|
120
|
-
- ".travis.yml"
|
|
121
128
|
- Appraisals
|
|
122
129
|
- CHANGELOG.md
|
|
123
130
|
- Gemfile
|
|
124
131
|
- LICENSE.txt
|
|
132
|
+
- Makefile
|
|
125
133
|
- README.md
|
|
126
134
|
- Rakefile
|
|
127
|
-
-
|
|
128
|
-
- gemfiles/
|
|
129
|
-
- gemfiles/
|
|
130
|
-
- gemfiles/
|
|
131
|
-
- gemfiles/activerecord_6.0.gemfile
|
|
135
|
+
- docker-compose.yml
|
|
136
|
+
- gemfiles/activerecord_6.1.gemfile
|
|
137
|
+
- gemfiles/activerecord_7.0.gemfile
|
|
138
|
+
- gemfiles/activerecord_7.1.gemfile
|
|
132
139
|
- lib/with_advisory_lock.rb
|
|
133
140
|
- lib/with_advisory_lock/base.rb
|
|
134
141
|
- lib/with_advisory_lock/concern.rb
|
|
135
142
|
- lib/with_advisory_lock/database_adapter_support.rb
|
|
143
|
+
- lib/with_advisory_lock/failed_to_acquire_lock.rb
|
|
136
144
|
- lib/with_advisory_lock/flock.rb
|
|
137
145
|
- lib/with_advisory_lock/mysql.rb
|
|
138
|
-
- lib/with_advisory_lock/mysql_no_nesting.rb
|
|
139
|
-
- lib/with_advisory_lock/nested_advisory_lock_error.rb
|
|
140
146
|
- lib/with_advisory_lock/postgresql.rb
|
|
141
147
|
- lib/with_advisory_lock/version.rb
|
|
142
|
-
-
|
|
143
|
-
- test/
|
|
144
|
-
- test/lock_test.rb
|
|
145
|
-
- test/minitest_helper.rb
|
|
146
|
-
- test/nesting_test.rb
|
|
147
|
-
- test/options_test.rb
|
|
148
|
-
- test/parallelism_test.rb
|
|
149
|
-
- test/shared_test.rb
|
|
148
|
+
- release-please-config.json
|
|
149
|
+
- test/test_helper.rb
|
|
150
150
|
- test/test_models.rb
|
|
151
|
-
- test/
|
|
152
|
-
- test/
|
|
153
|
-
-
|
|
151
|
+
- test/with_advisory_lock/base_test.rb
|
|
152
|
+
- test/with_advisory_lock/concern_test.rb
|
|
153
|
+
- test/with_advisory_lock/lock_test.rb
|
|
154
|
+
- test/with_advisory_lock/nesting_test.rb
|
|
155
|
+
- test/with_advisory_lock/options_test.rb
|
|
156
|
+
- test/with_advisory_lock/parallelism_test.rb
|
|
157
|
+
- test/with_advisory_lock/shared_test.rb
|
|
158
|
+
- test/with_advisory_lock/thread_test.rb
|
|
159
|
+
- test/with_advisory_lock/transaction_test.rb
|
|
154
160
|
- with_advisory_lock.gemspec
|
|
155
|
-
homepage: https://github.com/
|
|
161
|
+
homepage: https://github.com/ClosureTree/with_advisory_lock
|
|
156
162
|
licenses:
|
|
157
163
|
- MIT
|
|
158
|
-
metadata:
|
|
159
|
-
|
|
164
|
+
metadata:
|
|
165
|
+
rubygems_mfa_required: 'true'
|
|
166
|
+
yard.run: yri
|
|
167
|
+
homepage_uri: https://github.com/ClosureTree/with_advisory_lock
|
|
168
|
+
source_code_uri: https://github.com/ClosureTree/with_advisory_lock
|
|
169
|
+
changelog_uri: https://github.com/ClosureTree/with_advisory_lock/blob/master/CHANGELOG.md
|
|
160
170
|
rdoc_options: []
|
|
161
171
|
require_paths:
|
|
162
172
|
- lib
|
|
@@ -164,26 +174,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
164
174
|
requirements:
|
|
165
175
|
- - ">="
|
|
166
176
|
- !ruby/object:Gem::Version
|
|
167
|
-
version: 2.
|
|
177
|
+
version: 2.7.0
|
|
168
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
179
|
requirements:
|
|
170
180
|
- - ">="
|
|
171
181
|
- !ruby/object:Gem::Version
|
|
172
182
|
version: '0'
|
|
173
183
|
requirements: []
|
|
174
|
-
rubygems_version: 3.
|
|
175
|
-
signing_key:
|
|
184
|
+
rubygems_version: 3.6.7
|
|
176
185
|
specification_version: 4
|
|
177
186
|
summary: Advisory locking for ActiveRecord
|
|
178
187
|
test_files:
|
|
179
|
-
- test/
|
|
180
|
-
- test/database.yml
|
|
181
|
-
- test/lock_test.rb
|
|
182
|
-
- test/minitest_helper.rb
|
|
183
|
-
- test/nesting_test.rb
|
|
184
|
-
- test/options_test.rb
|
|
185
|
-
- test/parallelism_test.rb
|
|
186
|
-
- test/shared_test.rb
|
|
188
|
+
- test/test_helper.rb
|
|
187
189
|
- test/test_models.rb
|
|
188
|
-
- test/
|
|
189
|
-
- test/
|
|
190
|
+
- test/with_advisory_lock/base_test.rb
|
|
191
|
+
- test/with_advisory_lock/concern_test.rb
|
|
192
|
+
- test/with_advisory_lock/lock_test.rb
|
|
193
|
+
- test/with_advisory_lock/nesting_test.rb
|
|
194
|
+
- test/with_advisory_lock/options_test.rb
|
|
195
|
+
- test/with_advisory_lock/parallelism_test.rb
|
|
196
|
+
- test/with_advisory_lock/shared_test.rb
|
|
197
|
+
- test/with_advisory_lock/thread_test.rb
|
|
198
|
+
- test/with_advisory_lock/transaction_test.rb
|
data/.travis.yml
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
language: ruby
|
|
2
|
-
|
|
3
|
-
services:
|
|
4
|
-
- postgresql
|
|
5
|
-
- mysql
|
|
6
|
-
|
|
7
|
-
addons:
|
|
8
|
-
postgresql: "10"
|
|
9
|
-
|
|
10
|
-
rvm:
|
|
11
|
-
- 2.6.4
|
|
12
|
-
- 2.5.6
|
|
13
|
-
- 2.4.7
|
|
14
|
-
|
|
15
|
-
gemfile:
|
|
16
|
-
- gemfiles/activerecord_6.0.gemfile
|
|
17
|
-
- gemfiles/activerecord_5.2.gemfile
|
|
18
|
-
- gemfiles/activerecord_5.1.gemfile
|
|
19
|
-
- gemfiles/activerecord_5.0.gemfile
|
|
20
|
-
- gemfiles/activerecord_4.2.gemfile
|
|
21
|
-
|
|
22
|
-
env:
|
|
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
|
|
33
|
-
|
|
34
|
-
before_install:
|
|
35
|
-
- mysql -e 'create database with_advisory_lock_test'
|
|
36
|
-
- psql -c 'create database with_advisory_lock_test' -U postgres
|
|
37
|
-
|
|
38
|
-
script: bundle exec rake --trace
|