memcached 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +2 -0
- data/CHANGELOG +2 -0
- data/LICENSE +184 -0
- data/Manifest +19 -0
- data/README +51 -0
- data/ext/extconf.rb +16 -0
- data/ext/libmemcached.h +425 -0
- data/ext/libmemcached.i +96 -0
- data/ext/libmemcached_wrap.c +9159 -0
- data/lib/memcached.rb +6 -0
- data/lib/memcached/behaviors.rb +46 -0
- data/lib/memcached/exceptions.rb +30 -0
- data/lib/memcached/integer.rb +6 -0
- data/lib/memcached/memcached.rb +218 -0
- data/memcached.gemspec +38 -0
- data/test/benchmark/benchmark_test.rb +42 -0
- data/test/setup.rb +14 -0
- data/test/teardown.rb +0 -0
- data/test/test_helper.rb +19 -0
- data/test/unit/binding_test.rb +8 -0
- data/test/unit/memcached_test.rb +325 -0
- metadata +97 -0
- metadata.gz.sig +0 -0
data/lib/memcached.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
class Memcached
|
3
|
+
|
4
|
+
def self.load_constants(prefix, hash = {}, offset = 0)
|
5
|
+
Libmemcached.constants.grep(/^#{prefix}/).each do |const_name|
|
6
|
+
hash[const_name[prefix.length..-1].downcase.to_sym] = Libmemcached.const_get(const_name) + offset
|
7
|
+
end
|
8
|
+
hash
|
9
|
+
end
|
10
|
+
|
11
|
+
BEHAVIORS = load_constants("MEMCACHED_BEHAVIOR_")
|
12
|
+
|
13
|
+
BEHAVIOR_VALUES = {
|
14
|
+
false => 0,
|
15
|
+
true => 1
|
16
|
+
}
|
17
|
+
|
18
|
+
HASH_VALUES = {}
|
19
|
+
BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_HASH_", HASH_VALUES, 2))
|
20
|
+
|
21
|
+
DISTRIBUTION_VALUES = {}
|
22
|
+
BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_DISTRIBUTION_", DISTRIBUTION_VALUES, 2))
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def set_behavior(behavior, value)
|
27
|
+
raise ArgumentError, "No setting #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
|
28
|
+
raise ArgumentError, "No setting value #{value.inspect}" unless v_id = BEHAVIOR_VALUES[value]
|
29
|
+
|
30
|
+
# Scoped validations
|
31
|
+
msg = "Invalid setting value #{value.inspect} for #{behavior.inspect}"
|
32
|
+
if behavior == :hash
|
33
|
+
raise ArgumentError, msg unless HASH_VALUES[value]
|
34
|
+
elsif behavior == :distribution
|
35
|
+
raise ArgumentError, msg unless DISTRIBUTION_VALUES[value]
|
36
|
+
end
|
37
|
+
# STDERR.puts "Setting #{behavior}:#{b_id} => #{value}:#{v_id}"
|
38
|
+
|
39
|
+
unless value == false
|
40
|
+
# XXX Setting false still turns on the behavior; maybe a Libmemcached bug
|
41
|
+
Libmemcached.memcached_behavior_set(@struct, b_id, v_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
class Memcached
|
3
|
+
|
4
|
+
class Error < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
class NotImplemented < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
private
|
12
|
+
def camelize(string)
|
13
|
+
string.downcase.split(' ').map {|s| s.capitalize}.join
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
@@exceptions = []
|
18
|
+
@@empty_struct = Libmemcached::MemcachedSt.new
|
19
|
+
Libmemcached.memcached_create(@@empty_struct)
|
20
|
+
|
21
|
+
# Generate exception classes
|
22
|
+
Libmemcached::MEMCACHED_MAXIMUM_RETURN.times do |exception_index|
|
23
|
+
description = Libmemcached.memcached_strerror(@@empty_struct, exception_index)
|
24
|
+
exception_class = eval("class #{camelize(description)} < Error; self; end")
|
25
|
+
@@exceptions << exception_class
|
26
|
+
end
|
27
|
+
|
28
|
+
# Verify library version
|
29
|
+
# XXX Impossible with current libmemcached
|
30
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
|
2
|
+
class Memcached
|
3
|
+
|
4
|
+
FLAGS = 0x0
|
5
|
+
|
6
|
+
DEFAULTS = {
|
7
|
+
:hash => :default,
|
8
|
+
:distribution => :consistent,
|
9
|
+
:buffer_requests => false,
|
10
|
+
:support_cas => false,
|
11
|
+
:tcp_nodelay => false,
|
12
|
+
:no_block => false
|
13
|
+
}
|
14
|
+
|
15
|
+
IGNORED = 0
|
16
|
+
|
17
|
+
attr_reader :namespace
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
### Configuration
|
21
|
+
|
22
|
+
def initialize(servers, opts = {})
|
23
|
+
@struct = Libmemcached::MemcachedSt.new
|
24
|
+
Libmemcached.memcached_create(@struct)
|
25
|
+
|
26
|
+
# Servers
|
27
|
+
Array(servers).each_with_index do |server, index|
|
28
|
+
unless server.is_a? String and server =~ /^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$/
|
29
|
+
raise ArgumentError, "Servers must be in the format ip:port (e.g., '127.0.0.1:11211')"
|
30
|
+
end
|
31
|
+
host, port = server.split(":")
|
32
|
+
Libmemcached.memcached_server_add(@struct, host, port.to_i)
|
33
|
+
|
34
|
+
# XXX To be removed once Krow fixes the write_ptr bug
|
35
|
+
Libmemcached.memcached_repair_server_st(@struct,
|
36
|
+
Libmemcached.memcached_select_server_at(@struct, index)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Namespace
|
41
|
+
@namespace = opts[:namespace]
|
42
|
+
raise ArgumentError, "Invalid namespace" if namespace.to_s =~ / /
|
43
|
+
|
44
|
+
# Behaviors
|
45
|
+
@options = DEFAULTS.merge(opts)
|
46
|
+
options.each do |option, value|
|
47
|
+
set_behavior(option, value) unless option == :namespace
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def servers
|
52
|
+
server_structs.map do |server|
|
53
|
+
"#{server.hostname}:#{server.port}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def clone
|
58
|
+
# XXX Could be more efficient if we used Libmemcached.memcached_clone(@struct)
|
59
|
+
self.class.new(servers, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :dup :clone
|
63
|
+
|
64
|
+
### Configuration helpers
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def server_structs
|
69
|
+
array = []
|
70
|
+
@struct.hosts.count.times do |i|
|
71
|
+
array << Libmemcached.memcached_select_server_at(@struct, i)
|
72
|
+
end
|
73
|
+
array
|
74
|
+
end
|
75
|
+
|
76
|
+
### Operations
|
77
|
+
|
78
|
+
public
|
79
|
+
|
80
|
+
# Setters
|
81
|
+
|
82
|
+
def set(key, value, timeout=0, marshal=true)
|
83
|
+
value = marshal ? Marshal.dump(value) : value.to_s
|
84
|
+
check_return_code(
|
85
|
+
Libmemcached.memcached_set(@struct, ns(key), value, timeout, FLAGS)
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def add(key, value, timeout=0, marshal=true)
|
90
|
+
value = marshal ? Marshal.dump(value) : value.to_s
|
91
|
+
check_return_code(
|
92
|
+
Libmemcached.memcached_add(@struct, ns(key), value, timeout, FLAGS)
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
def increment(key, offset=1)
|
97
|
+
ret, value = Libmemcached.memcached_increment(@struct, ns(key), offset)
|
98
|
+
check_return_code(ret)
|
99
|
+
value
|
100
|
+
end
|
101
|
+
|
102
|
+
def decrement(key, offset=1)
|
103
|
+
ret, value = Libmemcached.memcached_decrement(@struct, ns(key), offset)
|
104
|
+
check_return_code(ret)
|
105
|
+
value
|
106
|
+
end
|
107
|
+
|
108
|
+
alias :incr :increment
|
109
|
+
alias :decr :decrement
|
110
|
+
|
111
|
+
def replace(key, value, timeout=0, marshal=true)
|
112
|
+
value = marshal ? Marshal.dump(value) : value.to_s
|
113
|
+
check_return_code(
|
114
|
+
Libmemcached.memcached_replace(@struct, ns(key), value, timeout, FLAGS)
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
def append(key, value)
|
119
|
+
# Requires memcached 1.2.4
|
120
|
+
check_return_code(
|
121
|
+
Libmemcached.memcached_append(@struct, ns(key), value.to_s, IGNORED, FLAGS)
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def prepend(key, value)
|
126
|
+
# Requires memcached 1.2.4
|
127
|
+
check_return_code(
|
128
|
+
Libmemcached.memcached_prepend(@struct, ns(key), value.to_s, IGNORED, FLAGS)
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
def cas
|
133
|
+
# Requires memcached HEAD
|
134
|
+
raise NotImplemented
|
135
|
+
raise "CAS not enabled" unless options[:support_cas]
|
136
|
+
end
|
137
|
+
|
138
|
+
# Deleters
|
139
|
+
|
140
|
+
def delete(key, timeout=0)
|
141
|
+
check_return_code(
|
142
|
+
Libmemcached.memcached_delete(@struct, ns(key), timeout)
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Getters
|
147
|
+
|
148
|
+
def get(key, marshal=true)
|
149
|
+
if key.is_a? Array
|
150
|
+
# Multi get
|
151
|
+
# XXX Waiting on the real implementation
|
152
|
+
key.map do |this_key|
|
153
|
+
begin
|
154
|
+
get(this_key, marshal)
|
155
|
+
rescue NotFound
|
156
|
+
# XXX Not sure how this behavior should be defined
|
157
|
+
end
|
158
|
+
end
|
159
|
+
else
|
160
|
+
# Single get
|
161
|
+
# XXX Server doesn't validate. Possibly a performance problem.
|
162
|
+
raise ClientError, "Invalid key" if !key.is_a? String or key =~ /\s/
|
163
|
+
|
164
|
+
value, flags, ret = Libmemcached.memcached_get_ruby_string(@struct, ns(key))
|
165
|
+
check_return_code(ret)
|
166
|
+
value = Marshal.load(value) if marshal
|
167
|
+
value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Information methods
|
172
|
+
|
173
|
+
def stats
|
174
|
+
stats = Hash.new([])
|
175
|
+
|
176
|
+
stat_struct, ret = Libmemcached.memcached_stat(@struct, "")
|
177
|
+
check_return_code(ret)
|
178
|
+
|
179
|
+
keys, ret = Libmemcached.memcached_stat_get_keys(@struct, stat_struct)
|
180
|
+
check_return_code(ret)
|
181
|
+
|
182
|
+
keys.each do |key|
|
183
|
+
server_structs.size.times do |index|
|
184
|
+
|
185
|
+
value, ret = Libmemcached.memcached_stat_get_value(
|
186
|
+
@struct,
|
187
|
+
Libmemcached.memcached_select_stat_at(@struct, stat_struct, index),
|
188
|
+
key)
|
189
|
+
check_return_code(ret)
|
190
|
+
|
191
|
+
value = case value
|
192
|
+
when /^\d+\.\d+$/: value.to_f
|
193
|
+
when /^\d+$/: value.to_i
|
194
|
+
else value
|
195
|
+
end
|
196
|
+
|
197
|
+
stats[key.to_sym] += [value]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
Libmemcached.memcached_stat_free(@struct, stat_struct)
|
202
|
+
stats
|
203
|
+
end
|
204
|
+
|
205
|
+
### Operations helpers
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def ns(key)
|
210
|
+
"#{@namespace}#{key}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def check_return_code(ret)
|
214
|
+
return if ret == 0
|
215
|
+
raise @@exceptions[ret]
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
data/memcached.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Memcached-0.5
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{memcached}
|
7
|
+
s.version = "0.5"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Evan Weaver"]
|
13
|
+
s.date = %q{2008-01-20}
|
14
|
+
s.description = %q{An interface to the libmemcached C client.}
|
15
|
+
s.email = %q{}
|
16
|
+
s.extensions = ["ext/extconf.rb"]
|
17
|
+
s.files = ["CHANGELOG", "ext/extconf.rb", "ext/libmemcached.h", "ext/libmemcached.i", "ext/libmemcached_wrap.c", "lib/memcached/behaviors.rb", "lib/memcached/exceptions.rb", "lib/memcached/integer.rb", "lib/memcached/memcached.rb", "lib/memcached.rb", "LICENSE", "Manifest", "README", "test/benchmark/benchmark_test.rb", "test/setup.rb", "test/teardown.rb", "test/test_helper.rb", "test/unit/binding_test.rb", "test/unit/memcached_test.rb", "memcached.gemspec"]
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/memcached/}
|
20
|
+
s.require_paths = ["lib", "ext"]
|
21
|
+
s.rubyforge_project = %q{fauna}
|
22
|
+
s.rubygems_version = %q{1.0.1}
|
23
|
+
s.summary = %q{An interface to the libmemcached C client.}
|
24
|
+
s.test_files = ["test/benchmark/benchmark_test.rb", "test/test_helper.rb", "test/unit/binding_test.rb", "test/unit/memcached_test.rb"]
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# # Original Rakefile source (requires the Echoe gem):
|
29
|
+
#
|
30
|
+
# require 'echoe'
|
31
|
+
#
|
32
|
+
# Echoe.new("memcached") do |p|
|
33
|
+
# p.author = "Evan Weaver"
|
34
|
+
# p.project = "fauna"
|
35
|
+
# p.summary = "An interface to the libmemcached C client."
|
36
|
+
# p.url = "http://blog.evanweaver.com/files/doc/fauna/memcached/"
|
37
|
+
# p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
|
38
|
+
# end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../test_helper"
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'benchmark/unit'
|
6
|
+
require 'memcache'
|
7
|
+
|
8
|
+
class BenchmarkTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
|
12
|
+
@opts = [
|
13
|
+
['127.0.0.1:43042', '127.0.0.1:43043'],
|
14
|
+
{:namespace => "benchmark_namespace"}
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_original_speed
|
19
|
+
@cache = MemCache.new(*@opts)
|
20
|
+
assert_faster(0.02) do
|
21
|
+
@cache.set 'key1', @value
|
22
|
+
@cache.get 'key1'
|
23
|
+
@cache.set 'key2', @value
|
24
|
+
@cache.set 'key3', @value
|
25
|
+
@cache.get 'key2'
|
26
|
+
@cache.get 'key3'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_new_speed
|
31
|
+
@cache = Memcached.new(*@opts)
|
32
|
+
assert_faster(0.005) do
|
33
|
+
@cache.set 'key1', @value
|
34
|
+
@cache.get 'key1'
|
35
|
+
@cache.set 'key2', @value
|
36
|
+
@cache.set 'key3', @value
|
37
|
+
@cache.get 'key2'
|
38
|
+
@cache.get 'key3'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/test/setup.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
# Start memcached
|
3
|
+
|
4
|
+
HERE = File.dirname(__FILE__)
|
5
|
+
|
6
|
+
`ps awx`.split("\n").grep(/4304[1-3]/).map do |process|
|
7
|
+
system("kill -9 #{process.to_i}")
|
8
|
+
end
|
9
|
+
|
10
|
+
log = "#{HERE}/log/memcached.log"
|
11
|
+
system "touch #{log}"
|
12
|
+
|
13
|
+
system "memcached -vv -p 43042 >> #{log} 2>&1 &"
|
14
|
+
system "memcached -vv -p 43043 >> #{log} 2>&1 &"
|
data/test/teardown.rb
ADDED
File without changes
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
|
3
|
+
|
4
|
+
if ENV['DEBUG']
|
5
|
+
require 'rubygems'
|
6
|
+
require 'ruby-debug'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'memcached'
|
10
|
+
require 'test/unit'
|
11
|
+
require 'ostruct'
|
12
|
+
|
13
|
+
class GenericClass
|
14
|
+
end
|
15
|
+
|
16
|
+
class Memcached
|
17
|
+
class StubError < Error
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../test_helper"
|
3
|
+
|
4
|
+
class MemcachedTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@servers = ['127.0.0.1:43042', '127.0.0.1:43043']
|
8
|
+
@cache = Memcached.new(
|
9
|
+
@servers,
|
10
|
+
:namespace => 'class_test_namespace'
|
11
|
+
)
|
12
|
+
@value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
|
13
|
+
@marshalled_value = Marshal.dump(@value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialize
|
17
|
+
cache = Memcached.new @servers, :namespace => 'test'
|
18
|
+
assert_equal 'test', cache.namespace
|
19
|
+
assert_equal 2, cache.send(:server_structs).size
|
20
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
|
21
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).last.hostname
|
22
|
+
assert_equal 43043, cache.send(:server_structs).last.port
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_initialize_with_invalid_server_strings
|
26
|
+
assert_raise(ArgumentError) { Memcached.new "localhost:43042" }
|
27
|
+
assert_raise(ArgumentError) { Memcached.new "127.0.0.1:memcached" }
|
28
|
+
assert_raise(ArgumentError) { Memcached.new "127.0.0.1:43043:1" }
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_initialize_without_namespace
|
32
|
+
cache = Memcached.new @servers
|
33
|
+
assert_equal nil, cache.namespace
|
34
|
+
assert_equal 2, cache.send(:server_structs).size
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_initialize_positive_behavior
|
38
|
+
cache = Memcached.new @servers,
|
39
|
+
:buffer_requests => true
|
40
|
+
assert_raise(Memcached::ActionQueued) do
|
41
|
+
cache.set key, @value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_initialize_negative_behavior
|
46
|
+
cache = Memcached.new @servers,
|
47
|
+
:buffer_requests => false
|
48
|
+
assert_nothing_raised do
|
49
|
+
cache.set key, @value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_initialize_single_server
|
54
|
+
cache = Memcached.new '127.0.0.1:43042'
|
55
|
+
assert_equal nil, cache.namespace
|
56
|
+
assert_equal 1, cache.send(:server_structs).size
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_initialize_strange_argument
|
60
|
+
assert_raise(ArgumentError) { Memcached.new 1 }
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_get
|
64
|
+
@cache.set key, @value
|
65
|
+
result = @cache.get key
|
66
|
+
assert_equal @value, result
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_get_with_namespace
|
70
|
+
@cache.set key, @value
|
71
|
+
result = @cache.get key, false
|
72
|
+
direct_result = Libmemcached.memcached_get(
|
73
|
+
@cache.instance_variable_get("@struct"),
|
74
|
+
"#{@cache.namespace}#{key}"
|
75
|
+
).first
|
76
|
+
assert_equal result, direct_result
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_get_nil
|
80
|
+
@cache.set key, nil, 0
|
81
|
+
result = @cache.get key
|
82
|
+
assert_equal nil, result
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_get_missing
|
86
|
+
@cache.delete key rescue nil
|
87
|
+
assert_raise(Memcached::NotFound) do
|
88
|
+
result = @cache.get key
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_truncation_issue_is_covered
|
93
|
+
value = OpenStruct.new(:a => Object.new) # Marshals with a null \000
|
94
|
+
@cache.set key, value
|
95
|
+
result = @cache.get key, false
|
96
|
+
non_wrapped_result = Libmemcached.memcached_get(
|
97
|
+
@cache.instance_variable_get("@struct"),
|
98
|
+
"#{@cache.namespace}#{key}"
|
99
|
+
).first
|
100
|
+
assert result.size > non_wrapped_result.size
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_get_invalid_key
|
104
|
+
assert_raise(Memcached::ClientError) { @cache.get(key * 100) }
|
105
|
+
assert_raise(Memcached::ClientError) { @cache.get "I'm so bad" }
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_get_multi
|
109
|
+
@cache.set "#{key}_1", 1
|
110
|
+
@cache.set "#{key}_2", 2
|
111
|
+
assert_equal [1, 2],
|
112
|
+
@cache.get(["#{key}_1", "#{key}_2"])
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_set_and_get_unmarshalled
|
116
|
+
@cache.set key, @value
|
117
|
+
result = @cache.get key, false
|
118
|
+
assert_equal @marshalled_value, result
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_set
|
122
|
+
assert_nothing_raised do
|
123
|
+
@cache.set(key, @value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_set_invalid_key
|
128
|
+
assert_raise(Memcached::ProtocolError) do
|
129
|
+
@cache.set "I'm so bad", @value
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_set_expiry
|
134
|
+
@cache.set key, @value, 1
|
135
|
+
assert_nothing_raised do
|
136
|
+
@cache.get key
|
137
|
+
end
|
138
|
+
sleep(1)
|
139
|
+
assert_raise(Memcached::NotFound) do
|
140
|
+
@cache.get key
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_set_object_too_large
|
145
|
+
assert_raise(Memcached::ServerError) do
|
146
|
+
@cache.set key, "I'm big" * 1000000
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_delete
|
151
|
+
@cache.set key, @value
|
152
|
+
@cache.delete key
|
153
|
+
assert_raise(Memcached::NotFound) do
|
154
|
+
@cache.get key
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_missing_delete
|
159
|
+
@cache.delete key rescue nil
|
160
|
+
assert_raise(Memcached::NotFound) do
|
161
|
+
@cache.delete key
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_add
|
166
|
+
@cache.delete key rescue nil
|
167
|
+
@cache.add key, @value
|
168
|
+
assert_equal @value, @cache.get(key)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_existing_add
|
172
|
+
@cache.set key, @value
|
173
|
+
assert_raise(Memcached::NotStored) do
|
174
|
+
@cache.add key, @value
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_add_expiry
|
179
|
+
@cache.delete key rescue nil
|
180
|
+
@cache.set key, @value, 1
|
181
|
+
assert_nothing_raised do
|
182
|
+
@cache.get key
|
183
|
+
end
|
184
|
+
sleep(1)
|
185
|
+
assert_raise(Memcached::NotFound) do
|
186
|
+
@cache.get key
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_unmarshalled_add
|
191
|
+
@cache.delete key rescue nil
|
192
|
+
@cache.add key, @marshalled_value, 0, false
|
193
|
+
assert_equal @marshalled_value, @cache.get(key, false)
|
194
|
+
assert_equal @value, @cache.get(key)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_increment
|
198
|
+
@cache.set key, 10, 0, false
|
199
|
+
assert_equal 11, @cache.increment(key)
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_increment_offset
|
203
|
+
@cache.set key, 10, 0, false
|
204
|
+
assert_equal 15, @cache.increment(key, 5)
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_missing_increment
|
208
|
+
# XXX Fails due to libmemcached bug
|
209
|
+
@cache.delete key rescue nil
|
210
|
+
assert_raise(Memcached::NotFound) do
|
211
|
+
@cache.increment key
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_decrement
|
216
|
+
@cache.set key, 10, 0, false
|
217
|
+
assert_equal 9, @cache.decrement(key)
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_decrement_offset
|
221
|
+
@cache.set key, 10, 0, false
|
222
|
+
assert_equal 5, @cache.decrement(key, 5)
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_missing_decrement
|
226
|
+
# XXX Fails due to libmemcached bug
|
227
|
+
@cache.delete key rescue nil
|
228
|
+
assert_raise(Memcached::NotFound) do
|
229
|
+
@cache.decrement key
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_replace
|
234
|
+
@cache.set key, nil
|
235
|
+
assert_nothing_raised do
|
236
|
+
@cache.replace key, @value
|
237
|
+
end
|
238
|
+
assert_equal @value, @cache.get(key)
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_missing_replace
|
242
|
+
@cache.delete key rescue nil
|
243
|
+
assert_raise(Memcached::NotStored) do
|
244
|
+
@cache.replace key, @value
|
245
|
+
end
|
246
|
+
assert_raise(Memcached::NotFound) do
|
247
|
+
assert_equal @value, @cache.get(key)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_append
|
252
|
+
@cache.set key, "start", 0, false
|
253
|
+
assert_nothing_raised do
|
254
|
+
@cache.append key, "end"
|
255
|
+
end
|
256
|
+
assert_equal "startend", @cache.get(key, false)
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_missing_append
|
260
|
+
@cache.delete key rescue nil
|
261
|
+
assert_raise(Memcached::NotStored) do
|
262
|
+
@cache.append key, "end"
|
263
|
+
end
|
264
|
+
assert_raise(Memcached::NotFound) do
|
265
|
+
assert_equal @value, @cache.get(key)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_prepend
|
270
|
+
@cache.set key, "end", 0, false
|
271
|
+
assert_nothing_raised do
|
272
|
+
@cache.prepend key, "start"
|
273
|
+
end
|
274
|
+
assert_equal "startend", @cache.get(key, false)
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_missing_prepend
|
278
|
+
@cache.delete key rescue nil
|
279
|
+
assert_raise(Memcached::NotStored) do
|
280
|
+
@cache.prepend key, "end"
|
281
|
+
end
|
282
|
+
assert_raise(Memcached::NotFound) do
|
283
|
+
assert_equal @value, @cache.get(key)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_cas
|
288
|
+
# XXX Not implemented
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_stats
|
292
|
+
stats = @cache.stats
|
293
|
+
assert_equal 2, stats[:pid].size
|
294
|
+
assert_instance_of Fixnum, stats[:pid].first
|
295
|
+
assert_instance_of String, stats[:version].first
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_clone
|
299
|
+
cache = @cache.clone
|
300
|
+
assert_equal cache.servers, @cache.servers
|
301
|
+
assert_not_equal cache, @cache
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_thread_contention
|
305
|
+
threads = []
|
306
|
+
4.times do |index|
|
307
|
+
threads << Thread.new do
|
308
|
+
cache = @cache.clone
|
309
|
+
assert_nothing_raised do
|
310
|
+
cache.set("test_thread_contention_#{index}", index)
|
311
|
+
end
|
312
|
+
assert_equal index, cache.get("test_thread_contention_#{index}")
|
313
|
+
end
|
314
|
+
end
|
315
|
+
threads.each {|thread| thread.join}
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def key
|
321
|
+
caller.first[/`(.*)'/, 1]
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|