activejob-locking 0.4.0 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|