memcached 0.8.1 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,23 +29,41 @@ class Memcached
29
29
 
30
30
  # Set a behavior option for this Memcached instance. Accepts a Symbol <tt>behavior</tt> and either <tt>true</tt>, <tt>false</tt>, or a Symbol for <tt>value</tt>. Arguments are validated and converted into integers for the struct setter method.
31
31
  def set_behavior(behavior, value) #:doc:
32
- raise ArgumentError, "No setting #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
33
- raise ArgumentError, "No setting value #{value.inspect}" unless v_id = BEHAVIOR_VALUES[value]
32
+ raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
34
33
 
35
- # Scoped validations
36
- msg = "Invalid setting value #{value.inspect} for #{behavior.inspect}"
37
- if behavior == :hash
38
- raise ArgumentError, msg unless HASH_VALUES[value]
39
- elsif behavior == :distribution
40
- raise ArgumentError, msg unless DISTRIBUTION_VALUES[value]
41
- end
42
- # STDERR.puts "Setting #{behavior}:#{b_id} => #{value}:#{v_id}"
43
-
44
- unless value == false
45
- # XXX Setting false still turns on the behavior; maybe a Lib bug
46
- Lib.memcached_behavior_set(@struct, b_id, v_id)
34
+ # Scoped validations; annoying
35
+ msg = "Invalid behavior value #{value.inspect} for #{behavior.inspect}"
36
+ case behavior
37
+ when :hash then raise(ArgumentError, msg) unless HASH_VALUES[value]
38
+ when :distribution then raise(ArgumentError, msg) unless DISTRIBUTION_VALUES[value]
39
+ when :retry_timeout, :connect_timeout then raise(ArgumentError, msg) unless value.is_a? Fixnum and value > 0
40
+ else
41
+ raise(ArgumentError, msg) unless BEHAVIOR_VALUES[value]
47
42
  end
48
43
 
44
+ value = BEHAVIOR_VALUES[value] || value
45
+ # STDERR.puts "Setting #{behavior}:#{b_id} => #{value}:#{value}"
46
+ Lib.memcached_behavior_set(@struct, b_id, value)
49
47
  end
50
-
48
+
49
+ # Get a behavior value for this Memcached instance. Accepts a Symbol.
50
+ def get_behavior(behavior)
51
+ raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
52
+ value = Lib.memcached_behavior_get(@struct, b_id)
53
+
54
+ if BEHAVIOR_VALUES.invert.has_key?(value)
55
+ # False, nil are valid values so we can not rely on direct lookups
56
+ case behavior
57
+ # Scoped values; still annoying
58
+ when :hash then HASH_VALUES.invert[value]
59
+ when :distribution then DISTRIBUTION_VALUES.invert[value]
60
+ when :retry_timeout, :connect_timeout then value
61
+ else
62
+ BEHAVIOR_VALUES.invert[value]
63
+ end
64
+ else
65
+ value
66
+ end
67
+ end
68
+
51
69
  end
@@ -85,7 +85,7 @@ Subclasses correspond one-to-one with server response strings or libmemcached er
85
85
  end
86
86
 
87
87
  # Verify library version
88
- # XXX Waiting on libmemcached 0.14
88
+ # XXX Waiting on libmemcached 0.18
89
89
 
90
90
  #:startdoc:
91
91
  end
@@ -14,10 +14,12 @@ class Memcached
14
14
  :support_cas => false,
15
15
  :tcp_nodelay => false,
16
16
  :show_not_found_backtraces => false,
17
- :namespace => nil
17
+ :retry_timeout => 60,
18
+ :connect_timeout => 5,
19
+ :namespace => nil,
20
+ :sort_hosts => false
18
21
  }
19
-
20
- # :sort_hosts => false # XXX No effect due to libmemcached 0.16 bug
22
+
21
23
  # :verify_key => false # XXX We do this ourselves already in Rlibmemcached.ns()
22
24
 
23
25
  #:stopdoc:
@@ -38,13 +40,14 @@ Hostname lookups are not currently supported; you need to use the IP address.
38
40
  Valid option parameters are:
39
41
 
40
42
  <tt>:namespace</tt>:: A namespace string to prepend to every key.
