redis_locks 0.0.1 → 0.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e54f88975c7c22202a262495733bef3bbf25384b
4
- data.tar.gz: dd6f4a948434588a31ec2086b0bce9e90fb29680
3
+ metadata.gz: e975e1077350b297e9a2e29dd2c1874ee36015cd
4
+ data.tar.gz: 43c25d4b9302e7530cbbfcaa12c9f75f81ad51b8
5
5
  SHA512:
6
- metadata.gz: 9c96f4d857b9461508678e335797dda5ffa2b625902ea43621b4af3c1b0efa59b10fd9c71448880dbef58abd2f8354afca7e4f7f9acc82ab435670fef506466f
7
- data.tar.gz: ac7146e7944e074836d66eef2f12e908b637194151812ac9c96b689944ad66c5264f4029afb1612bae807e1eac0e2ddcb191715dd968efb19540752c2028837a
6
+ metadata.gz: 836660edb41f89794435460298714126fb583cfe95655c6d915061a2d42660412309804724fcf2c2749be0b59daa20bb446e6014d499ef8b85f058394b6c3363
7
+ data.tar.gz: f3ca402e401fbfc4a2cdac4167dc714ba327a7d58eb36d7b101fa875d2e77946d15c2876c757fb1523164e06a57c791e5df7e23b6d6da483e692d4b114569aff
data/README.md CHANGED
@@ -16,7 +16,9 @@ A simple mutex using `setnx`.
16
16
  require 'redis'
17
17
  require 'redis_locks'
18
18
 
19
- lock = RedisLocks::Mutex.new('my_key', redis: Redis.new)
19
+ RedisLocks.redis = Redis.new
20
+
21
+ lock = RedisLocks::Mutex.new('my_key')
20
22
 
21
23
  # high-level use
22
24
  lock.lock! do
@@ -29,9 +31,8 @@ A simple mutex using `setnx`.
29
31
  lock.unlock # now lock can be acquired again
