arunthampi-memcached 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+
2
+ unless defined? UNIX_SOCKET_NAME
3
+ HERE = File.dirname(__FILE__)
4
+ UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached')
5
+
6
+ # Kill memcached
7
+ system("killall -9 memcached")
8
+
9
+ # Start memcached
10
+ verbosity = (ENV['DEBUG'] ? "-vv" : "")
11
+ log = "/tmp/memcached.log"
12
+ system ">#{log}"
13
+
14
+ # TCP memcached
15
+ (43042..43046).each do |port|
16
+ cmd = "memcached #{verbosity} -U 0 -p #{port} >> #{log} 2>&1 &"
17
+ raise "'#{cmd}' failed to start" unless system(cmd)
18
+ end
19
+ # UDP memcached
20
+ (43052..43053).each do |port|
21
+ cmd = "memcached #{verbosity} -U #{port} -p 0 >> #{log} 2>&1 &"
22
+ raise "'#{cmd}' failed to start" unless system(cmd)
23
+ end
24
+ # Domain socket memcached
25
+ (0..1).each do |i|
26
+ cmd = "memcached -M -s #{UNIX_SOCKET_NAME}#{i} #{verbosity} >> #{log} 2>&1 &"
27
+ raise "'#{cmd}' failed to start" unless system(cmd)
28
+ end
29
+ end
File without changes
@@ -0,0 +1,18 @@
1
+
2
+ $LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
3
+
4
+ require 'rubygems'
5
+ require 'mocha'
6
+
7
+ if ENV['DEBUG']
8
+ require 'ruby-debug'
9
+ end
10
+
11
+ require 'memcached'
12
+ require 'test/unit'
13
+ require 'ostruct'
14
+
15
+ UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached') unless defined? UNIX_SOCKET_NAME
16
+
17
+ class GenericClass
18
+ end
@@ -0,0 +1,8 @@
1
+
2
+ require "#{File.dirname(__FILE__)}/../test_helper"
3
+
4
+ class BindingTest < Test::Unit::TestCase
5
+ def test_libmemcached_loaded
6
+ assert_nothing_raised { Rlibmemcached }
7
+ end
8
+ end
@@ -0,0 +1,1132 @@
1
+
2
+ require "#{File.dirname(__FILE__)}/../test_helper"
3
+ require 'socket'
4
+ require 'mocha'
5
+ require 'benchmark'
6
+
7
+ class MemcachedTest < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @servers = ['localhost:43042', 'localhost:43043', "#{UNIX_SOCKET_NAME}0"]
11
+ @udp_servers = ['localhost:43052', 'localhost:43053']
12
+
13
+ # Maximum allowed prefix key size for :hash_with_prefix_key_key => false
14
+ @prefix_key = 'prefix_key_'
15
+
16
+ @options = {
17
+ :chunk_split_size => 1048300,
18
+ :prefix_key => @prefix_key,
19
+ :hash => :default,
20
+ :distribution => :modula}
21
+ @cache = Memcached.new(@servers, @options)
22
+
23
+ @binary_protocol_options = {
24
+ :prefix_key => @prefix_key,
25
+ :hash => :default,
26
+ :distribution => :modula,
27
+ :binary_protocol => true}
28
+ @binary_protocol_cache = Memcached.new(@servers, @binary_protocol_options)
29
+
30
+ @udp_options = {
31
+ :prefix_key => @prefix_key,
32
+ :hash => :default,
33
+ :use_udp => true,
34
+ :distribution => :modula}
35
+ @udp_cache = Memcached.new(@udp_servers, @udp_options)
36
+
37
+ @noblock_options = {
38
+ :prefix_key => @prefix_key,
39
+ :no_block => true,
40
+ :buffer_requests => true,
41
+ :hash => :default}
42
+ @noblock_cache = Memcached.new(@servers, @noblock_options)
43
+
44
+ @value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
45
+ @marshalled_value = Marshal.dump(@value)
46
+
47
+ @large_value = 'a' * 1048576 + 'b' * 10 # 1MB of 'a' + 10 bytes of 'b'
48
+ @large_marshalled_value = Marshal.dump(@large_value)
49
+ end
50
+
51
+ # Initialize
52
+
53
+ def test_initialize
54
+ cache = Memcached.new @servers, :prefix_key => 'test'
55
+ assert_equal 'test', cache.options[:prefix_key]
56
+ assert_equal 3, cache.send(:server_structs).size
57
+ assert_equal 'localhost', cache.send(:server_structs).first.hostname
58
+ assert_equal 43042, cache.send(:server_structs).first.port
59
+ end
60
+
61
+ def test_initialize_with_ip_addresses
62
+ cache = Memcached.new ['127.0.0.1:43042', '127.0.0.1:43043']
63
+ assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
64
+ assert_equal '127.0.0.1', cache.send(:server_structs).last.hostname
65
+ end
66
+
67
+ def test_initialize_without_port
68
+ cache = Memcached.new ['localhost']
69
+ assert_equal 'localhost', cache.send(:server_structs).first.hostname
70
+ assert_equal 11211, cache.send(:server_structs).first.port
71
+ end
72
+
73
+ def test_initialize_with_ports_and_weights
74
+ cache = Memcached.new ['localhost:43042:2', 'localhost:43043:10']
75
+ assert_equal 2, cache.send(:server_structs).first.weight
76
+ assert_equal 43043, cache.send(:server_structs).last.port
77
+ assert_equal 10, cache.send(:server_structs).last.weight
78
+ end
79
+
80
+ def test_initialize_with_hostname_only
81
+ addresses = (1..8).map { |i| "app-cache-%02d" % i }
82
+ cache = Memcached.new(addresses)
83
+ addresses.each_with_index do |address, index|
84
+ assert_equal address, cache.send(:server_structs)[index].hostname
85
+ assert_equal 11211, cache.send(:server_structs)[index].port
86
+ end
87
+ end
88
+
89
+ def test_initialize_with_ip_address_and_options
90
+ cache = Memcached.new '127.0.0.1:43042', :ketama_weighted => false
91
+ assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
92
+ assert_equal false, cache.options[:ketama_weighted]
93
+ end
94
+
95
+ def test_options_are_set
96
+ Memcached::DEFAULTS.merge(@noblock_options).each do |key, expected|
97
+ value = @noblock_cache.options[key]
98
+ unless key == :rcv_timeout or key == :poll_timeout
99
+ assert(expected == value, "#{key} should be #{expected} but was #{value}")
100
+ end
101
+ end
102
+ end
103
+
104
+ def test_options_are_frozen
105
+ assert_raise(TypeError, RuntimeError) do
106
+ @cache.options[:no_block] = true
107
+ end
108
+ end
109
+
110
+ def test_behaviors_are_set
111
+ Memcached::BEHAVIORS.keys.each do |key, value|
112
+ assert_not_nil @cache.send(:get_behavior, key)
113
+ end
114
+ end
115
+
116
+ def test_initialize_with_invalid_server_strings
117
+ assert_raise(ArgumentError) { Memcached.new ":43042" }
118
+ assert_raise(ArgumentError) { Memcached.new "localhost:memcached" }
119
+ assert_raise(ArgumentError) { Memcached.new "local host:43043:1" }
120
+ end
121
+
122
+ def test_initialize_with_invalid_options
123
+ assert_raise(ArgumentError) do
124
+ Memcached.new @servers, :sort_hosts => true, :distribution => :consistent
125
+ end
126
+ end
127
+
128
+ def test_initialize_with_invalid_prefix_key
129
+ assert_raise(ArgumentError) do
130
+ Memcached.new @servers, :prefix_key => "x" * 128
131
+ end
132
+ end
133
+
134
+ def test_initialize_without_prefix_key
135
+ cache = Memcached.new @servers
136
+ assert_equal nil, cache.options[:prefix_key]
137
+ assert_equal 3, cache.send(:server_structs).size
138
+ end
139
+
140
+ def test_initialize_negative_behavior
141
+ cache = Memcached.new @servers,
142
+ :buffer_requests => false
143
+ assert_nothing_raised do
144
+ cache.set key, @value
145
+ end
146
+ end
147
+
148
+ def test_initialize_without_backtraces
149
+ cache = Memcached.new @servers,
150
+ :show_backtraces => false
151
+ cache.delete key rescue
152
+ begin
153
+ cache.get key
154
+ rescue Memcached::NotFound => e
155
+ assert e.backtrace.empty?
156
+ end
157
+ begin
158
+ cache.append key, @value
159
+ rescue Memcached::NotStored => e
160
+ assert e.backtrace.empty?
161
+ end
162
+ end
163
+
164
+ def test_initialize_with_backtraces
165
+ cache = Memcached.new @servers,
166
+ :show_backtraces => true
167
+ cache.delete key rescue
168
+ begin
169
+ cache.get key
170
+ rescue Memcached::NotFound => e
171
+ assert !e.backtrace.empty?
172
+ end
173
+ end
174
+
175
+ def test_initialize_sort_hosts
176
+ # Original
177
+ cache = Memcached.new(@servers.sort,
178
+ :sort_hosts => false,
179
+ :distribution => :modula
180
+ )
181
+ assert_equal @servers.sort,
182
+ cache.servers
183
+
184
+ # Original with sort_hosts
185
+ cache = Memcached.new(@servers.sort,
186
+ :sort_hosts => true,
187
+ :distribution => :modula
188
+ )
189
+ assert_equal @servers.sort,
190
+ cache.servers
191
+
192
+ # Reversed
193
+ cache = Memcached.new(@servers.sort.reverse,
194
+ :sort_hosts => false,
195
+ :distribution => :modula
196
+ )
197
+ assert_equal @servers.sort.reverse,
198
+ cache.servers
199
+
200
+ # Reversed with sort_hosts
201
+ cache = Memcached.new(@servers.sort.reverse,
202
+ :sort_hosts => true,
203
+ :distribution => :modula
204
+ )
205
+ assert_equal @servers.sort,
206
+ cache.servers
207
+ end
208
+
209
+ def test_initialize_single_server
210
+ cache = Memcached.new 'localhost:43042'
211
+ assert_equal nil, cache.options[:prefix_key]
212
+ assert_equal 1, cache.send(:server_structs).size
213
+ end
214
+
215
+ def test_initialize_strange_argument
216
+ assert_raise(ArgumentError) { Memcached.new 1 }
217
+ end
218
+
219
+ # Get
220
+
221
+ def test_get
222
+ @cache.set key, @value
223
+ result = @cache.get key
224
+ assert_equal @value, result
225
+
226
+ @binary_protocol_cache.set key, @value
227
+ result = @binary_protocol_cache.get key
228
+ assert_equal @value, result
229
+
230
+ @udp_cache.set(key, @value)
231
+ assert_raises(Memcached::ActionNotSupported) do
232
+ @udp_cache.get(key)
233
+ end
234
+ end
235
+
236
+ def test_get_nil
237
+ @cache.set key, nil, 0
238
+ result = @cache.get key
239
+ assert_equal nil, result
240
+ end
241
+
242
+ def test_get_missing
243
+ @cache.delete key rescue nil
244
+ assert_raise(Memcached::NotFound) do
245
+ result = @cache.get key
246
+ end
247
+ end
248
+
249
+ def test_get_with_server_timeout
250
+ socket = stub_server 43047
251
+ cache = Memcached.new("localhost:43047:1", :timeout => 0.5)
252
+ assert 0.49 < (Benchmark.measure do
253
+ assert_raise(Memcached::ATimeoutOccurred) do
254
+ result = cache.get key
255
+ end
256
+ end).real
257
+
258
+ cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.001, :rcv_timeout => 0.5)
259
+ assert 0.49 < (Benchmark.measure do
260
+ assert_raise(Memcached::ATimeoutOccurred) do
261
+ result = cache.get key
262
+ end
263
+ end).real
264
+
265
+ cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.25, :rcv_timeout => 0.25)
266
+ assert 0.51 > (Benchmark.measure do
267
+ assert_raise(Memcached::ATimeoutOccurred) do
268
+ result = cache.get key
269
+ end
270
+ end).real
271
+
272
+ socket.close
273
+ end
274
+
275
+ def test_get_with_no_block_server_timeout
276
+ socket = stub_server 43048
277
+ cache = Memcached.new("localhost:43048:1", :no_block => true, :timeout => 0.25)
278
+ assert 0.24 < (Benchmark.measure do
279
+ assert_raise(Memcached::ATimeoutOccurred) do
280
+ result = cache.get key
281
+ end
282
+ end).real
283
+
284
+ cache = Memcached.new("localhost:43048:1", :no_block => true, :poll_timeout => 0.25, :rcv_timeout => 0.001)
285
+ assert 0.24 < (Benchmark.measure do
286
+ assert_raise(Memcached::ATimeoutOccurred) do
287
+ result = cache.get key
288
+ end
289
+ end).real
290
+
291
+ cache = Memcached.new("localhost:43048:1", :no_block => true,
292
+ :poll_timeout => 0.001,
293
+ :rcv_timeout => 0.25 # No affect in no-block mode
294
+ )
295
+ assert 0.24 > (Benchmark.measure do
296
+ assert_raise(Memcached::ATimeoutOccurred) do
297
+ result = cache.get key
298
+ end
299
+ end).real
300
+
301
+ socket.close
302
+ end
303
+
304
+ def test_get_with_prefix_key
305
+ # Prefix_key
306
+ cache = Memcached.new(
307
+ # We can only use one server because the key is hashed separately from the prefix key
308
+ @servers.first,
309
+ :prefix_key => @prefix_key,
310
+ :hash => :default,
311
+ :distribution => :modula
312
+ )
313
+ cache.set key, @value
314
+ assert_equal @value, cache.get(key)
315
+
316
+ # No prefix_key specified
317
+ cache = Memcached.new(
318
+ @servers.first,
319
+ :hash => :default,
320
+ :distribution => :modula
321
+ )
322
+ assert_nothing_raised do
323
+ assert_equal @value, cache.get("#{@prefix_key}#{key}")
324
+ end
325
+ end
326
+
327
+ def test_values_with_null_characters_are_not_truncated
328
+ value = OpenStruct.new(:a => Object.new) # Marshals with a null \000
329
+ @cache.set key, value
330
+ result = @cache.get key, false
331
+ non_wrapped_result = Rlibmemcached.memcached_get(
332
+ @cache.instance_variable_get("@struct"),
333
+ key
334
+ ).first
335
+ assert result.size > non_wrapped_result.size
336
+ end
337
+
338
+ def test_get_multi
339
+ @cache.set "#{key}_1", 1
340
+ @cache.set "#{key}_2", 2
341
+ assert_equal({"#{key}_1" => 1, "#{key}_2" => 2},
342
+ @cache.get(["#{key}_1", "#{key}_2"]))
343
+ end
344
+
345
+ def test_get_multi_missing
346
+ @cache.set "#{key}_1", 1
347
+ @cache.delete "#{key}_2" rescue nil
348
+ @cache.set "#{key}_3", 3
349
+ @cache.delete "#{key}_4" rescue nil
350
+ assert_equal(
351
+ {"test_get_multi_missing_3"=>3, "test_get_multi_missing_1"=>1},
352
+ @cache.get(["#{key}_1", "#{key}_2", "#{key}_3", "#{key}_4"])
353
+ )
354
+ end
355
+
356
+ def test_get_multi_completely_missing
357
+ @cache.delete "#{key}_1" rescue nil
358
+ @cache.delete "#{key}_2" rescue nil
359
+ assert_equal(
360
+ {},
361
+ @cache.get(["#{key}_1", "#{key}_2"])
362
+ )
363
+ end
364
+
365
+ def test_get_multi_checks_types
366
+ assert_raises(TypeError, ArgumentError) do
367
+ @cache.get([nil])
368
+ end
369
+ end
370
+
371
+ def test_set_and_get_unmarshalled
372
+ @cache.set key, @value
373
+ result = @cache.get key, false
374
+ assert_equal @marshalled_value, result
375
+ end
376
+
377
+ def test_get_multi_unmarshalled
378
+ @cache.set "#{key}_1", 1, 0, false
379
+ @cache.set "#{key}_2", 2, 0, false
380
+ assert_equal(
381
+ {"#{key}_1" => "1", "#{key}_2" => "2"},
382
+ @cache.get(["#{key}_1", "#{key}_2"], false)
383
+ )
384
+ end
385
+
386
+ def test_get_multi_mixed_marshalling
387
+ @cache.set "#{key}_1", 1
388
+ @cache.set "#{key}_2", 2, 0, false
389
+ assert_nothing_raised do
390
+ @cache.get(["#{key}_1", "#{key}_2"], false)
391
+ end
392
+ assert_raise(ArgumentError) do
393
+ @cache.get(["#{key}_1", "#{key}_2"])
394
+ end
395
+ end
396
+
397
+ def test_random_distribution_is_statistically_random
398
+ cache = Memcached.new(@servers, :distribution => :random)
399
+ cache.flush
400
+ 20.times { |i| cache.set "#{key}#{i}", @value }
401
+
402
+ cache, hits = Memcached.new(@servers.first), 0
403
+ 20.times do |i|
404
+ begin
405
+ cache.get "#{key}#{i}"
406
+ hits += 1
407
+ rescue Memcached::NotFound
408
+ end
409
+ end
410
+
411
+ assert_not_equal 4, hits
412
+ end
413
+
414
+ # Set
415
+
416
+ def test_set
417
+ assert_nothing_raised do
418
+ @cache.set(key, @value)
419
+ end
420
+
421
+ assert_nothing_raised do
422
+ @binary_protocol_cache.set(key, @value)
423
+ end
424
+
425
+ assert_nothing_raised do
426
+ @udp_cache.set(key, @value)
427
+ end
428
+ end
429
+
430
+ def test_set_expiry
431
+ @cache.set key, @value, 1
432
+ assert_nothing_raised do
433
+ @cache.get key
434
+ end
435
+ sleep(2)
436
+ assert_raise(Memcached::NotFound) do
437
+ @cache.get key
438
+ end
439
+ end
440
+
441
+ # big_set
442
+
443
+ def test_big_set
444
+ assert_nothing_raised do
445
+ @cache.big_set key, @value
446
+ end
447
+ end
448
+
449
+ def test_big_set_sets_header_in_key
450
+ @cache.big_set(key, @large_value, 0, false)
451
+
452
+ expected_header = OpenStruct.new(:chunks => 2) # @large_value splits into 2 chunks.
453
+
454
+ assert_equal expected_header, @cache.get(key)
455
+ end
456
+
457
+ def test_big_set_splits_into_chunks
458
+ @cache.big_set(key, @large_value, 0, false)
459
+
460
+ expected_1st_chunk = 'a' * @options[:chunk_split_size]
461
+ expected_2nd_chunk = 'a' * (1048576 - @options[:chunk_split_size]) + 'b' * 10
462
+
463
+ assert_equal expected_1st_chunk, @cache.get("#{key}_0", false)
464
+ assert_equal expected_2nd_chunk, @cache.get("#{key}_1", false)
465
+ end
466
+
467
+ def test_big_set_single_chunk_sets_header_and_single_chunk
468
+ @cache.big_set(key, 'bar', 0, false)
469
+
470
+ expected_header = OpenStruct.new(:chunks => 1)
471
+
472
+ assert_equal expected_header, @cache.get(key)
473
+
474
+ assert_equal 'bar', @cache.get("#{key}_0", false)
475
+
476
+ assert_raise(Memcached::NotFound) do
477
+ puts @cache.get("#{key}_1", false)
478
+ end
479
+ end
480
+
481
+ def test_big_set_marshalled
482
+ @cache.big_set(key, @large_value, 0, true)
483
+
484
+ expected_1st_chunk = @large_marshalled_value[0, @options[:chunk_split_size]]
485
+ expected_2nd_chunk = @large_marshalled_value[@options[:chunk_split_size], @options[:chunk_split_size]]
486
+
487
+ assert_equal expected_1st_chunk, @cache.get("#{key}_0", false)
488
+ assert_equal expected_2nd_chunk, @cache.get("#{key}_1", false)
489
+
490
+ # Unmarshalling the sum of the chunks should result in the original value.
491
+ assert_equal Marshal.load(expected_1st_chunk + expected_2nd_chunk), @large_value
492
+ end
493
+
494
+ # big_get
495
+
496
+ def test_big_get
497
+ @cache.big_set key, @large_value
498
+ result = @cache.big_get key
499
+ assert_equal @large_value, result
500
+ end
501
+
502
+ def test_big_get_marshalled
503
+ @cache.big_set(key, @large_value, 0, true)
504
+ result = @cache.big_get key, true
505
+ assert_equal @large_value, result
506
+ end
507
+
508
+ def test_big_set_and_get_unmarshalled
509
+ @cache.big_set key, @large_value
510
+ result = @cache.big_get key, false
511
+ assert_equal @large_marshalled_value, result
512
+ end
513
+
514
+ def test_big_get_missing_chunk_raises_not_found
515
+ @cache.big_set key, @large_value
516
+
517
+ # Remove the 2nd chunk.
518
+ @cache.delete "#{key}_1"
519
+
520
+ assert_raise(Memcached::NotFound) do
521
+ @cache.big_get key
522
+ end
523
+ end
524
+
525
+ def test_big_get_invalid_chunk_header_fallsback_to_get
526
+ @cache.big_set key, @large_value
527
+
528
+ # Overwrite the chunk header.
529
+ @cache.set key, 'foo'
530
+
531
+ assert_equal 'foo', @cache.big_get(key)
532
+ end
533
+
534
+ def test_big_get_missing
535
+ assert_raise(Memcached::NotFound) do
536
+ @cache.big_get key
537
+ end
538
+ end
539
+
540
+ def test_set_with_default_ttl
541
+ cache = Memcached.new(
542
+ @servers,
543
+ :default_ttl => 1
544
+ )
545
+ cache.set key, @value
546
+ assert_nothing_raised do
547
+ cache.get key
548
+ end
549
+ sleep(2)
550
+ assert_raise(Memcached::NotFound) do
551
+ cache.get key
552
+ end
553
+ end
554
+
555
+ def disabled_test_set_retry_on_client_error
556
+ # FIXME Test passes, but Mocha doesn't restore the original method properly
557
+ Rlibmemcached.stubs(:memcached_set).raises(Memcached::ClientError)
558
+ Rlibmemcached.stubs(:memcached_set).returns(0)
559
+
560
+ assert_nothing_raised do
561
+ @cache.set(key, @value)
562
+ end
563
+ end
564
+
565
+ # Delete
566
+
567
+ def test_delete
568
+ @cache.set key, @value
569
+ @cache.delete key
570
+ assert_raise(Memcached::NotFound) do
571
+ @cache.get key
572
+ end
573
+ end
574
+
575
+ def test_missing_delete
576
+ @cache.delete key rescue nil
577
+ assert_raise(Memcached::NotFound) do
578
+ @cache.delete key
579
+ end
580
+ end
581
+
582
+ # Flush
583
+
584
+ def test_flush
585
+ @cache.set key, @value
586
+ assert_equal @value,
587
+ @cache.get(key)
588
+ @cache.flush
589
+ assert_raise(Memcached::NotFound) do
590
+ @cache.get key
591
+ end
592
+ end
593
+
594
+ # Add
595
+
596
+ def test_add
597
+ @cache.delete key rescue nil
598
+ @cache.add key, @value
599
+ assert_equal @value, @cache.get(key)
600
+ end
601
+
602
+ def test_existing_add
603
+ @cache.set key, @value
604
+ assert_raise(Memcached::NotStored) do
605
+ @cache.add key, @value
606
+ end
607
+ end
608
+
609
+ def test_add_expiry
610
+ @cache.delete key rescue nil
611
+ @cache.set key, @value, 1
612
+ assert_nothing_raised do
613
+ @cache.get key
614
+ end
615
+ sleep(1)
616
+ assert_raise(Memcached::NotFound) do
617
+ @cache.get key
618
+ end
619
+ end
620
+
621
+ def test_unmarshalled_add
622
+ @cache.delete key rescue nil
623
+ @cache.add key, @marshalled_value, 0, false
624
+ assert_equal @marshalled_value, @cache.get(key, false)
625
+ assert_equal @value, @cache.get(key)
626
+ end
627
+
628
+ # Increment and decrement
629
+
630
+ def test_increment
631
+ @cache.set key, 10, 0, false
632
+ assert_equal 11, @cache.increment(key)
633
+ end
634
+
635
+ def test_increment_offset
636
+ @cache.set key, 10, 0, false
637
+ assert_equal 15, @cache.increment(key, 5)
638
+ end
639
+
640
+ def test_missing_increment
641
+ @cache.delete key rescue nil
642
+ assert_raise(Memcached::NotFound) do
643
+ @cache.increment key
644
+ end
645
+ end
646
+
647
+ def test_decrement
648
+ @cache.set key, 10, 0, false
649
+ assert_equal 9, @cache.decrement(key)
650
+ end
651
+
652
+ def test_decrement_offset
653
+ @cache.set key, 10, 0, false
654
+ assert_equal 5, @cache.decrement(key, 5)
655
+ end
656
+
657
+ def test_missing_decrement
658
+ @cache.delete key rescue nil
659
+ assert_raise(Memcached::NotFound) do
660
+ @cache.decrement key
661
+ end
662
+ end
663
+
664
+ # Replace
665
+
666
+ def test_replace
667
+ @cache.set key, nil
668
+ assert_nothing_raised do
669
+ @cache.replace key, @value
670
+ end
671
+ assert_equal @value, @cache.get(key)
672
+ end
673
+
674
+ def test_missing_replace
675
+ @cache.delete key rescue nil
676
+ assert_raise(Memcached::NotStored) do
677
+ @cache.replace key, @value
678
+ end
679
+ assert_raise(Memcached::NotFound) do
680
+ assert_equal @value, @cache.get(key)
681
+ end
682
+ end
683
+
684
+ # Append and prepend
685
+
686
+ def test_append
687
+ @cache.set key, "start", 0, false
688
+ assert_nothing_raised do
689
+ @cache.append key, "end"
690
+ end
691
+ assert_equal "startend", @cache.get(key, false)
692
+
693
+ @binary_protocol_cache.set key, "start", 0, false
694
+ assert_nothing_raised do
695
+ @binary_protocol_cache.append key, "end"
696
+ end
697
+ assert_equal "startend", @binary_protocol_cache.get(key, false)
698
+ end
699
+
700
+ def test_missing_append
701
+ @cache.delete key rescue nil
702
+ assert_raise(Memcached::NotStored) do
703
+ @cache.append key, "end"
704
+ end
705
+ assert_raise(Memcached::NotFound) do
706
+ assert_equal @value, @cache.get(key)
707
+ end
708
+
709
+ @binary_protocol_cache.delete key rescue nil
710
+ assert_raise(Memcached::NotStored) do
711
+ @binary_protocol_cache.append key, "end"
712
+ end
713
+ assert_raise(Memcached::NotFound) do
714
+ assert_equal @value, @binary_protocol_cache.get(key)
715
+ end
716
+ end
717
+
718
+ def test_prepend
719
+ @cache.set key, "end", 0, false
720
+ assert_nothing_raised do
721
+ @cache.prepend key, "start"
722
+ end
723
+ assert_equal "startend", @cache.get(key, false)
724
+ end
725
+
726
+ def test_missing_prepend
727
+ @cache.delete key rescue nil
728
+ assert_raise(Memcached::NotStored) do
729
+ @cache.prepend key, "end"
730
+ end
731
+ assert_raise(Memcached::NotFound) do
732
+ assert_equal @value, @cache.get(key)
733
+ end
734
+ end
735
+
736
+ def test_cas
737
+ cache = Memcached.new(
738
+ @servers,
739
+ :prefix_key => @prefix_key,
740
+ :support_cas => true
741
+ )
742
+ value2 = OpenStruct.new(:d => 3, :e => 4, :f => GenericClass)
743
+
744
+ # Existing set
745
+ cache.set key, @value
746
+ cache.cas(key) do |current|
747
+ assert_equal @value, current
748
+ value2
749
+ end
750
+ assert_equal value2, cache.get(key)
751
+
752
+ # Existing test without marshalling
753
+ cache.set(key, "foo", 0, false)
754
+ cache.cas(key, 0, false) do |current|
755
+ "#{current}bar"
756
+ end
757
+ assert_equal "foobar", cache.get(key, false)
758
+
759
+ # Missing set
760
+ cache.delete key
761
+ assert_raises(Memcached::NotFound) do
762
+ cache.cas(key) {}
763
+ end
764
+
765
+ # Conflicting set
766
+ cache.set key, @value
767
+ assert_raises(Memcached::ConnectionDataExists) do
768
+ cache.cas(key) do |current|
769
+ cache.set key, value2
770
+ current
771
+ end
772
+ end
773
+ end
774
+
775
+ # Error states
776
+
777
+ def test_key_with_spaces
778
+ key = "i have a space"
779
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
780
+ @cache.set key, @value
781
+ end
782
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
783
+ @cache.get(key)
784
+ end
785
+ end
786
+
787
+ def test_key_with_null
788
+ key = "with\000null"
789
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
790
+ @cache.set key, @value
791
+ end
792
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
793
+ @cache.get(key)
794
+ end
795
+
796
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
797
+ response = @cache.get([key])
798
+ end
799
+ end
800
+
801
+ def test_key_with_invalid_control_characters
802
+ key = "ch\303\242teau"
803
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
804
+ @cache.set key, @value
805
+ end
806
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
807
+ @cache.get(key)
808
+ end
809
+
810
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
811
+ response = @cache.get([key])
812
+ end
813
+ end
814
+
815
+ def test_key_too_long
816
+ key = "x"*251
817
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
818
+ @cache.set key, @value
819
+ end
820
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
821
+ @cache.get(key)
822
+ end
823
+
824
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
825
+ @cache.get([key])
826
+ end
827
+ end
828
+
829
+ def test_server_error_message
830
+ @cache.set key, "I'm big" * 1000000
831
+ assert false # Never reached
832
+ rescue Memcached::ServerError => e
833
+ assert_match /^"object too large for cache". Key/, e.message
834
+ end
835
+
836
+ def test_errno_message
837
+ Rlibmemcached::MemcachedServerSt.any_instance.stubs("cached_errno").returns(1)
838
+ @cache.send(:check_return_code, Rlibmemcached::MEMCACHED_ERRNO, key)
839
+ rescue Memcached::SystemError => e
840
+ assert_match /^Errno 1: "Operation not permitted". Key/, e.message
841
+ end
842
+
843
+ # Stats
844
+
845
+ def test_stats
846
+ stats = @cache.stats
847
+ assert_equal 3, stats[:pid].size
848
+ assert_instance_of Fixnum, stats[:pid].first
849
+ assert_instance_of String, stats[:version].first
850
+ end
851
+
852
+ def test_missing_stats
853
+ cache = Memcached.new('localhost:43041')
854
+ assert_raises(Memcached::SomeErrorsWereReported) { cache.stats }
855
+ end
856
+
857
+ # Clone
858
+
859
+ def test_clone
860
+ cache = @cache.clone
861
+ assert_equal cache.servers, @cache.servers
862
+ assert_not_equal cache, @cache
863
+
864
+ # Definitely check that the structs are unlinked
865
+ assert_not_equal @cache.instance_variable_get('@struct').object_id,
866
+ cache.instance_variable_get('@struct').object_id
867
+
868
+ assert_nothing_raised do
869
+ @cache.set key, @value
870
+ end
871
+ end
872
+
873
+ # Non-blocking IO
874
+
875
+ def test_buffered_requests_return_value
876
+ cache = Memcached.new @servers,
877
+ :buffer_requests => true
878
+ assert_nothing_raised do
879
+ cache.set key, @value
880
+ end
881
+ ret = Rlibmemcached.memcached_set(
882
+ cache.instance_variable_get("@struct"),
883
+ key,
884
+ @marshalled_value,
885
+ 0,
886
+ Memcached::FLAGS
887
+ )
888
+ assert_equal Rlibmemcached::MEMCACHED_BUFFERED, ret
889
+ end
890
+
891
+ def test_no_block_return_value
892
+ assert_nothing_raised do
893
+ @noblock_cache.set key, @value
894
+ end
895
+ ret = Rlibmemcached.memcached_set(
896
+ @noblock_cache.instance_variable_get("@struct"),
897
+ key,
898
+ @marshalled_value,
899
+ 0,
900
+ Memcached::FLAGS
901
+ )
902
+ assert_equal Rlibmemcached::MEMCACHED_BUFFERED, ret
903
+ end
904
+
905
+ def test_no_block_get
906
+ @noblock_cache.set key, @value
907
+ assert_equal @value,
908
+ @noblock_cache.get(key)
909
+ end
910
+
911
+ def test_no_block_missing_delete
912
+ @noblock_cache.delete key rescue nil
913
+ assert_nothing_raised do
914
+ @noblock_cache.delete key
915
+ end
916
+ end
917
+
918
+ def test_no_block_set_invalid_key
919
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
920
+ @noblock_cache.set "I'm so bad", @value
921
+ end
922
+ end
923
+
924
+ def test_no_block_set_object_too_large
925
+ assert_nothing_raised do
926
+ @noblock_cache.set key, "I'm big" * 1000000
927
+ end
928
+ end
929
+
930
+ def test_no_block_existing_add
931
+ # Should still raise
932
+ @noblock_cache.set key, @value
933
+ assert_raise(Memcached::NotStored) do
934
+ @noblock_cache.add key, @value
935
+ end
936
+ end
937
+
938
+ # Server removal and consistent hashing
939
+
940
+ def test_unresponsive_server
941
+ socket = stub_server 43041
942
+
943
+ cache = Memcached.new(
944
+ [@servers.last, 'localhost:43041'],
945
+ :prefix_key => @prefix_key,
946
+ :auto_eject_hosts => true,
947
+ :server_failure_limit => 2,
948
+ :retry_timeout => 1,
949
+ :hash_with_prefix_key => false,
950
+ :hash => :md5
951
+ )
952
+
953
+ # Hit second server up to the server_failure_limit
954
+ key2 = 'test_missing_server'
955
+ assert_raise(Memcached::ATimeoutOccurred) { cache.set(key2, @value) }
956
+ assert_raise(Memcached::ATimeoutOccurred) { cache.get(key2, @value) }
957
+
958
+ # Hit second server and pass the limit
959
+ key2 = 'test_missing_server'
960
+ begin
961
+ cache.get(key2)
962
+ rescue => e
963
+ assert_equal Memcached::ServerIsMarkedDead, e.class
964
+ assert_match /localhost:43041/, e.message
965
+ end
966
+
967
+ # Hit first server on retry
968
+ assert_nothing_raised do
969
+ cache.set(key2, @value)
970
+ assert_equal cache.get(key2), @value
971
+ end
972
+
973
+ sleep(2)
974
+
975
+ # Hit second server again after restore, expect same failure
976
+ key2 = 'test_missing_server'
977
+ assert_raise(Memcached::ATimeoutOccurred) do
978
+ cache.set(key2, @value)
979
+ end
980
+
981
+ socket.close
982
+ end
983
+
984
+ def test_missing_server
985
+ cache = Memcached.new(
986
+ [@servers.last, 'localhost:43041'],
987
+ :prefix_key => @prefix_key,
988
+ :auto_eject_hosts => true,
989
+ :server_failure_limit => 2,
990
+ :retry_timeout => 1,
991
+ :hash_with_prefix_key => false,
992
+ :hash => :md5
993
+ )
994
+
995
+ # Hit second server up to the server_failure_limit
996
+ key2 = 'test_missing_server'
997
+ assert_raise(Memcached::SystemError) { cache.set(key2, @value) }
998
+ assert_raise(Memcached::SystemError) { cache.get(key2, @value) }
999
+
1000
+ # Hit second server and pass the limit
1001
+ key2 = 'test_missing_server'
1002
+ begin
1003
+ cache.get(key2)
1004
+ rescue => e
1005
+ assert_equal Memcached::ServerIsMarkedDead, e.class
1006
+ assert_match /localhost:43041/, e.message
1007
+ end
1008
+
1009
+ # Hit first server on retry
1010
+ assert_nothing_raised do
1011
+ cache.set(key2, @value)
1012
+ assert_equal cache.get(key2), @value
1013
+ end
1014
+
1015
+ sleep(2)
1016
+
1017
+ # Hit second server again after restore, expect same failure
1018
+ key2 = 'test_missing_server'
1019
+ assert_raise(Memcached::SystemError) do
1020
+ cache.set(key2, @value)
1021
+ end
1022
+ end
1023
+
1024
+ def test_unresponsive_with_random_distribution
1025
+ socket = stub_server 43041
1026
+ failures = [Memcached::ATimeoutOccurred, Memcached::ServerIsMarkedDead]
1027
+
1028
+ cache = Memcached.new(
1029
+ [@servers.last, 'localhost:43041'],
1030
+ :auto_eject_hosts => true,
1031
+ :distribution => :random,
1032
+ :server_failure_limit => 1,
1033
+ :retry_timeout => 1
1034
+ )
1035
+
1036
+ # Provoke the errors in 'failures'
1037
+ exceptions = []
1038
+ 100.times { begin; cache.set key, @value; rescue => e; exceptions << e; end }
1039
+ assert_equal failures, exceptions.map { |x| x.class }
1040
+
1041
+ # Hit first server on retry
1042
+ assert_nothing_raised { cache.set(key, @value) }
1043
+
1044
+ # Hit second server again after restore, expect same failures
1045
+ sleep(2)
1046
+ exceptions = []
1047
+ 100.times { begin; cache.set key, @value; rescue => e; exceptions << e; end }
1048
+ assert_equal failures, exceptions.map { |x| x.class }
1049
+
1050
+ socket.close
1051
+ end
1052
+
1053
+ def test_consistent_hashing
1054
+ keys = %w(EN6qtgMW n6Oz2W4I ss4A8Brr QShqFLZt Y3hgP9bs CokDD4OD Nd3iTSE1 24vBV4AU H9XBUQs5 E5j8vUq1 AzSh8fva PYBlK2Pi Ke3TgZ4I AyAIYanO oxj8Xhyd eBFnE6Bt yZyTikWQ pwGoU7Pw 2UNDkKRN qMJzkgo2 keFXbQXq pBl2QnIg ApRl3mWY wmalTJW1 TLueug8M wPQL4Qfg uACwus23 nmOk9R6w lwgZJrzJ v1UJtKdG RK629Cra U2UXFRqr d9OQLNl8 KAm1K3m5 Z13gKZ1v tNVai1nT LhpVXuVx pRib1Itj I1oLUob7 Z1nUsd5Q ZOwHehUa aXpFX29U ZsnqxlGz ivQRjOdb mB3iBEAj)
1055
+
1056
+ # Five servers
1057
+ cache = Memcached.new(
1058
+ @servers + ['localhost:43044', 'localhost:43045', 'localhost:43046'],
1059
+ :prefix_key => @prefix_key
1060
+ )
1061
+
1062
+ cache.flush
1063
+ keys.each do |key|
1064
+ cache.set(key, @value)
1065
+ end
1066
+
1067
+ # Pull a server
1068
+ cache = Memcached.new(
1069
+ @servers + ['localhost:43044', 'localhost:43046'],
1070
+ :prefix_key => @prefix_key
1071
+ )
1072
+
1073
+ failed = 0
1074
+ keys.each_with_index do |key, i|
1075
+ begin
1076
+ cache.get(key)
1077
+ rescue Memcached::NotFound
1078
+ failed += 1
1079
+ end
1080
+ end
1081
+
1082
+ assert(failed < keys.size / 3, "#{failed} failed out of #{keys.size}")
1083
+ end
1084
+
1085
+ # Concurrency
1086
+
1087
+ def test_thread_contention
1088
+ threads = []
1089
+ 4.times do |index|
1090
+ threads << Thread.new do
1091
+ cache = @cache.clone
1092
+ assert_nothing_raised do
1093
+ cache.set("test_thread_contention_#{index}", index)
1094
+ end
1095
+ assert_equal index, cache.get("test_thread_contention_#{index}")
1096
+ end
1097
+ end
1098
+ threads.each {|thread| thread.join}
1099
+ end
1100
+
1101
+ # Hash
1102
+
1103
+ def test_hash
1104
+ assert_equal 3157003241,
1105
+ Rlibmemcached.memcached_generate_hash_rvalue("test", Rlibmemcached::MEMCACHED_HASH_FNV1_32)
1106
+ end
1107
+
1108
+ # Memory cleanup
1109
+
1110
+ def test_reset
1111
+ original_struct = @cache.instance_variable_get("@struct")
1112
+ assert_nothing_raised do
1113
+ @cache.reset
1114
+ end
1115
+ assert_not_equal original_struct,
1116
+ @cache.instance_variable_get("@struct")
1117
+ end
1118
+
1119
+ private
1120
+
1121
+ def key
1122
+ caller.first[/.*[` ](.*)'/, 1] # '
1123
+ end
1124
+
1125
+ def stub_server(port)
1126
+ socket = TCPServer.new('127.0.0.1', port)
1127
+ Thread.new { socket.accept }
1128
+ socket
1129
+ end
1130
+
1131
+ end
1132
+