41
- <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>:ketama</tt>, and <tt>:md5</tt>. <tt>:default</tt> is the fastest.
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>:ketama</tt>, <tt>:md5</tt>, and <tt>:murmur</tt>. <tt>:default</tt> is the fastest.
42
44
  <tt>:distribution</tt>:: The type of distribution function to use. Possible values are <tt>:modula</tt> and <tt>:consistent</tt>. Note that this is decoupled from the choice of hash function.
43
45
  <tt>:support_cas</tt>:: Flag CAS support in the client. Accepts <tt>true</tt> or <tt>false</tt>. Note that your server must also support CAS or you will trigger <b>Memcached::ProtocolError</b> exceptions.
44
46
  <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.
45
47
  <tt>:no_block</tt>:: Whether to use non-blocking, asynchronous IO for writes. Accepts <tt>true</tt> or <tt>false</tt>.
46
48
  <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.
47
49
  <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 only allowed if <tt>:distribution => :modula</tt>.
48
51
 
49
52
  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.
50
53
 
@@ -55,29 +58,42 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
55
58
  Lib.memcached_create(@struct)
56
59
 
57
60
  # Servers
58
- # XXX We have to sort them ahead of time due to a libmemcached 0.16 bug
59
- Array(servers).sort.each_with_index do |server, index|
61
+ Array(servers).each_with_index do |server, index|
60
62
  unless server.is_a? String and server =~ /^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$/
61
63
  raise ArgumentError, "Servers must be in the format ip:port (e.g., '127.0.0.1:11211')"
62
64
  end
63
65
  host, port = server.split(":")
64
66
  Lib.memcached_server_add(@struct, host, port.to_i)
65
67
  end
66
-
67
- # Behaviors
68
+
69
+ # Merge option defaults
68
70
  @options = DEFAULTS.merge(opts)
71
+
72
+ # Force :buffer_requests to use :no_block
73
+ # XXX Deleting the :no_block key should also work, but libmemcached doesn't seem to set it
74
+ # consistently
75
+ options[:no_block] = true if options[:buffer_requests]
76
+
77
+ # Disallow :sort_hosts with consistent hashing
78
+ if options[:sort_hosts] and options[:distribution] == :consistent
79
+ raise ArgumentError, ":sort_hosts defeats :consistent hashing"
80
+ end
81
+
82
+ # Set the behaviors
69
83
  options.each do |option, value|
70
84
  unless [:namespace, :show_not_found_backtraces].include? option
71
85
  set_behavior(option, value)
72
86
  end
73
87
  end
74
88
 
75
- # Make sure :buffer_requests uses :no_block
76
- # XXX Not enforcing this for now.
77
- # if options[:buffer_requests] and not options[:no_block]
78
- # raise ArgumentError, "Invalid options; :buffer_requests does not work well without :no_block."
79
- # end
89
+ # Merge the actual behaviors back in
90
+ BEHAVIORS.keys.each do |behavior|
91
+ options[behavior] = get_behavior(behavior)
92
+ end
80
93
 
94
+ # Freeze the hash
95
+ options.freeze
96
+
81
97
  # Namespace
82
98
  raise ArgumentError, "Invalid namespace" if options[:namespace].to_s =~ / /
83
99
  @namespace = options[:namespace].to_s
@@ -241,7 +257,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
241
257
  def cas(key, timeout = 0, marshal = true)
242
258
  raise ClientError, "CAS not enabled for this Memcached instance" unless options[:support_cas]
243
259
 
244
- value = get(key)
260
+ value = get(key, marshal)
245
261
  value = yield value
246
262
  value = marshal ? Marshal.dump(value) : value.to_s
247
263
 
@@ -335,6 +351,11 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
335
351
  ### Operations helpers
336
352
 
337
353
  private
354
+
355
+ # Returns the hash value for a master key
356
+ def hash(key)
357
+ Lib.memcached_generate_hash(@struct, Lib.ns(@namespace, key))
358
+ end
338
359
 
339
360
  # Checks the return code from Rlibmemcached against the exception list. Raises the corresponding exception if the return code is not Memcached::Success or Memcached::ActionQueued. Accepts an integer return code.