30
32
  ```
31
33
 
32
- Supports lock expiry via `expires_in`/`expires_at` arguments to the constructor.
33
- By default, locks expire after 24 hours.
34
-
34
+ Supports lock expiry via an `expires_in` argument to the constructor or
35
+ `expires_at` argument to `lock`/`lock!`. By default, locks expire after 24 hours.
35
36
 
36
37
  ## RedisLocks::Semaphore
37
38
 
@@ -43,7 +44,9 @@ Supports multiple resources, waits to acquire a resource, and timeouts.
43
44
  require 'redis'
44
45
  require 'redis_locks'
45
46
 
46
- semaphore = RedisLocks::Semaphore.new('my_key', redis: Redis.new, resources: 2)
47
+ RedisLocks.redis = Redis.new
48
+
49
+ semaphore = RedisLocks::Semaphore.new('my_key', resources: 2)
47
50
 
48
51
  # high-level use
49
52
  semaphore.lock! do
@@ -75,8 +78,10 @@ A [token-bucket](https://en.wikipedia.org/wiki/Token_bucket) rate limiter implem
75
78
  require 'redis'
76
79
  require 'redis_locks'
77
80
 
81
+ RedisLocks.redis = Redis.new
82
+
78
83
  # allows up to two calls to `take`/`take!` every five seconds
79
- limiter = RedisLocks::TokenBucket.new('my_key', redis: Redis.new, period: 5, number: 2)
84
+ limiter = RedisLocks::TokenBucket.new('my_key', period: 5, number: 2)
80
85
 
81
86
  2.times { limiter.take! }
82
87
 
@@ -9,23 +9,26 @@ module RedisLocks
9
9
 
10
10
  NAMESPACE = "mutex"
11
11
 
12
- def initialize(key, redis:, expires_in: 86400, expires_at: nil)
12
+ def initialize(key, expires_in: 86400, redis: RedisLocks.redis)
13
13
  @key = "#{NAMESPACE}:#{key}"
14
14
  @redis = redis
15
- @expires_at = (expires_at.to_i if expires_at) || (Time.now.utc.to_i + expires_in)
15
+ @expires_in = expires_in.to_i
16
+
17
+ raise ArgumentError.new("Invalid expires_in: #{expires_in}") unless expires_in > 0
16
18
  end
17
19
 
18
- def lock(&block)
20
+ def lock(expires_at: nil, &block)
19
21
  now = Time.now.utc.to_i
20
22
  locked = false
23
+ expires_at ||= now + @expires_in
21
24
 
22
- if @redis.setnx(@key, @expires_at)
23
- @redis.expire(@key, @expires_at - now)
25
+ if @redis.setnx(@key, expires_at)
26
+ @redis.expire(@key, expires_at - now)
24
27
  locked = true
25
28
  else # it was locked
26
29
  if (old_value = @redis.get(@key)).to_i <= now
27
30
  # lock has expired
28
- if @redis.getset_value(@key, @expires_at) == old_value
31
+ if @redis.getset(@key, expires_at) == old_value
29
32
  locked = true
30
33
  end
31
34
  end
@@ -36,16 +39,22 @@ module RedisLocks
36
39
  return_or_yield(&block)
37
40
  end
38
41
 
39
- def lock!(&block)
40
- locked = lock
42
+ def lock!(expires_at: nil, &block)
43
+ locked = lock(expires_at: expires_at)
41
44
  raise AlreadyLocked.new(@key) unless locked
42
45
  return_or_yield(&block)
43
46
  end
44
47
 
45
- # only delete the key if it's still valid, and will be for another 2 seconds
46
48
  def unlock
47
- if Time.now.utc.to_i - 2 < @expires_at
48
- @redis.del(@key)
49
+ @redis.watch(@key) do
50
+ # only delete the key if it's still valid, and will be for another 2 seconds
51
+ if @redis.get(@key).to_i > Time.now.utc.to_i + 2
52
+ @redis.multi do |multi|
53
+ multi.del(@key)
54
+ end
55
+ else
56
+ @redis.unwatch
57
+ end
49
58
  end
50
59
  end
51
60
 
@@ -62,7 +62,7 @@ module RedisLocks
62
62
  #
63
63
  # `stale_client_timeout` is the threshold of time before we assume that
64
64
  # something has gone terribly wrong with a client and we invalidate its lock.
65
- def initialize(key, redis:, resources: 1, stale_client_timeout: 86400)
65
+ def initialize(key, resources: 1, stale_client_timeout: 86400, redis: RedisLocks.redis)
66
66
  @key = key
67
67
  @resource_count = resources.to_i
68
68
  @stale_client_timeout = stale_client_timeout.to_f
@@ -41,7 +41,7 @@ module RedisLocks
41
41
  # max of `number` tokens being available). Each time a resource is used, a
42
42
  # token is removed from the bucket; if no tokens are available, no resource
43
43
  # may be used.
44
- def initialize(key, redis:, period: 1, number: 1)
44
+ def initialize(key, period: 1, number: 1, redis: RedisLocks.redis)
45
45
  @key = "#{NAMESPACE}:#{key}".freeze
46
46
  @rps = number.to_f / period.to_i
47
47
  @burst = number.to_i
@@ -1,3 +1,3 @@
1
1
  module RedisLocks
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
data/lib/redis_locks.rb CHANGED
@@ -6,4 +6,14 @@ require 'redis_locks/semaphore'
6
6
  require 'redis_locks/token_bucket'
7
7
 
8
8
  module RedisLocks
9
+
10
+ def self.redis=(redis)
11
+ @redis = redis
12
+ end
13
+
14
+ def self.redis
15
+ raise "RedisLocks.redis is not set!" unless @redis
16
+ @redis
17
+ end
18
+
9
19
  end
@@ -2,10 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe RedisLocks::Mutex do
4
4
  let(:mutex) {
5
- RedisLocks::Mutex.new(
6
- 'testmutex',
7
- redis: $redis
8
- )
5
+ RedisLocks::Mutex.new('testmutex')
9
6
  }
10
7
 
11
8
  it 'allows locking' do
@@ -31,7 +28,7 @@ describe RedisLocks::Mutex do
31
28
 
32
29
  context 'when locked' do
33
30
  before do
34
- mutex.lock!
31
+ mutex.lock!
35
32
  end
36
33
 
37
34
  it 'does not allow locking again' do
@@ -58,4 +55,15 @@ describe RedisLocks::Mutex do
58
55
  end
59
56
  end
60
57
  end
58
+
59
+ context 'when locked but expired' do
60
+ before do
61
+ mutex.lock!(expires_at: Time.now.utc.to_i+1)
62
+ sleep(2)
63
+ end
64
+
65
+ it 'allows lock' do
66
+ expect(mutex.lock).to be_truthy
67
+ end
68
+ end
61
69
  end
@@ -5,7 +5,6 @@ describe RedisLocks::Semaphore do
5
5
  let(:semaphore) {
6
6
  RedisLocks::Semaphore.new(
7
7
  'testsemaphore',
8
- redis: $redis,
9
8
  resources: 1
10
9
  )
11
10
  }
@@ -88,7 +87,6 @@ describe RedisLocks::Semaphore do
88
87
  let(:semaphore) {
89
88
  RedisLocks::Semaphore.new(
90
89
  'testsem',
91
- redis: $redis,
92
90
  resources: 2
93
91
  )
94
92
  }
@@ -5,7 +5,6 @@ describe RedisLocks::TokenBucket do
5
5
  let(:bucket) {
6
6
  RedisLocks::TokenBucket.new(
7
7
  "testbucket",
8
- redis: $redis,
9
8
  period: 2,
10
9
  number: 2
11
10
  )
data/spec/spec_helper.rb CHANGED
@@ -5,12 +5,14 @@ require 'redis'
5
5
  require 'redis_locks'
6
6
  require 'thread'
7
7
 
8
- $redis = Redis.new(db: ENV['REDIS_LOCKS_SPEC_DB'] || 15)
8
+ redis = Redis.new(db: ENV['REDIS_LOCKS_SPEC_DB'] || 15)
9
9
 
10
- raise "#{$redis.inspect} is non-empty!" if $redis.keys.any?
10
+ raise "#{redis.inspect} is non-empty!" if redis.keys.any?
11
+
12
+ RedisLocks.redis = redis
11
13
 
12
14
  RSpec.configure do |config|
13
15
  config.after(:each) do
14
- $redis.flushdb
16
+ redis.flushdb
15
17
  end
16
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_locks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Academia.edu