redis-semaphore 0.3.0 → 0.3.1
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/README.md +15 -46
- data/lib/redis/semaphore.rb +31 -12
- data/spec/semaphore_spec.rb +39 -0
- data/spec/spec_helper.rb +5 -0
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b9f39ef8423ccca320813836891182abf1d93ec
|
4
|
+
data.tar.gz: f631ca62601208fe26125808c049abe755de3ad9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c38139829552918fbc64bef11059fd3ac8a8117d6f25bbda7bfac2d355c791232d9a2b9c5458cc173d53b8d210b0aa01efd61e8c7aff6e1a5dc22253a6c1070
|
7
|
+
data.tar.gz: 0be29fe2f67e984c6e2757c0da6df45eba3ec8a97702cae4a623e185407e347f0642f624338ec66213c012e9f26220c71f4155e26cc1b9bd8a65ba65381689f9
|
data/README.md
CHANGED
@@ -10,8 +10,8 @@ The mutex and semaphore is blocking, not polling, and has a fair queue serving p
|
|
10
10
|
|
11
11
|
For more info see [Wikipedia](http://en.wikipedia.org/wiki/Semaphore_(programming)).
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
Important change in v0.3.0
|
14
|
+
===========================
|
15
15
|
|
16
16
|
If you've been using `redis-semaphore` before version `0.3.0` you should be aware that the interface for `lock` has changed slightly. Before `0.3` calling `semaphore.lock(0)` (with `0` as the timeout) would block the semaphore indefinitely, just like a redis `blpop` command would.
|
17
17
|
|
@@ -98,6 +98,8 @@ s = Redis::Semaphore.new(:another_name, :redis => r)
|
|
98
98
|
#...
|
99
99
|
```
|
100
100
|
|
101
|
+
Note that it's [a bad idea to reuse the same redis client across threads](https://github.com/dv/redis-semaphore/issues/18), due to the blocking nature of the `blpop` command. We might add support for this in a future version.
|
102
|
+
|
101
103
|
If an exception happens during a lock, the lock will automatically be released:
|
102
104
|
|
103
105
|
```ruby
|
@@ -217,6 +219,10 @@ Testing
|
|
217
219
|
Changelog
|
218
220
|
---------
|
219
221
|
|
222
|
+
###0.3.1 April 17, 2016 (Pending)
|
223
|
+
- Fix `sem.lock(0)` bug (thanks eugenk!).
|
224
|
+
- Fix `release_stale_locks!` deadlock bug (thanks mfischer-zd for the bug-report!).
|
225
|
+
|
220
226
|
###0.3.0 January 24, 2016
|
221
227
|
- Change API to include non-blocking option for `#lock` (thanks tomclose!).
|
222
228
|
- Fix unwanted persisting of `available_key` (thanks dany1468!).
|
@@ -227,50 +233,7 @@ Changelog
|
|
227
233
|
- Add expiration option (thanks jcalvert!).
|
228
234
|
- Update API version logic.
|
229
235
|
|
230
|
-
|
231
|
-
- Block-based locking return the value of the block (thanks frobcode!).
|
232
|
-
|
233
|
-
###0.2.2 June 16, 2014
|
234
|
-
- Fixed bug in `all_tokens` (thanks presskey!).
|
235
|
-
- Fixed bug in error message (thanks Dmitriy!).
|
236
|
-
|
237
|
-
###0.2.1 August 6, 2013
|
238
|
-
- Remove dependency on Redis 2.6+ using fallback for TIME command (thanks dubdromic!).
|
239
|
-
- Add ```:use_local_time``` option
|
240
|
-
|
241
|
-
###0.2.0 June 2, 2013
|
242
|
-
- Use Redis TIME command for lock timeouts (thanks dubdromic!).
|
243
|
-
- Version increase because of new dependency on Redis 2.6+
|
244
|
-
|
245
|
-
###0.1.7 April 18, 2013
|
246
|
-
- Fix bug where ```release_stale_locks!``` was not public (thanks scomma!).
|
247
|
-
|
248
|
-
###0.1.6 March 31, 2013
|
249
|
-
- Add non-ownership of tokens
|
250
|
-
- Add stale client timeout (thanks timgaleckas!).
|
251
|
-
|
252
|
-
###0.1.5 October 1, 2012
|
253
|
-
- Add detection of Redis::Namespace definition to avoid potential bug (thanks ruud!).
|
254
|
-
|
255
|
-
###0.1.4 October 1, 2012
|
256
|
-
- Fixed empty namespaces (thanks ruurd!).
|
257
|
-
|
258
|
-
###0.1.3 July 9, 2012
|
259
|
-
- Tokens are now identifiable (thanks timgaleckas!).
|
260
|
-
|
261
|
-
###0.1.2 June 1, 2012
|
262
|
-
- Add redis-namespace support (thanks neovintage!).
|
263
|
-
|
264
|
-
### 0.1.1 September 17, 2011
|
265
|
-
- When an exception is raised during locked period, ensure it unlocks.
|
266
|
-
|
267
|
-
### 0.1.0 August 4, 2011
|
268
|
-
- Initial release.
|
269
|
-
|
270
|
-
Author
|
271
|
-
------
|
272
|
-
|
273
|
-
[David Verhasselt](http://davidverhasselt.com) - david@crowdway.com
|
236
|
+
More in [CHANGELOG](CHANGELOG.md).
|
274
237
|
|
275
238
|
Contributors
|
276
239
|
------------
|
@@ -291,3 +254,9 @@ Thanks to these awesome people for their contributions:
|
|
291
254
|
- [Jonathan Calvert](https://github.com/jcalvert)
|
292
255
|
- [mikeryz](https://github.com/mikeryz)
|
293
256
|
- [tomclose](https://github.com/tomclose)
|
257
|
+
- [Eugen Kuksa](https://github.com/eugenk)
|
258
|
+
- [Eugene Kenny](https://github.com/eugeneius)
|
259
|
+
|
260
|
+
### "Merge"-button clicker
|
261
|
+
|
262
|
+
[David Verhasselt](http://davidverhasselt.com) - david@crowdway.com
|
data/lib/redis/semaphore.rb
CHANGED
@@ -62,14 +62,13 @@ class Redis
|
|
62
62
|
|
63
63
|
if timeout.nil? || timeout > 0
|
64
64
|
# passing timeout 0 to blpop causes it to block
|
65
|
-
|
65
|
+
_key, current_token = @redis.blpop(available_key, timeout || 0)
|
66
66
|
else
|
67
|
-
|
67
|
+
current_token = @redis.lpop(available_key)
|
68
68
|
end
|
69
69
|
|
70
|
-
return false if
|
70
|
+
return false if current_token.nil?
|
71
71
|
|
72
|
-
current_token = token_pair[1]
|
73
72
|
@tokens.push(current_token)
|
74
73
|
@redis.hset(grabbed_key, current_token, current_time.to_f)
|
75
74
|
return_value = current_token
|
@@ -135,7 +134,7 @@ class Redis
|
|
135
134
|
end
|
136
135
|
|
137
136
|
def release_stale_locks!
|
138
|
-
|
137
|
+
simple_expiring_mutex(:release_locks, 10) do
|
139
138
|
@redis.hgetall(grabbed_key).each do |token, locked_at|
|
140
139
|
timed_out_at = locked_at.to_f + @stale_client_timeout
|
141
140
|
|
@@ -148,17 +147,37 @@ class Redis
|
|
148
147
|
|
149
148
|
private
|
150
149
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
150
|
+
def simple_expiring_mutex(key_name, expires_in)
|
151
|
+
# Using the locking mechanism as described in
|
152
|
+
# http://redis.io/commands/setnx
|
154
153
|
|
155
|
-
|
156
|
-
|
154
|
+
key_name = namespaced_key(key_name)
|
155
|
+
cached_current_time = current_time.to_f
|
156
|
+
my_lock_expires_at = cached_current_time + expires_in + 1
|
157
|
+
|
158
|
+
got_lock = @redis.setnx(key_name, my_lock_expires_at)
|
159
|
+
|
160
|
+
if !got_lock
|
161
|
+
# Check if expired
|
162
|
+
other_lock_expires_at = @redis.get(key_name).to_f
|
163
|
+
|
164
|
+
if other_lock_expires_at < cached_current_time
|
165
|
+
old_expires_at = @redis.getset(key_name, my_lock_expires_at).to_f
|
166
|
+
|
167
|
+
# Check if another client started cleanup yet. If not,
|
168
|
+
# then we now have the lock.
|
169
|
+
got_lock = (old_expires_at == other_lock_expires_at)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
return false if !got_lock
|
157
174
|
|
158
175
|
begin
|
159
|
-
yield
|
176
|
+
yield
|
160
177
|
ensure
|
161
|
-
|
178
|
+
# Make sure not to delete the lock in case someone else already expired
|
179
|
+
# our lock, with one second in between to account for some lag.
|
180
|
+
@redis.del(key_name) if my_lock_expires_at > (current_time.to_f - 1)
|
162
181
|
end
|
163
182
|
end
|
164
183
|
|
data/spec/semaphore_spec.rb
CHANGED
@@ -137,6 +137,12 @@ describe "redis" do
|
|
137
137
|
|
138
138
|
expect(did_we_get_in).to be false
|
139
139
|
end
|
140
|
+
|
141
|
+
it "should be locked when the timeout is zero" do
|
142
|
+
semaphore.lock(0) do
|
143
|
+
expect(semaphore.locked?).to be true
|
144
|
+
end
|
145
|
+
end
|
140
146
|
end
|
141
147
|
|
142
148
|
describe "semaphore with expiration" do
|
@@ -268,4 +274,37 @@ describe "redis" do
|
|
268
274
|
end
|
269
275
|
end
|
270
276
|
|
277
|
+
# Private method tests, do not use
|
278
|
+
describe "simple_expiring_mutex" do
|
279
|
+
let(:semaphore) { Redis::Semaphore.new(:my_semaphore, :redis => @redis) }
|
280
|
+
|
281
|
+
before do
|
282
|
+
semaphore.class.send(:public, :simple_expiring_mutex)
|
283
|
+
end
|
284
|
+
|
285
|
+
it "gracefully expires stale lock" do
|
286
|
+
expiration = 1
|
287
|
+
|
288
|
+
thread =
|
289
|
+
Thread.new do
|
290
|
+
semaphore.simple_expiring_mutex(:test, expiration) do
|
291
|
+
sleep 3
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
sleep 1.5
|
296
|
+
|
297
|
+
expect(semaphore.simple_expiring_mutex(:test, expiration)).to be_falsy
|
298
|
+
|
299
|
+
sleep expiration
|
300
|
+
|
301
|
+
it_worked = false
|
302
|
+
semaphore.simple_expiring_mutex(:test, expiration) do
|
303
|
+
it_worked = true
|
304
|
+
end
|
305
|
+
|
306
|
+
expect(it_worked).to be_truthy
|
307
|
+
thread.join
|
308
|
+
end
|
309
|
+
end
|
271
310
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-semaphore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Verhasselt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '11'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "<"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '11'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.14'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: timecop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
114
|
version: '0'
|
115
115
|
requirements: []
|
116
116
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.4.5
|
117
|
+
rubygems_version: 2.4.5.1
|
118
118
|
signing_key:
|
119
119
|
specification_version: 4
|
120
120
|
summary: Implements a distributed semaphore or mutex using Redis.
|