340
361
  def check_return_code(ret) #:doc:
data/memcached.gemspec CHANGED
@@ -1,25 +1,27 @@
1
1
 
2
- # Gem::Specification for Memcached-0.8.1
2
+ # Gem::Specification for Memcached-0.9
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.8.1"
7
+ s.version = "0.9"
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-02-21}
13
+ s.date = %q{2008-04-09}
14
14
  s.description = %q{An interface to the libmemcached C client.}
15
15
  s.email = %q{}
16
16
  s.extensions = ["ext/extconf.rb"]
17
+ s.extra_rdoc_files = ["BENCHMARKS", "CHANGELOG", "COMPATIBILITY", "lib/memcached/behaviors.rb", "lib/memcached/exceptions.rb", "lib/memcached/memcached.rb", "lib/memcached/rails.rb", "lib/memcached.rb", "LICENSE", "README", "TODO"]
17
18
  s.files = ["BENCHMARKS", "CHANGELOG", "COMPATIBILITY", "ext/extconf.rb", "ext/rlibmemcached.i", "ext/rlibmemcached_wrap.c", "lib/memcached/behaviors.rb", "lib/memcached/exceptions.rb", "lib/memcached/integer.rb", "lib/memcached/memcached.rb", "lib/memcached/rails.rb", "lib/memcached.rb", "LICENSE", "Manifest", "README", "test/profile/benchmark.rb", "test/profile/key.rb", "test/profile/profile.rb", "test/profile/valgrind.rb", "test/setup.rb", "test/teardown.rb", "test/test_helper.rb", "test/unit/binding_test.rb", "test/unit/memcached_test.rb", "test/unit/rails_test.rb", "TODO", "memcached.gemspec"]
18
19
  s.has_rdoc = true
19
20
  s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/memcached/}
21
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Memcached", "--main", "README"]
20
22
  s.require_paths = ["lib", "ext"]
21
23
  s.rubyforge_project = %q{fauna}
22
- s.rubygems_version = %q{1.0.1}
24
+ s.rubygems_version = %q{1.1.0}
23
25
  s.summary = %q{An interface to the libmemcached C client.}
24
26
  s.test_files = ["test/test_helper.rb", "test/unit/binding_test.rb", "test/unit/memcached_test.rb", "test/unit/rails_test.rb"]
25
27
  end
@@ -50,7 +50,10 @@ class Bench
50
50
  @key1 = "Short"
51
51
  @key2 = "Sym1-2-3::45"*8
52
52
  @key3 = "Long"*40
53
- @key4 = "Medium"*8
53
+ @key4 = "Medium"*8
54
+ # 5 and 6 are only used for multiget miss test
55
+ @key5 = "Medium2"*8
56
+ @key6 = "Long3"*40
54
57
 
55
58
  restart_servers
56
59
  end
@@ -245,6 +248,37 @@ class Bench
245
248
  end
246
249
  end
247
250
  end
251
+
252
+ if defined? Memcached
253
+ @m = Memcached.new(*@opts)
254
+
255
+ # Avoid rebuilding the array every request
256
+ keys = [@key1, @key2, @key3, @key4, @key5, @key6]
257
+
258
+ x.report("multiget:ruby:memcached") do
259
+ n.times do
260
+ @m.get keys
261
+ end
262
+ end
263
+ end
264
+ if defined? Caffeine
265
+ @m = Caffeine::MemCache.new(@opts[1]); @m.servers = @opts[0]
266
+ x.report("multiget:ruby:caffeine") do
267
+ n.times do
268
+ # We don't use the keys array because splat is slow
269
+ @m.get @key1, @key2, @key3, @key4, @key5, @key6
270
+ end
271
+ end
272
+ end
273
+ if defined? MemCache
274
+ @m = MemCache.new(*@opts)
275
+ x.report("multiget:ruby:memcache-client") do
276
+ n.times do
277
+ # We don't use the keys array because splat is slow
278
+ @m.get_multi @key1, @key2, @key3, @key4, @key5, @key6
279
+ end
280
+ end
281
+ end
248
282
 
249
283
  # restart_servers
250
284
 
