redis_lock 0.1.0 → 0.2.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/Gemfile +1 -1
- data/README.md +156 -10
- data/lib/redis_lock/configuration.rb +34 -0
- data/lib/redis_lock/version.rb +1 -1
- data/lib/redis_lock.rb +43 -22
- data/redis_lock.gemspec +1 -0
- metadata +18 -4
- data/lib/redis_lock/config.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ad868ca731e9080d8c65e079d50f14718f696e7
|
4
|
+
data.tar.gz: 5c6e6dc414ed41926ffd3544db3f4f506971e225
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd7fc6334ce0b05e4cc2d60d3bf275703e3207b84b659e3c55e222e6b1ef9ef6d630e239d5a58f70aa7dcf2d17494318f8e54bffbd6f2a406825cf136e395031
|
7
|
+
data.tar.gz: eaf6171599bc5688c6a00e8bc926b2654f4a52c514753f83eae3cd5a5a370153c660cc2879687064531e47b0c91cb033e930f0ab6fc35a4d0a5ad466301d3086
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# RedisLock
|
2
|
+
**use cases:**
|
2
3
|
|
3
|
-
Do not allow anyone to
|
4
|
-
Do not perform this operation unless the previous was executed in more than 5 minutes ago.
|
4
|
+
- Do not allow anyone to perform de same operation while this is running.
|
5
|
+
- Do not perform this operation unless the previous was executed in more than 5 minutes ago.
|
5
6
|
|
6
7
|
## Installation
|
7
8
|
|
@@ -21,11 +22,41 @@ Or install it yourself as:
|
|
21
22
|
|
22
23
|
## Setup
|
23
24
|
|
25
|
+
This setup it's optional in any instance of `RedisLock` you can provide an optional
|
26
|
+
argument `:redis`.
|
27
|
+
But if you do not want to provide it in all the instances is a good shortcut to
|
28
|
+
set it here.
|
29
|
+
|
24
30
|
```ruby
|
25
31
|
RedisLock.setup do |config|
|
32
|
+
# redis
|
33
|
+
# Accepts `block` (or something responding to `#call`) or `Hash`
|
34
|
+
#
|
35
|
+
# In test configuration like your `spec_helper`
|
36
|
+
# recommend `mock_redis` gem
|
37
|
+
# example:
|
38
|
+
# config.redis = -> { MockRedis.new }
|
39
|
+
#
|
40
|
+
# When using Sidekiq
|
41
|
+
# example:
|
42
|
+
# config.redis = -> { Sidekiq.redis{ |r| r } }
|
43
|
+
#
|
44
|
+
# In `Rails`
|
45
|
+
# example:
|
46
|
+
# config.redis = -> do
|
47
|
+
# if Rails.env.test?
|
48
|
+
# MockRedis.new
|
49
|
+
# elsif Rails.env.development?
|
50
|
+
# { host: '127.0.0.1', port: 6379 }
|
51
|
+
# else
|
52
|
+
# Sidekiq.redis{ |r| r }
|
53
|
+
# end
|
54
|
+
# end
|
26
55
|
config.redis = { host: '127.0.0.1'
|
27
56
|
port: 6379
|
28
57
|
db: 2 }
|
58
|
+
# logger
|
59
|
+
# default: Logger.new(STDOUT)
|
29
60
|
config.logger = Rails.logger
|
30
61
|
end
|
31
62
|
```
|
@@ -42,20 +73,135 @@ lock.locked? #=> true
|
|
42
73
|
lock.remove #=> true
|
43
74
|
lock.locked? #=> false
|
44
75
|
```
|
45
|
-
|
76
|
+
__as Mutex__
|
46
77
|
```ruby
|
47
78
|
lock = RedisLock.new('my_key')
|
48
|
-
out =
|
49
|
-
#no one can perform the same operation while this is running
|
50
|
-
|
51
|
-
|
52
|
-
|
79
|
+
out = lock.if_open do |l|
|
80
|
+
# no one can perform the same operation while this is running
|
81
|
+
l.set(30) # place the lock so no one else can perform this tasks
|
82
|
+
sleep 3 # Do something
|
83
|
+
l.unlock! # release the lock
|
84
|
+
:hello
|
53
85
|
end
|
54
|
-
out
|
55
|
-
# once the block has finished releases the lock
|
86
|
+
out #=> :hello
|
56
87
|
lock.locked? #=> false
|
57
88
|
```
|
58
89
|
|
90
|
+
__blocking for a time__
|
91
|
+
|
92
|
+
Send email to user. The User should receive only 1 email per day
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
ttl = (24 * 3600) # one day
|
96
|
+
lock = RedisLock.new("User:1-sales-products")
|
97
|
+
lock.if_open do |l|
|
98
|
+
# Send Email
|
99
|
+
l.set(ttl)
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
## Methods:
|
104
|
+
|
105
|
+
__set__
|
106
|
+
Will store the key to redis with a ttl (time to live).
|
107
|
+
args:
|
108
|
+
- ttl # default: 60
|
109
|
+
- opts # default: {}
|
110
|
+
* value (String) - default: time now
|
111
|
+
* px (true) - miliseconds instead of seconds default: false
|
112
|
+
* nk (true) - Only set the key if it does not already exist.
|
113
|
+
* xx (true) - Only set the key if it already exist.
|
114
|
+
```ruby
|
115
|
+
lock = RedisLock.new('my_key')
|
116
|
+
|
117
|
+
lock.set(60)
|
118
|
+
lock.ttl #=> 60
|
119
|
+
lock.open? # => false
|
120
|
+
```
|
121
|
+
|
122
|
+
_with options_
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
lock = RedisLock.new('my_key')
|
126
|
+
|
127
|
+
lock.set(60, nx: true) # only if the key does not exists
|
128
|
+
# => true (key has been stored)
|
129
|
+
lock.ttl #=> 60
|
130
|
+
lock.open? # => false
|
131
|
+
```
|
132
|
+
|
133
|
+
Redis documentation: https://redis.io/commands/set
|
134
|
+
|
135
|
+
Set key to hold the string value. If key already holds a value, it is overwritten, regardless of its type. Any previous time to live associated with the key is discarded on successful SET operation.
|
136
|
+
|
137
|
+
EX seconds -- Set the specified expire time, in seconds.
|
138
|
+
PX milliseconds -- Set the specified expire time, in milliseconds.
|
139
|
+
NX -- Only set the key if it does not already exist.
|
140
|
+
XX -- Only set the key if it already exist.
|
141
|
+
|
142
|
+
|
143
|
+
__locked?__
|
144
|
+
Returns `true` if lock is set
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
lock = RedisLock.new('my_key')
|
148
|
+
lock.set(60) # => true (key has been stored)
|
149
|
+
lock.locked? # => true
|
150
|
+
lock.remove
|
151
|
+
lock.locked? # => false
|
152
|
+
```
|
153
|
+
_alias method:_ `exists?`
|
154
|
+
|
155
|
+
__open?__
|
156
|
+
Returns `true` if NO lock is set
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
lock = RedisLock.new('my_key')
|
160
|
+
lock.open? # => true
|
161
|
+
lock.set(60) # => true (key has been stored)
|
162
|
+
lock.open? # => false
|
163
|
+
```
|
164
|
+
_alias method:_ `unlocked?`
|
165
|
+
|
166
|
+
__delete__
|
167
|
+
Removes the key from the Redis store
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
lock = RedisLock.new('my_key')
|
171
|
+
lock.set(60) # => true (key has been stored)
|
172
|
+
lock.locked? # => true
|
173
|
+
lock.delete
|
174
|
+
lock.locked? # => false
|
175
|
+
```
|
176
|
+
_alias methods:_ `unlock!`,`open!`
|
177
|
+
|
178
|
+
__value__
|
179
|
+
Returns the value stored in redis
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
lock = RedisLock.new('my_key')
|
183
|
+
lock.set(60, value: 'hi there!')
|
184
|
+
lock.value # => 'hi there!'
|
185
|
+
```
|
186
|
+
__ttl__
|
187
|
+
Returns the pending ttl (time to live)
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
lock = RedisLock.new('my_key')
|
191
|
+
lock.set(60)
|
192
|
+
lock.ttl # => 60
|
193
|
+
sleep 10
|
194
|
+
lock.ttl # => 50
|
195
|
+
```
|
196
|
+
|
197
|
+
__having already a connection:__ _example: Sidekiq_
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
Sidekiq.redis do |connection|
|
201
|
+
lock = RedisLock.new('my_key', redis: connection)
|
202
|
+
# do something
|
203
|
+
end
|
204
|
+
```
|
59
205
|
## Development
|
60
206
|
|
61
207
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'logger'
|
2
|
+
class RedisLock
|
3
|
+
class Configuration
|
4
|
+
class RedisNotSet < StandardError; end
|
5
|
+
def redis=(hash = {})
|
6
|
+
@redis = hash
|
7
|
+
end
|
8
|
+
|
9
|
+
def redis
|
10
|
+
fail RedisNotSet, "[#{self.class}] redis connection setup is not set" unless @redis
|
11
|
+
if @redis.respond_to?(:call)
|
12
|
+
return @redis.call
|
13
|
+
else
|
14
|
+
self.redis_instance = @redis
|
15
|
+
@redis_instance
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def logger=(logger)
|
21
|
+
@logger = logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ? @logger : Logger.new(STDOUT)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def redis_instance=(args)
|
31
|
+
@redis_instance ||= Redis.new(args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/redis_lock/version.rb
CHANGED
data/lib/redis_lock.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'redis'
|
2
2
|
require "redis_lock/version"
|
3
|
-
require "redis_lock/
|
3
|
+
require "redis_lock/configuration"
|
4
4
|
|
5
5
|
class RedisLock
|
6
6
|
attr_reader :key
|
7
7
|
|
8
8
|
def self.config
|
9
|
-
@config ||=
|
9
|
+
@config ||= Configuration.new
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.setup
|
@@ -21,46 +21,67 @@ class RedisLock
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def redis
|
24
|
-
@redis ||=
|
24
|
+
@redis ||= config.redis
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
)
|
27
|
+
# Redis SET options:
|
28
|
+
# - EX seconds -- Set the specified expire time, in seconds.
|
29
|
+
# - PX milliseconds -- Set the specified expire time, in milliseconds.
|
30
|
+
# - NX -- Only set the key if it does not already exist.
|
31
|
+
# - XX -- Only set the key if it already exist.
|
32
|
+
def set(expiration_time = 60, opts = {})
|
33
|
+
value = opts.delete(:value) || Time.now.strftime('%FT%T')
|
34
|
+
args = if opts[:px]
|
35
|
+
{ px: expiration_time }
|
36
|
+
else
|
37
|
+
{ ex: expiration_time }
|
38
|
+
end
|
39
|
+
redis.set(key, value, args.merge(opts)) == "OK" ? true : false
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
42
|
+
def if_open(args = {}, &block)
|
37
43
|
return if locked?
|
38
|
-
|
39
|
-
set(expiration)
|
40
|
-
# If error occurs, we remove the lock
|
41
|
-
out = _perform(&block)
|
42
|
-
remove
|
43
|
-
out
|
44
|
+
_perform(&block)
|
44
45
|
end
|
46
|
+
alias_method :perform, :if_open
|
45
47
|
|
46
|
-
|
48
|
+
def if_locked(args = {}, &block)
|
49
|
+
return if open?
|
50
|
+
_perform(&block)
|
51
|
+
end
|
47
52
|
|
48
53
|
def locked?
|
49
|
-
|
54
|
+
ttl == -2 ? false : true
|
50
55
|
end
|
51
56
|
alias_method :exists?, :locked?
|
52
57
|
|
53
|
-
def
|
58
|
+
def ttl
|
59
|
+
redis.ttl(key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def open?
|
63
|
+
!locked?
|
64
|
+
end
|
65
|
+
alias_method :unlocked?, :open?
|
66
|
+
|
67
|
+
def delete
|
54
68
|
redis.del(key) == 1 ? true : false
|
55
69
|
end
|
70
|
+
alias_method :unlock!, :delete
|
71
|
+
alias_method :open!, :delete
|
72
|
+
alias_method :remove, :delete
|
73
|
+
|
74
|
+
def value
|
75
|
+
redis.get(key)
|
76
|
+
end
|
56
77
|
|
57
78
|
private
|
58
79
|
|
59
80
|
def _perform(&block)
|
60
81
|
yield self
|
61
82
|
rescue => e
|
62
|
-
config.logger.error "[
|
83
|
+
config.logger.error "[#{self.class}] key: `#{key}` error:"
|
63
84
|
config.logger.error e
|
64
|
-
|
85
|
+
raise e
|
65
86
|
end
|
66
87
|
end
|
data/redis_lock.gemspec
CHANGED
@@ -23,5 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.15"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.6"
|
26
|
+
spec.add_development_dependency "mock_redis", "~> 0.17"
|
26
27
|
spec.add_dependency "redis", "~> 3"
|
27
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_lock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artur Pañach
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mock_redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.17'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.17'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: redis
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +95,7 @@ files:
|
|
81
95
|
- bin/console
|
82
96
|
- bin/setup
|
83
97
|
- lib/redis_lock.rb
|
84
|
-
- lib/redis_lock/
|
98
|
+
- lib/redis_lock/configuration.rb
|
85
99
|
- lib/redis_lock/version.rb
|
86
100
|
- redis_lock.gemspec
|
87
101
|
homepage: https://github.com/arturictus/redis_lock.git
|
@@ -103,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
117
|
version: '0'
|
104
118
|
requirements: []
|
105
119
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
120
|
+
rubygems_version: 2.2.3
|
107
121
|
signing_key:
|
108
122
|
specification_version: 4
|
109
123
|
summary: Lock with redis
|
data/lib/redis_lock/config.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
class RedisLock
|
2
|
-
class Config
|
3
|
-
def redis=(hash = {})
|
4
|
-
@redis = hash
|
5
|
-
end
|
6
|
-
|
7
|
-
def redis
|
8
|
-
fail "[RedisLock::Config] redis connection setup is not set" unless @redis
|
9
|
-
@redis
|
10
|
-
end
|
11
|
-
|
12
|
-
def logger=(logger)
|
13
|
-
@logger = logger
|
14
|
-
end
|
15
|
-
|
16
|
-
def logger
|
17
|
-
@logger ? @logger : Logger.new(STDOUT)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|