activejob-locking 0.4.0 → 0.6.2
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 +5 -5
- data/Gemfile +10 -10
- data/HISTORY.md +37 -16
- data/LICENSE +20 -20
- data/README.md +274 -247
- data/Rakefile +20 -20
- data/lib/activejob-locking.rb +24 -23
- data/lib/activejob/locking/adapters/base.rb +31 -31
- data/lib/activejob/locking/adapters/memory.rb +57 -57
- data/lib/activejob/locking/adapters/redis-semaphore.rb +26 -26
- data/lib/activejob/locking/adapters/redlock.rb +26 -26
- data/lib/activejob/locking/adapters/suo-redis.rb +25 -25
- data/lib/activejob/locking/base.rb +54 -50
- data/lib/activejob/locking/options.rb +72 -56
- data/lib/activejob/locking/serialized.rb +23 -23
- data/lib/activejob/locking/unique.rb +25 -25
- data/test/jobs/fail_job.rb +14 -14
- data/test/jobs/serial_job.rb +14 -14
- data/test/jobs/unique_job.rb +14 -14
- data/test/serialized_tests.rb +62 -62
- data/test/test_helper.rb +19 -19
- data/test/test_serialized_memory.rb +10 -10
- data/test/test_serialized_redis_semaphore.rb +11 -11
- data/test/test_serialized_redlock.rb +13 -13
- data/test/test_serialized_suo_redis.rb +11 -11
- data/test/test_suite.rb +12 -12
- data/test/test_unique_memory.rb +12 -12
- data/test/test_unique_redis_semaphore.rb +11 -11
- data/test/test_unique_redlock.rb +13 -13
- data/test/test_unique_suo_redis.rb +11 -11
- data/test/unique_tests.rb +99 -99
- metadata +11 -12
@@ -1,57 +1,73 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
|
-
module ActiveJob
|
4
|
-
module Locking
|
5
|
-
class Options
|
6
|
-
attr_accessor :adapter
|
7
|
-
attr_accessor :hosts
|
8
|
-
attr_accessor :lock_time
|
9
|
-
attr_accessor :lock_acquire_time
|
10
|
-
attr_accessor :
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
when
|
25
|
-
|
26
|
-
when
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
when
|
38
|
-
|
39
|
-
when
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Locking
|
5
|
+
class Options
|
6
|
+
attr_accessor :adapter
|
7
|
+
attr_accessor :hosts
|
8
|
+
attr_accessor :lock_time
|
9
|
+
attr_accessor :lock_acquire_time
|
10
|
+
attr_accessor :enqueue_time
|
11
|
+
attr_accessor :adapter_options
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@adapter = options[:adapter]
|
15
|
+
@hosts = options[:hosts]
|
16
|
+
@enqueue_time = options[:enqueue_time]
|
17
|
+
@lock_time = options[:lock_time]
|
18
|
+
@lock_acquire_time = options[:lock_acquire_time]
|
19
|
+
@adapter_options = options[:adapter_options]
|
20
|
+
end
|
21
|
+
|
22
|
+
def enqueue_time=(value)
|
23
|
+
case value
|
24
|
+
when NilClass
|
25
|
+
raise(ArgumentError, 'Enqueue time must be set')
|
26
|
+
when ActiveSupport::Duration
|
27
|
+
@enqueue_time = value.value
|
28
|
+
when 0
|
29
|
+
raise(ArgumentError, 'Enqueue time must be greater than zero')
|
30
|
+
else
|
31
|
+
@enqueue_time = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def lock_time=(value)
|
36
|
+
case value
|
37
|
+
when NilClass
|
38
|
+
raise(ArgumentError, 'Lock time must be set')
|
39
|
+
when ActiveSupport::Duration
|
40
|
+
@lock_time = value.value
|
41
|
+
when 0
|
42
|
+
raise(ArgumentError, 'Lock time must be greater than zero')
|
43
|
+
else
|
44
|
+
@lock_time = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def lock_acquire_time=(value)
|
49
|
+
case value
|
50
|
+
when NilClass
|
51
|
+
raise(ArgumentError, 'Lock acquire time must be set')
|
52
|
+
when ActiveSupport::Duration
|
53
|
+
@lock_acquire_time = value.value
|
54
|
+
when 0
|
55
|
+
raise(ArgumentError, 'Lock acquire time must be greater than zero')
|
56
|
+
else
|
57
|
+
@lock_acquire_time = value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def merge(other)
|
62
|
+
result = self.dup
|
63
|
+
result.adapter = other.adapter if other.adapter
|
64
|
+
result.hosts = other.hosts if other.hosts
|
65
|
+
result.enqueue_time = other.enqueue_time if other.enqueue_time
|
66
|
+
result.lock_time = other.lock_time if other.lock_time
|
67
|
+
result.lock_acquire_time = other.lock_acquire_time if other.lock_acquire_time
|
68
|
+
result.adapter_options = other.adapter_options if other.adapter_options
|
69
|
+
result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
57
73
|
end
|
@@ -1,23 +1,23 @@
|
|
1
|
-
module ActiveJob
|
2
|
-
module Locking
|
3
|
-
module Serialized
|
4
|
-
extend ::ActiveSupport::Concern
|
5
|
-
|
6
|
-
included do
|
7
|
-
include ::ActiveJob::Locking::Base
|
8
|
-
|
9
|
-
around_perform do |job, block|
|
10
|
-
if job.adapter.lock
|
11
|
-
begin
|
12
|
-
block.call
|
13
|
-
ensure
|
14
|
-
job.adapter.unlock
|
15
|
-
end
|
16
|
-
else
|
17
|
-
job.class.set(wait: job.
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
module ActiveJob
|
2
|
+
module Locking
|
3
|
+
module Serialized
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ::ActiveJob::Locking::Base
|
8
|
+
|
9
|
+
around_perform do |job, block|
|
10
|
+
if job.adapter.lock
|
11
|
+
begin
|
12
|
+
block.call
|
13
|
+
ensure
|
14
|
+
job.adapter.unlock
|
15
|
+
end
|
16
|
+
else
|
17
|
+
job.class.set(wait: job.adapter.options.enqueue_time).perform_later(*job.arguments)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,25 +1,25 @@
|
|
1
|
-
module ActiveJob
|
2
|
-
module Locking
|
3
|
-
module Unique
|
4
|
-
extend ::ActiveSupport::Concern
|
5
|
-
|
6
|
-
included do
|
7
|
-
include ::ActiveJob::Locking::Base
|
8
|
-
|
9
|
-
before_enqueue do |job|
|
10
|
-
lock = job.adapter.lock
|
11
|
-
throw :abort unless lock
|
12
|
-
end
|
13
|
-
|
14
|
-
rescue_from(Exception) do |exception|
|
15
|
-
self.adapter.unlock
|
16
|
-
raise
|
17
|
-
end
|
18
|
-
|
19
|
-
after_perform do |job|
|
20
|
-
job.adapter.unlock
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
module ActiveJob
|
2
|
+
module Locking
|
3
|
+
module Unique
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ::ActiveJob::Locking::Base
|
8
|
+
|
9
|
+
before_enqueue do |job|
|
10
|
+
lock = job.adapter.lock
|
11
|
+
throw :abort unless lock
|
12
|
+
end
|
13
|
+
|
14
|
+
rescue_from(Exception) do |exception|
|
15
|
+
self.adapter.unlock
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
|
19
|
+
after_perform do |job|
|
20
|
+
job.adapter.unlock
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/jobs/fail_job.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
class FailJob < ActiveJob::Base
|
2
|
-
include ActiveJob::Locking::Unique
|
3
|
-
|
4
|
-
self.lock_acquire_time = 2
|
5
|
-
|
6
|
-
# We want the job ids to be all the same for testing
|
7
|
-
def lock_key(index, sleep_time)
|
8
|
-
self.class.name
|
9
|
-
end
|
10
|
-
|
11
|
-
# Pass in index so we can distinguish different jobs
|
12
|
-
def perform(index, sleep_time)
|
13
|
-
raise(ArgumentError, 'Job failed')
|
14
|
-
end
|
1
|
+
class FailJob < ActiveJob::Base
|
2
|
+
include ActiveJob::Locking::Unique
|
3
|
+
|
4
|
+
self.lock_acquire_time = 2
|
5
|
+
|
6
|
+
# We want the job ids to be all the same for testing
|
7
|
+
def lock_key(index, sleep_time)
|
8
|
+
self.class.name
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pass in index so we can distinguish different jobs
|
12
|
+
def perform(index, sleep_time)
|
13
|
+
raise(ArgumentError, 'Job failed')
|
14
|
+
end
|
15
15
|
end
|
data/test/jobs/serial_job.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
class SerialJob < ActiveJob::Base
|
2
|
-
include ActiveJob::Locking::Serialized
|
3
|
-
|
4
|
-
self.lock_acquire_time = 2
|
5
|
-
|
6
|
-
# We want the job ids to be all the same for testing
|
7
|
-
def lock_key(index, sleep_time)
|
8
|
-
self.class.name
|
9
|
-
end
|
10
|
-
|
11
|
-
# Pass in index so we can distinguish different jobs
|
12
|
-
def perform(index, sleep_time)
|
13
|
-
sleep(sleep_time)
|
14
|
-
end
|
1
|
+
class SerialJob < ActiveJob::Base
|
2
|
+
include ActiveJob::Locking::Serialized
|
3
|
+
|
4
|
+
self.lock_acquire_time = 2
|
5
|
+
|
6
|
+
# We want the job ids to be all the same for testing
|
7
|
+
def lock_key(index, sleep_time)
|
8
|
+
self.class.name
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pass in index so we can distinguish different jobs
|
12
|
+
def perform(index, sleep_time)
|
13
|
+
sleep(sleep_time)
|
14
|
+
end
|
15
15
|
end
|
data/test/jobs/unique_job.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
class UniqueJob < ActiveJob::Base
|
2
|
-
include ActiveJob::Locking::Unique
|
3
|
-
|
4
|
-
self.lock_acquire_time = 2
|
5
|
-
|
6
|
-
# We want the job ids to be all the same for testing
|
7
|
-
def lock_key(index, sleep_time)
|
8
|
-
self.class.name
|
9
|
-
end
|
10
|
-
|
11
|
-
# Pass in index so we can distinguish different jobs
|
12
|
-
def perform(index, sleep_time)
|
13
|
-
sleep(sleep_time)
|
14
|
-
end
|
1
|
+
class UniqueJob < ActiveJob::Base
|
2
|
+
include ActiveJob::Locking::Unique
|
3
|
+
|
4
|
+
self.lock_acquire_time = 2
|
5
|
+
|
6
|
+
# We want the job ids to be all the same for testing
|
7
|
+
def lock_key(index, sleep_time)
|
8
|
+
self.class.name
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pass in index so we can distinguish different jobs
|
12
|
+
def perform(index, sleep_time)
|
13
|
+
sleep(sleep_time)
|
14
|
+
end
|
15
15
|
end
|
data/test/serialized_tests.rb
CHANGED
@@ -1,63 +1,63 @@
|
|
1
|
-
require File.expand_path('../test_helper', __FILE__)
|
2
|
-
|
3
|
-
module SerializedTests
|
4
|
-
def test_one_completed
|
5
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
6
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
7
|
-
|
8
|
-
start_time = Time.now
|
9
|
-
sleep_time = SerialJob.lock_acquire_time / 0.9
|
10
|
-
threads = 3.times.map do |i|
|
11
|
-
Thread.new do
|
12
|
-
SerialJob.perform_later(i, sleep_time)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
17
|
-
threads.each {|thread| thread.join}
|
18
|
-
assert_equal(2, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
19
|
-
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
20
|
-
|
21
|
-
assert(Time.now - start_time > (1 * sleep_time))
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_some_completed
|
25
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
26
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
27
|
-
|
28
|
-
start_time = Time.now
|
29
|
-
sleep_time = SerialJob.lock_acquire_time / 1.9
|
30
|
-
threads = 3.times.map do |i|
|
31
|
-
Thread.new do
|
32
|
-
SerialJob.perform_later(i, sleep_time)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
37
|
-
threads.each {|thread| thread.join}
|
38
|
-
assert_equal(1, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
39
|
-
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
40
|
-
|
41
|
-
assert(Time.now - start_time > (1 * sleep_time))
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_all_completed
|
45
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
46
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
47
|
-
|
48
|
-
start_time = Time.now
|
49
|
-
sleep_time = SerialJob.lock_acquire_time / 4
|
50
|
-
threads = 3.times.map do |i|
|
51
|
-
Thread.new do
|
52
|
-
SerialJob.perform_later(i, sleep_time)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
57
|
-
threads.each {|thread| thread.join}
|
58
|
-
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
59
|
-
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
60
|
-
|
61
|
-
assert(Time.now - start_time > (1 * sleep_time))
|
62
|
-
end
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module SerializedTests
|
4
|
+
def test_one_completed
|
5
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
6
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
7
|
+
|
8
|
+
start_time = Time.now
|
9
|
+
sleep_time = SerialJob.lock_acquire_time / 0.9
|
10
|
+
threads = 3.times.map do |i|
|
11
|
+
Thread.new do
|
12
|
+
SerialJob.perform_later(i, sleep_time)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
17
|
+
threads.each {|thread| thread.join}
|
18
|
+
assert_equal(2, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
19
|
+
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
20
|
+
|
21
|
+
assert(Time.now - start_time > (1 * sleep_time))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_some_completed
|
25
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
26
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
27
|
+
|
28
|
+
start_time = Time.now
|
29
|
+
sleep_time = SerialJob.lock_acquire_time / 1.9
|
30
|
+
threads = 3.times.map do |i|
|
31
|
+
Thread.new do
|
32
|
+
SerialJob.perform_later(i, sleep_time)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
37
|
+
threads.each {|thread| thread.join}
|
38
|
+
assert_equal(1, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
39
|
+
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
40
|
+
|
41
|
+
assert(Time.now - start_time > (1 * sleep_time))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_all_completed
|
45
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
46
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
47
|
+
|
48
|
+
start_time = Time.now
|
49
|
+
sleep_time = SerialJob.lock_acquire_time / 4
|
50
|
+
threads = 3.times.map do |i|
|
51
|
+
Thread.new do
|
52
|
+
SerialJob.perform_later(i, sleep_time)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# All the threads will complete after the sleep time has expired - since two jobs get requeued
|
57
|
+
threads.each {|thread| thread.join}
|
58
|
+
assert_equal(0, ActiveJob::Base.queue_adapter.enqueued_jobs.count)
|
59
|
+
assert_equal(3, ActiveJob::Base.queue_adapter.performed_jobs.count)
|
60
|
+
|
61
|
+
assert(Time.now - start_time > (1 * sleep_time))
|
62
|
+
end
|
63
63
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
Bundler.require(:default, :test)
|
3
|
-
|
4
|
-
require 'minitest/autorun'
|
5
|
-
|
6
|
-
# To make debugging easier, test within this source tree versus an installed gem
|
7
|
-
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
8
|
-
require 'activejob-locking'
|
9
|
-
|
10
|
-
require 'activejob/locking/adapters/redis-semaphore'
|
11
|
-
require 'activejob/locking/adapters/redlock'
|
12
|
-
require 'activejob/locking/adapters/suo-redis'
|
13
|
-
|
14
|
-
require_relative './jobs/unique_job'
|
15
|
-
require_relative './jobs/fail_job'
|
16
|
-
require_relative './jobs/serial_job'
|
17
|
-
|
18
|
-
def redis_reset
|
19
|
-
Kernel.system('redis-cli FLUSHALL')
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require(:default, :test)
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
# To make debugging easier, test within this source tree versus an installed gem
|
7
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
8
|
+
require 'activejob-locking'
|
9
|
+
|
10
|
+
require 'activejob/locking/adapters/redis-semaphore'
|
11
|
+
require 'activejob/locking/adapters/redlock'
|
12
|
+
require 'activejob/locking/adapters/suo-redis'
|
13
|
+
|
14
|
+
require_relative './jobs/unique_job'
|
15
|
+
require_relative './jobs/fail_job'
|
16
|
+
require_relative './jobs/serial_job'
|
17
|
+
|
18
|
+
def redis_reset
|
19
|
+
Kernel.system('redis-cli FLUSHALL')
|
20
20
|
end
|