suo 0.2.1 → 0.2.3
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/CHANGELOG.md +12 -1
- data/README.md +31 -7
- data/lib/suo/client/base.rb +23 -27
- data/lib/suo/client/memcached.rb +2 -2
- data/lib/suo/client/redis.rb +2 -2
- data/lib/suo/version.rb +1 -1
- data/suo.gemspec +3 -2
- data/test/client_test.rb +219 -5
- data/test/test_helper.rb +5 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3c678d658e34c698d95ca20d8d7ddfea5c11c6c
|
4
|
+
data.tar.gz: cb7bf87ef58efff50356c9310adc1fb10fad6ad1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c33bc02743629ab053afa32ff38e0c837cc1b2deacdbb50342e14de50c83321f7d232df4e2dd2338ee8ec16e54b0aa7cc81b97adc67bd86d75c597c06822be49
|
7
|
+
data.tar.gz: ef2790ad5ad8ac1567346fd04b340288ec8ea6924a267d8b9ea4daea02808b6c665b267bbaee61c90c544bfeb4de3a6953100d12e36d70365ee505765b73c833
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 0.2.3
|
2
|
+
|
3
|
+
- Clarify documentation further with respect to semaphores.
|
4
|
+
|
5
|
+
## 0.2.2
|
6
|
+
|
7
|
+
- Fix bug with refresh - typo would've prevented real use.
|
8
|
+
- Clean up code.
|
9
|
+
- Improve documentation a bit.
|
10
|
+
- 100% test coverage.
|
11
|
+
|
1
12
|
## 0.2.1
|
2
13
|
|
3
14
|
- Fix bug when dealing with real-world Redis error conditions.
|
@@ -18,7 +29,7 @@
|
|
18
29
|
|
19
30
|
## 0.1.1
|
20
31
|
|
21
|
-
- Use [MessagePack](https://github.com/msgpack/msgpack-ruby) for
|
32
|
+
- Use [MessagePack](https://github.com/msgpack/msgpack-ruby) for lock serialization.
|
22
33
|
|
23
34
|
## 0.1.0
|
24
35
|
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Suo [](https://travis-ci.org/nickelser/suo) [](http://badge.fury.io/rb/suo)
|
1
|
+
# Suo [](https://travis-ci.org/nickelser/suo) [](https://codeclimate.com/github/nickelser/suo) [](https://codeclimate.com/github/nickelser/suo) [](http://badge.fury.io/rb/suo)
|
2
2
|
|
3
|
-
:lock: Distributed
|
3
|
+
:lock: Distributed locks using Memcached or Redis in Ruby.
|
4
4
|
|
5
|
-
Suo provides a very performant distributed lock solution using Compare-And-Set (`CAS`) commands in Memcached, and `WATCH/MULTI` in Redis.
|
5
|
+
Suo provides a very performant distributed lock solution using Compare-And-Set (`CAS`) commands in Memcached, and `WATCH/MULTI` in Redis. It allows locking both single exclusion (a mutex - sharing one resource), and multiple resources.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -31,6 +31,7 @@ suo.lock("some_key") do
|
|
31
31
|
@puppies.pet!
|
32
32
|
end
|
33
33
|
|
34
|
+
# The second argument to lock is the number of arguments (defaulting to one - a mutex)
|
34
35
|
Thread.new { suo.lock("other_key", 2) { puts "One"; sleep 2 } }
|
35
36
|
Thread.new { suo.lock("other_key", 2) { puts "Two"; sleep 2 } }
|
36
37
|
Thread.new { suo.lock("other_key", 2) { puts "Three" } }
|
@@ -41,16 +42,39 @@ Thread.new { suo.lock("other_key", 2) { puts "Three" } }
|
|
41
42
|
suo = Suo::Client::Memcached.new(client: some_dalli_client, acquisition_timeout: 1) # in seconds
|
42
43
|
|
43
44
|
# manually locking/unlocking
|
44
|
-
|
45
|
+
# the return value from lock without a block is a unique token valid only for the current lock
|
46
|
+
# which must be unlocked manually
|
47
|
+
lock = suo.lock("a_key")
|
45
48
|
foo.baz!
|
46
|
-
suo.unlock("a_key")
|
49
|
+
suo.unlock("a_key", lock)
|
47
50
|
|
48
|
-
# custom stale lock
|
51
|
+
# custom stale lock expiration (cleaning of dead locks)
|
49
52
|
suo = Suo::Client::Redis.new(client: some_redis_client, stale_lock_expiration: 60*5)
|
50
53
|
```
|
51
54
|
|
55
|
+
### Stale locks
|
56
|
+
|
57
|
+
"Stale locks" - those acquired more than `stale_lock_expiration` (defaulting to 3600 or one hour) ago - are automatically cleared during any operation on the key (`lock`, `unlock`, `refresh`). The `locked?` method will not return true if only stale locks exist, but will not modify the key itself.
|
58
|
+
|
59
|
+
To re-acquire a lock in the middle of a block, you can use the refresh method on client.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
suo = Suo::Client::Redis.new
|
63
|
+
|
64
|
+
# lock is the same token as seen in the manual example, above
|
65
|
+
suo.lock("foo") do |lock|
|
66
|
+
5.times do
|
67
|
+
baz.bar!
|
68
|
+
suo.refresh("foo", lock)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## Semaphore
|
74
|
+
|
75
|
+
With multiple resources, Suo acts like a semaphore, but is not strictly a semaphore according to the traditional definition, as the lock acquires ownership.
|
76
|
+
|
52
77
|
## TODO
|
53
|
-
- better stale key handling (refresh blocks)
|
54
78
|
- more race condition tests
|
55
79
|
|
56
80
|
## History
|
data/lib/suo/client/base.rb
CHANGED
@@ -24,7 +24,7 @@ module Suo
|
|
24
24
|
|
25
25
|
if block_given? && token
|
26
26
|
begin
|
27
|
-
yield
|
27
|
+
yield(token)
|
28
28
|
ensure
|
29
29
|
unlock(key, token)
|
30
30
|
end
|
@@ -39,9 +39,9 @@ module Suo
|
|
39
39
|
|
40
40
|
def locks(key)
|
41
41
|
val, _ = get(key)
|
42
|
-
|
42
|
+
cleared_locks = deserialize_and_clear_locks(val)
|
43
43
|
|
44
|
-
|
44
|
+
cleared_locks
|
45
45
|
end
|
46
46
|
|
47
47
|
def refresh(key, acquisition_token)
|
@@ -53,11 +53,11 @@ module Suo
|
|
53
53
|
next
|
54
54
|
end
|
55
55
|
|
56
|
-
|
56
|
+
cleared_locks = deserialize_and_clear_locks(val)
|
57
57
|
|
58
|
-
refresh_lock(
|
58
|
+
refresh_lock(cleared_locks, acquisition_token)
|
59
59
|
|
60
|
-
break if set(key, serialize_locks(
|
60
|
+
break if set(key, serialize_locks(cleared_locks), cas)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -69,12 +69,12 @@ module Suo
|
|
69
69
|
|
70
70
|
break if val.nil?
|
71
71
|
|
72
|
-
|
72
|
+
cleared_locks = deserialize_and_clear_locks(val)
|
73
73
|
|
74
|
-
acquisition_lock = remove_lock(
|
74
|
+
acquisition_lock = remove_lock(cleared_locks, acquisition_token)
|
75
75
|
|
76
76
|
break unless acquisition_lock
|
77
|
-
break if set(key, serialize_locks(
|
77
|
+
break if set(key, serialize_locks(cleared_locks), cas)
|
78
78
|
end
|
79
79
|
rescue LockClientError => _ # rubocop:disable Lint/HandleExceptions
|
80
80
|
# ignore - assume success due to optimistic locking
|
@@ -87,7 +87,6 @@ module Suo
|
|
87
87
|
private
|
88
88
|
|
89
89
|
def acquire_lock(key, resources = 1)
|
90
|
-
acquisition_token = nil
|
91
90
|
token = SecureRandom.base64(16)
|
92
91
|
|
93
92
|
retry_with_timeout(key) do
|
@@ -98,32 +97,29 @@ module Suo
|
|
98
97
|
next
|
99
98
|
end
|
100
99
|
|
101
|
-
|
100
|
+
cleared_locks = deserialize_and_clear_locks(val)
|
102
101
|
|
103
|
-
if
|
104
|
-
add_lock(
|
102
|
+
if cleared_locks.size < resources
|
103
|
+
add_lock(cleared_locks, token)
|
105
104
|
|
106
|
-
newval = serialize_locks(
|
105
|
+
newval = serialize_locks(cleared_locks)
|
107
106
|
|
108
|
-
if set(key, newval, cas)
|
109
|
-
acquisition_token = token
|
110
|
-
break
|
111
|
-
end
|
107
|
+
return token if set(key, newval, cas)
|
112
108
|
end
|
113
109
|
end
|
114
110
|
|
115
|
-
|
111
|
+
nil
|
116
112
|
end
|
117
113
|
|
118
114
|
def get(key) # rubocop:disable Lint/UnusedMethodArgument
|
119
115
|
fail NotImplementedError
|
120
116
|
end
|
121
117
|
|
122
|
-
def set(key, newval,
|
118
|
+
def set(key, newval, cas) # rubocop:disable Lint/UnusedMethodArgument
|
123
119
|
fail NotImplementedError
|
124
120
|
end
|
125
121
|
|
126
|
-
def initial_set(key) # rubocop:disable Lint/UnusedMethodArgument
|
122
|
+
def initial_set(key, val = "") # rubocop:disable Lint/UnusedMethodArgument
|
127
123
|
fail NotImplementedError
|
128
124
|
end
|
129
125
|
|
@@ -135,8 +131,8 @@ module Suo
|
|
135
131
|
start = Time.now.to_f
|
136
132
|
|
137
133
|
@retry_count.times do
|
138
|
-
|
139
|
-
break if
|
134
|
+
elapsed = Time.now.to_f - start
|
135
|
+
break if elapsed >= @options[:acquisition_timeout]
|
140
136
|
|
141
137
|
synchronize(key) do
|
142
138
|
yield
|
@@ -162,7 +158,7 @@ module Suo
|
|
162
158
|
unpacked.map do |time, token|
|
163
159
|
[Time.at(time), token]
|
164
160
|
end
|
165
|
-
rescue EOFError => _
|
161
|
+
rescue EOFError, MessagePack::MalformedFormatError => _
|
166
162
|
[]
|
167
163
|
end
|
168
164
|
|
@@ -171,8 +167,8 @@ module Suo
|
|
171
167
|
locks.reject { |time, _| time < expired }
|
172
168
|
end
|
173
169
|
|
174
|
-
def add_lock(locks, token)
|
175
|
-
locks << [
|
170
|
+
def add_lock(locks, token, time = Time.now.to_f)
|
171
|
+
locks << [time, token]
|
176
172
|
end
|
177
173
|
|
178
174
|
def remove_lock(locks, acquisition_token)
|
@@ -182,7 +178,7 @@ module Suo
|
|
182
178
|
|
183
179
|
def refresh_lock(locks, acquisition_token)
|
184
180
|
remove_lock(locks, acquisition_token)
|
185
|
-
add_lock(locks,
|
181
|
+
add_lock(locks, acquisition_token)
|
186
182
|
end
|
187
183
|
end
|
188
184
|
end
|
data/lib/suo/client/memcached.rb
CHANGED
data/lib/suo/client/redis.rb
CHANGED
data/lib/suo/version.rb
CHANGED
data/suo.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Nick Elser"]
|
10
10
|
spec.email = ["nick.elser@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary = %q(Distributed
|
13
|
-
spec.description = %q(Distributed
|
12
|
+
spec.summary = %q(Distributed locks using Memcached or Redis.)
|
13
|
+
spec.description = %q(Distributed locks using Memcached or Redis.)
|
14
14
|
spec.homepage = "https://github.com/nickelser/suo"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
31
|
spec.add_development_dependency "rubocop", "~> 0.30.0"
|
32
32
|
spec.add_development_dependency "minitest", "~> 5.5.0"
|
33
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.7"
|
33
34
|
end
|
data/test/client_test.rb
CHANGED
@@ -3,6 +3,10 @@ require "test_helper"
|
|
3
3
|
TEST_KEY = "suo_test_key".freeze
|
4
4
|
|
5
5
|
module ClientTests
|
6
|
+
def client(options = {})
|
7
|
+
@client.class.new(options.merge(client: @client.client))
|
8
|
+
end
|
9
|
+
|
6
10
|
def test_throws_failed_error_on_bad_client
|
7
11
|
assert_raises(Suo::LockClientError) do
|
8
12
|
client = @client.class.new(client: {})
|
@@ -27,6 +31,23 @@ module ClientTests
|
|
27
31
|
assert_equal false, locked
|
28
32
|
end
|
29
33
|
|
34
|
+
def test_empty_lock_on_invalid_data
|
35
|
+
@client.send(:initial_set, TEST_KEY, "bad value")
|
36
|
+
locked = @client.locked?(TEST_KEY)
|
37
|
+
assert_equal false, locked
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_clear
|
41
|
+
lock1 = @client.lock(TEST_KEY, 1)
|
42
|
+
refute_nil lock1
|
43
|
+
|
44
|
+
@client.clear(TEST_KEY)
|
45
|
+
|
46
|
+
locked = @client.locked?(TEST_KEY, 1)
|
47
|
+
|
48
|
+
assert_equal false, locked
|
49
|
+
end
|
50
|
+
|
30
51
|
def test_multiple_resource_locking
|
31
52
|
lock1 = @client.lock(TEST_KEY, 2)
|
32
53
|
refute_nil lock1
|
@@ -77,7 +98,7 @@ module ClientTests
|
|
77
98
|
sleep 0.1
|
78
99
|
threads << Thread.new { @client.lock(TEST_KEY, 2) { output << "Three" } }
|
79
100
|
|
80
|
-
threads.
|
101
|
+
threads.each(&:join)
|
81
102
|
|
82
103
|
ret = []
|
83
104
|
|
@@ -88,13 +109,14 @@ module ClientTests
|
|
88
109
|
|
89
110
|
assert_equal 0, output.size
|
90
111
|
assert_equal %w(One Two), ret
|
112
|
+
assert_equal false, @client.locked?(TEST_KEY)
|
91
113
|
end
|
92
114
|
|
93
115
|
def test_block_multiple_resource_locking
|
94
116
|
success_counter = Queue.new
|
95
117
|
failure_counter = Queue.new
|
96
118
|
|
97
|
-
client =
|
119
|
+
client = client(acquisition_timeout: 0.9)
|
98
120
|
|
99
121
|
100.times.map do |i|
|
100
122
|
Thread.new do
|
@@ -105,17 +127,18 @@ module ClientTests
|
|
105
127
|
|
106
128
|
failure_counter << i unless success
|
107
129
|
end
|
108
|
-
end.
|
130
|
+
end.each(&:join)
|
109
131
|
|
110
132
|
assert_equal 50, success_counter.size
|
111
133
|
assert_equal 50, failure_counter.size
|
134
|
+
assert_equal false, client.locked?(TEST_KEY)
|
112
135
|
end
|
113
136
|
|
114
137
|
def test_block_multiple_resource_locking_longer_timeout
|
115
138
|
success_counter = Queue.new
|
116
139
|
failure_counter = Queue.new
|
117
140
|
|
118
|
-
client =
|
141
|
+
client = client(acquisition_timeout: 3)
|
119
142
|
|
120
143
|
100.times.map do |i|
|
121
144
|
Thread.new do
|
@@ -126,10 +149,187 @@ module ClientTests
|
|
126
149
|
|
127
150
|
failure_counter << i unless success
|
128
151
|
end
|
129
|
-
end.
|
152
|
+
end.each(&:join)
|
130
153
|
|
131
154
|
assert_equal 100, success_counter.size
|
132
155
|
assert_equal 0, failure_counter.size
|
156
|
+
assert_equal false, client.locked?(TEST_KEY)
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_unstale_lock_acquisition
|
160
|
+
success_counter = Queue.new
|
161
|
+
failure_counter = Queue.new
|
162
|
+
|
163
|
+
client = client(stale_lock_expiration: 0.5)
|
164
|
+
|
165
|
+
t1 = Thread.new { client.lock(TEST_KEY) { sleep 0.6; success_counter << 1 } }
|
166
|
+
sleep 0.3
|
167
|
+
t2 = Thread.new do
|
168
|
+
locked = client.lock(TEST_KEY) { success_counter << 1 }
|
169
|
+
failure_counter << 1 unless locked
|
170
|
+
end
|
171
|
+
|
172
|
+
[t1, t2].each(&:join)
|
173
|
+
|
174
|
+
assert_equal 1, success_counter.size
|
175
|
+
assert_equal 1, failure_counter.size
|
176
|
+
assert_equal false, client.locked?(TEST_KEY)
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_stale_lock_acquisition
|
180
|
+
success_counter = Queue.new
|
181
|
+
failure_counter = Queue.new
|
182
|
+
|
183
|
+
client = client(stale_lock_expiration: 0.5)
|
184
|
+
|
185
|
+
t1 = Thread.new { client.lock(TEST_KEY) { sleep 0.6; success_counter << 1 } }
|
186
|
+
sleep 0.55
|
187
|
+
t2 = Thread.new do
|
188
|
+
locked = client.lock(TEST_KEY) { success_counter << 1 }
|
189
|
+
failure_counter << 1 unless locked
|
190
|
+
end
|
191
|
+
|
192
|
+
[t1, t2].each(&:join)
|
193
|
+
|
194
|
+
assert_equal 2, success_counter.size
|
195
|
+
assert_equal 0, failure_counter.size
|
196
|
+
assert_equal false, client.locked?(TEST_KEY)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_refresh
|
200
|
+
client = client(stale_lock_expiration: 0.5)
|
201
|
+
|
202
|
+
lock1 = client.lock(TEST_KEY)
|
203
|
+
|
204
|
+
assert_equal true, client.locked?(TEST_KEY)
|
205
|
+
|
206
|
+
client.refresh(TEST_KEY, lock1)
|
207
|
+
|
208
|
+
assert_equal true, client.locked?(TEST_KEY)
|
209
|
+
|
210
|
+
sleep 0.55
|
211
|
+
|
212
|
+
assert_equal false, client.locked?(TEST_KEY)
|
213
|
+
|
214
|
+
lock2 = client.lock(TEST_KEY)
|
215
|
+
|
216
|
+
client.refresh(TEST_KEY, lock1)
|
217
|
+
|
218
|
+
assert_equal true, client.locked?(TEST_KEY)
|
219
|
+
|
220
|
+
client.unlock(TEST_KEY, lock1)
|
221
|
+
|
222
|
+
# edge case with refresh lock in the middle
|
223
|
+
assert_equal true, client.locked?(TEST_KEY)
|
224
|
+
|
225
|
+
client.clear(TEST_KEY)
|
226
|
+
|
227
|
+
assert_equal false, client.locked?(TEST_KEY)
|
228
|
+
|
229
|
+
client.refresh(TEST_KEY, lock2)
|
230
|
+
|
231
|
+
assert_equal true, client.locked?(TEST_KEY)
|
232
|
+
|
233
|
+
client.unlock(TEST_KEY, lock2)
|
234
|
+
|
235
|
+
# now finally unlocked
|
236
|
+
assert_equal false, client.locked?(TEST_KEY)
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_block_refresh
|
240
|
+
success_counter = Queue.new
|
241
|
+
failure_counter = Queue.new
|
242
|
+
|
243
|
+
client = client(stale_lock_expiration: 0.5)
|
244
|
+
|
245
|
+
t1 = Thread.new do
|
246
|
+
client.lock(TEST_KEY) do |token|
|
247
|
+
sleep 0.6
|
248
|
+
client.refresh(TEST_KEY, token)
|
249
|
+
sleep 1
|
250
|
+
success_counter << 1
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
t2 = Thread.new do
|
255
|
+
sleep 0.8
|
256
|
+
locked = client.lock(TEST_KEY) { success_counter << 1 }
|
257
|
+
failure_counter << 1 unless locked
|
258
|
+
end
|
259
|
+
|
260
|
+
[t1, t2].each(&:join)
|
261
|
+
|
262
|
+
assert_equal 1, success_counter.size
|
263
|
+
assert_equal 1, failure_counter.size
|
264
|
+
assert_equal false, client.locked?(TEST_KEY)
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_refresh_multi
|
268
|
+
success_counter = Queue.new
|
269
|
+
failure_counter = Queue.new
|
270
|
+
|
271
|
+
client = client(stale_lock_expiration: 0.5)
|
272
|
+
|
273
|
+
t1 = Thread.new do
|
274
|
+
client.lock(TEST_KEY, 2) do |token|
|
275
|
+
sleep 0.4
|
276
|
+
client.refresh(TEST_KEY, token)
|
277
|
+
success_counter << 1
|
278
|
+
sleep 0.5
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
t2 = Thread.new do
|
283
|
+
sleep 0.55
|
284
|
+
locked = client.lock(TEST_KEY, 2) do
|
285
|
+
success_counter << 1
|
286
|
+
sleep 0.5
|
287
|
+
end
|
288
|
+
|
289
|
+
failure_counter << 1 unless locked
|
290
|
+
end
|
291
|
+
|
292
|
+
t3 = Thread.new do
|
293
|
+
sleep 0.75
|
294
|
+
locked = client.lock(TEST_KEY, 2) { success_counter << 1 }
|
295
|
+
failure_counter << 1 unless locked
|
296
|
+
end
|
297
|
+
|
298
|
+
[t1, t2, t3].each(&:join)
|
299
|
+
|
300
|
+
assert_equal 2, success_counter.size
|
301
|
+
assert_equal 1, failure_counter.size
|
302
|
+
assert_equal false, client.locked?(TEST_KEY)
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_increment_reused_client
|
306
|
+
i = 0
|
307
|
+
|
308
|
+
threads = 2.times.map do
|
309
|
+
Thread.new do
|
310
|
+
@client.lock(TEST_KEY) { i += 1 }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
threads.each(&:join)
|
315
|
+
|
316
|
+
assert_equal 2, i
|
317
|
+
assert_equal false, client.locked?(TEST_KEY)
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_increment_new_client
|
321
|
+
i = 0
|
322
|
+
|
323
|
+
threads = 2.times.map do
|
324
|
+
Thread.new do
|
325
|
+
client.lock(TEST_KEY) { i += 1 }
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
threads.each(&:join)
|
330
|
+
|
331
|
+
assert_equal 2, i
|
332
|
+
assert_equal false, client.locked?(TEST_KEY)
|
133
333
|
end
|
134
334
|
end
|
135
335
|
|
@@ -142,6 +342,18 @@ class TestBaseClient < Minitest::Test
|
|
142
342
|
assert_raises(NotImplementedError) do
|
143
343
|
@client.send(:get, TEST_KEY)
|
144
344
|
end
|
345
|
+
|
346
|
+
assert_raises(NotImplementedError) do
|
347
|
+
@client.send(:set, TEST_KEY, "", "")
|
348
|
+
end
|
349
|
+
|
350
|
+
assert_raises(NotImplementedError) do
|
351
|
+
@client.send(:initial_set, TEST_KEY)
|
352
|
+
end
|
353
|
+
|
354
|
+
assert_raises(NotImplementedError) do
|
355
|
+
@client.send(:clear, TEST_KEY)
|
356
|
+
end
|
145
357
|
end
|
146
358
|
end
|
147
359
|
|
@@ -151,6 +363,7 @@ class TestMemcachedClient < Minitest::Test
|
|
151
363
|
def setup
|
152
364
|
@dalli = Dalli::Client.new("127.0.0.1:11211")
|
153
365
|
@client = Suo::Client::Memcached.new
|
366
|
+
teardown
|
154
367
|
end
|
155
368
|
|
156
369
|
def teardown
|
@@ -164,6 +377,7 @@ class TestRedisClient < Minitest::Test
|
|
164
377
|
def setup
|
165
378
|
@redis = Redis.new
|
166
379
|
@client = Suo::Client::Redis.new
|
380
|
+
teardown
|
167
381
|
end
|
168
382
|
|
169
383
|
def teardown
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Elser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dalli
|
@@ -108,7 +108,21 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 5.5.0
|
111
|
-
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: codeclimate-test-reporter
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.4.7
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.4.7
|
125
|
+
description: Distributed locks using Memcached or Redis.
|
112
126
|
email:
|
113
127
|
- nick.elser@gmail.com
|
114
128
|
executables:
|
@@ -159,7 +173,7 @@ rubyforge_project:
|
|
159
173
|
rubygems_version: 2.4.5
|
160
174
|
signing_key:
|
161
175
|
specification_version: 4
|
162
|
-
summary: Distributed
|
176
|
+
summary: Distributed locks using Memcached or Redis.
|
163
177
|
test_files:
|
164
178
|
- test/client_test.rb
|
165
179
|
- test/test_helper.rb
|