dalli 1.1.4 → 2.7.11
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/Gemfile +8 -4
- data/History.md +267 -1
- data/LICENSE +1 -1
- data/README.md +129 -85
- data/lib/action_dispatch/middleware/session/dalli_store.rb +7 -1
- data/lib/active_support/cache/dalli_store.rb +361 -104
- data/lib/dalli.rb +9 -7
- data/lib/dalli/cas/client.rb +59 -0
- data/lib/dalli/client.rb +298 -103
- data/lib/dalli/compressor.rb +30 -0
- data/lib/dalli/options.rb +19 -0
- data/lib/dalli/railtie.rb +8 -0
- data/lib/dalli/ring.rb +59 -22
- data/lib/dalli/server.rb +346 -125
- data/lib/dalli/socket.rb +105 -105
- data/lib/dalli/version.rb +2 -1
- data/lib/rack/session/dalli.rb +156 -22
- metadata +29 -104
- data/Performance.md +0 -85
- data/Rakefile +0 -39
- data/Upgrade.md +0 -45
- data/dalli.gemspec +0 -31
- data/lib/active_support/cache/dalli_store23.rb +0 -172
- data/lib/dalli/compatibility.rb +0 -52
- data/lib/dalli/memcache-client.rb +0 -1
- data/test/abstract_unit.rb +0 -282
- data/test/benchmark_test.rb +0 -170
- data/test/helper.rb +0 -39
- data/test/memcached_mock.rb +0 -126
- data/test/test_active_support.rb +0 -201
- data/test/test_compatibility.rb +0 -33
- data/test/test_dalli.rb +0 -450
- data/test/test_encoding.rb +0 -43
- data/test/test_failover.rb +0 -107
- data/test/test_network.rb +0 -54
- data/test/test_ring.rb +0 -85
- data/test/test_sasl.rb +0 -83
- data/test/test_session_store.rb +0 -225
- data/test/test_synchrony.rb +0 -175
data/test/helper.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
$TESTING = true
|
2
|
-
require 'rubygems'
|
3
|
-
# require 'simplecov'
|
4
|
-
# SimpleCov.start
|
5
|
-
WANT_RAILS_VERSION = ENV['RAILS_VERSION'] || '>= 3.0.0'
|
6
|
-
gem 'rails', WANT_RAILS_VERSION
|
7
|
-
require 'rails'
|
8
|
-
puts "Testing with Rails #{Rails.version}"
|
9
|
-
|
10
|
-
require 'test/unit'
|
11
|
-
require 'shoulda'
|
12
|
-
require 'memcached_mock'
|
13
|
-
require 'mocha'
|
14
|
-
|
15
|
-
require 'dalli'
|
16
|
-
require 'logger'
|
17
|
-
|
18
|
-
Dalli.logger = Logger.new(STDOUT)
|
19
|
-
Dalli.logger.level = Logger::ERROR
|
20
|
-
|
21
|
-
class Test::Unit::TestCase
|
22
|
-
include MemcachedMock::Helper
|
23
|
-
|
24
|
-
def assert_error(error, regexp=nil, &block)
|
25
|
-
ex = assert_raise(error, &block)
|
26
|
-
assert_match(regexp, ex.message, "#{ex.class.name}: #{ex.message}\n#{ex.backtrace.join("\n\t")}")
|
27
|
-
end
|
28
|
-
|
29
|
-
def with_activesupport
|
30
|
-
require 'active_support/all'
|
31
|
-
yield
|
32
|
-
end
|
33
|
-
|
34
|
-
def with_actionpack
|
35
|
-
require 'action_dispatch'
|
36
|
-
require 'action_controller'
|
37
|
-
yield
|
38
|
-
end
|
39
|
-
end
|
data/test/memcached_mock.rb
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
require "socket"
|
2
|
-
|
3
|
-
$started = {}
|
4
|
-
|
5
|
-
module MemcachedMock
|
6
|
-
def self.start(port=19123, &block)
|
7
|
-
server = TCPServer.new("localhost", port)
|
8
|
-
session = server.accept
|
9
|
-
block.call session
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.delayed_start(port=19123, wait=1, &block)
|
13
|
-
server = TCPServer.new("localhost", port)
|
14
|
-
sleep wait
|
15
|
-
block.call server
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.tmp_socket_path
|
19
|
-
"#{Dir.pwd}/tmp.sock"
|
20
|
-
end
|
21
|
-
|
22
|
-
module Helper
|
23
|
-
# Forks the current process and starts a new mock Memcached server on
|
24
|
-
# port 22122.
|
25
|
-
#
|
26
|
-
# memcached_mock(lambda {|sock| socket.write('123') }) do
|
27
|
-
# assert_equal "PONG", Dalli::Client.new('localhost:22122').get('abc')
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
def memcached_mock(proc, meth = :start)
|
31
|
-
return unless supports_fork?
|
32
|
-
begin
|
33
|
-
pid = fork do
|
34
|
-
trap("TERM") { exit }
|
35
|
-
|
36
|
-
MemcachedMock.send(meth) do |*args|
|
37
|
-
proc.call(*args)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
sleep 0.3 # Give time for the socket to start listening.
|
42
|
-
yield
|
43
|
-
ensure
|
44
|
-
if pid
|
45
|
-
Process.kill("TERM", pid)
|
46
|
-
Process.wait(pid)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
PATHS = %w(
|
52
|
-
/usr/local/bin/
|
53
|
-
/opt/local/bin/
|
54
|
-
/usr/bin/
|
55
|
-
)
|
56
|
-
|
57
|
-
def find_memcached
|
58
|
-
output = `memcached -h | head -1`.strip
|
59
|
-
if output && output =~ /^memcached (\d.\d.\d+)/ && $1 > '1.4'
|
60
|
-
return (puts "Found #{output} in PATH"; '')
|
61
|
-
end
|
62
|
-
PATHS.each do |path|
|
63
|
-
output = `memcached -h | head -1`.strip
|
64
|
-
if output && output =~ /^memcached (\d\.\d\.\d+)/ && $1 > '1.4'
|
65
|
-
return (puts "Found #{output} in #{path}"; path)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
raise Errno::ENOENT, "Unable to find memcached 1.4+ locally"
|
70
|
-
nil
|
71
|
-
end
|
72
|
-
|
73
|
-
def memcached(port=19122, args='', options={})
|
74
|
-
return unless supports_fork?
|
75
|
-
Memcached.path ||= find_memcached
|
76
|
-
|
77
|
-
cmd = if options[:unix]
|
78
|
-
"#{Memcached.path}memcached #{args} -s #{MemcachedMock.tmp_socket_path}"
|
79
|
-
else
|
80
|
-
"#{Memcached.path}memcached #{args} -p #{port}"
|
81
|
-
end
|
82
|
-
|
83
|
-
$started[port] ||= begin
|
84
|
-
#puts "Starting: #{cmd}..."
|
85
|
-
pid = IO.popen(cmd).pid
|
86
|
-
at_exit do
|
87
|
-
begin
|
88
|
-
Process.kill("TERM", pid)
|
89
|
-
Process.wait(pid)
|
90
|
-
File.delete(MemcachedMock.tmp_socket_path) if options[:unix]
|
91
|
-
rescue Errno::ECHILD, Errno::ESRCH
|
92
|
-
end
|
93
|
-
end
|
94
|
-
sleep 0.1
|
95
|
-
pid
|
96
|
-
end
|
97
|
-
if options[:unix]
|
98
|
-
yield Dalli::Client.new(MemcachedMock.tmp_socket_path)
|
99
|
-
else
|
100
|
-
yield Dalli::Client.new(["localhost:#{port}", "127.0.0.1:#{port}"], options)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def supports_fork?
|
105
|
-
!defined?(RUBY_ENGINE) || RUBY_ENGINE != 'jruby'
|
106
|
-
end
|
107
|
-
|
108
|
-
def memcached_kill(port)
|
109
|
-
pid = $started.delete(port)
|
110
|
-
if pid
|
111
|
-
begin
|
112
|
-
Process.kill("TERM", pid)
|
113
|
-
Process.wait(pid)
|
114
|
-
rescue Errno::ECHILD, Errno::ESRCH
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
module Memcached
|
123
|
-
class << self
|
124
|
-
attr_accessor :path
|
125
|
-
end
|
126
|
-
end
|
data/test/test_active_support.rb
DELETED
@@ -1,201 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'helper'
|
3
|
-
|
4
|
-
class TestActiveSupport < Test::Unit::TestCase
|
5
|
-
context 'active_support caching' do
|
6
|
-
|
7
|
-
should 'support fetch' do
|
8
|
-
with_activesupport do
|
9
|
-
memcached do
|
10
|
-
connect
|
11
|
-
mvalue = @mc.fetch('somekeywithoutspaces', :expires_in => 1.second) { 123 }
|
12
|
-
dvalue = @dalli.fetch('someotherkeywithoutspaces', :expires_in => 1.second) { 123 }
|
13
|
-
assert_equal 123, dvalue
|
14
|
-
assert_equal mvalue, dvalue
|
15
|
-
|
16
|
-
o = Object.new
|
17
|
-
o.instance_variable_set :@foo, 'bar'
|
18
|
-
mvalue = @mc.fetch(rand_key, :raw => true) { o }
|
19
|
-
dvalue = @dalli.fetch(rand_key, :raw => true) { o }
|
20
|
-
assert_equal mvalue, dvalue
|
21
|
-
assert_equal o, mvalue
|
22
|
-
|
23
|
-
mvalue = @mc.fetch(rand_key) { o }
|
24
|
-
dvalue = @dalli.fetch(rand_key) { o }
|
25
|
-
assert_equal mvalue, dvalue
|
26
|
-
assert_equal o, dvalue
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
should 'support keys with spaces on Rails3' do
|
32
|
-
with_activesupport do
|
33
|
-
memcached do
|
34
|
-
connect
|
35
|
-
dvalue = @mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
|
36
|
-
mvalue = @dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
|
37
|
-
assert_equal mvalue, dvalue
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
should 'support read_multi' do
|
43
|
-
with_activesupport do
|
44
|
-
memcached do
|
45
|
-
connect
|
46
|
-
x = rand_key
|
47
|
-
y = rand_key
|
48
|
-
assert_equal({}, @mc.read_multi(x, y))
|
49
|
-
assert_equal({}, @dalli.read_multi(x, y))
|
50
|
-
@dalli.write(x, '123')
|
51
|
-
@dalli.write(y, 123)
|
52
|
-
@mc.write(x, '123')
|
53
|
-
@mc.write(y, 123)
|
54
|
-
assert_equal({ x => '123', y => 123 }, @dalli.read_multi(x, y))
|
55
|
-
assert_equal({ x => '123', y => 123 }, @mc.read_multi(x, y))
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
should 'support read_multi with an array' do
|
61
|
-
with_activesupport do
|
62
|
-
memcached do
|
63
|
-
connect
|
64
|
-
x = rand_key
|
65
|
-
y = rand_key
|
66
|
-
assert_equal({}, @mc.read_multi([x, y]))
|
67
|
-
assert_equal({}, @dalli.read_multi([x, y]))
|
68
|
-
@dalli.write(x, '123')
|
69
|
-
@dalli.write(y, 123)
|
70
|
-
@mc.write(x, '123')
|
71
|
-
@mc.write(y, 123)
|
72
|
-
assert_equal({ x => '123', y => 123 }, @dalli.read_multi([x, y]))
|
73
|
-
assert_equal({}, @mc.read_multi([x,y]))
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
should 'support raw read_multi' do
|
79
|
-
with_activesupport do
|
80
|
-
memcached do
|
81
|
-
connect
|
82
|
-
@mc.write("abc", 5, :raw => true)
|
83
|
-
@mc.write("cba", 5, :raw => true)
|
84
|
-
assert_equal({'abc' => '5', 'cba' => '5' }, @mc.read_multi("abc", "cba"))
|
85
|
-
|
86
|
-
@dalli.write("abc", 5, :raw => true)
|
87
|
-
@dalli.write("cba", 5, :raw => true)
|
88
|
-
assert_equal({'abc' => '5', 'cba' => '5' }, @dalli.read_multi("abc", "cba"))
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
should 'support read, write and delete' do
|
94
|
-
with_activesupport do
|
95
|
-
memcached do
|
96
|
-
connect
|
97
|
-
x = rand_key
|
98
|
-
y = rand_key
|
99
|
-
assert_nil @mc.read(x)
|
100
|
-
assert_nil @dalli.read(y)
|
101
|
-
mres = @mc.write(x, 123)
|
102
|
-
dres = @dalli.write(y, 123)
|
103
|
-
assert_equal mres, dres
|
104
|
-
|
105
|
-
mres = @mc.read(x)
|
106
|
-
dres = @dalli.read(y)
|
107
|
-
assert_equal mres, dres
|
108
|
-
assert_equal 123, dres
|
109
|
-
|
110
|
-
mres = @mc.delete(x)
|
111
|
-
dres = @dalli.delete(y)
|
112
|
-
assert_equal mres, dres
|
113
|
-
assert_equal true, dres
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
should 'support increment/decrement commands' do
|
119
|
-
with_activesupport do
|
120
|
-
memcached do
|
121
|
-
connect
|
122
|
-
assert_equal true, @mc.write('counter', 0, :raw => true)
|
123
|
-
assert_equal 1, @mc.increment('counter')
|
124
|
-
assert_equal 2, @mc.increment('counter')
|
125
|
-
assert_equal 1, @mc.decrement('counter')
|
126
|
-
assert_equal "1", @mc.read('counter', :raw => true)
|
127
|
-
|
128
|
-
assert_equal true, @dalli.write('counter', 0, :raw => true)
|
129
|
-
assert_equal 1, @dalli.increment('counter')
|
130
|
-
assert_equal 2, @dalli.increment('counter')
|
131
|
-
assert_equal 1, @dalli.decrement('counter')
|
132
|
-
assert_equal "1", @dalli.read('counter', :raw => true)
|
133
|
-
|
134
|
-
assert_equal 0, @mc.increment('counterX')
|
135
|
-
assert_equal 0, @mc.increment('counterX')
|
136
|
-
assert_equal nil, @mc.read('counterX')
|
137
|
-
|
138
|
-
assert_equal 1, @dalli.increment('counterX')
|
139
|
-
assert_equal 2, @dalli.increment('counterX')
|
140
|
-
assert_equal 2, @dalli.read('counterX', :raw => true).to_i
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
should 'support other esoteric commands' do
|
146
|
-
with_activesupport do
|
147
|
-
memcached do
|
148
|
-
connect
|
149
|
-
ms = @mc.stats
|
150
|
-
ds = @dalli.stats
|
151
|
-
assert_equal ms.keys.sort, ds.keys.sort
|
152
|
-
assert_equal ms[ms.keys.first].keys.sort, ds[ds.keys.first].keys.sort
|
153
|
-
|
154
|
-
assert_equal true, @dalli.write(:foo, 'a')
|
155
|
-
assert_equal true, @mc.write(:foo, 'a')
|
156
|
-
|
157
|
-
assert_equal true, @mc.exist?(:foo)
|
158
|
-
assert_equal true, @dalli.exist?(:foo)
|
159
|
-
|
160
|
-
assert_equal false, @mc.exist?(:bar)
|
161
|
-
assert_equal false, @dalli.exist?(:bar)
|
162
|
-
|
163
|
-
@dalli.reset
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
should 'handle crazy characters from far-away lands' do
|
170
|
-
with_activesupport do
|
171
|
-
memcached do
|
172
|
-
connect
|
173
|
-
key = "fooƒ"
|
174
|
-
value = 'bafƒ'
|
175
|
-
# assert_equal true, @mc.write(key, value)
|
176
|
-
assert_equal true, @dalli.write(key, value)
|
177
|
-
# assert_equal true, @mc.read(key, value)
|
178
|
-
assert_equal value, @dalli.read(key)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
should 'normalize options as expected' do
|
184
|
-
with_activesupport do
|
185
|
-
memcached do
|
186
|
-
@dalli = ActiveSupport::Cache::DalliStore.new('localhost:19122', :expires_in => 1, :race_condition_ttl => 1)
|
187
|
-
assert_equal 2, @dalli.instance_variable_get(:@data).instance_variable_get(:@options)[:expires_in]
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def connect
|
193
|
-
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122', :expires_in => 10.seconds, :namespace => 'x')
|
194
|
-
@mc = ActiveSupport::Cache.lookup_store(:mem_cache_store, 'localhost:19122', :expires_in => 10.seconds, :namespace => 'a')
|
195
|
-
@dalli.clear
|
196
|
-
end
|
197
|
-
|
198
|
-
def rand_key
|
199
|
-
rand(1_000_000_000)
|
200
|
-
end
|
201
|
-
end
|
data/test/test_compatibility.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class TestCompatibility < Test::Unit::TestCase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
require 'dalli/memcache-client'
|
7
|
-
end
|
8
|
-
|
9
|
-
context 'dalli in memcache-client mode' do
|
10
|
-
|
11
|
-
should 'handle old raw flag to set/add/replace' do
|
12
|
-
memcached do |dc|
|
13
|
-
assert_equal "STORED\r\n", dc.set('abc', 123, 5, true)
|
14
|
-
assert_equal '123', dc.get('abc', true)
|
15
|
-
|
16
|
-
assert_equal "NOT_STORED\r\n", dc.add('abc', 456, 5, true)
|
17
|
-
assert_equal '123', dc.get('abc', true)
|
18
|
-
|
19
|
-
assert_equal "STORED\r\n", dc.replace('abc', 456, 5, false)
|
20
|
-
assert_equal 456, dc.get('abc', false)
|
21
|
-
|
22
|
-
assert_equal "DELETED\r\n", dc.delete('abc')
|
23
|
-
assert_equal "NOT_DELETED\r\n", dc.delete('abc')
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def teardown
|
30
|
-
Dalli::Client.compatibility_mode = false
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
data/test/test_dalli.rb
DELETED
@@ -1,450 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'memcached_mock'
|
3
|
-
|
4
|
-
class TestDalli < Test::Unit::TestCase
|
5
|
-
|
6
|
-
should "default to localhost:11211" do
|
7
|
-
dc = Dalli::Client.new
|
8
|
-
ring = dc.send(:ring)
|
9
|
-
s1 = ring.servers.first.hostname
|
10
|
-
assert_equal 1, ring.servers.size
|
11
|
-
dc.close
|
12
|
-
|
13
|
-
dc = Dalli::Client.new('localhost:11211')
|
14
|
-
ring = dc.send(:ring)
|
15
|
-
s2 = ring.servers.first.hostname
|
16
|
-
assert_equal 1, ring.servers.size
|
17
|
-
dc.close
|
18
|
-
|
19
|
-
dc = Dalli::Client.new(['localhost:11211'])
|
20
|
-
ring = dc.send(:ring)
|
21
|
-
s3 = ring.servers.first.hostname
|
22
|
-
assert_equal 1, ring.servers.size
|
23
|
-
dc.close
|
24
|
-
|
25
|
-
assert_equal '127.0.0.1', s1
|
26
|
-
assert_equal s2, s3
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'using unix sockets' do
|
30
|
-
should 'pass smoke test' do
|
31
|
-
memcached(nil,'',{:unix => true}) do |dc|
|
32
|
-
# get/set
|
33
|
-
dc.flush
|
34
|
-
assert_nil dc.get(:a)
|
35
|
-
assert dc.set(:a,1)
|
36
|
-
assert_equal 1, dc.get(:a)
|
37
|
-
|
38
|
-
# replace
|
39
|
-
dc.set(:a,1)
|
40
|
-
dc.replace(:a,2)
|
41
|
-
assert_equal 2, dc.get(:a)
|
42
|
-
|
43
|
-
# delete
|
44
|
-
dc.delete(:a)
|
45
|
-
assert_nil dc.get(:a)
|
46
|
-
|
47
|
-
# fetch
|
48
|
-
executed, expected = false, 1
|
49
|
-
|
50
|
-
value = dc.fetch(:fetched) do
|
51
|
-
executed = true
|
52
|
-
expected
|
53
|
-
end
|
54
|
-
|
55
|
-
assert executed
|
56
|
-
assert_equal expected, value
|
57
|
-
|
58
|
-
executed = false
|
59
|
-
value = dc.fetch(:fetched) do
|
60
|
-
executed = true
|
61
|
-
expected
|
62
|
-
end
|
63
|
-
|
64
|
-
assert !executed
|
65
|
-
assert_equal expected, value
|
66
|
-
|
67
|
-
# cas
|
68
|
-
dc.set(:a,1)
|
69
|
-
3.times { dc.cas(:a){|a| a+=1} }
|
70
|
-
assert_equal 4, dc.get(:a)
|
71
|
-
|
72
|
-
# get_multi
|
73
|
-
resp = dc.get_multi(%w(b c d))
|
74
|
-
assert_equal({}, resp)
|
75
|
-
dc.set("b",1)
|
76
|
-
dc.set("c",11)
|
77
|
-
resp = dc.get_multi(%w(b c d))
|
78
|
-
assert_equal({"b" => 1, "c" => 11}, resp)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
context 'using a live server' do
|
84
|
-
|
85
|
-
should "support get/set" do
|
86
|
-
memcached do |dc|
|
87
|
-
dc.flush
|
88
|
-
|
89
|
-
val1 = "1234567890"*105000
|
90
|
-
assert_error Dalli::DalliError, /too large/ do
|
91
|
-
dc.set('a', val1)
|
92
|
-
val2 = dc.get('a')
|
93
|
-
assert_equal val1, val2
|
94
|
-
end
|
95
|
-
|
96
|
-
val1 = "1234567890"*100000
|
97
|
-
dc.set('a', val1)
|
98
|
-
val2 = dc.get('a')
|
99
|
-
assert_equal val1, val2
|
100
|
-
|
101
|
-
assert_equal true, dc.set('a', nil)
|
102
|
-
assert_nil dc.get('a')
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
should "support the fetch operation" do
|
107
|
-
memcached do |dc|
|
108
|
-
dc.flush
|
109
|
-
|
110
|
-
expected = { 'blah' => 'blerg!' }
|
111
|
-
executed = false
|
112
|
-
value = dc.fetch('fetch_key') do
|
113
|
-
executed = true
|
114
|
-
expected
|
115
|
-
end
|
116
|
-
assert_equal expected, value
|
117
|
-
assert_equal true, executed
|
118
|
-
|
119
|
-
executed = false
|
120
|
-
value = dc.fetch('fetch_key') do
|
121
|
-
executed = true
|
122
|
-
expected
|
123
|
-
end
|
124
|
-
assert_equal expected, value
|
125
|
-
assert_equal false, executed
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
should "support the cas operation" do
|
130
|
-
memcached do |dc|
|
131
|
-
dc.flush
|
132
|
-
|
133
|
-
expected = { 'blah' => 'blerg!' }
|
134
|
-
|
135
|
-
resp = dc.cas('cas_key') do |value|
|
136
|
-
fail('Value should not exist')
|
137
|
-
end
|
138
|
-
assert_nil resp
|
139
|
-
|
140
|
-
mutated = { 'blah' => 'foo!' }
|
141
|
-
dc.set('cas_key', expected)
|
142
|
-
resp = dc.cas('cas_key') do |value|
|
143
|
-
assert_equal expected, value
|
144
|
-
mutated
|
145
|
-
end
|
146
|
-
assert_equal true, resp
|
147
|
-
|
148
|
-
resp = dc.get('cas_key')
|
149
|
-
assert_equal mutated, resp
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
should "support multi-get" do
|
154
|
-
memcached do |dc|
|
155
|
-
dc.close
|
156
|
-
dc.flush
|
157
|
-
resp = dc.get_multi(%w(a b c d e f))
|
158
|
-
assert_equal({}, resp)
|
159
|
-
|
160
|
-
dc.set('a', 'foo')
|
161
|
-
dc.set('b', 123)
|
162
|
-
dc.set('c', %w(a b c))
|
163
|
-
resp = dc.get_multi(%w(a b c d e f))
|
164
|
-
assert_equal({ 'a' => 'foo', 'b' => 123, 'c' => %w(a b c) }, resp)
|
165
|
-
|
166
|
-
# Perform a huge multi-get with 10,000 elements.
|
167
|
-
arr = []
|
168
|
-
dc.multi do
|
169
|
-
10_000.times do |idx|
|
170
|
-
dc.set idx, idx
|
171
|
-
arr << idx
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
result = dc.get_multi(arr)
|
176
|
-
assert_equal(10_000, result.size)
|
177
|
-
assert_equal(1000, result['1000'])
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
should 'support raw incr/decr' do
|
182
|
-
memcached do |client|
|
183
|
-
client.flush
|
184
|
-
|
185
|
-
assert_equal true, client.set('fakecounter', 0, 0, :raw => true)
|
186
|
-
assert_equal 1, client.incr('fakecounter', 1)
|
187
|
-
assert_equal 2, client.incr('fakecounter', 1)
|
188
|
-
assert_equal 3, client.incr('fakecounter', 1)
|
189
|
-
assert_equal 1, client.decr('fakecounter', 2)
|
190
|
-
assert_equal "1", client.get('fakecounter', :raw => true)
|
191
|
-
|
192
|
-
resp = client.incr('mycounter', 0)
|
193
|
-
assert_nil resp
|
194
|
-
|
195
|
-
resp = client.incr('mycounter', 1, 0, 2)
|
196
|
-
assert_equal 2, resp
|
197
|
-
resp = client.incr('mycounter', 1)
|
198
|
-
assert_equal 3, resp
|
199
|
-
|
200
|
-
resp = client.set('rawcounter', 10, 0, :raw => true)
|
201
|
-
assert_equal true, resp
|
202
|
-
|
203
|
-
resp = client.get('rawcounter', :raw => true)
|
204
|
-
assert_equal '10', resp
|
205
|
-
|
206
|
-
resp = client.incr('rawcounter', 1)
|
207
|
-
assert_equal 11, resp
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
should "support incr/decr operations" do
|
212
|
-
memcached do |dc|
|
213
|
-
dc.flush
|
214
|
-
|
215
|
-
resp = dc.decr('counter', 100, 5, 0)
|
216
|
-
assert_equal 0, resp
|
217
|
-
|
218
|
-
resp = dc.decr('counter', 10)
|
219
|
-
assert_equal 0, resp
|
220
|
-
|
221
|
-
resp = dc.incr('counter', 10)
|
222
|
-
assert_equal 10, resp
|
223
|
-
|
224
|
-
current = 10
|
225
|
-
100.times do |x|
|
226
|
-
resp = dc.incr('counter', 10)
|
227
|
-
assert_equal current + ((x+1)*10), resp
|
228
|
-
end
|
229
|
-
|
230
|
-
resp = dc.decr('10billion', 0, 5, 10)
|
231
|
-
# go over the 32-bit mark to verify proper (un)packing
|
232
|
-
resp = dc.incr('10billion', 10_000_000_000)
|
233
|
-
assert_equal 10_000_000_010, resp
|
234
|
-
|
235
|
-
resp = dc.decr('10billion', 1)
|
236
|
-
assert_equal 10_000_000_009, resp
|
237
|
-
|
238
|
-
resp = dc.decr('10billion', 0)
|
239
|
-
assert_equal 10_000_000_009, resp
|
240
|
-
|
241
|
-
resp = dc.incr('10billion', 0)
|
242
|
-
assert_equal 10_000_000_009, resp
|
243
|
-
|
244
|
-
assert_nil dc.incr('DNE', 10)
|
245
|
-
assert_nil dc.decr('DNE', 10)
|
246
|
-
|
247
|
-
resp = dc.incr('big', 100, 5, 0xFFFFFFFFFFFFFFFE)
|
248
|
-
assert_equal 0xFFFFFFFFFFFFFFFE, resp
|
249
|
-
resp = dc.incr('big', 1)
|
250
|
-
assert_equal 0xFFFFFFFFFFFFFFFF, resp
|
251
|
-
|
252
|
-
# rollover the 64-bit value, we'll get something undefined.
|
253
|
-
resp = dc.incr('big', 1)
|
254
|
-
assert_not_equal 0x10000000000000000, resp
|
255
|
-
dc.reset
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
should 'support the append and prepend operations' do
|
260
|
-
memcached do |dc|
|
261
|
-
resp = dc.flush
|
262
|
-
assert_equal true, dc.set('456', 'xyz', 0, :raw => true)
|
263
|
-
assert_equal true, dc.prepend('456', '0')
|
264
|
-
assert_equal true, dc.append('456', '9')
|
265
|
-
assert_equal '0xyz9', dc.get('456', :raw => true)
|
266
|
-
assert_equal '0xyz9', dc.get('456')
|
267
|
-
|
268
|
-
assert_equal false, dc.append('nonexist', 'abc')
|
269
|
-
assert_equal false, dc.prepend('nonexist', 'abc')
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
should "pass a simple smoke test" do
|
274
|
-
memcached do |dc|
|
275
|
-
resp = dc.flush
|
276
|
-
assert_not_nil resp
|
277
|
-
assert_equal [true, true], resp
|
278
|
-
|
279
|
-
assert_equal true, dc.set(:foo, 'bar')
|
280
|
-
assert_equal 'bar', dc.get(:foo)
|
281
|
-
|
282
|
-
resp = dc.get('123')
|
283
|
-
assert_equal nil, resp
|
284
|
-
|
285
|
-
resp = dc.set('123', 'xyz')
|
286
|
-
assert_equal true, resp
|
287
|
-
|
288
|
-
resp = dc.get('123')
|
289
|
-
assert_equal 'xyz', resp
|
290
|
-
|
291
|
-
resp = dc.set('123', 'abc')
|
292
|
-
assert_equal true, resp
|
293
|
-
|
294
|
-
dc.prepend('123', '0')
|
295
|
-
dc.append('123', '0')
|
296
|
-
|
297
|
-
assert_raises Dalli::DalliError do
|
298
|
-
resp = dc.get('123')
|
299
|
-
end
|
300
|
-
|
301
|
-
dc.close
|
302
|
-
dc = nil
|
303
|
-
|
304
|
-
dc = Dalli::Client.new('localhost:19122')
|
305
|
-
|
306
|
-
resp = dc.set('456', 'xyz', 0, :raw => true)
|
307
|
-
assert_equal true, resp
|
308
|
-
|
309
|
-
resp = dc.prepend '456', '0'
|
310
|
-
assert_equal true, resp
|
311
|
-
|
312
|
-
resp = dc.append '456', '9'
|
313
|
-
assert_equal true, resp
|
314
|
-
|
315
|
-
resp = dc.get('456', :raw => true)
|
316
|
-
assert_equal '0xyz9', resp
|
317
|
-
|
318
|
-
resp = dc.stats
|
319
|
-
assert_equal Hash, resp.class
|
320
|
-
|
321
|
-
dc.close
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
should "support multithreaded access" do
|
326
|
-
memcached do |cache|
|
327
|
-
cache.flush
|
328
|
-
workers = []
|
329
|
-
|
330
|
-
cache.set('f', 'zzz')
|
331
|
-
assert_equal true, (cache.cas('f') do |value|
|
332
|
-
value << 'z'
|
333
|
-
end)
|
334
|
-
assert_equal 'zzzz', cache.get('f')
|
335
|
-
|
336
|
-
# Have a bunch of threads perform a bunch of operations at the same time.
|
337
|
-
# Verify the result of each operation to ensure the request and response
|
338
|
-
# are not intermingled between threads.
|
339
|
-
10.times do
|
340
|
-
workers << Thread.new do
|
341
|
-
100.times do
|
342
|
-
cache.set('a', 9)
|
343
|
-
cache.set('b', 11)
|
344
|
-
inc = cache.incr('cat', 10, 0, 10)
|
345
|
-
cache.set('f', 'zzz')
|
346
|
-
assert_not_nil(cache.cas('f') do |value|
|
347
|
-
value << 'z'
|
348
|
-
end)
|
349
|
-
assert_equal false, cache.add('a', 11)
|
350
|
-
assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
|
351
|
-
inc = cache.incr('cat', 10)
|
352
|
-
assert_equal 0, inc % 5
|
353
|
-
dec = cache.decr('cat', 5)
|
354
|
-
assert_equal 11, cache.get('b')
|
355
|
-
end
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
workers.each { |w| w.join }
|
360
|
-
cache.flush
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
should "handle namespaced keys" do
|
365
|
-
memcached do |dc|
|
366
|
-
dc = Dalli::Client.new('localhost:19122', :namespace => 'a')
|
367
|
-
dc.set('namespaced', 1)
|
368
|
-
dc2 = Dalli::Client.new('localhost:19122', :namespace => 'b')
|
369
|
-
dc2.set('namespaced', 2)
|
370
|
-
assert_equal 1, dc.get('namespaced')
|
371
|
-
assert_equal 2, dc2.get('namespaced')
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
should "handle namespaced keys in multi_get" do
|
376
|
-
memcached do |dc|
|
377
|
-
dc = Dalli::Client.new('localhost:19122', :namespace => 'a')
|
378
|
-
dc.set('a', 1)
|
379
|
-
dc.set('b', 2)
|
380
|
-
assert_equal({'a' => 1, 'b' => 2}, dc.get_multi('a', 'b'))
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
should "handle application marshalling issues" do
|
385
|
-
memcached do |dc|
|
386
|
-
old = Dalli.logger
|
387
|
-
Dalli.logger = Logger.new(nil)
|
388
|
-
begin
|
389
|
-
assert_equal false, dc.set('a', Proc.new { true })
|
390
|
-
ensure
|
391
|
-
Dalli.logger = old
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
context 'with compression' do
|
397
|
-
should 'allow large values' do
|
398
|
-
memcached do |dc|
|
399
|
-
dalli = Dalli::Client.new(dc.instance_variable_get(:@servers), :compression => true)
|
400
|
-
|
401
|
-
value = "0"*1024*1024
|
402
|
-
assert_raise Dalli::DalliError, /too large/ do
|
403
|
-
dc.set('verylarge', value)
|
404
|
-
end
|
405
|
-
dalli.set('verylarge', value)
|
406
|
-
end
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
context 'in low memory conditions' do
|
411
|
-
|
412
|
-
should 'handle error response correctly' do
|
413
|
-
memcached(19125, '-m 1 -M') do |dc|
|
414
|
-
failed = false
|
415
|
-
value = "1234567890"*100
|
416
|
-
1_000.times do |idx|
|
417
|
-
begin
|
418
|
-
assert_equal true, dc.set(idx, value)
|
419
|
-
rescue Dalli::DalliError
|
420
|
-
failed = true
|
421
|
-
assert((800..960).include?(idx), "unexpected failure on iteration #{idx}")
|
422
|
-
break
|
423
|
-
end
|
424
|
-
end
|
425
|
-
assert failed, 'did not fail under low memory conditions'
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
should 'fit more values with compression' do
|
430
|
-
memcached(19126, '-m 1 -M') do |dc|
|
431
|
-
dalli = Dalli::Client.new('localhost:19126', :compression => true)
|
432
|
-
failed = false
|
433
|
-
value = "1234567890"*1000
|
434
|
-
10_000.times do |idx|
|
435
|
-
begin
|
436
|
-
assert_equal true, dalli.set(idx, value)
|
437
|
-
rescue Dalli::DalliError
|
438
|
-
failed = true
|
439
|
-
assert((6000..7800).include?(idx), "unexpected failure on iteration #{idx}")
|
440
|
-
break
|
441
|
-
end
|
442
|
-
end
|
443
|
-
assert failed, 'did not fail under low memory conditions'
|
444
|
-
end
|
445
|
-
end
|
446
|
-
|
447
|
-
end
|
448
|
-
|
449
|
-
end
|
450
|
-
end
|