activejob-locking 0.3.0 → 0.4.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/HISTORY.md +5 -1
- data/README.md +5 -6
- data/lib/activejob-locking.rb +1 -1
- 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/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/test_suite.rb +12 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cd905211ccba06fbbf2fac424009b84ab5fe769
|
4
|
+
data.tar.gz: c7c970994a0e5f3ad816c7b5da8ca28722d4596b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f5f4247c8f15cd41dc0692f1a638a775229404620cee8a6318e4c410dca20e922762145fe5af991c24d71cfacc045ec9dd53f266b4bfa6d9cf22bbd4eed0e6b
|
7
|
+
data.tar.gz: 7f9a485dac7c34835b7209ed3c7e2736d02ef735a8f6fc6575f8bdb9dcd2831289abe19a9d7c0823960b24a1b6eb6f74b231ac9c7408501abd9437b24d7bc912
|
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -159,15 +159,15 @@ class ExampleJob < ActiveJob::Base
|
|
159
159
|
end
|
160
160
|
```
|
161
161
|
|
162
|
-
###
|
162
|
+
### Hosts
|
163
163
|
|
164
|
-
|
165
|
-
documentation.
|
164
|
+
An array of hosts for the distributed system. This format is dependent on the locking gem, but generally is a url or an existing Memcache or Redis
|
165
|
+
connection. Please refer to the appropriate locking gem's documentation documentation.
|
166
166
|
|
167
167
|
Globally update:
|
168
168
|
|
169
169
|
```ruby
|
170
|
-
ActiveJob::Locking.options.hosts = 'localhost'
|
170
|
+
ActiveJob::Locking.options.hosts = ['localhost']
|
171
171
|
```
|
172
172
|
Locally update:
|
173
173
|
|
@@ -175,7 +175,7 @@ Locally update:
|
|
175
175
|
class ExampleJob < ActiveJob::Base
|
176
176
|
include ActiveJob::Locking::Serialized
|
177
177
|
|
178
|
-
self.hosts = 'localhost'
|
178
|
+
self.hosts = ['localhost']
|
179
179
|
end
|
180
180
|
```
|
181
181
|
|
@@ -226,7 +226,6 @@ class ExampleJob < ActiveJob::Base
|
|
226
226
|
end
|
227
227
|
```
|
228
228
|
|
229
|
-
|
230
229
|
### AdapterOptions
|
231
230
|
|
232
231
|
This is a hash table of options that should be sent to the lock gem when it is instantiated. Read the lock
|
data/lib/activejob-locking.rb
CHANGED
@@ -10,7 +10,7 @@ require 'activejob/locking/options'
|
|
10
10
|
module ActiveJob
|
11
11
|
module Locking
|
12
12
|
@options = ActiveJob::Locking::Options.new(adapter: ActiveJob::Locking::Adapters::Memory,
|
13
|
-
hosts: 'localhost',
|
13
|
+
hosts: ['localhost'],
|
14
14
|
lock_time: 100,
|
15
15
|
lock_acquire_time: 1,
|
16
16
|
adapter_options: {})
|
@@ -1,32 +1,32 @@
|
|
1
|
-
module ActiveJob
|
2
|
-
module Locking
|
3
|
-
module Adapters
|
4
|
-
class Base
|
5
|
-
attr_reader :key, :options, :lock_manager
|
6
|
-
attr_accessor :lock_token
|
7
|
-
|
8
|
-
def initialize(key, options)
|
9
|
-
@key = key
|
10
|
-
@options = options
|
11
|
-
@lock_manager = self.create_lock_manager
|
12
|
-
end
|
13
|
-
|
14
|
-
def create_lock_manager
|
15
|
-
raise('Subclass must implement')
|
16
|
-
end
|
17
|
-
|
18
|
-
def lock
|
19
|
-
raise('Subclass must implement')
|
20
|
-
end
|
21
|
-
|
22
|
-
def unlock
|
23
|
-
raise('Subclass must implement')
|
24
|
-
end
|
25
|
-
|
26
|
-
def refresh_lock!(refresh)
|
27
|
-
raise('Subclass must implement')
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
1
|
+
module ActiveJob
|
2
|
+
module Locking
|
3
|
+
module Adapters
|
4
|
+
class Base
|
5
|
+
attr_reader :key, :options, :lock_manager
|
6
|
+
attr_accessor :lock_token
|
7
|
+
|
8
|
+
def initialize(key, options)
|
9
|
+
@key = key
|
10
|
+
@options = options
|
11
|
+
@lock_manager = self.create_lock_manager
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_lock_manager
|
15
|
+
raise('Subclass must implement')
|
16
|
+
end
|
17
|
+
|
18
|
+
def lock
|
19
|
+
raise('Subclass must implement')
|
20
|
+
end
|
21
|
+
|
22
|
+
def unlock
|
23
|
+
raise('Subclass must implement')
|
24
|
+
end
|
25
|
+
|
26
|
+
def refresh_lock!(refresh)
|
27
|
+
raise('Subclass must implement')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
32
|
end
|
@@ -1,58 +1,58 @@
|
|
1
|
-
module ActiveJob
|
2
|
-
module Locking
|
3
|
-
module Adapters
|
4
|
-
class Memory < Base
|
5
|
-
@hash = Hash.new
|
6
|
-
@mutex = Mutex.new
|
7
|
-
|
8
|
-
def self.lock(key)
|
9
|
-
@mutex.synchronize do
|
10
|
-
if @hash[key]
|
11
|
-
false
|
12
|
-
else
|
13
|
-
@hash[key] = Time.now
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.unlock(key)
|
19
|
-
@mutex.synchronize do
|
20
|
-
@hash.delete(key)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.locked?(key)
|
25
|
-
@mutex.synchronize do
|
26
|
-
@hash.include?(key)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.reset
|
31
|
-
@mutex.synchronize do
|
32
|
-
@hash = Hash.new
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def create_lock_manager
|
37
|
-
end
|
38
|
-
|
39
|
-
def lock
|
40
|
-
finish = Time.now + self.options.lock_acquire_time
|
41
|
-
sleep_time = [5, self.options.lock_acquire_time / 5].min
|
42
|
-
|
43
|
-
begin
|
44
|
-
lock = self.class.lock(key)
|
45
|
-
return lock if lock
|
46
|
-
sleep(sleep_time)
|
47
|
-
end while Time.now < finish
|
48
|
-
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
|
-
def unlock
|
53
|
-
self.class.unlock(self.key)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
1
|
+
module ActiveJob
|
2
|
+
module Locking
|
3
|
+
module Adapters
|
4
|
+
class Memory < Base
|
5
|
+
@hash = Hash.new
|
6
|
+
@mutex = Mutex.new
|
7
|
+
|
8
|
+
def self.lock(key)
|
9
|
+
@mutex.synchronize do
|
10
|
+
if @hash[key]
|
11
|
+
false
|
12
|
+
else
|
13
|
+
@hash[key] = Time.now
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.unlock(key)
|
19
|
+
@mutex.synchronize do
|
20
|
+
@hash.delete(key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.locked?(key)
|
25
|
+
@mutex.synchronize do
|
26
|
+
@hash.include?(key)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.reset
|
31
|
+
@mutex.synchronize do
|
32
|
+
@hash = Hash.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_lock_manager
|
37
|
+
end
|
38
|
+
|
39
|
+
def lock
|
40
|
+
finish = Time.now + self.options.lock_acquire_time
|
41
|
+
sleep_time = [5, self.options.lock_acquire_time / 5].min
|
42
|
+
|
43
|
+
begin
|
44
|
+
lock = self.class.lock(key)
|
45
|
+
return lock if lock
|
46
|
+
sleep(sleep_time)
|
47
|
+
end while Time.now < finish
|
48
|
+
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def unlock
|
53
|
+
self.class.unlock(self.key)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
58
|
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
require 'redis-semaphore'
|
2
|
-
|
3
|
-
module ActiveJob
|
4
|
-
module Locking
|
5
|
-
module Adapters
|
6
|
-
class RedisSemaphore < Base
|
7
|
-
def create_lock_manager
|
8
|
-
mapped_options = {host: self.options.hosts,
|
9
|
-
resources: 1,
|
10
|
-
stale_client_timeout: self.options.lock_time}.merge(self.options.adapter_options)
|
11
|
-
|
12
|
-
Redis::Semaphore.new(self.key, mapped_options)
|
13
|
-
end
|
14
|
-
|
15
|
-
def lock
|
16
|
-
self.lock_token = self.lock_manager.lock(self.options.lock_acquire_time)
|
17
|
-
end
|
18
|
-
|
19
|
-
def unlock
|
20
|
-
self.lock_manager.signal(self.lock_token)
|
21
|
-
self.lock_token = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
1
|
+
require 'redis-semaphore'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Locking
|
5
|
+
module Adapters
|
6
|
+
class RedisSemaphore < Base
|
7
|
+
def create_lock_manager
|
8
|
+
mapped_options = {host: self.options.hosts.first,
|
9
|
+
resources: 1,
|
10
|
+
stale_client_timeout: self.options.lock_time}.merge(self.options.adapter_options)
|
11
|
+
|
12
|
+
Redis::Semaphore.new(self.key, mapped_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def lock
|
16
|
+
self.lock_token = self.lock_manager.lock(self.options.lock_acquire_time)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unlock
|
20
|
+
self.lock_manager.signal(self.lock_token)
|
21
|
+
self.lock_token = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
require 'redlock'
|
2
|
-
|
3
|
-
module ActiveJob
|
4
|
-
module Locking
|
5
|
-
module Adapters
|
6
|
-
class Redlock < Base
|
7
|
-
def create_lock_manager
|
8
|
-
mapped_options = self.options.adapter_options
|
9
|
-
mapped_options[:retry_count] = 2 # Try to get the lock and then try again when timeout is expiring--
|
10
|
-
mapped_options[:retry_delay] = self.options.lock_acquire_time * 1000 # convert from seconds to milliseconds
|
11
|
-
|
12
|
-
::Redlock::Client.new(
|
13
|
-
end
|
14
|
-
|
15
|
-
def lock
|
16
|
-
self.lock_token = self.lock_manager.lock(self.key, self.options.lock_time * 1000)
|
17
|
-
end
|
18
|
-
|
19
|
-
def unlock
|
20
|
-
self.lock_manager.unlock(self.lock_token)
|
21
|
-
self.lock_token = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
1
|
+
require 'redlock'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Locking
|
5
|
+
module Adapters
|
6
|
+
class Redlock < Base
|
7
|
+
def create_lock_manager
|
8
|
+
mapped_options = self.options.adapter_options
|
9
|
+
mapped_options[:retry_count] = 2 # Try to get the lock and then try again when timeout is expiring--
|
10
|
+
mapped_options[:retry_delay] = self.options.lock_acquire_time * 1000 # convert from seconds to milliseconds
|
11
|
+
|
12
|
+
::Redlock::Client.new(self.options.hosts, mapped_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def lock
|
16
|
+
self.lock_token = self.lock_manager.lock(self.key, self.options.lock_time * 1000)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unlock
|
20
|
+
self.lock_manager.unlock(self.lock_token)
|
21
|
+
self.lock_token = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,25 +1,25 @@
|
|
1
|
-
require 'suo'
|
2
|
-
|
3
|
-
module ActiveJob
|
4
|
-
module Locking
|
5
|
-
module Adapters
|
6
|
-
class SuoRedis < Base
|
7
|
-
def create_lock_manager
|
8
|
-
mapped_options = {connection: {host: self.options.hosts},
|
9
|
-
stale_lock_expiration: self.options.lock_time,
|
10
|
-
acquisition_timeout: self.options.lock_acquire_time}
|
11
|
-
|
12
|
-
Suo::Client::Redis.new(self.key, mapped_options)
|
13
|
-
end
|
14
|
-
|
15
|
-
def lock
|
16
|
-
self.lock_token = self.lock_manager.lock
|
17
|
-
end
|
18
|
-
|
19
|
-
def unlock
|
20
|
-
self.lock_manager.unlock(self.lock_token)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
require 'suo'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Locking
|
5
|
+
module Adapters
|
6
|
+
class SuoRedis < Base
|
7
|
+
def create_lock_manager
|
8
|
+
mapped_options = {connection: {host: self.options.hosts.first},
|
9
|
+
stale_lock_expiration: self.options.lock_time,
|
10
|
+
acquisition_timeout: self.options.lock_acquire_time}
|
11
|
+
|
12
|
+
Suo::Client::Redis.new(self.key, mapped_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def lock
|
16
|
+
self.lock_token = self.lock_manager.lock
|
17
|
+
end
|
18
|
+
|
19
|
+
def unlock
|
20
|
+
self.lock_manager.unlock(self.lock_token)
|
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/test_suite.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
%w(
|
2
|
-
test_serialized_memory
|
3
|
-
test_serialized_redis_semaphore
|
4
|
-
test_serialized_redlock
|
5
|
-
test_serialized_suo_redis
|
6
|
-
test_unique_memory
|
7
|
-
test_unique_redis_semaphore
|
8
|
-
test_unique_redlock
|
9
|
-
test_unique_suo_redis
|
10
|
-
).each do |test|
|
11
|
-
require File.expand_path("../#{test}", __FILE__)
|
12
|
-
end
|
1
|
+
%w(
|
2
|
+
test_serialized_memory
|
3
|
+
test_serialized_redis_semaphore
|
4
|
+
test_serialized_redlock
|
5
|
+
test_serialized_suo_redis
|
6
|
+
test_unique_memory
|
7
|
+
test_unique_redis_semaphore
|
8
|
+
test_unique_redlock
|
9
|
+
test_unique_suo_redis
|
10
|
+
).each do |test|
|
11
|
+
require File.expand_path("../#{test}", __FILE__)
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activejob-locking
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Savage
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|