memcached-northscale 0.19.5.2

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