data/test/test_helper.rb CHANGED
@@ -12,8 +12,3 @@ require 'ostruct'
12
12
 
13
13
  class GenericClass
14
14
  end
15
-
16
- class Memcached
17
- class StubError < Error
18
- end
19
- end
@@ -6,18 +6,21 @@ class MemcachedTest < Test::Unit::TestCase
6
6
  def setup
7
7
  @servers = ['127.0.0.1:43042', '127.0.0.1:43043']
8
8
  @namespace = 'memcached_test_namespace'
9
- @cache = Memcached.new(
10
- @servers,
9
+
10
+ @options = {
11
11
  :namespace => @namespace,
12
12
  :distribution => :modula
13
- )
14
- @nb_cache = Memcached.new(
15
- @servers,
13
+ }
14
+ @cache = Memcached.new(@servers, @options)
15
+
16
+ @nb_options = {
16
17
  :namespace => @namespace,
17
18
  :no_block => true,
18
19
  :buffer_requests => true,
19
20
  :distribution => :modula
20
- )
21
+ }
22
+ @nb_cache = Memcached.new(@servers, @nb_options)
23
+
21
24
  @value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
22
25
  @marshalled_value = Marshal.dump(@value)
23
26
  end
@@ -39,6 +42,19 @@ class MemcachedTest < Test::Unit::TestCase
39
42
  assert_equal 43043, cache.send(:server_structs).last.port
40
43
  end
41
44
 
45
+ def test_options_are_set
46
+ Memcached::DEFAULTS.merge(@nb_options).each do |key, expected|
47
+ value = @nb_cache.options[key]
48
+ assert(expected == value, "#{key} should be #{expected} but was #{value}")
49
+ end
50
+ end
51
+
52
+ def test_options_are_frozen
53
+ assert_raise(TypeError) do
54
+ @cache.options[:no_block] = true
55
+ end
56
+ end
57
+
42
58
  def test_destroy
43
59
  cache = Memcached.new @servers, :namespace => 'test'
44
60
  cache.destroy
@@ -53,14 +69,12 @@ class MemcachedTest < Test::Unit::TestCase
53
69
  assert_raise(ArgumentError) { Memcached.new "127.0.0.1:43043:1" }
54
70
  end
55
71
 
56
- def test_initialize_with_missing_server
57
- # XXX Triggers abort trap with libmemcached --enable-debug.
58
- cache = Memcached.new "127.0.0.1:43044"
59
- assert_raises Memcached::SystemError do
60
- cache.get key
72
+ def test_initialize_with_invalid_options
73
+ assert_raise(ArgumentError) do
74
+ Memcached.new @servers, :sort_hosts => true, :distribution => :consistent
61
75
  end
62
76
  end
63
-
77
+
64
78
  def test_initialize_without_namespace
65
79
  cache = Memcached.new @servers
66
80
  assert_equal nil, cache.options[:namespace]
@@ -105,24 +119,21 @@ class MemcachedTest < Test::Unit::TestCase
105
119
  cache.destroy
106
120
 
107
121
  # Reversed
108
- # XXX Fails due to libmemcached 0.16 bug
109
- # cache = Memcached.new(@servers.sort.reverse)
110
- # assert_equal @servers.sort.reverse,
111
- # cache.servers
112
- # cache.destroy
122
+ cache = Memcached.new(@servers.sort.reverse)
123
+ assert_equal @servers.sort.reverse,
124
+ cache.servers
125
+ cache.destroy
113
126
 
114
127
  # Reversed with sort_hosts
115
128
  cache = Memcached.new(@servers.sort.reverse,
116
- :sort_hosts => true)
129
+ :sort_hosts => true,
130
+ :distribution => :modula
131
+ )
117
132
  assert_equal @servers.sort,
118
133
  cache.servers
119
134
  cache.destroy
120
135
  end
121
136
 
122
- # def test_initialize_verify_key
123
- # # XXX Not necessary
124
- # end
125
-
126
137
  def test_initialize_single_server
127
138
  cache = Memcached.new '127.0.0.1:43042'
128
139
  assert_equal nil, cache.options[:namespace]
@@ -408,6 +419,13 @@ class MemcachedTest < Test::Unit::TestCase
408
419
  end
