arunthampi-memcached 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/BENCHMARKS +120 -0
- data/CHANGELOG +58 -0
- data/LICENSE +184 -0
- data/Manifest +27 -0
- data/README +118 -0
- data/Rakefile +45 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/ext/extconf.rb +106 -0
- data/ext/libmemcached-0.32.tar.gz +0 -0
- data/ext/libmemcached.patch +270 -0
- data/ext/rlibmemcached.i +212 -0
- data/ext/rlibmemcached_wrap.c +13090 -0
- data/lib/memcached.rb +31 -0
- data/lib/memcached/behaviors.rb +78 -0
- data/lib/memcached/exceptions.rb +84 -0
- data/lib/memcached/integer.rb +6 -0
- data/lib/memcached/memcached.rb +554 -0
- data/lib/memcached/rails.rb +97 -0
- data/test/profile/benchmark.rb +210 -0
- data/test/profile/profile.rb +14 -0
- data/test/profile/valgrind.rb +147 -0
- data/test/setup.rb +29 -0
- data/test/teardown.rb +0 -0
- data/test/test_helper.rb +18 -0
- data/test/unit/binding_test.rb +8 -0
- data/test/unit/memcached_test.rb +1132 -0
- data/test/unit/rails_test.rb +102 -0
- metadata +94 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
class Memcached
|
2
|
+
|
3
|
+
(instance_methods - NilClass.instance_methods).each do |method_name|
|
4
|
+
eval("alias :'#{method_name}_orig' :'#{method_name}'")
|
5
|
+
end
|
6
|
+
|
7
|
+
# A legacy compatibility wrapper for the Memcached class. It has basic compatibility with the <b>memcache-client</b> API.
|
8
|
+
class Rails < ::Memcached
|
9
|
+
|
10
|
+
alias :servers= :set_servers
|
11
|
+
|
12
|
+
# See Memcached#new for details.
|
13
|
+
def initialize(*args)
|
14
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
15
|
+
servers = Array(
|
16
|
+
args.any? ? args.unshift : opts.delete(:servers)
|
17
|
+
).flatten.compact
|
18
|
+
|
19
|
+
opts[:prefix_key] ||= opts[:namespace]
|
20
|
+
super(servers, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Wraps Memcached#get so that it doesn't raise. This has the side-effect of preventing you from
|
24
|
+
# storing <tt>nil</tt> values.
|
25
|
+
def get(key, raw=false)
|
26
|
+
super(key, !raw)
|
27
|
+
rescue NotFound
|
28
|
+
end
|
29
|
+
|
30
|
+
# Wraps Memcached#cas so that it doesn't raise. Doesn't set anything if no value is present.
|
31
|
+
def cas(key, ttl=@default_ttl, raw=false, &block)
|
32
|
+
super(key, ttl, !raw, &block)
|
33
|
+
rescue NotFound
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :compare_and_swap :cas
|
37
|
+
|
38
|
+
# Wraps Memcached#get.
|
39
|
+
def get_multi(keys, raw=false)
|
40
|
+
get_orig(keys, !raw)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Wraps Memcached#set.
|
44
|
+
def set(key, value, ttl=@default_ttl, raw=false)
|
45
|
+
super(key, value, ttl, !raw)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Wraps Memcached#add so that it doesn't raise.
|
49
|
+
def add(key, value, ttl=@default_ttl, raw=false)
|
50
|
+
super(key, value, ttl, !raw)
|
51
|
+
true
|
52
|
+
rescue NotStored
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Wraps Memcached#delete so that it doesn't raise.
|
57
|
+
def delete(key)
|
58
|
+
super
|
59
|
+
rescue NotFound
|
60
|
+
end
|
61
|
+
|
62
|
+
# Wraps Memcached#incr so that it doesn't raise.
|
63
|
+
def incr(*args)
|
64
|
+
super
|
65
|
+
rescue NotFound
|
66
|
+
end
|
67
|
+
|
68
|
+
# Wraps Memcached#decr so that it doesn't raise.
|
69
|
+
def decr(*args)
|
70
|
+
super
|
71
|
+
rescue NotFound
|
72
|
+
end
|
73
|
+
|
74
|
+
# Wraps Memcached#append so that it doesn't raise.
|
75
|
+
def append(*args)
|
76
|
+
super
|
77
|
+
rescue NotStored
|
78
|
+
end
|
79
|
+
|
80
|
+
# Wraps Memcached#prepend so that it doesn't raise.
|
81
|
+
def prepend(*args)
|
82
|
+
super
|
83
|
+
rescue NotStored
|
84
|
+
end
|
85
|
+
|
86
|
+
# Namespace accessor.
|
87
|
+
def namespace
|
88
|
+
options[:prefix_key]
|
89
|
+
end
|
90
|
+
|
91
|
+
alias :flush_all :flush
|
92
|
+
|
93
|
+
alias :"[]" :get
|
94
|
+
alias :"[]=" :set
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
|
2
|
+
HERE = File.dirname(__FILE__)
|
3
|
+
$LOAD_PATH << "#{HERE}/../../lib/"
|
4
|
+
UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached')
|
5
|
+
|
6
|
+
require 'memcached'
|
7
|
+
require 'benchmark'
|
8
|
+
require 'rubygems'
|
9
|
+
require 'ruby-debug' if ENV['DEBUG']
|
10
|
+
begin; require 'memory'; rescue LoadError; end
|
11
|
+
|
12
|
+
puts `uname -a`
|
13
|
+
puts "Ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}"
|
14
|
+
|
15
|
+
[ ["memcached", "memcached"],
|
16
|
+
["binary42-remix-stash", "remix/stash"],
|
17
|
+
# ["astro-remcached", "remcached"], # Clobbers the "Memcached" constant
|
18
|
+
["memcache-client", "memcache"]].each do |gem_name, requirement|
|
19
|
+
require requirement
|
20
|
+
gem gem_name
|
21
|
+
puts "Loaded #{gem_name} #{Gem.loaded_specs[gem_name].version.to_s rescue nil}"
|
22
|
+
end
|
23
|
+
|
24
|
+
class Remix::Stash
|
25
|
+
# Remix::Stash API doesn't let you set servers
|
26
|
+
@@clusters = {:default => Remix::Stash::Cluster.new(['127.0.0.1:43042', '127.0.0.1:43043'])}
|
27
|
+
end
|
28
|
+
|
29
|
+
class Bench
|
30
|
+
|
31
|
+
def initialize(loops = nil, stack_depth = nil)
|
32
|
+
@loops = (loops || 20000).to_i
|
33
|
+
@stack_depth = (stack_depth || 0).to_i
|
34
|
+
|
35
|
+
puts "Loops is #{@loops}"
|
36
|
+
puts "Stack depth is #{@stack_depth}"
|
37
|
+
|
38
|
+
@m_value = Marshal.dump(
|
39
|
+
@small_value = ["testing"])
|
40
|
+
@m_large_value = Marshal.dump(
|
41
|
+
@large_value = [{"test" => "1", "test2" => "2", Object.new => "3", 4 => 4, "test5" => 2**65}] * 2048)
|
42
|
+
|
43
|
+
puts "Small value size is: #{@m_value.size} bytes"
|
44
|
+
puts "Large value size is: #{@m_large_value.size} bytes"
|
45
|
+
|
46
|
+
@keys = [
|
47
|
+
@k1 = "Short",
|
48
|
+
@k2 = "Sym1-2-3::45" * 8,
|
49
|
+
@k3 = "Long" * 40,
|
50
|
+
@k4 = "Medium" * 8,
|
51
|
+
@k5 = "Medium2" * 8,
|
52
|
+
@k6 = "Long3" * 40]
|
53
|
+
|
54
|
+
reset_servers
|
55
|
+
reset_clients
|
56
|
+
|
57
|
+
Benchmark.bm(36) do |x|
|
58
|
+
@benchmark = x
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def run(level = @stack_depth)
|
63
|
+
level > 0 ? run(level - 1) : run_without_recursion
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def reset_servers
|
69
|
+
system("ruby #{HERE}/../setup.rb")
|
70
|
+
sleep(1)
|
71
|
+
end
|
72
|
+
|
73
|
+
def reset_clients
|
74
|
+
@clients = {
|
75
|
+
"libm" => Memcached::Rails.new(
|
76
|
+
['127.0.0.1:43042', '127.0.0.1:43043'],
|
77
|
+
:buffer_requests => false, :no_block => false, :namespace => "namespace"),
|
78
|
+
"libm:noblock" => Memcached::Rails.new(
|
79
|
+
['127.0.0.1:43042', '127.0.0.1:43043'],
|
80
|
+
:no_block => true, :buffer_requests => true, :namespace => "namespace"),
|
81
|
+
"libm:udp" => Memcached::Rails.new(
|
82
|
+
["#{UNIX_SOCKET_NAME}0", "#{UNIX_SOCKET_NAME}1"],
|
83
|
+
:buffer_requests => false, :no_block => false, :namespace => "namespace"),
|
84
|
+
"libm:binary" => Memcached::Rails.new(
|
85
|
+
['127.0.0.1:43042', '127.0.0.1:43043'],
|
86
|
+
:buffer_requests => false, :no_block => false, :namespace => "namespace", :binary_protocol => true),
|
87
|
+
"libm:noblock_binary" => Memcached::Rails.new(
|
88
|
+
['127.0.0.1:43042', '127.0.0.1:43043'],
|
89
|
+
:no_block => true, :buffer_requests => true, :namespace => "namespace", :binary_protocol => true),
|
90
|
+
"ruby" => MemCache.new(['127.0.0.1:43042', '127.0.0.1:43043'], :namespace => "namespace"),
|
91
|
+
"stash" => Remix::Stash.new(:root)}
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def benchmark_clients(test_name, clients = @clients)
|
96
|
+
clients.keys.sort.each do |client_name|
|
97
|
+
next if client_name == "stash" and test_name == "set-large" # Don't let stash break the world
|
98
|
+
client = clients[client_name]
|
99
|
+
begin
|
100
|
+
yield client
|
101
|
+
@benchmark.report("#{test_name}:#{client_name}") { @loops.times { yield client } }
|
102
|
+
rescue => e
|
103
|
+
# puts "#{test_name}:#{client_name} => #{e.class}"
|
104
|
+
# reset_clients
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def benchmark_hashes(hashes, test_name)
|
110
|
+
hashes.each do |hash_name, int|
|
111
|
+
@m = Memcached::Rails.new(:hash => hash_name)
|
112
|
+
@benchmark.report("#{test_name}:#{hash_name}") do
|
113
|
+
@loops.times { yield int }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def run_without_recursion
|
119
|
+
benchmark_clients("set") do |c|
|
120
|
+
c.set @k1, @m_value, 0, true
|
121
|
+
c.set @k2, @m_value, 0, true
|
122
|
+
c.set @k3, @m_value, 0, true
|
123
|
+
end
|
124
|
+
|
125
|
+
benchmark_clients("get") do |c|
|
126
|
+
c.get @k1, true
|
127
|
+
c.get @k2, true
|
128
|
+
c.get @k3, true
|
129
|
+
end
|
130
|
+
|
131
|
+
benchmark_clients("get-multi") do |c|
|
132
|
+
c.get_multi @keys, true
|
133
|
+
end
|
134
|
+
|
135
|
+
benchmark_clients("append") do |c|
|
136
|
+
c.append @k1, @m_value
|
137
|
+
c.append @k2, @m_value
|
138
|
+
c.append @k3, @m_value
|
139
|
+
end
|
140
|
+
|
141
|
+
benchmark_clients("delete") do |c|
|
142
|
+
c.delete @k1
|
143
|
+
c.delete @k2
|
144
|
+
c.delete @k3
|
145
|
+
end
|
146
|
+
|
147
|
+
benchmark_clients("get-missing") do |c|
|
148
|
+
c.get @k1
|
149
|
+
c.get @k2
|
150
|
+
c.get @k3
|
151
|
+
end
|
152
|
+
|
153
|
+
benchmark_clients("append-missing") do |c|
|
154
|
+
c.append @k1, @m_value
|
155
|
+
c.append @k2, @m_value
|
156
|
+
c.append @k3, @m_value
|
157
|
+
end
|
158
|
+
|
159
|
+
benchmark_clients("set-large") do |c|
|
160
|
+
c.set @k1, @m_large_value, 0, true
|
161
|
+
c.set @k2, @m_large_value, 0, true
|
162
|
+
c.set @k3, @m_large_value, 0, true
|
163
|
+
end
|
164
|
+
|
165
|
+
benchmark_clients("get-large") do |c|
|
166
|
+
c.get @k1, true
|
167
|
+
c.get @k2, true
|
168
|
+
c.get @k3, true
|
169
|
+
end
|
170
|
+
|
171
|
+
benchmark_clients("set-ruby") do |c|
|
172
|
+
c.set @k1, @value
|
173
|
+
c.set @k2, @value
|
174
|
+
c.set @k3, @value
|
175
|
+
end
|
176
|
+
|
177
|
+
benchmark_clients("get-ruby") do |c|
|
178
|
+
c.get @k1
|
179
|
+
c.get @k2
|
180
|
+
c.get @k3
|
181
|
+
end
|
182
|
+
|
183
|
+
benchmark_clients("set-ruby-large") do |c|
|
184
|
+
c.set @k1, @large_value
|
185
|
+
c.set @k2, @large_value
|
186
|
+
c.set @k3, @large_value
|
187
|
+
end
|
188
|
+
|
189
|
+
benchmark_clients("get-ruby-large") do |c|
|
190
|
+
c.get @k1
|
191
|
+
c.get @k2
|
192
|
+
c.get @k3
|
193
|
+
end
|
194
|
+
|
195
|
+
benchmark_hashes(Memcached::HASH_VALUES, "hash") do |i|
|
196
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k1, i)
|
197
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k2, i)
|
198
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k3, i)
|
199
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k4, i)
|
200
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k5, i)
|
201
|
+
Rlibmemcached.memcached_generate_hash_rvalue(@k6, i)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Bench.new(ENV["LOOPS"], ENV["STACK_DEPTH"]).run
|
207
|
+
|
208
|
+
Process.memory.each do |key, value|
|
209
|
+
puts "#{key}: #{value/1024.0}M"
|
210
|
+
end if Process.respond_to? :memory
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../setup"
|
3
|
+
|
4
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../../lib/"
|
5
|
+
require 'rubygems'
|
6
|
+
require 'memcached'
|
7
|
+
require 'ruby-prof'
|
8
|
+
|
9
|
+
result = RubyProf.profile do
|
10
|
+
load "#{HERE}/profile/valgrind.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
printer = RubyProf::GraphPrinter.new(result)
|
14
|
+
printer.print(STDOUT, 0)
|
@@ -0,0 +1,147 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../setup"
|
3
|
+
|
4
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../../lib/"
|
5
|
+
require 'memcached'
|
6
|
+
require 'rubygems'
|
7
|
+
|
8
|
+
class Worker
|
9
|
+
def initialize(method_name, iterations)
|
10
|
+
@method = method_name || 'mixed'
|
11
|
+
@i = (iterations || 10000).to_i
|
12
|
+
|
13
|
+
puts "*** Running #{@method.inspect} test for #{@i} loops. ***"
|
14
|
+
|
15
|
+
@key1 = "key1-"*8
|
16
|
+
@key2 = "key2-"*8
|
17
|
+
|
18
|
+
@value = []
|
19
|
+
@marshalled = Marshal.dump(@value)
|
20
|
+
|
21
|
+
@opts = [
|
22
|
+
["#{UNIX_SOCKET_NAME}0", "#{UNIX_SOCKET_NAME}1"],
|
23
|
+
{
|
24
|
+
:buffer_requests => false,
|
25
|
+
:no_block => false,
|
26
|
+
:namespace => "namespace"
|
27
|
+
}
|
28
|
+
]
|
29
|
+
@cache = Memcached.new(*@opts)
|
30
|
+
|
31
|
+
@cache.set @key1, @value
|
32
|
+
end
|
33
|
+
|
34
|
+
def work
|
35
|
+
case @method
|
36
|
+
when "set"
|
37
|
+
@i.times do
|
38
|
+
@cache.set @key1, @value
|
39
|
+
end
|
40
|
+
when "get"
|
41
|
+
@i.times do
|
42
|
+
@cache.get @key1
|
43
|
+
end
|
44
|
+
when "delete"
|
45
|
+
@i.times do
|
46
|
+
@cache.set @key1, @value
|
47
|
+
@cache.delete @key1
|
48
|
+
end
|
49
|
+
when "delete-miss"
|
50
|
+
@i.times do
|
51
|
+
@cache.delete @key1
|
52
|
+
end
|
53
|
+
when "get-miss"
|
54
|
+
@i.times do
|
55
|
+
begin
|
56
|
+
@cache.get @key2
|
57
|
+
rescue Memcached::NotFound
|
58
|
+
end
|
59
|
+
end
|
60
|
+
when "get-increasing"
|
61
|
+
one_k = "x"*1024
|
62
|
+
@i.times do |i|
|
63
|
+
@cache.set @key1, one_k*(i+1), 0, false
|
64
|
+
@cache.get @key1, false
|
65
|
+
end
|
66
|
+
when "get-miss-increasing"
|
67
|
+
@i.times do |i|
|
68
|
+
@cache.delete @key2 rescue nil
|
69
|
+
begin
|
70
|
+
@cache.get @key2
|
71
|
+
rescue Memcached::NotFound
|
72
|
+
end
|
73
|
+
end
|
74
|
+
when "add"
|
75
|
+
@i.times do
|
76
|
+
begin
|
77
|
+
@cache.delete @key1
|
78
|
+
rescue
|
79
|
+
end
|
80
|
+
@cache.add @key1, @value
|
81
|
+
end
|
82
|
+
when "add-present"
|
83
|
+
@cache.set @key1, @value
|
84
|
+
@i.times do
|
85
|
+
begin
|
86
|
+
@cache.add @key1, @value
|
87
|
+
rescue Memcached::NotStored
|
88
|
+
end
|
89
|
+
end
|
90
|
+
when "mixed"
|
91
|
+
@i.times do
|
92
|
+
@cache.set @key1, @value
|
93
|
+
@cache.get @key1
|
94
|
+
end
|
95
|
+
when "stats"
|
96
|
+
@i.times do
|
97
|
+
@cache.stats
|
98
|
+
end
|
99
|
+
when "multiget"
|
100
|
+
@i.times do
|
101
|
+
@cache.get([@key1, @key2])
|
102
|
+
end
|
103
|
+
when "clone"
|
104
|
+
@i.times do
|
105
|
+
cache = @cache.clone
|
106
|
+
end
|
107
|
+
when "reset"
|
108
|
+
@i.times do
|
109
|
+
@cache.reset
|
110
|
+
end
|
111
|
+
when "servers"
|
112
|
+
@i.times do
|
113
|
+
@cache.servers
|
114
|
+
end
|
115
|
+
when "server_by_key"
|
116
|
+
@i.times do
|
117
|
+
@cache.server_by_key(@key1)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
raise "No such method"
|
121
|
+
end
|
122
|
+
|
123
|
+
puts "*** Garbage collect. ***"
|
124
|
+
10.times do
|
125
|
+
GC.start
|
126
|
+
sleep 0.1
|
127
|
+
end
|
128
|
+
|
129
|
+
sts, server_sts, clients = 0, 0, 0
|
130
|
+
ObjectSpace.each_object(Memcached) { clients += 1 }
|
131
|
+
ObjectSpace.each_object(Rlibmemcached::MemcachedSt) { sts += 1 }
|
132
|
+
ObjectSpace.each_object(Rlibmemcached::MemcachedServerSt) { server_sts += 1 }
|
133
|
+
puts "*** Structs: #{sts} ***"
|
134
|
+
puts "*** Server structs: #{server_sts} ***"
|
135
|
+
puts "*** Clients: #{clients} ***"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
Worker.new(ENV['METHOD'], ENV['LOOPS']).work
|
140
|
+
|
141
|
+
begin
|
142
|
+
require 'memory'
|
143
|
+
Process.memory.each do |key, value|
|
144
|
+
puts "#{key}: #{value/1024.0}M"
|
145
|
+
end
|
146
|
+
rescue LoadError
|
147
|
+
end
|