memcached 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|