dalli 2.0.1 → 3.2.8

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +671 -0
  3. data/Gemfile +15 -3
  4. data/LICENSE +1 -1
  5. data/README.md +33 -148
  6. data/lib/dalli/cas/client.rb +3 -0
  7. data/lib/dalli/client.rb +293 -131
  8. data/lib/dalli/compressor.rb +40 -0
  9. data/lib/dalli/key_manager.rb +121 -0
  10. data/lib/dalli/options.rb +22 -4
  11. data/lib/dalli/pid_cache.rb +40 -0
  12. data/lib/dalli/pipelined_getter.rb +177 -0
  13. data/lib/dalli/protocol/base.rb +250 -0
  14. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  15. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  16. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  17. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  18. data/lib/dalli/protocol/binary.rb +173 -0
  19. data/lib/dalli/protocol/connection_manager.rb +255 -0
  20. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  21. data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
  22. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  23. data/lib/dalli/protocol/meta.rb +178 -0
  24. data/lib/dalli/protocol/response_buffer.rb +54 -0
  25. data/lib/dalli/protocol/server_config_parser.rb +86 -0
  26. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  27. data/lib/dalli/protocol/value_compressor.rb +85 -0
  28. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  29. data/lib/dalli/protocol/value_serializer.rb +91 -0
  30. data/lib/dalli/protocol.rb +19 -0
  31. data/lib/dalli/ring.rb +98 -50
  32. data/lib/dalli/server.rb +4 -524
  33. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  34. data/lib/dalli/socket.rb +154 -53
  35. data/lib/dalli/version.rb +5 -1
  36. data/lib/dalli.rb +49 -13
  37. data/lib/rack/session/dalli.rb +169 -26
  38. metadata +53 -88
  39. data/History.md +0 -262
  40. data/Performance.md +0 -42
  41. data/Rakefile +0 -39
  42. data/dalli.gemspec +0 -28
  43. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -76
  44. data/lib/active_support/cache/dalli_store.rb +0 -203
  45. data/test/abstract_unit.rb +0 -281
  46. data/test/benchmark_test.rb +0 -187
  47. data/test/helper.rb +0 -41
  48. data/test/memcached_mock.rb +0 -113
  49. data/test/test_active_support.rb +0 -163
  50. data/test/test_dalli.rb +0 -461
  51. data/test/test_encoding.rb +0 -43
  52. data/test/test_failover.rb +0 -107
  53. data/test/test_network.rb +0 -54
  54. data/test/test_ring.rb +0 -85
  55. data/test/test_sasl.rb +0 -83
  56. data/test/test_session_store.rb +0 -224