409
420
  assert_equal value2, cache.get(key)
410
421
 
422
+ # Existing test without marshalling
423
+ cache.set(key, "foo", 0, false)
424
+ cache.cas(key, 0, false) do |current|
425
+ "#{current}bar"
426
+ end
427
+ assert_equal "foobar", cache.get(key, false)
428
+
411
429
  # Missing set
412
430
  cache.delete key
413
431
  assert_raises(Memcached::NotFound) do
@@ -586,7 +604,34 @@ class MemcachedTest < Test::Unit::TestCase
586
604
  @nb_cache.add key, @value
587
605
  end
588
606
  end
607
+
608
+ # Server removal and consistent hashing
609
+
610
+ def test_missing_server
611
+ cache = Memcached.new(
612
+ [@servers.last, '127.0.0.1:43044'], # Use a server that isn't running
613
+ :namespace => @namespace
614
+ )
615
+
616
+ # Verify that the second server is the hash target
617
+ key = 'test_missing_server3'
618
+ assert_equal 1, cache.send(:hash, key)
589
619
 
620
+ assert_raise(Memcached::SystemError) do
621
+ cache.set(key, @value)
622
+ cache.get(key)
623
+ end
624
+
625
+ return
626
+ # XXX Waiting on failover support in Libmemcached
627
+ assert_nothing_raised do
628
+ cache.set(key, @value)
629
+ cache.get(key)
630
+ end
631
+ end
632
+
633
+ # Concurrency
634
+
590
635
  def test_thread_contention
591
636
  threads = []
592
637
  4.times do |index|
@@ -601,6 +646,8 @@ class MemcachedTest < Test::Unit::TestCase
601
646
  threads.each {|thread| thread.join}
602
647
  end
603
648
 
649
+ # Memory cleanup
650
+
604
651
  def test_reset
605
652
  original_struct = @cache.instance_variable_get("@struct")
606
653
  assert_nothing_raised do
@@ -610,6 +657,12 @@ class MemcachedTest < Test::Unit::TestCase
610
657
  @cache.instance_variable_get("@struct")
611
658
  end
612
659
 
660
+ # Private hash generation method
661
+
662
+ def test_hash_generation
663
+ assert [0, 1].include?(@cache.send(:hash, key))
664
+ end
665
+
613
666
  private
614
667
 
615
668
  def key
data.tar.gz.sig CHANGED
Binary file
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.8.1
4
+ version: "0.9"
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-02-21 00:00:00 -05:00
33
+ date: 2008-04-09 00:00:00 -04:00
34
34
  default_executable:
35
35
  dependencies: []
36
36
 
@@ -40,8 +40,18 @@ executables: []
40
40
 
41
41
  extensions:
42
42
  - ext/extconf.rb
43
- extra_rdoc_files: []
44
-
43
+ extra_rdoc_files:
44
+ - BENCHMARKS
45
+ - CHANGELOG
46
+ - COMPATIBILITY
47
+ - lib/memcached/behaviors.rb
48
+ - lib/memcached/exceptions.rb
49
+ - lib/memcached/memcached.rb
50
+ - lib/memcached/rails.rb
51
+ - lib/memcached.rb
52
+ - LICENSE
53
+ - README
54
+ - TODO
45
55
  files:
46
56
  - BENCHMARKS
47
57
  - CHANGELOG
@@ -73,8 +83,13 @@ files:
73
83
  has_rdoc: true
74
84
  homepage: http://blog.evanweaver.com/files/doc/fauna/memcached/
75
85
  post_install_message:
76
- rdoc_options: []
77
-
86
+ rdoc_options:
87
+ - --line-numbers
88
+ - --inline-source
89
+ - --title
90
+ - Memcached
91
+ - --main
92
+ - README
78
93
  require_paths:
79
94
  - lib
80
95
  - ext
@@ -93,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
108
  requirements: []
94
109
 
95
110
  rubyforge_project: fauna
96
- rubygems_version: 1.0.1
111
+ rubygems_version: 1.1.0
97
112
  signing_key:
98
113
  specification_version: 2
99
114
  summary: An interface to the libmemcached C client.
metadata.gz.sig CHANGED
Binary file