dalli 0.9.4 → 0.9.5
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.
- data/Gemfile +1 -1
- data/History.md +10 -0
- data/README.md +5 -4
- data/Rakefile +6 -1
- data/Upgrade.md +6 -17
- data/dalli.gemspec +1 -1
- data/lib/active_support/cache/dalli_store.rb +7 -18
- data/lib/active_support/cache/dalli_store23.rb +14 -25
- data/lib/dalli.rb +6 -5
- data/lib/dalli/client.rb +30 -26
- data/lib/dalli/options.rb +0 -27
- data/lib/dalli/server.rb +9 -9
- data/lib/dalli/version.rb +1 -1
- data/test/abstract_unit.rb +0 -1
- data/test/benchmark_test.rb +27 -15
- data/test/helper.rb +15 -8
- data/test/test_active_support.rb +35 -5
- data/test/test_dalli.rb +41 -9
- data/test/test_session_store.rb +5 -0
- metadata +3 -3
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
HEAD
|
5
|
+
-----
|
6
|
+
|
7
|
+
- Major design change - raw support is back to maximize compatibility with Rails
|
8
|
+
and the increment/decrement operations. You can now pass :raw => true to most methods
|
9
|
+
to bypass (un)marshalling.
|
10
|
+
- Support symbols as keys (ddollar)
|
11
|
+
|
12
|
+
|
4
13
|
0.9.4
|
5
14
|
-----
|
6
15
|
|
@@ -23,6 +32,7 @@ Dalli Changelog
|
|
23
32
|
|
24
33
|
- Verify proper operation in Heroku.
|
25
34
|
|
35
|
+
|
26
36
|
0.9.1
|
27
37
|
----
|
28
38
|
|
data/README.md
CHANGED
@@ -91,10 +91,7 @@ Put this at the bottom of `config/environment.rb`:
|
|
91
91
|
Features and Changes
|
92
92
|
------------------------
|
93
93
|
|
94
|
-
Dalli is **NOT** 100% API compatible with memcache-client. If you have code which uses the MemCache
|
95
|
-
API directly, it will likely need small tweaks. Method parameters and return values changed slightly.
|
96
|
-
|
97
|
-
memcache-client allowed developers to store either raw or marshalled values with each API call. I feel this is needless complexity; Dalli allows you to control marshalling per-Client with the `:marshal => false` flag but you cannot explicitly set the raw flag for each API call. By default, marshalling is enabled.
|
94
|
+
Dalli is **NOT** 100% API compatible with memcache-client. If you have code which uses the MemCache API directly, it will likely need small tweaks. Method parameters and return values changed slightly. See Upgrade.md for more detail.
|
98
95
|
|
99
96
|
I've removed support for key namespaces and automatic pruning of keys longer than 250 characters. ActiveSupport::Cache implements these features so there is little need for Dalli to reinvent them.
|
100
97
|
|
@@ -114,6 +111,10 @@ Thanks
|
|
114
111
|
|
115
112
|
Brian Mitchell - for his remix-stash project which was helpful when implementing and testing the binary protocol support.
|
116
113
|
|
114
|
+
[NorthScale](http://northscale.com) - for their project sponsorship
|
115
|
+
|
116
|
+
[Bootspring](http://bootspring.com) is my Ruby and Rails consulting company. We specialize in Ruby infrastructure, performance and scalability tuning for Rails applications. If you need help, please [contact us](mailto:info@bootspring.com) today.
|
117
|
+
|
117
118
|
|
118
119
|
Author
|
119
120
|
----------
|
data/Rakefile
CHANGED
@@ -9,4 +9,9 @@ Rake::TestTask.new(:bench) do |test|
|
|
9
9
|
test.pattern = 'test/benchmark_test.rb'
|
10
10
|
end
|
11
11
|
|
12
|
-
task :default => :test
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
task :test_all do
|
15
|
+
system('rake test RAILS_VERSION="~> 2.3.0"')
|
16
|
+
system('rake test RAILS_VERSION="~> 3.0.0"')
|
17
|
+
end
|
data/Upgrade.md
CHANGED
@@ -1,37 +1,26 @@
|
|
1
1
|
Upgrading from memcache-client
|
2
2
|
========
|
3
3
|
|
4
|
-
Dalli is not meant to be 100% compatible with memcache-client, there are
|
4
|
+
Dalli is not meant to be 100% compatible with memcache-client, there are a few minor differences in the API.
|
5
5
|
|
6
6
|
|
7
7
|
Marshalling
|
8
8
|
---------------
|
9
9
|
|
10
|
-
Dalli has
|
11
|
-
|
12
|
-
Take this typical operation:
|
10
|
+
Dalli has changed the raw parameter to a :raw option. The memcache-client API allowed you to control marshalling on a per-method basis using a boolean 'raw' parameter to several of the API methods:
|
13
11
|
|
14
12
|
cache = MemCache.new
|
15
|
-
cache.set('abc', 123)
|
16
|
-
|
17
|
-
Technically 123 is an Integer and presumably you want `cache.get('abc')` to return an Integer. Since memcached stores values as binary blobs, Dalli will serialize the value to a binary blob for storage. When you get() the value back, Ruby will deserialize it properly to an Integer and all will be well. Without marshalling, Dalli will convert values to Strings and so get() would return a String, not an Integer.
|
18
|
-
|
19
|
-
The memcache-client API allowed you to control marshalling on a per-method basis using a boolean 'raw' parameter to several of the API methods:
|
20
|
-
|
21
|
-
cache = MemCache.new
|
22
13
|
cache.set('abc', 123, 0, true)
|
23
14
|
cache.get('abc', true) => '123'
|
24
|
-
|
15
|
+
|
25
16
|
cache.set('abc', 123, 0)
|
26
17
|
cache.get('abc') => 123
|
27
18
|
|
28
|
-
Note that the last
|
29
|
-
|
30
|
-
If the code specifies raw as false, you can simply remove that parameter. If the code is using raw = true, you will need to use the :marshal option to create a Dalli::Client instance that does not perform marshalling:
|
19
|
+
Note that the last parameter is set to true in the first two API calls and so `get` returns a string, not an integer. In the second example, we don't provide the raw parameter. Since it defaults to false, it works exactly like Dalli.
|
31
20
|
|
32
|
-
|
21
|
+
If the code specifies raw as false, you can simply remove that parameter. If the code is using raw = true, you will need to use the :raw option:
|
33
22
|
|
34
|
-
|
23
|
+
cache.set('abc', 123, 0, :raw => true)
|
35
24
|
|
36
25
|
|
37
26
|
Return Values
|
data/dalli.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Dalli::VERSION
|
8
8
|
|
9
9
|
s.authors = ["Mike Perham"]
|
10
|
-
s.date = %
|
10
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
11
11
|
s.description = %q{High performance memcached client for Ruby}
|
12
12
|
s.email = %q{mperham@gmail.com}
|
13
13
|
s.files = Dir.glob("lib/**/*") + [
|
@@ -15,13 +15,6 @@ module ActiveSupport
|
|
15
15
|
# DalliStore implements the Strategy::LocalCache strategy which implements
|
16
16
|
# an in memory cache inside of a block.
|
17
17
|
class DalliStore < Store
|
18
|
-
module Response # :nodoc:
|
19
|
-
STORED = "STORED\r\n"
|
20
|
-
NOT_STORED = "NOT_STORED\r\n"
|
21
|
-
EXISTS = "EXISTS\r\n"
|
22
|
-
NOT_FOUND = "NOT_FOUND\r\n"
|
23
|
-
DELETED = "DELETED\r\n"
|
24
|
-
end
|
25
18
|
|
26
19
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
27
20
|
|
@@ -46,13 +39,9 @@ module ActiveSupport
|
|
46
39
|
options = addresses.extract_options!
|
47
40
|
super(options)
|
48
41
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
mem_cache_options = options.dup
|
53
|
-
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
54
|
-
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
55
|
-
end
|
42
|
+
mem_cache_options = options.dup
|
43
|
+
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
44
|
+
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
56
45
|
|
57
46
|
extend Strategy::LocalCache
|
58
47
|
extend LocalCacheWithRaw
|
@@ -64,7 +53,7 @@ module ActiveSupport
|
|
64
53
|
options = names.extract_options!
|
65
54
|
options = merged_options(options)
|
66
55
|
keys_to_names = names.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
|
67
|
-
raw_values = @data.get_multi(keys_to_names.keys)
|
56
|
+
raw_values = @data.get_multi(keys_to_names.keys, options)
|
68
57
|
values = {}
|
69
58
|
raw_values.each do |key, value|
|
70
59
|
entry = deserialize_entry(value)
|
@@ -111,13 +100,13 @@ module ActiveSupport
|
|
111
100
|
end
|
112
101
|
|
113
102
|
def reset
|
114
|
-
@
|
103
|
+
@data.reset
|
115
104
|
end
|
116
105
|
|
117
106
|
protected
|
118
107
|
# Read an entry from the cache.
|
119
108
|
def read_entry(key, options) # :nodoc:
|
120
|
-
deserialize_entry(@data.get(escape_key(key)))
|
109
|
+
deserialize_entry(@data.get(escape_key(key), options))
|
121
110
|
rescue Dalli::DalliError => e
|
122
111
|
logger.error("DalliError (#{e}): #{e.message}") if logger
|
123
112
|
nil
|
@@ -132,7 +121,7 @@ module ActiveSupport
|
|
132
121
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
133
122
|
expires_in += 5.minutes
|
134
123
|
end
|
135
|
-
@data.send(method, escape_key(key), value, expires_in)
|
124
|
+
@data.send(method, escape_key(key), value, expires_in, options)
|
136
125
|
rescue Dalli::DalliError => e
|
137
126
|
logger.error("DalliError (#{e}): #{e.message}") if logger
|
138
127
|
false
|
@@ -14,19 +14,11 @@ module ActiveSupport
|
|
14
14
|
# DalliStore implements the Strategy::LocalCache strategy which implements
|
15
15
|
# an in memory cache inside of a block.
|
16
16
|
class DalliStore < Store
|
17
|
-
module Response # :nodoc:
|
18
|
-
STORED = "STORED\r\n"
|
19
|
-
NOT_STORED = "NOT_STORED\r\n"
|
20
|
-
EXISTS = "EXISTS\r\n"
|
21
|
-
NOT_FOUND = "NOT_FOUND\r\n"
|
22
|
-
DELETED = "DELETED\r\n"
|
23
|
-
end
|
24
17
|
|
25
18
|
def self.build_mem_cache(*addresses)
|
26
19
|
addresses = addresses.flatten
|
27
20
|
options = addresses.extract_options!
|
28
21
|
addresses = ["localhost"] if addresses.empty?
|
29
|
-
# No need to marshal since we marshal here.
|
30
22
|
Dalli::Client.new(addresses, options)
|
31
23
|
end
|
32
24
|
|
@@ -42,13 +34,10 @@ module ActiveSupport
|
|
42
34
|
def initialize(*addresses)
|
43
35
|
addresses = addresses.flatten
|
44
36
|
options = addresses.extract_options!
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@namespace = mem_cache_options.delete(:namespace)
|
50
|
-
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
51
|
-
end
|
37
|
+
|
38
|
+
mem_cache_options = options.dup
|
39
|
+
@namespace = mem_cache_options.delete(:namespace)
|
40
|
+
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
52
41
|
|
53
42
|
extend Strategy::LocalCache
|
54
43
|
end
|
@@ -78,26 +67,26 @@ module ActiveSupport
|
|
78
67
|
# to zero.
|
79
68
|
def increment(key, amount = 1) # :nodoc:
|
80
69
|
log("incrementing", key, amount)
|
81
|
-
|
82
|
-
response = @data.incr(escape_key(key), amount)
|
83
|
-
response == Response::NOT_FOUND ? nil : response
|
70
|
+
@data.incr(escape_key(key), amount)
|
84
71
|
rescue Dalli::DalliError
|
72
|
+
logger.error("DalliError (#{e}): #{e.message}")
|
85
73
|
nil
|
86
74
|
end
|
75
|
+
|
87
76
|
# Decrement a cached value. This method uses the memcached decr atomic
|
88
77
|
# operator and can only be used on values written with the :raw option.
|
89
78
|
# Calling it on a value not stored with :raw will initialize that value
|
90
79
|
# to zero.
|
91
80
|
def decrement(key, amount = 1) # :nodoc:
|
92
81
|
log("decrement", key, amount)
|
93
|
-
|
94
|
-
response == Response::NOT_FOUND ? nil : response
|
82
|
+
@data.decr(escape_key(key), amount)
|
95
83
|
rescue Dalli::DalliError
|
84
|
+
logger.error("DalliError (#{e}): #{e.message}")
|
96
85
|
nil
|
97
86
|
end
|
98
87
|
|
99
88
|
def reset
|
100
|
-
@
|
89
|
+
@data.reset
|
101
90
|
end
|
102
91
|
|
103
92
|
# Clear the entire cache on all memcached servers. This method should
|
@@ -114,9 +103,9 @@ module ActiveSupport
|
|
114
103
|
# Read an entry from the cache.
|
115
104
|
def read(key, options = nil) # :nodoc:
|
116
105
|
super
|
117
|
-
value = @data.get(escape_key(key))
|
106
|
+
value = @data.get(escape_key(key), options)
|
118
107
|
return nil if value.nil?
|
119
|
-
value = Marshal.load
|
108
|
+
value = options && options[:raw] ? value : Marshal.load(value)
|
120
109
|
value
|
121
110
|
rescue Dalli::DalliError => e
|
122
111
|
logger.error("DalliError (#{e}): #{e.message}")
|
@@ -133,8 +122,8 @@ module ActiveSupport
|
|
133
122
|
def write(key, value, options = nil)
|
134
123
|
super
|
135
124
|
method = options && options[:unless_exist] ? :add : :set
|
136
|
-
value = Marshal.dump
|
137
|
-
@data.send(method, escape_key(key), value, expires_in(options))
|
125
|
+
value = options && options[:raw] ? value.to_s : Marshal.dump(value)
|
126
|
+
@data.send(method, escape_key(key), value, expires_in(options), options)
|
138
127
|
rescue Dalli::DalliError => e
|
139
128
|
logger.error("DalliError (#{e}): #{e.message}")
|
140
129
|
false
|
data/lib/dalli.rb
CHANGED
@@ -11,11 +11,12 @@ module Dalli
|
|
11
11
|
class NetworkError < DalliError; end
|
12
12
|
|
13
13
|
def self.logger
|
14
|
-
@logger ||=
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
@logger ||= (rails_logger || default_logger)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.rails_logger
|
18
|
+
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
19
|
+
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
|
19
20
|
end
|
20
21
|
|
21
22
|
def self.default_logger
|
data/lib/dalli/client.rb
CHANGED
@@ -6,7 +6,7 @@ module Dalli
|
|
6
6
|
# the memcached server. Usage:
|
7
7
|
# <pre>
|
8
8
|
# Dalli::Client.new(['localhost:11211:10', 'cache-2.example.com:11211:5', '192.168.0.1:22122:5'],
|
9
|
-
# :threadsafe =>
|
9
|
+
# :threadsafe => true, :failover => true)
|
10
10
|
# </pre>
|
11
11
|
# servers is an Array of "host:port:weight" where weight allows you to distribute cache unevenly.
|
12
12
|
# Both weight and port are optional.
|
@@ -14,64 +14,62 @@ module Dalli
|
|
14
14
|
# Options:
|
15
15
|
# :failover - if a server is down, store the value on another server. Default: true.
|
16
16
|
# :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
|
17
|
-
# :marshal - ensure that the value you store is exactly what is returned. Otherwise you can see this:
|
18
|
-
# set('abc', 123)
|
19
|
-
# get('abc') ==> '123' (Note you set an Integer but got back a String)
|
20
|
-
# Default: true.
|
21
17
|
#
|
22
18
|
def initialize(servers=nil, options={})
|
23
19
|
@servers = servers
|
24
20
|
@options = options
|
25
|
-
self.extend(Dalli::Marshal) unless options[:marshal] == false
|
26
21
|
end
|
27
22
|
|
28
23
|
#
|
29
24
|
# The standard memcached instruction set
|
30
25
|
#
|
31
26
|
|
32
|
-
def get(key)
|
27
|
+
def get(key, options=nil)
|
33
28
|
resp = perform(:get, key)
|
34
|
-
(!resp || resp == 'Not found') ? nil : deserialize(resp)
|
29
|
+
(!resp || resp == 'Not found') ? nil : deserialize(resp, options)
|
35
30
|
end
|
36
31
|
|
37
32
|
def get_multi(*keys)
|
33
|
+
return {} if keys.empty?
|
34
|
+
options = nil
|
35
|
+
options = keys.pop if keys.last.is_a?(Hash)
|
38
36
|
ring.lock do
|
39
37
|
keys.flatten.each do |key|
|
40
38
|
perform(:getkq, key)
|
41
39
|
end
|
42
40
|
values = ring.servers.inject({}) { |hash, s| hash.merge!(s.request(:noop)); hash }
|
43
|
-
values.inject(values) { |memo, (k,v)| memo[k] = deserialize(v); memo }
|
41
|
+
values.inject(values) { |memo, (k,v)| memo[k] = deserialize(v, options); memo }
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
|
-
def fetch(key, ttl=0)
|
48
|
-
val = get(key)
|
45
|
+
def fetch(key, ttl=0, options=nil)
|
46
|
+
val = get(key, options)
|
49
47
|
if val.nil? && block_given?
|
50
48
|
val = yield
|
51
|
-
add(key, val, ttl)
|
49
|
+
add(key, val, ttl, options)
|
52
50
|
end
|
53
51
|
val
|
54
52
|
end
|
55
53
|
|
56
|
-
def cas(key, ttl=0, &block)
|
54
|
+
def cas(key, ttl=0, options=nil, &block)
|
57
55
|
(value, cas) = perform(:cas, key)
|
58
|
-
value = (!value || value == 'Not found') ? nil : deserialize(value)
|
56
|
+
value = (!value || value == 'Not found') ? nil : deserialize(value, options)
|
59
57
|
if value
|
60
58
|
newvalue = block.call(value)
|
61
|
-
perform(:add, key, serialize(newvalue), ttl, cas)
|
59
|
+
perform(:add, key, serialize(newvalue, options), ttl, cas)
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|
65
|
-
def set(key, value, ttl=0)
|
66
|
-
perform(:set, key, serialize(value), ttl)
|
63
|
+
def set(key, value, ttl=0, options=nil)
|
64
|
+
perform(:set, key, serialize(value, options), ttl)
|
67
65
|
end
|
68
66
|
|
69
|
-
def add(key, value, ttl=0)
|
70
|
-
perform(:add, key, serialize(value), ttl, 0)
|
67
|
+
def add(key, value, ttl=0, options=nil)
|
68
|
+
perform(:add, key, serialize(value, options), ttl, 0)
|
71
69
|
end
|
72
70
|
|
73
|
-
def replace(key, value, ttl=0)
|
74
|
-
perform(:replace, key, serialize(value), ttl)
|
71
|
+
def replace(key, value, ttl=0, options=nil)
|
72
|
+
perform(:replace, key, serialize(value, options), ttl)
|
75
73
|
end
|
76
74
|
|
77
75
|
def delete(key)
|
@@ -148,12 +146,14 @@ module Dalli
|
|
148
146
|
)
|
149
147
|
end
|
150
148
|
|
151
|
-
def serialize(value)
|
152
|
-
value.to_s
|
149
|
+
def serialize(value, options)
|
150
|
+
options && options[:raw] ? value.to_s : ::Marshal.dump(value)
|
153
151
|
end
|
154
|
-
|
155
|
-
def deserialize(value)
|
156
|
-
value
|
152
|
+
|
153
|
+
def deserialize(value, options)
|
154
|
+
options && options[:raw] ? value : ::Marshal.load(value)
|
155
|
+
rescue TypeError
|
156
|
+
raise Dalli::DalliError, "Invalid marshalled data in memcached: #{value}"
|
157
157
|
end
|
158
158
|
|
159
159
|
def env_servers
|
@@ -163,6 +163,10 @@ module Dalli
|
|
163
163
|
# Chokepoint method for instrumentation
|
164
164
|
def perform(op, *args)
|
165
165
|
key = args.first
|
166
|
+
if key.is_a?(Symbol)
|
167
|
+
key = key.to_s
|
168
|
+
args[0] = key
|
169
|
+
end
|
166
170
|
validate_key(key)
|
167
171
|
server = ring.server_for_key(key)
|
168
172
|
server.request(op, *args)
|
data/lib/dalli/options.rb
CHANGED
@@ -2,33 +2,6 @@ require 'thread'
|
|
2
2
|
|
3
3
|
module Dalli
|
4
4
|
|
5
|
-
# Auto-marshal all values in/out of memcached.
|
6
|
-
# Otherwise, Dalli will just use to_s on all values.
|
7
|
-
#
|
8
|
-
# Dalli::Client.extend(Dalli::Marshal)
|
9
|
-
#
|
10
|
-
module Marshal
|
11
|
-
def serialize(value)
|
12
|
-
::Marshal.dump(value)
|
13
|
-
end
|
14
|
-
|
15
|
-
def deserialize(value)
|
16
|
-
begin
|
17
|
-
::Marshal.load(value)
|
18
|
-
rescue TypeError
|
19
|
-
raise Dalli::DalliError, "Invalid marshalled data in memcached, this happens if you switch the :marshal option and still have old data in memcached: #{value}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def append(key, value)
|
24
|
-
raise Dalli::DalliError, "Marshalling and append do not work together"
|
25
|
-
end
|
26
|
-
|
27
|
-
def prepend(key, value)
|
28
|
-
raise Dalli::DalliError, "Marshalling and prepend do not work together"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
5
|
# Make Dalli threadsafe by using a lock around all
|
33
6
|
# public server methods.
|
34
7
|
#
|
data/lib/dalli/server.rb
CHANGED
@@ -167,7 +167,7 @@ module Dalli
|
|
167
167
|
def cas_response
|
168
168
|
header = read(24)
|
169
169
|
raise Dalli::NetworkError, 'No response' if !header
|
170
|
-
(extras, status, count, _, cas) = header.unpack(CAS_HEADER)
|
170
|
+
(extras, type, status, count, _, cas) = header.unpack(CAS_HEADER)
|
171
171
|
data = read(count) if count > 0
|
172
172
|
if status == 1
|
173
173
|
nil
|
@@ -182,7 +182,7 @@ module Dalli
|
|
182
182
|
def generic_response
|
183
183
|
header = read(24)
|
184
184
|
raise Dalli::NetworkError, 'No response' if !header
|
185
|
-
(extras, status, count) = header.unpack(NORMAL_HEADER)
|
185
|
+
(extras, type, status, count) = header.unpack(NORMAL_HEADER)
|
186
186
|
data = read(count) if count > 0
|
187
187
|
if status == 1
|
188
188
|
nil
|
@@ -287,15 +287,15 @@ module Dalli
|
|
287
287
|
end
|
288
288
|
|
289
289
|
def split(n)
|
290
|
-
[
|
290
|
+
[n >> 32, 0xFFFFFFFF & n]
|
291
291
|
end
|
292
292
|
|
293
293
|
def longlong(a, b)
|
294
|
-
a
|
294
|
+
(a << 32) | b
|
295
295
|
end
|
296
296
|
|
297
|
-
CAS_HEADER = '@
|
298
|
-
NORMAL_HEADER = '@
|
297
|
+
CAS_HEADER = '@4CCnNNQ'
|
298
|
+
NORMAL_HEADER = '@4CCnN'
|
299
299
|
KV_HEADER = '@2n@6nN'
|
300
300
|
|
301
301
|
REQUEST = 0x80
|
@@ -379,7 +379,7 @@ module Dalli
|
|
379
379
|
|
380
380
|
def sasl_authentication(socket)
|
381
381
|
init_sasl if !defined?(::SASL)
|
382
|
-
|
382
|
+
|
383
383
|
Dalli.logger.info { "Dalli/SASL authenticating as #{username}" }
|
384
384
|
|
385
385
|
# negotiate
|
@@ -387,7 +387,7 @@ module Dalli
|
|
387
387
|
socket.write(req)
|
388
388
|
header = read(24, socket)
|
389
389
|
raise Dalli::NetworkError, 'No response' if !header
|
390
|
-
(extras, status, count) = header.unpack(NORMAL_HEADER)
|
390
|
+
(extras, type, status, count) = header.unpack(NORMAL_HEADER)
|
391
391
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
392
392
|
content = read(count, socket)
|
393
393
|
return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
|
@@ -403,7 +403,7 @@ module Dalli
|
|
403
403
|
|
404
404
|
header = read(24, socket)
|
405
405
|
raise Dalli::NetworkError, 'No response' if !header
|
406
|
-
(extras, status, count) = header.unpack(NORMAL_HEADER)
|
406
|
+
(extras, type, status, count) = header.unpack(NORMAL_HEADER)
|
407
407
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
408
408
|
content = read(count, socket)
|
409
409
|
return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
|
data/lib/dalli/version.rb
CHANGED
data/test/abstract_unit.rb
CHANGED
data/test/benchmark_test.rb
CHANGED
@@ -18,6 +18,7 @@ class TestBenchmark < Test::Unit::TestCase
|
|
18
18
|
# 5 and 6 are only used for multiget miss test
|
19
19
|
@key5 = "Medium2"*8
|
20
20
|
@key6 = "Long3"*40
|
21
|
+
@counter = 'counter'
|
21
22
|
end
|
22
23
|
|
23
24
|
def test_benchmark
|
@@ -27,15 +28,15 @@ class TestBenchmark < Test::Unit::TestCase
|
|
27
28
|
|
28
29
|
n = 2500
|
29
30
|
|
30
|
-
@m = Dalli::Client.new(@servers
|
31
|
+
@m = Dalli::Client.new(@servers)
|
31
32
|
x.report("set:plain:dalli") do
|
32
33
|
n.times do
|
33
|
-
@m.set @key1, @marshalled
|
34
|
-
@m.set @key2, @marshalled
|
35
|
-
@m.set @key3, @marshalled
|
36
|
-
@m.set @key1, @marshalled
|
37
|
-
@m.set @key2, @marshalled
|
38
|
-
@m.set @key3, @marshalled
|
34
|
+
@m.set @key1, @marshalled, 0, :raw => true
|
35
|
+
@m.set @key2, @marshalled, 0, :raw => true
|
36
|
+
@m.set @key3, @marshalled, 0, :raw => true
|
37
|
+
@m.set @key1, @marshalled, 0, :raw => true
|
38
|
+
@m.set @key2, @marshalled, 0, :raw => true
|
39
|
+
@m.set @key3, @marshalled, 0, :raw => true
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
@@ -51,15 +52,15 @@ class TestBenchmark < Test::Unit::TestCase
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
|
-
@m = Dalli::Client.new(@servers
|
55
|
+
@m = Dalli::Client.new(@servers)
|
55
56
|
x.report("get:plain:dalli") do
|
56
57
|
n.times do
|
57
|
-
@m.get @key1
|
58
|
-
@m.get @key2
|
59
|
-
@m.get @key3
|
60
|
-
@m.get @key1
|
61
|
-
@m.get @key2
|
62
|
-
@m.get @key3
|
58
|
+
@m.get @key1, :raw => true
|
59
|
+
@m.get @key2, :raw => true
|
60
|
+
@m.get @key3, :raw => true
|
61
|
+
@m.get @key1, :raw => true
|
62
|
+
@m.get @key2, :raw => true
|
63
|
+
@m.get @key3, :raw => true
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
@@ -113,7 +114,18 @@ class TestBenchmark < Test::Unit::TestCase
|
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
116
|
-
|
117
|
+
@m = Dalli::Client.new(@servers)
|
118
|
+
x.report("incr:ruby:dalli") do
|
119
|
+
n.times do
|
120
|
+
@m.incr @counter, 1, 0, 1
|
121
|
+
end
|
122
|
+
n.times do
|
123
|
+
@m.decr @counter, 1
|
124
|
+
end
|
125
|
+
|
126
|
+
assert_equal 0, @m.incr(@counter, 0)
|
127
|
+
end
|
128
|
+
|
117
129
|
end
|
118
130
|
end
|
119
131
|
|
data/test/helper.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
# require 'simplecov
|
2
|
+
# require 'simplecov'
|
3
3
|
# SimpleCov.start
|
4
|
+
RAILS_VERSION = ENV['RAILS_VERSION'] || '~> 3.0.0'
|
5
|
+
#puts "Testing with Rails #{RAILS_VERSION}"
|
6
|
+
gem 'rails', RAILS_VERSION
|
4
7
|
|
5
8
|
require 'test/unit'
|
6
9
|
require 'shoulda'
|
@@ -12,25 +15,29 @@ require 'dalli'
|
|
12
15
|
class Test::Unit::TestCase
|
13
16
|
include MemcachedMock::Helper
|
14
17
|
|
18
|
+
def rails3?
|
19
|
+
RAILS_VERSION =~ /3\.0\./
|
20
|
+
end
|
21
|
+
|
15
22
|
def assert_error(error, regexp=nil, &block)
|
16
23
|
ex = assert_raise(error, &block)
|
17
24
|
assert_match(regexp, ex.message, "#{ex.class.name}: #{ex.message}\n#{ex.backtrace.join("\n\t")}")
|
18
25
|
end
|
19
26
|
|
20
27
|
def with_activesupport
|
21
|
-
case
|
22
|
-
when
|
28
|
+
case
|
29
|
+
when rails3?
|
23
30
|
require 'active_support/all'
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
else
|
32
|
+
require 'active_support'
|
33
|
+
require 'active_support/cache/dalli_store23'
|
27
34
|
end
|
28
35
|
yield
|
29
36
|
end
|
30
37
|
|
31
38
|
def with_actionpack
|
32
|
-
case
|
33
|
-
when
|
39
|
+
case
|
40
|
+
when rails3?
|
34
41
|
require 'action_dispatch'
|
35
42
|
require 'action_controller'
|
36
43
|
# when '2.3.0'
|
data/test/test_active_support.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require 'rails'
|
3
2
|
|
4
3
|
class TestActiveSupport < Test::Unit::TestCase
|
5
4
|
context 'active_support caching' do
|
@@ -27,13 +26,23 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
|
-
should 'support keys with spaces' do
|
29
|
+
should 'support keys with spaces on Rails3' do
|
31
30
|
with_activesupport do
|
32
31
|
memcached do
|
33
32
|
connect
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
case
|
34
|
+
when rails3?
|
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
|
+
else
|
39
|
+
assert_raises ArgumentError do
|
40
|
+
@mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
|
41
|
+
end
|
42
|
+
assert_raises ArgumentError do
|
43
|
+
@dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
|
44
|
+
end
|
45
|
+
end
|
37
46
|
end
|
38
47
|
end
|
39
48
|
end
|
@@ -81,6 +90,25 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
81
90
|
end
|
82
91
|
end
|
83
92
|
|
93
|
+
should 'support increment/decrement commands' do
|
94
|
+
with_activesupport do
|
95
|
+
memcached do
|
96
|
+
connect
|
97
|
+
assert_equal true, @mc.write('counter', 0, :raw => true)
|
98
|
+
assert_equal 1, @mc.increment('counter')
|
99
|
+
assert_equal 2, @mc.increment('counter')
|
100
|
+
assert_equal 1, @mc.decrement('counter')
|
101
|
+
assert_equal "1", @mc.read('counter', :raw => true)
|
102
|
+
|
103
|
+
assert_equal true, @dalli.write('counter', 0, :raw => true)
|
104
|
+
assert_equal 1, @dalli.increment('counter')
|
105
|
+
assert_equal 2, @dalli.increment('counter')
|
106
|
+
assert_equal 1, @dalli.decrement('counter')
|
107
|
+
assert_equal "1", @dalli.read('counter', :raw => true)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
84
112
|
should 'support other esoteric commands' do
|
85
113
|
with_activesupport do
|
86
114
|
memcached do
|
@@ -89,6 +117,8 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
89
117
|
ds = @dalli.stats
|
90
118
|
assert_equal ms.keys.sort, ds.keys.sort
|
91
119
|
assert_equal ms[ms.keys.first].keys.sort, ds[ds.keys.first].keys.sort
|
120
|
+
|
121
|
+
@dalli.reset
|
92
122
|
end
|
93
123
|
end
|
94
124
|
end
|
data/test/test_dalli.rb
CHANGED
@@ -86,6 +86,36 @@ class TestDalli < Test::Unit::TestCase
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
should 'support raw incr/decr' do
|
90
|
+
memcached do |client|
|
91
|
+
client.flush
|
92
|
+
|
93
|
+
assert_equal true, client.set('fakecounter', 0, 0, :raw => true)
|
94
|
+
assert_equal 1, client.incr('fakecounter', 1)
|
95
|
+
assert_equal 2, client.incr('fakecounter', 1)
|
96
|
+
assert_equal 3, client.incr('fakecounter', 1)
|
97
|
+
assert_equal 1, client.decr('fakecounter', 2)
|
98
|
+
assert_equal "1", client.get('fakecounter', :raw => true)
|
99
|
+
|
100
|
+
resp = client.incr('mycounter', 0)
|
101
|
+
assert_nil resp
|
102
|
+
|
103
|
+
resp = client.incr('mycounter', 1, 0, 2)
|
104
|
+
assert_equal 2, resp
|
105
|
+
resp = client.incr('mycounter', 1)
|
106
|
+
assert_equal 3, resp
|
107
|
+
|
108
|
+
resp = client.set('rawcounter', 10, 0, :raw => true)
|
109
|
+
assert_equal true, resp
|
110
|
+
|
111
|
+
resp = client.get('rawcounter', :raw => true)
|
112
|
+
assert_equal '10', resp
|
113
|
+
|
114
|
+
resp = client.incr('rawcounter', 1)
|
115
|
+
assert_equal 11, resp
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
89
119
|
should "support incr/decr operations" do
|
90
120
|
memcached do |dc|
|
91
121
|
dc.flush
|
@@ -140,6 +170,9 @@ class TestDalli < Test::Unit::TestCase
|
|
140
170
|
assert_not_nil resp
|
141
171
|
assert_equal [true, true], resp
|
142
172
|
|
173
|
+
assert_equal true, dc.set(:foo, 'bar')
|
174
|
+
assert_equal 'bar', dc.get(:foo)
|
175
|
+
|
143
176
|
resp = dc.get('123')
|
144
177
|
assert_equal nil, resp
|
145
178
|
|
@@ -152,22 +185,19 @@ class TestDalli < Test::Unit::TestCase
|
|
152
185
|
resp = dc.set('123', 'abc')
|
153
186
|
assert_equal true, resp
|
154
187
|
|
155
|
-
|
156
|
-
|
157
|
-
end
|
188
|
+
dc.prepend('123', '0')
|
189
|
+
dc.append('123', '0')
|
158
190
|
|
159
191
|
assert_raises Dalli::DalliError do
|
160
|
-
dc.
|
192
|
+
resp = dc.get('123')
|
161
193
|
end
|
162
194
|
|
163
|
-
resp = dc.get('123')
|
164
|
-
assert_equal 'abc', resp
|
165
195
|
dc.close
|
166
196
|
dc = nil
|
167
197
|
|
168
|
-
dc = Dalli::Client.new('localhost:19122'
|
198
|
+
dc = Dalli::Client.new('localhost:19122')
|
169
199
|
|
170
|
-
resp = dc.set('456', 'xyz')
|
200
|
+
resp = dc.set('456', 'xyz', 0, :raw => true)
|
171
201
|
assert_equal true, resp
|
172
202
|
|
173
203
|
resp = dc.prepend '456', '0'
|
@@ -176,7 +206,7 @@ class TestDalli < Test::Unit::TestCase
|
|
176
206
|
resp = dc.append '456', '9'
|
177
207
|
assert_equal true, resp
|
178
208
|
|
179
|
-
resp = dc.get('456')
|
209
|
+
resp = dc.get('456', :raw => true)
|
180
210
|
assert_equal '0xyz9', resp
|
181
211
|
|
182
212
|
resp = dc.stats
|
@@ -251,6 +281,8 @@ class TestDalli < Test::Unit::TestCase
|
|
251
281
|
|
252
282
|
should 'support SASL authentication' do
|
253
283
|
memcached(19121, '-S') do |dc|
|
284
|
+
# I get "Dalli::NetworkError: Error authenticating: 32" in OSX
|
285
|
+
# but SASL works on Heroku servers. YMMV.
|
254
286
|
assert_equal true, dc.set('abc', 123)
|
255
287
|
assert_equal 123, dc.get('abc')
|
256
288
|
assert_equal({"localhost:19121"=>{}}, dc.stats)
|
data/test/test_session_store.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
+
if RAILS_VERSION =~ /3\.0\./
|
4
|
+
# Session testing is complex enough that I can't test it in both Rails
|
5
|
+
# 3.0 and 2.3. Help is welcome.
|
6
|
+
|
3
7
|
require 'abstract_unit'
|
4
8
|
require 'action_dispatch/middleware/session/dalli_store'
|
5
9
|
|
@@ -197,4 +201,5 @@ class TestSessionStore < ActionController::IntegrationTest
|
|
197
201
|
yield
|
198
202
|
end
|
199
203
|
end
|
204
|
+
end
|
200
205
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 5
|
9
|
+
version: 0.9.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mike Perham
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-09-15 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|