redis_locks 0.0.1 → 0.0.2

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