data/test/test_dalli.rb DELETED
@@ -1,461 +0,0 @@
1
- require 'helper'
2
- require 'memcached_mock'
3
-
4
- describe 'Dalli' do
5
- describe 'options parsing' do
6
- should 'handle deprecated options' do
7
- dc = Dalli::Client.new('foo', :compression => true)
8
- assert dc.instance_variable_get(:@options)[:compress]
9
- end
10
- end
11
-
12
- describe 'key validation' do
13
- should 'not allow whitespace' do
14
- dc = Dalli::Client.new
15
- assert_raises ArgumentError do
16
- dc.set ' ', 1
17
- end
18
- assert_raises ArgumentError do
19
- dc.set "\t", 1
20
- end
21
- assert_raises ArgumentError do
22
- dc.set "\n", 1
23
- end
24
- assert_raises ArgumentError do
25
- dc.set "", 1
26
- end
27
- assert_raises ArgumentError do
28
- dc.set nil, 1
29
- end
30
- end
31
- end
32
-
33
- should "default to localhost:11211" do
34
- dc = Dalli::Client.new
35
- ring = dc.send(:ring)
36
- s1 = ring.servers.first.hostname
37
- assert_equal 1, ring.servers.size
38
- dc.close
39
-
40
- dc = Dalli::Client.new('localhost:11211')
41
- ring = dc.send(:ring)
42
- s2 = ring.servers.first.hostname
43
- assert_equal 1, ring.servers.size
44
- dc.close
45
-
46
- dc = Dalli::Client.new(['localhost:11211'])
47
- ring = dc.send(:ring)
48
- s3 = ring.servers.first.hostname
49
- assert_equal 1, ring.servers.size
50
- dc.close
51
-
52
- assert_equal '127.0.0.1', s1
53
- assert_equal s2, s3
54
- end
55
-
56
- context 'using a live server' do
57
-
58
- should "support get/set" do
59
- memcached do |dc|
60
- dc.flush
61
-
62
- val1 = "1234567890"*105000
63
- assert_error Dalli::DalliError, /too large/ do
64
- dc.set('a', val1)
65
- val2 = dc.get('a')
66
- assert_equal val1, val2
67
- end
68
-
69
- val1 = "1234567890"*100000
70
- dc.set('a', val1)
71
- val2 = dc.get('a')
72
- assert_equal val1, val2
73
-
74
- assert_equal true, dc.set('a', nil)
75
- assert_nil dc.get('a')
76
- end
77
- end
78
-
79
- should "support stats" do
80
- memcached do |dc|
81
- # make sure that get_hits would not equal 0
82
- dc.get(:a)
83
-
84
- stats = dc.stats
85
- servers = stats.keys
86
- assert(servers.any? do |s|
87
- stats[s]["get_hits"].to_i != 0
88
- end)
89
-
90
- # reset_stats test
91
- results = dc.reset_stats
92
- assert(results.all? { |x| x })
93
- stats = dc.stats
94
- servers = stats.keys
95
-
96
- # check if reset was performed
97
- servers.each do |s|
98
- assert_equal 0, dc.stats[s]["get_hits"].to_i
99
- end
100
- end
101
- end
102
-
103
- should "support the fetch operation" do
104
- memcached do |dc|
105
- dc.flush
106
-
107
- expected = { 'blah' => 'blerg!' }
108
- executed = false
109
- value = dc.fetch('fetch_key') do
110
- executed = true
111
- expected
112
- end
113
- assert_equal expected, value
114
- assert_equal true, executed
115
-
116
- executed = false
117
- value = dc.fetch('fetch_key') do
118
- executed = true
119
- expected
120
- end
121
- assert_equal expected, value
122
- assert_equal false, executed
123
- end
124
- end
125
-
126
- should "support the cas operation" do
127
- memcached do |dc|
128
- dc.flush
129
-
130
- expected = { 'blah' => 'blerg!' }
131
-
132
- resp = dc.cas('cas_key') do |value|
133
- fail('Value should not exist')
134
- end
135
- assert_nil resp
136
-
137
- mutated = { 'blah' => 'foo!' }
138
- dc.set('cas_key', expected)
139
- resp = dc.cas('cas_key') do |value|
140
- assert_equal expected, value
141
- mutated
142
- end
143
- assert_equal true, resp
144
-
145
- resp = dc.get('cas_key')
146
- assert_equal mutated, resp
147
- end
148
- end
149
-
150
- should "support multi-get" do
151
- memcached do |dc|
152
- dc.close
153
- dc.flush
154
- resp = dc.get_multi(%w(a b c d e f))
155
- assert_equal({}, resp)
156
-
157
- dc.set('a', 'foo')
158
- dc.set('b', 123)
159
- dc.set('c', %w(a b c))
160
- resp = dc.get_multi(%w(a b c d e f))
161
- assert_equal({ 'a' => 'foo', 'b' => 123, 'c' => %w(a b c) }, resp)
162
-
163
- # Perform a huge multi-get with 10,000 elements.
164
- arr = []
165
- dc.multi do
166
- 10_000.times do |idx|
167
- dc.set idx, idx
168
- arr << idx
169
- end
170
- end
171
-
172
- result = dc.get_multi(arr)
173
- assert_equal(10_000, result.size)
174
- assert_equal(1000, result['1000'])
175
- end
176
- end
177
-
178
- should 'support raw incr/decr' do
179
- memcached do |client|
180
- client.flush
181
-
182
- assert_equal true, client.set('fakecounter', 0, 0, :raw => true)
183
- assert_equal 1, client.incr('fakecounter', 1)
184
- assert_equal 2, client.incr('fakecounter', 1)
185
- assert_equal 3, client.incr('fakecounter', 1)
186
- assert_equal 1, client.decr('fakecounter', 2)
187
- assert_equal "1", client.get('fakecounter', :raw => true)
188
-
189
- resp = client.incr('mycounter', 0)
190
- assert_nil resp
191
-
192
- resp = client.incr('mycounter', 1, 0, 2)
193
- assert_equal 2, resp
194
- resp = client.incr('mycounter', 1)
195
- assert_equal 3, resp
196
-
197
- resp = client.set('rawcounter', 10, 0, :raw => true)
198
- assert_equal true, resp
199
-
200
- resp = client.get('rawcounter', :raw => true)
201
- assert_equal '10', resp
202
-
203
- resp = client.incr('rawcounter', 1)
204
- assert_equal 11, resp
205
- end
206
- end
207
-
208
- should "support incr/decr operations" do
209
- memcached do |dc|
210
- dc.flush
211
-
212
- resp = dc.decr('counter', 100, 5, 0)
213
- assert_equal 0, resp
214
-
215
- resp = dc.decr('counter', 10)
216
- assert_equal 0, resp
217
-
218
- resp = dc.incr('counter', 10)
219
- assert_equal 10, resp
220
-
221
- current = 10
222
- 100.times do |x|
223
- resp = dc.incr('counter', 10)
224
- assert_equal current + ((x+1)*10), resp
225
- end
226
-
227
- resp = dc.decr('10billion', 0, 5, 10)
228
- # go over the 32-bit mark to verify proper (un)packing
229
- resp = dc.incr('10billion', 10_000_000_000)
230
- assert_equal 10_000_000_010, resp
231
-
232
- resp = dc.decr('10billion', 1)
233
- assert_equal 10_000_000_009, resp
234
-
235
- resp = dc.decr('10billion', 0)
236
- assert_equal 10_000_000_009, resp
237
-
238
- resp = dc.incr('10billion', 0)
239
- assert_equal 10_000_000_009, resp
240
-
241
- assert_nil dc.incr('DNE', 10)
242
- assert_nil dc.decr('DNE', 10)
243
-
244
- resp = dc.incr('big', 100, 5, 0xFFFFFFFFFFFFFFFE)
245
- assert_equal 0xFFFFFFFFFFFFFFFE, resp
246
- resp = dc.incr('big', 1)
247
- assert_equal 0xFFFFFFFFFFFFFFFF, resp
248
-
249
- # rollover the 64-bit value, we'll get something undefined.
250
- resp = dc.incr('big', 1)
251
- 0x10000000000000000.wont_equal resp
252
- dc.reset
253
- end
254
- end
255
-
256
- should 'support the append and prepend operations' do
257
- memcached do |dc|
258
- resp = dc.flush
259
- assert_equal true, dc.set('456', 'xyz', 0, :raw => true)
260
- assert_equal true, dc.prepend('456', '0')
261
- assert_equal true, dc.append('456', '9')
262
- assert_equal '0xyz9', dc.get('456', :raw => true)
263
- assert_equal '0xyz9', dc.get('456')
264
-
265
- assert_equal false, dc.append('nonexist', 'abc')
266
- assert_equal false, dc.prepend('nonexist', 'abc')
267
- end
268
- end
269
-
270
- should 'allow TCP connections to be configured for keepalive' do
271
- memcached(19122, '', :keepalive => true) do |dc|
272
- dc.set(:a, 1)
273
- ring = dc.send(:ring)
274
- server = ring.servers.first
275
- socket = server.instance_variable_get('@sock')
276
-
277
- optval = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE)
278
- optval = optval.unpack 'i'
279
-
280
- assert_equal true, (optval[0] != 0)
281
- end
282
- end
283
-
284
- should "pass a simple smoke test" do
285
- memcached do |dc|
286
- resp = dc.flush
287
- resp.wont_be_nil
288
- assert_equal [true, true], resp
289
-
290
- assert_equal true, dc.set(:foo, 'bar')
291
- assert_equal 'bar', dc.get(:foo)
292
-
293
- resp = dc.get('123')
294
- assert_equal nil, resp
295
-
296
- resp = dc.set('123', 'xyz')
297
- assert_equal true, resp
298
-
299
- resp = dc.get('123')
300
- assert_equal 'xyz', resp
301
-
302
- resp = dc.set('123', 'abc')
303
- assert_equal true, resp
304
-
305
- dc.prepend('123', '0')
306
- dc.append('123', '0')
307
-
308
- assert_raises Dalli::DalliError do
309
- resp = dc.get('123')
310
- end
311
-
312
- dc.close
313
- dc = nil
314
-
315
- dc = Dalli::Client.new('localhost:19122')
316
-
317
- resp = dc.set('456', 'xyz', 0, :raw => true)
318
- assert_equal true, resp
319
-
320
- resp = dc.prepend '456', '0'
321
- assert_equal true, resp
322
-
323
- resp = dc.append '456', '9'
324
- assert_equal true, resp
325
-
326
- resp = dc.get('456', :raw => true)
327
- assert_equal '0xyz9', resp
328
-
329
- resp = dc.stats
330
- assert_equal Hash, resp.class
331
-
332
- dc.close
333
- end
334
- end
335
-
336
- should "support multithreaded access" do
337
- memcached do |cache|
338
- cache.flush
339
- workers = []
340
-
341
- cache.set('f', 'zzz')
342
- assert_equal true, (cache.cas('f') do |value|
343
- value << 'z'
344
- end)
345
- assert_equal 'zzzz', cache.get('f')
346
-
347
- # Have a bunch of threads perform a bunch of operations at the same time.
348
- # Verify the result of each operation to ensure the request and response
349
- # are not intermingled between threads.
350
- 10.times do
351
- workers << Thread.new do
352
- 100.times do
353
- cache.set('a', 9)
354
- cache.set('b', 11)
355
- inc = cache.incr('cat', 10, 0, 10)
356
- cache.set('f', 'zzz')
357
- wont_be_nil(cache.cas('f') do |value|
358
- value << 'z'
359
- end)
360
- assert_equal false, cache.add('a', 11)
361
- assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
362
- inc = cache.incr('cat', 10)
363
- assert_equal 0, inc % 5
364
- dec = cache.decr('cat', 5)
365
- assert_equal 11, cache.get('b')
366
- end
367
- end
368
- end
369
-
370
- workers.each { |w| w.join }
371
- cache.flush
372
- end
373
- end
374
-
375
- should "handle namespaced keys" do
376
- memcached do |dc|
377
- dc = Dalli::Client.new('localhost:19122', :namespace => 'a')
378
- dc.set('namespaced', 1)
379
- dc2 = Dalli::Client.new('localhost:19122', :namespace => 'b')
380
- dc2.set('namespaced', 2)
381
- assert_equal 1, dc.get('namespaced')
382
- assert_equal 2, dc2.get('namespaced')
383
- end
384
- end
385
-
386
- should "handle namespaced keys in multi_get" do
387
- memcached do |dc|
388
- dc = Dalli::Client.new('localhost:19122', :namespace => 'a')
389
- dc.set('a', 1)
390
- dc.set('b', 2)
391
- assert_equal({'a' => 1, 'b' => 2}, dc.get_multi('a', 'b'))
392
- end
393
- end
394
-
395
- should "handle application marshalling issues" do
396
- memcached do |dc|
397
- old = Dalli.logger
398
- Dalli.logger = Logger.new(nil)
399
- begin
400
- assert_equal false, dc.set('a', Proc.new { true })
401
- ensure
402
- Dalli.logger = old
403
- end
404
- end
405
- end
406
-
407
- context 'with compression' do
408
- should 'allow large values' do
409
- memcached do |dc|
410
- dalli = Dalli::Client.new(dc.instance_variable_get(:@servers), :compress => true)
411
-
412
- value = "0"*1024*1024
413
- assert_raises Dalli::DalliError, /too large/ do
414
- dc.set('verylarge', value)
415
- end
416
- dalli.set('verylarge', value)
417
- end
418
- end
419
- end
420
-
421
- context 'in low memory conditions' do
422
-
423
- should 'handle error response correctly' do
424
- memcached(19125, '-m 1 -M') do |dc|
425
- failed = false
426
- value = "1234567890"*100
427
- 1_000.times do |idx|
428
- begin
429
- assert_equal true, dc.set(idx, value)
430
- rescue Dalli::DalliError
431
- failed = true
432
- assert((800..960).include?(idx), "unexpected failure on iteration #{idx}")
433
- break
434
- end
435
- end
436
- assert failed, 'did not fail under low memory conditions'
437
- end
438
- end
439
-
440
- should 'fit more values with compression' do
441
- memcached(19126, '-m 1 -M') do |dc|
442
- dalli = Dalli::Client.new('localhost:19126', :compress => true)
443
- failed = false
444
- value = "1234567890"*1000
445
- 10_000.times do |idx|
446
- begin
447
- assert_equal true, dalli.set(idx, value)
448
- rescue Dalli::DalliError
449
- failed = true
450
- assert((6000..7800).include?(idx), "unexpected failure on iteration #{idx}")
451
- break
452
- end
453
- end
454
- assert failed, 'did not fail under low memory conditions'
455
- end
456
- end
457
-
458
- end
459
-
460
- end
461
- end
@@ -1,43 +0,0 @@
1
- # encoding: utf-8
2
- require 'helper'
3
- require 'memcached_mock'
4
-
5
- describe 'Encoding' do
6
-
7
- context 'using a live server' do
8
- should 'support i18n content' do
9
- memcached do |dc|
10
- key = 'foo'
11
- bad_key = utf8 = 'ƒ©åÍÎ'
12
-
13
- assert dc.set(key, utf8)
14
- assert_equal utf8, dc.get(key)
15
-
16
- # keys must be ASCII
17
- assert_raises ArgumentError, /illegal character/ do
18
- dc.set(bad_key, utf8)
19
- end
20
- end
21
- end
22
-
23
- should 'support content expiry' do
24
- memcached do |dc|
25
- key = 'foo'
26
- assert dc.set(key, 'bar', 1)
27
- assert_equal 'bar', dc.get(key)
28
- sleep 2
29
- assert_equal nil, dc.get(key)
30
- end
31
- end
32
-
33
- should 'not allow non-ASCII keys' do
34
- memcached do |dc|
35
- key = 'fooƒ'
36
- assert_raises ArgumentError do
37
- dc.set(key, 'bar')
38
- end
39
- end
40
- end
41
-
42
- end
43
- end
@@ -1,107 +0,0 @@
1
- require 'helper'
2
-
3
- describe 'FailOver' do
4
- context 'assuming some bad servers' do
5
-
6
- should 'silently reconnect if server hiccups' do
7
- memcached(29125) do
8
- dc = Dalli::Client.new ['localhost:29125']
9
- dc.set 'foo', 'bar'
10
- foo = dc.get 'foo'
11
- assert_equal foo, 'bar'
12
-
13
- memcached_kill(29125)
14
- memcached(29125) do
15
-
16
- foo = dc.get 'foo'
17
- assert_nil foo
18
-
19
- memcached_kill(29125)
20
- end
21
- end
22
- end
23
-
24
- should 'handle graceful failover' do
25
- memcached(29125) do
26
- memcached(29126) do
27
- dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
28
- dc.set 'foo', 'bar'
29
- foo = dc.get 'foo'
30
- assert_equal foo, 'bar'
31
-
32
- memcached_kill(29125)
33
-
34
- dc.set 'foo', 'bar'
35
- foo = dc.get 'foo'
36
- assert_equal foo, 'bar'
37
-
38
- memcached_kill(29126)
39
-
40
- assert_raises Dalli::RingError, :message => "No server available" do
41
- dc.set 'foo', 'bar'
42
- end
43
- end
44
- end
45
- end
46
-
47
- should 'handle them gracefully in get_multi' do
48
- memcached(29125) do
49
- memcached(29126) do
50
- dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
51
- dc.set 'a', 'a1'
52
- result = dc.get_multi ['a']
53
- assert_equal result, {'a' => 'a1'}
54
-
55
- memcached_kill(29125)
56
-
57
- result = dc.get_multi ['a']
58
- assert_equal result, {'a' => 'a1'}
59
- end
60
- end
61
- end
62
-
63
- should 'handle graceful failover in get_multi' do
64
- memcached(29125) do
65
- memcached(29126) do
66
- dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
67
- dc.set 'foo', 'foo1'
68
- dc.set 'bar', 'bar1'
69
- result = dc.get_multi ['foo', 'bar']
70
- assert_equal result, {'foo' => 'foo1', 'bar' => 'bar1'}
71
-
72
- memcached_kill(29125)
73
-
74
- dc.set 'foo', 'foo1'
75
- dc.set 'bar', 'bar1'
76
- result = dc.get_multi ['foo', 'bar']
77
- assert_equal result, {'foo' => 'foo1', 'bar' => 'bar1'}
78
-
79
- memcached_kill(29126)
80
-
81
- result = dc.get_multi ['foo', 'bar']
82
- assert_equal result, {}
83
- end
84
- end
85
- end
86
-
87
- should 'stats should still properly report' do
88
- memcached(29125) do
89
- memcached(29126) do
90
- dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
91
- result = dc.stats
92
- assert_instance_of Hash, result['localhost:29125']
93
- assert_instance_of Hash, result['localhost:29126']
94
-
95
- memcached_kill(29125)
96
-
97
- dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
98
- result = dc.stats
99
- assert_instance_of NilClass, result['localhost:29125']
100
- assert_instance_of Hash, result['localhost:29126']
101
-
102
- memcached_kill(29126)
103
- end
104
- end
105
- end
106
- end
107
- end
data/test/test_network.rb DELETED
@@ -1,54 +0,0 @@
1
- require 'helper'
2
-
3
- describe 'Network' do
4
-
5
- context 'assuming a bad network' do
6
-
7
- should 'handle no server available' do
8
- assert_raises Dalli::RingError, :message => "No server available" do
9
- dc = Dalli::Client.new 'localhost:19333'
10
- dc.get 'foo'
11
- end
12
- end
13
-
14
- context 'with a fake server' do
15
- should 'handle connection reset' do
16
- memcached_mock(lambda {|sock| sock.close }) do
17
- assert_raises Dalli::RingError, :message => "No server available" do
18
- dc = Dalli::Client.new('localhost:19123')
19
- dc.get('abc')
20
- end
21
- end
22
- end
23
-
24
- should 'handle malformed response' do
25
- memcached_mock(lambda {|sock| sock.write('123') }) do
26
- assert_raises Dalli::RingError, :message => "No server available" do
27
- dc = Dalli::Client.new('localhost:19123')
28
- dc.get('abc')
29
- end
30
- end
31
- end
32
-
33
- should 'handle connect timeouts' do
34
- memcached_mock(lambda {|sock| sleep(0.6); sock.close }, :delayed_start) do
35
- assert_raises Dalli::RingError, :message => "No server available" do
36
- dc = Dalli::Client.new('localhost:19123')
37
- dc.get('abc')
38
- end
39
- end
40
- end
41
-
42
- should 'handle read timeouts' do
43
- memcached_mock(lambda {|sock| sleep(0.6); sock.write('giraffe') }) do
44
- assert_raises Dalli::RingError, :message => "No server available" do
45
- dc = Dalli::Client.new('localhost:19123')
46
- dc.get('abc')
47
- end
48
- end
49
- end
50
-
51
- end
52
-
53
- end
54
- end