memcached 0.9 → 0.10
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +2 -0
- data/COMPATIBILITY +1 -0
- data/README +3 -3
- data/ext/extconf.rb +2 -2
- data/ext/rlibmemcached_wrap.c +1148 -549
- data/lib/memcached/behaviors.rb +4 -2
- data/lib/memcached/memcached.rb +76 -29
- data/memcached.gemspec +3 -3
- data/test/setup.rb +4 -3
- data/test/unit/memcached_test.rb +74 -9
- metadata +2 -2
- metadata.gz.sig +0 -0
data/lib/memcached/behaviors.rb
CHANGED
@@ -22,6 +22,8 @@ class Memcached
|
|
22
22
|
|
23
23
|
DISTRIBUTION_VALUES = {}
|
24
24
|
BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_DISTRIBUTION_", DISTRIBUTION_VALUES, 2))
|
25
|
+
|
26
|
+
DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :socket_recv_size, :poll_timeout, :socket_send_size]
|
25
27
|
|
26
28
|
#:startdoc:
|
27
29
|
|
@@ -36,7 +38,7 @@ class Memcached
|
|
36
38
|
case behavior
|
37
39
|
when :hash then raise(ArgumentError, msg) unless HASH_VALUES[value]
|
38
40
|
when :distribution then raise(ArgumentError, msg) unless DISTRIBUTION_VALUES[value]
|
39
|
-
when
|
41
|
+
when *DIRECT_VALUE_BEHAVIORS then raise(ArgumentError, msg) unless value.is_a? Fixnum and value > 0
|
40
42
|
else
|
41
43
|
raise(ArgumentError, msg) unless BEHAVIOR_VALUES[value]
|
42
44
|
end
|
@@ -57,7 +59,7 @@ class Memcached
|
|
57
59
|
# Scoped values; still annoying
|
58
60
|
when :hash then HASH_VALUES.invert[value]
|
59
61
|
when :distribution then DISTRIBUTION_VALUES.invert[value]
|
60
|
-
when
|
62
|
+
when *DIRECT_VALUE_BEHAVIORS then value
|
61
63
|
else
|
62
64
|
BEHAVIOR_VALUES.invert[value]
|
63
65
|
end
|
data/lib/memcached/memcached.rb
CHANGED
@@ -8,16 +8,19 @@ class Memcached
|
|
8
8
|
|
9
9
|
DEFAULTS = {
|
10
10
|
:hash => :default,
|
11
|
-
:distribution => :consistent,
|
12
11
|
:no_block => false,
|
12
|
+
:distribution => :consistent,
|
13
13
|
:buffer_requests => false,
|
14
|
+
:cache_lookups => true,
|
14
15
|
:support_cas => false,
|
15
16
|
:tcp_nodelay => false,
|
16
17
|
:show_not_found_backtraces => false,
|
17
18
|
:retry_timeout => 60,
|
19
|
+
# :poll_timeout => 5,
|
18
20
|
:connect_timeout => 5,
|
19
21
|
:namespace => nil,
|
20
|
-
:sort_hosts => false
|
22
|
+
:sort_hosts => false,
|
23
|
+
:failover => false
|
21
24
|
}
|
22
25
|
|
23
26
|
# :verify_key => false # XXX We do this ourselves already in Rlibmemcached.ns()
|
@@ -40,14 +43,15 @@ Hostname lookups are not currently supported; you need to use the IP address.
|
|
40
43
|
Valid option parameters are:
|
41
44
|
|
42
45
|
<tt>:namespace</tt>:: A namespace string to prepend to every key.
|
43
|
-
<tt>:hash</tt>:: The name of a hash function to use. Possible values are: <tt>:crc</tt>, <tt>:default</tt>, <tt>:fnv1_32</tt>, <tt>:fnv1_64</tt>, <tt>:fnv1a_32</tt>, <tt>:fnv1a_64</tt>, <tt>:hsieh</tt>, <tt>:
|
44
|
-
<tt>:distribution</tt>::
|
45
|
-
<tt>:
|
46
|
+
<tt>:hash</tt>:: The name of a hash function to use. Possible values are: <tt>:crc</tt>, <tt>:default</tt>, <tt>:fnv1_32</tt>, <tt>:fnv1_64</tt>, <tt>:fnv1a_32</tt>, <tt>:fnv1a_64</tt>, <tt>:hsieh</tt>, <tt>:md5</tt>, and <tt>:murmur</tt>. <tt>:default</tt> is the fastest. Use <tt>:md5</tt> for compatibility with other ketama clients.
|
47
|
+
<tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent</tt>, or <tt>:consistent_wheel</tt>. Defaults to <tt>:consistent</tt>, which is ketama-compatible.
|
48
|
+
<tt>:failover</tt>:: Whether to permanently eject failed hosts from the pool. Defaults to <tt>false</tt>. Note that in the event of a server failure, <tt>:failover</tt> will remap the entire pool unless <tt>:distribution</tt> is set to <tt>:consistent</tt>.
|
49
|
+
<tt>:support_cas</tt>:: Flag CAS support in the client. Accepts <tt>true</tt> or <tt>false</tt>. Defaults to <tt>false</tt> because it imposes a slight performance penalty. Note that your server must also support CAS or you will trigger <b>Memcached::ProtocolError</b> exceptions.
|
46
50
|
<tt>:tcp_nodelay</tt>:: Turns on the no-delay feature for connecting sockets. Accepts <tt>true</tt> or <tt>false</tt>. Performance may or may not change, depending on your system.
|
47
51
|
<tt>:no_block</tt>:: Whether to use non-blocking, asynchronous IO for writes. Accepts <tt>true</tt> or <tt>false</tt>.
|
48
52
|
<tt>:buffer_requests</tt>:: Whether to use an internal write buffer. Accepts <tt>true</tt> or <tt>false</tt>. Calling <tt>get</tt> or closing the connection will force the buffer to flush. Note that <tt>:buffer_requests</tt> might not work well without <tt>:no_block</tt> also enabled.
|
49
53
|
<tt>:show_not_found_backtraces</tt>:: Whether <b>Memcached::NotFound</b> exceptions should include backtraces. Generating backtraces is slow, so this is off by default. Turn it on to ease debugging.
|
50
|
-
<tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is
|
54
|
+
<tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is rarely useful.
|
51
55
|
|
52
56
|
Please note that when non-blocking IO is enabled, setter and deleter methods do not raise on errors. For example, if you try to set an invalid key with <tt>:no_block => true</tt>, it will appear to succeed. The actual setting of the key occurs after libmemcached has returned control to your program, so there is no way to backtrack and raise the exception.
|
53
57
|
|
@@ -57,15 +61,9 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
57
61
|
@struct = Lib::MemcachedSt.new
|
58
62
|
Lib.memcached_create(@struct)
|
59
63
|
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
raise ArgumentError, "Servers must be in the format ip:port (e.g., '127.0.0.1:11211')"
|
64
|
-
end
|
65
|
-
host, port = server.split(":")
|
66
|
-
Lib.memcached_server_add(@struct, host, port.to_i)
|
67
|
-
end
|
68
|
-
|
64
|
+
# Set the servers on the struct
|
65
|
+
set_servers(servers)
|
66
|
+
|
69
67
|
# Merge option defaults
|
70
68
|
@options = DEFAULTS.merge(opts)
|
71
69
|
|
@@ -79,17 +77,13 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
79
77
|
raise ArgumentError, ":sort_hosts defeats :consistent hashing"
|
80
78
|
end
|
81
79
|
|
82
|
-
# Set the behaviors
|
83
|
-
|
84
|
-
|
85
|
-
set_behavior(option, value)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
80
|
+
# Set the behaviors on the struct
|
81
|
+
set_behaviors
|
82
|
+
|
89
83
|
# Merge the actual behaviors back in
|
90
84
|
BEHAVIORS.keys.each do |behavior|
|
91
85
|
options[behavior] = get_behavior(behavior)
|
92
|
-
end
|
86
|
+
end
|
93
87
|
|
94
88
|
# Freeze the hash
|
95
89
|
options.freeze
|
@@ -134,6 +128,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
134
128
|
# runs much faster, but your instance will segfault if you try to call any other methods on it
|
135
129
|
# after destroy. Defaults to <tt>true</tt>, which safely overwrites all instance methods.
|
136
130
|
def destroy(disable_methods = true)
|
131
|
+
# XXX Should be implemented with rb_wrap_struct
|
137
132
|
Lib.memcached_free(@struct)
|
138
133
|
@struct = nil
|
139
134
|
|
@@ -148,11 +143,14 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
148
143
|
end
|
149
144
|
end
|
150
145
|
|
151
|
-
# Reset the state of the libmemcached struct.
|
152
|
-
def reset
|
153
|
-
|
154
|
-
Lib.memcached_free(@struct)
|
155
|
-
@struct =
|
146
|
+
# Reset the state of the libmemcached struct. This is useful for changing the server list at runtime.
|
147
|
+
def reset(current_servers = nil)
|
148
|
+
current_servers ||= servers
|
149
|
+
Lib.memcached_free(@struct)
|
150
|
+
@struct = Lib::MemcachedSt.new
|
151
|
+
Lib.memcached_create(@struct)
|
152
|
+
set_servers(current_servers)
|
153
|
+
set_behaviors
|
156
154
|
end
|
157
155
|
|
158
156
|
#:stopdoc:
|
@@ -275,6 +273,13 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
275
273
|
)
|
276
274
|
end
|
277
275
|
|
276
|
+
# Flushes all key/value pairs from all the servers.
|
277
|
+
def flush
|
278
|
+
check_return_code(
|
279
|
+
Lib.memcached_flush(@struct, IGNORED)
|
280
|
+
)
|
281
|
+
end
|
282
|
+
|
278
283
|
### Getters
|
279
284
|
|
280
285
|
# Gets a key's value from the server. Accepts a String <tt>key</tt> or array of String <tt>keys</tt>.
|
@@ -361,7 +366,49 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
361
366
|
def check_return_code(ret) #:doc:
|
362
367
|
# 0.16 --enable-debug returns 0 for an ActionQueued result but --disable-debug does not
|
363
368
|
return if ret == 0 or ret == 31
|
364
|
-
|
369
|
+
|
370
|
+
# SystemError; eject from the pool
|
371
|
+
if ret == 25 and options[:failover]
|
372
|
+
failed = sweep_servers
|
373
|
+
raise EXCEPTIONS[ret], "Server #{failed} failed permanently"
|
374
|
+
else
|
375
|
+
raise EXCEPTIONS[ret], ""
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
# Eject the first dead server we find from the pool and reset the struct
|
380
|
+
def sweep_servers
|
381
|
+
# XXX This method is annoying, but necessary until we get Lib.memcached_delete_server or equivalent.
|
382
|
+
server_structs.each do |server|
|
383
|
+
if server.next_retry > Time.now
|
384
|
+
server_name = "#{server.hostname}:#{server.port}"
|
385
|
+
current_servers = servers
|
386
|
+
current_servers.delete(server_name)
|
387
|
+
reset(current_servers)
|
388
|
+
return server_name
|
389
|
+
end
|
390
|
+
end
|
391
|
+
"(unknown)"
|
392
|
+
end
|
393
|
+
|
394
|
+
# Set the servers on the struct
|
395
|
+
def set_servers(servers)
|
396
|
+
Array(servers).each_with_index do |server, index|
|
397
|
+
unless server.is_a? String and server =~ /^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$/
|
398
|
+
raise ArgumentError, "Servers must be in the format ip:port (e.g., '127.0.0.1:11211')"
|
399
|
+
end
|
400
|
+
host, port = server.split(":")
|
401
|
+
Lib.memcached_server_add(@struct, host, port.to_i)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Set the behaviors on the struct from the current options
|
406
|
+
def set_behaviors
|
407
|
+
options.each do |option, value|
|
408
|
+
unless [:namespace, :show_not_found_backtraces, :failover].include? option
|
409
|
+
set_behavior(option, value)
|
410
|
+
end
|
411
|
+
end
|
365
412
|
end
|
366
413
|
|
367
414
|
end
|
data/memcached.gemspec
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Memcached-0.
|
2
|
+
# Gem::Specification for Memcached-0.10
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{memcached}
|
7
|
-
s.version = "0.
|
7
|
+
s.version = "0.10"
|
8
8
|
|
9
9
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.authors = ["Evan Weaver"]
|
13
|
-
s.date = %q{2008-
|
13
|
+
s.date = %q{2008-05-06}
|
14
14
|
s.description = %q{An interface to the libmemcached C client.}
|
15
15
|
s.email = %q{}
|
16
16
|
s.extensions = ["ext/extconf.rb"]
|
data/test/setup.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
HERE = File.dirname(__FILE__)
|
5
5
|
|
6
|
-
`ps awx`.split("\n").grep(/4304[2-
|
6
|
+
`ps awx`.split("\n").grep(/4304[2-6]/).map do |process|
|
7
7
|
system("kill -9 #{process.to_i}")
|
8
8
|
end
|
9
9
|
|
@@ -12,5 +12,6 @@ system ">#{log}"
|
|
12
12
|
|
13
13
|
verbosity = (ENV['DEBUG'] ? "-vv" : "")
|
14
14
|
|
15
|
-
|
16
|
-
system "memcached #{verbosity} -p
|
15
|
+
(43042..43046).each do |port|
|
16
|
+
system "memcached #{verbosity} -p #{port} >> #{log} 2>&1 &"
|
17
|
+
end
|
data/test/unit/memcached_test.rb
CHANGED
@@ -9,6 +9,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
@options = {
|
11
11
|
:namespace => @namespace,
|
12
|
+
:hash => :default,
|
12
13
|
:distribution => :modula
|
13
14
|
}
|
14
15
|
@cache = Memcached.new(@servers, @options)
|
@@ -17,7 +18,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
17
18
|
:namespace => @namespace,
|
18
19
|
:no_block => true,
|
19
20
|
:buffer_requests => true,
|
20
|
-
:
|
21
|
+
:hash => :default
|
21
22
|
}
|
22
23
|
@nb_cache = Memcached.new(@servers, @nb_options)
|
23
24
|
|
@@ -113,13 +114,28 @@ class MemcachedTest < Test::Unit::TestCase
|
|
113
114
|
|
114
115
|
def test_initialize_sort_hosts
|
115
116
|
# Original
|
116
|
-
cache = Memcached.new(@servers.sort
|
117
|
+
cache = Memcached.new(@servers.sort,
|
118
|
+
:sort_hosts => false,
|
119
|
+
:distribution => :modula
|
120
|
+
)
|
121
|
+
assert_equal @servers.sort,
|
122
|
+
cache.servers
|
123
|
+
cache.destroy
|
124
|
+
|
125
|
+
# Original with sort_hosts
|
126
|
+
cache = Memcached.new(@servers.sort,
|
127
|
+
:sort_hosts => true,
|
128
|
+
:distribution => :modula
|
129
|
+
)
|
117
130
|
assert_equal @servers.sort,
|
118
131
|
cache.servers
|
119
132
|
cache.destroy
|
120
133
|
|
121
134
|
# Reversed
|
122
|
-
cache = Memcached.new(@servers.sort.reverse
|
135
|
+
cache = Memcached.new(@servers.sort.reverse,
|
136
|
+
:sort_hosts => false,
|
137
|
+
:distribution => :modula
|
138
|
+
)
|
123
139
|
assert_equal @servers.sort.reverse,
|
124
140
|
cache.servers
|
125
141
|
cache.destroy
|
@@ -273,7 +289,19 @@ class MemcachedTest < Test::Unit::TestCase
|
|
273
289
|
assert_raise(Memcached::NotFound) do
|
274
290
|
@cache.delete key
|
275
291
|
end
|
276
|
-
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Flush
|
295
|
+
|
296
|
+
def test_flush
|
297
|
+
@cache.set key, @value
|
298
|
+
assert_equal @value,
|
299
|
+
@cache.get(key)
|
300
|
+
@cache.flush
|
301
|
+
assert_raise(Memcached::NotFound) do
|
302
|
+
@cache.get key
|
303
|
+
end
|
304
|
+
end
|
277
305
|
|
278
306
|
# Add
|
279
307
|
|
@@ -608,9 +636,12 @@ class MemcachedTest < Test::Unit::TestCase
|
|
608
636
|
# Server removal and consistent hashing
|
609
637
|
|
610
638
|
def test_missing_server
|
639
|
+
# XXX Does this test actually do anything? :hash behaves oddly
|
611
640
|
cache = Memcached.new(
|
612
|
-
[@servers.last, '127.0.0.1:
|
613
|
-
:namespace => @namespace
|
641
|
+
[@servers.last, '127.0.0.1:43041'], # Use a server that isn't running
|
642
|
+
:namespace => @namespace,
|
643
|
+
:failover => true,
|
644
|
+
:hash => :md5
|
614
645
|
)
|
615
646
|
|
616
647
|
# Verify that the second server is the hash target
|
@@ -622,13 +653,47 @@ class MemcachedTest < Test::Unit::TestCase
|
|
622
653
|
cache.get(key)
|
623
654
|
end
|
624
655
|
|
625
|
-
|
626
|
-
|
656
|
+
# Verify that we are targeting the first server now
|
657
|
+
assert_equal 0, cache.send(:hash, key)
|
658
|
+
|
627
659
|
assert_nothing_raised do
|
628
660
|
cache.set(key, @value)
|
629
661
|
cache.get(key)
|
630
662
|
end
|
631
663
|
end
|
664
|
+
|
665
|
+
def test_consistent_hashing
|
666
|
+
|
667
|
+
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)
|
668
|
+
|
669
|
+
# Five servers
|
670
|
+
cache = Memcached.new(
|
671
|
+
@servers + ['127.0.0.1:43044', '127.0.0.1:43045', '127.0.0.1:43046'],
|
672
|
+
:namespace => @namespace
|
673
|
+
)
|
674
|
+
|
675
|
+
cache.flush
|
676
|
+
keys.each do |key|
|
677
|
+
cache.set(key, @value)
|
678
|
+
end
|
679
|
+
|
680
|
+
# Pull a server
|
681
|
+
cache = Memcached.new(
|
682
|
+
@servers + ['127.0.0.1:43044', '127.0.0.1:43046'],
|
683
|
+
:namespace => @namespace
|
684
|
+
)
|
685
|
+
|
686
|
+
failed = 0
|
687
|
+
keys.each_with_index do |key, i|
|
688
|
+
begin
|
689
|
+
cache.get(key)
|
690
|
+
rescue Memcached::NotFound
|
691
|
+
failed += 1
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
assert(failed < keys.size / 3, "#{failed} failed out of #{keys.size}")
|
696
|
+
end
|
632
697
|
|
633
698
|
# Concurrency
|
634
699
|
|
@@ -668,6 +733,6 @@ class MemcachedTest < Test::Unit::TestCase
|
|
668
733
|
def key
|
669
734
|
caller.first[/`(.*)'/, 1]
|
670
735
|
end
|
671
|
-
|
736
|
+
|
672
737
|
end
|
673
738
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memcached
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.10"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Weaver
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2008-
|
33
|
+
date: 2008-05-06 00:00:00 -04:00
|
34
34
|
default_executable:
|
35
35
|
dependencies: []
|
36
36
|
|
metadata.gz.sig
CHANGED
Binary file
|