dalli 2.6.2 → 2.6.3
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 +2 -2
- data/History.md +8 -0
- data/README.md +3 -3
- data/Rakefile +1 -0
- data/lib/active_support/cache/dalli_store.rb +10 -4
- data/lib/dalli/client.rb +19 -11
- data/lib/dalli/ring.rb +7 -5
- data/lib/dalli/server.rb +11 -5
- data/lib/dalli/version.rb +1 -1
- data/test/memcached_mock.rb +0 -1
- data/test/test_active_support.rb +27 -1
- data/test/test_compressor.rb +15 -11
- data/test/test_dalli.rb +35 -1
- data/test/test_serializer.rb +14 -10
- metadata +3 -3
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
2.6.3
|
5
|
+
=======
|
6
|
+
|
7
|
+
- Support specific stats by passing `:items` or `:slabs` to `stats` method [bukhamseen]
|
8
|
+
- Fix 'can't modify frozen String' errors in `ActiveSupport::Cache::DalliStore` [dblock]
|
9
|
+
- Protect against objects with custom equality checking [theron17]
|
10
|
+
- Warn if value for key is too large to store [locriani]
|
11
|
+
|
4
12
|
2.6.2
|
5
13
|
=======
|
6
14
|
|
data/README.md
CHANGED
@@ -114,7 +114,7 @@ Configuration
|
|
114
114
|
------------------------
|
115
115
|
Dalli::Client accepts the following options. All times are in seconds.
|
116
116
|
|
117
|
-
**expires_in**: Global default for key TTL.
|
117
|
+
**expires_in**: Global default for key TTL. Default is 0, which means no expiry.
|
118
118
|
|
119
119
|
**failover**: Boolean, if true Dalli will failover to another server if the main server for a key is down.
|
120
120
|
|
@@ -141,7 +141,7 @@ Default is Marshal.
|
|
141
141
|
|
142
142
|
**password**: The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
|
143
143
|
|
144
|
-
**keepalive**: Boolean
|
144
|
+
**keepalive**: Boolean. If true, Dalli will enable keep-alive for socket connections. Default is false.
|
145
145
|
|
146
146
|
**compressor**: The compressor to use for objects being stored.
|
147
147
|
Default is zlib, implemented under `Dalli::Compressor`.
|
@@ -158,7 +158,7 @@ socket.
|
|
158
158
|
|
159
159
|
Note that Dalli does not require ActiveSupport or Rails. You can safely use it in your own Ruby projects.
|
160
160
|
|
161
|
-
|
161
|
+
[View the API](http://www.ruby-doc.org/gems/docs/d/dalli-2.6.2/Dalli/Client.html)
|
162
162
|
|
163
163
|
Helping Out
|
164
164
|
-------------
|
data/Rakefile
CHANGED
@@ -63,8 +63,12 @@ module ActiveSupport
|
|
63
63
|
if block_given?
|
64
64
|
unless options[:force]
|
65
65
|
entry = instrument(:read, name, options) do |payload|
|
66
|
-
|
67
|
-
|
66
|
+
read_entry(name, options).tap do |result|
|
67
|
+
if payload
|
68
|
+
payload[:super_operation] = :fetch
|
69
|
+
payload[:hit] = !!result
|
70
|
+
end
|
71
|
+
end
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
@@ -124,7 +128,6 @@ module ActiveSupport
|
|
124
128
|
# servers for all keys. Keys must be Strings.
|
125
129
|
def read_multi(*names)
|
126
130
|
names.extract_options!
|
127
|
-
names = names.flatten
|
128
131
|
mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
|
129
132
|
instrument(:read_multi, names) do
|
130
133
|
results = {}
|
@@ -267,7 +270,10 @@ module ActiveSupport
|
|
267
270
|
end
|
268
271
|
|
269
272
|
key = key.to_param
|
270
|
-
|
273
|
+
if key.respond_to? :force_encoding
|
274
|
+
key = key.dup
|
275
|
+
key.force_encoding('binary')
|
276
|
+
end
|
271
277
|
key
|
272
278
|
end
|
273
279
|
|
data/lib/dalli/client.rb
CHANGED
@@ -21,17 +21,27 @@ module Dalli
|
|
21
21
|
# - :failover - if a server is down, look for and store values on another server in the ring. Default: true.
|
22
22
|
# - :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
|
23
23
|
# - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
|
24
|
-
# - :compress - defaults to false, if true Dalli will compress values larger than 1024 bytes before
|
24
|
+
# - :compress - defaults to false, if true Dalli will compress values larger than 1024 bytes before sending them to memcached.
|
25
25
|
# - :serializer - defaults to Marshal
|
26
|
-
# sending them to memcached.
|
27
26
|
# - :compressor - defaults to zlib
|
28
27
|
#
|
29
28
|
def initialize(servers=nil, options={})
|
30
|
-
@servers = servers ||
|
29
|
+
@servers = normalize_servers(servers || ENV["MEMCACHE_SERVERS"] || '127.0.0.1:11211')
|
31
30
|
@options = normalize_options(options)
|
32
31
|
@ring = nil
|
33
32
|
end
|
34
33
|
|
34
|
+
##
|
35
|
+
# Normalizes the argument into an array of servers. If the argument is a string, it's expected to be of
|
36
|
+
# the format "memcache1.example.com:11211[,memcache2.example.com:11211[,memcache3.example.com:11211[...]]]
|
37
|
+
def normalize_servers(servers)
|
38
|
+
if servers.is_a? String
|
39
|
+
return servers.split(",")
|
40
|
+
else
|
41
|
+
return servers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
35
45
|
#
|
36
46
|
# The standard memcached instruction set
|
37
47
|
#
|
@@ -50,7 +60,7 @@ module Dalli
|
|
50
60
|
|
51
61
|
def get(key, options=nil)
|
52
62
|
resp = perform(:get, key)
|
53
|
-
resp.nil? ||
|
63
|
+
resp.nil? || 'Not found' == resp ? nil : resp
|
54
64
|
end
|
55
65
|
|
56
66
|
##
|
@@ -263,11 +273,13 @@ module Dalli
|
|
263
273
|
|
264
274
|
##
|
265
275
|
# Collect the stats for each server.
|
276
|
+
# You can optionally pass a type including :items or :slabs to get specific stats
|
266
277
|
# Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
|
267
|
-
def stats
|
278
|
+
def stats(type=nil)
|
279
|
+
type = nil if ![nil, :items,:slabs].include? type
|
268
280
|
values = {}
|
269
281
|
ring.servers.each do |server|
|
270
|
-
values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:stats) : nil
|
282
|
+
values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:stats,type.to_s) : nil
|
271
283
|
end
|
272
284
|
values
|
273
285
|
end
|
@@ -295,7 +307,7 @@ module Dalli
|
|
295
307
|
|
296
308
|
def ring
|
297
309
|
@ring ||= Dalli::Ring.new(
|
298
|
-
|
310
|
+
@servers.map do |s|
|
299
311
|
server_options = {}
|
300
312
|
if s =~ %r{\Amemcached://}
|
301
313
|
uri = URI.parse(s)
|
@@ -308,10 +320,6 @@ module Dalli
|
|
308
320
|
)
|
309
321
|
end
|
310
322
|
|
311
|
-
def env_servers
|
312
|
-
ENV['MEMCACHE_SERVERS'] ? ENV['MEMCACHE_SERVERS'].split(',') : nil
|
313
|
-
end
|
314
|
-
|
315
323
|
# Chokepoint method for instrumentation
|
316
324
|
def perform(op, key, *args)
|
317
325
|
key = key.to_s
|
data/lib/dalli/ring.rb
CHANGED
@@ -79,16 +79,18 @@ module Dalli
|
|
79
79
|
inline do |builder|
|
80
80
|
builder.c <<-EOM
|
81
81
|
int binary_search(VALUE ary, unsigned int r) {
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
long upper = RARRAY_LEN(ary) - 1;
|
83
|
+
long lower = 0;
|
84
|
+
long idx = 0;
|
85
85
|
ID value = rb_intern("value");
|
86
|
+
VALUE continuumValue;
|
87
|
+
unsigned int l;
|
86
88
|
|
87
89
|
while (lower <= upper) {
|
88
90
|
idx = (lower + upper) / 2;
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
+
continuumValue = rb_funcall(RARRAY_PTR(ary)[idx], value, 0);
|
93
|
+
l = NUM2UINT(continuumValue);
|
92
94
|
if (l == r) {
|
93
95
|
return idx;
|
94
96
|
}
|
data/lib/dalli/server.rb
CHANGED
@@ -43,6 +43,7 @@ module Dalli
|
|
43
43
|
@options = DEFAULTS.merge(options)
|
44
44
|
@sock = nil
|
45
45
|
@msg = nil
|
46
|
+
@error = nil
|
46
47
|
@pid = nil
|
47
48
|
@inprogress = nil
|
48
49
|
end
|
@@ -259,7 +260,7 @@ module Dalli
|
|
259
260
|
def set(key, value, ttl, cas, options)
|
260
261
|
(value, flags) = serialize(key, value, options)
|
261
262
|
|
262
|
-
if under_max_value_size?(value)
|
263
|
+
if under_max_value_size?(key, value)
|
263
264
|
req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
|
264
265
|
write(req)
|
265
266
|
generic_response unless multi?
|
@@ -271,7 +272,7 @@ module Dalli
|
|
271
272
|
def add(key, value, ttl, options)
|
272
273
|
(value, flags) = serialize(key, value, options)
|
273
274
|
|
274
|
-
if under_max_value_size?(value)
|
275
|
+
if under_max_value_size?(key, value)
|
275
276
|
req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
|
276
277
|
write(req)
|
277
278
|
generic_response unless multi?
|
@@ -283,7 +284,7 @@ module Dalli
|
|
283
284
|
def replace(key, value, ttl, options)
|
284
285
|
(value, flags) = serialize(key, value, options)
|
285
286
|
|
286
|
-
if under_max_value_size?(value)
|
287
|
+
if under_max_value_size?(key, value)
|
287
288
|
req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:replace])
|
288
289
|
write(req)
|
289
290
|
generic_response unless multi?
|
@@ -450,8 +451,13 @@ module Dalli
|
|
450
451
|
NORMAL_HEADER = '@4CCnN'
|
451
452
|
KV_HEADER = '@2n@6nN'
|
452
453
|
|
453
|
-
def under_max_value_size?(value)
|
454
|
-
value.bytesize <= @options[:value_max_bytes]
|
454
|
+
def under_max_value_size?(key, value)
|
455
|
+
if value.bytesize <= @options[:value_max_bytes]
|
456
|
+
true
|
457
|
+
else
|
458
|
+
Dalli.logger.warn "Value for #{key} over max size"
|
459
|
+
false
|
460
|
+
end
|
455
461
|
end
|
456
462
|
|
457
463
|
def generic_response(unpack=false)
|
data/lib/dalli/version.rb
CHANGED
data/test/memcached_mock.rb
CHANGED
data/test/test_active_support.rb
CHANGED
@@ -98,7 +98,9 @@ describe 'ActiveSupport' do
|
|
98
98
|
assert_equal({}, @dalli.read_multi([x, y]))
|
99
99
|
@dalli.write(x, '123')
|
100
100
|
@dalli.write(y, 123)
|
101
|
-
assert_equal({
|
101
|
+
assert_equal({}, @dalli.read_multi([x, y]))
|
102
|
+
@dalli.write([x, y], '123')
|
103
|
+
assert_equal({ [x, y] => '123' }, @dalli.read_multi([x, y]))
|
102
104
|
end
|
103
105
|
end
|
104
106
|
end
|
@@ -328,6 +330,30 @@ describe 'ActiveSupport' do
|
|
328
330
|
end
|
329
331
|
end
|
330
332
|
|
333
|
+
should 'allow keys to be frozen' do
|
334
|
+
with_activesupport do
|
335
|
+
memcached do
|
336
|
+
connect
|
337
|
+
key = "foo"
|
338
|
+
key.freeze
|
339
|
+
assert_equal true, @dalli.write(key, "value")
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
should 'allow keys from a hash' do
|
345
|
+
with_activesupport do
|
346
|
+
memcached do
|
347
|
+
connect
|
348
|
+
map = { "one" => "one", "two" => "two" }
|
349
|
+
map.each_pair do |k, v|
|
350
|
+
assert_equal true, @dalli.write(k, v)
|
351
|
+
end
|
352
|
+
assert_equal map, @dalli.read_multi(*(map.keys))
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
331
357
|
def connect
|
332
358
|
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122', :expires_in => 10.seconds, :namespace => lambda{33.to_s(36)})
|
333
359
|
@dalli.clear
|
data/test/test_compressor.rb
CHANGED
@@ -16,20 +16,24 @@ end
|
|
16
16
|
describe 'Compressor' do
|
17
17
|
|
18
18
|
should 'default to Dalli::Compressor' do
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
memcached_kill(29199) do |dc|
|
20
|
+
memcache = Dalli::Client.new('127.0.0.1:29199')
|
21
|
+
memcache.set 1,2
|
22
|
+
assert_equal Dalli::Compressor, memcache.instance_variable_get('@ring').servers.first.compressor
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
should 'support a custom compressor' do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
memcached_kill(29199) do |dc|
|
28
|
+
memcache = Dalli::Client.new('127.0.0.1:29199', :compressor => NoopCompressor)
|
29
|
+
memcache.set 1,2
|
30
|
+
begin
|
31
|
+
assert_equal NoopCompressor, memcache.instance_variable_get('@ring').servers.first.compressor
|
32
|
+
|
33
|
+
memcached(19127) do |dc|
|
34
|
+
assert dc.set("string-test", "a test string")
|
35
|
+
assert_equal("a test string", dc.get("string-test"))
|
36
|
+
end
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
data/test/test_dalli.rb
CHANGED
@@ -67,6 +67,24 @@ describe 'Dalli' do
|
|
67
67
|
assert_equal s2, s3
|
68
68
|
end
|
69
69
|
|
70
|
+
should "accept comma separated string" do
|
71
|
+
dc = Dalli::Client.new("server1.example.com:11211,server2.example.com:11211")
|
72
|
+
ring = dc.send(:ring)
|
73
|
+
assert_equal 2, ring.servers.size
|
74
|
+
s1,s2 = ring.servers.map(&:hostname)
|
75
|
+
assert_equal "server1.example.com", s1
|
76
|
+
assert_equal "server2.example.com", s2
|
77
|
+
end
|
78
|
+
|
79
|
+
should "accept array of servers" do
|
80
|
+
dc = Dalli::Client.new(["server1.example.com:11211","server2.example.com:11211"])
|
81
|
+
ring = dc.send(:ring)
|
82
|
+
assert_equal 2, ring.servers.size
|
83
|
+
s1,s2 = ring.servers.map(&:hostname)
|
84
|
+
assert_equal "server1.example.com", s1
|
85
|
+
assert_equal "server2.example.com", s2
|
86
|
+
end
|
87
|
+
|
70
88
|
context 'using a live server' do
|
71
89
|
|
72
90
|
should "support get/set" do
|
@@ -95,7 +113,23 @@ describe 'Dalli' do
|
|
95
113
|
servers = stats.keys
|
96
114
|
assert(servers.any? do |s|
|
97
115
|
stats[s]["get_hits"].to_i != 0
|
98
|
-
end)
|
116
|
+
end, "general stats failed")
|
117
|
+
|
118
|
+
stats_items = dc.stats(:items)
|
119
|
+
servers = stats_items.keys
|
120
|
+
assert(servers.all? do |s|
|
121
|
+
stats_items[s].keys.any? do |key|
|
122
|
+
key =~ /items:[0-9]+:number/
|
123
|
+
end
|
124
|
+
end, "stats items failed")
|
125
|
+
|
126
|
+
stats_slabs = dc.stats(:slabs)
|
127
|
+
servers = stats_slabs.keys
|
128
|
+
assert(servers.all? do |s|
|
129
|
+
stats_slabs[s].keys.any? do |key|
|
130
|
+
key == "active_slabs"
|
131
|
+
end
|
132
|
+
end, "stats slabs failed")
|
99
133
|
|
100
134
|
# reset_stats test
|
101
135
|
results = dc.reset_stats
|
data/test/test_serializer.rb
CHANGED
@@ -6,20 +6,24 @@ require 'memcached_mock'
|
|
6
6
|
describe 'Serializer' do
|
7
7
|
|
8
8
|
should 'default to Marshal' do
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
memcached_kill(29198) do |dc|
|
10
|
+
memcache = Dalli::Client.new('127.0.0.1:29198')
|
11
|
+
memcache.set 1,2
|
12
|
+
assert_equal Marshal, memcache.instance_variable_get('@ring').servers.first.serializer
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
should 'support a custom serializer' do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
memcached_kill(29198) do |dc|
|
18
|
+
memcache = Dalli::Client.new('127.0.0.1:29198', :serializer => JSON)
|
19
|
+
memcache.set 1,2
|
20
|
+
begin
|
21
|
+
assert_equal JSON, memcache.instance_variable_get('@ring').servers.first.serializer
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
memcached(19128) do |dc|
|
24
|
+
assert dc.set("json_test", {"foo" => "bar"})
|
25
|
+
assert_equal({"foo" => "bar"}, dc.get("json_test"))
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
25
29
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dalli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.
|
4
|
+
version: 2.6.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mini_shoulda
|
@@ -119,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
version: '0'
|
120
120
|
requirements: []
|
121
121
|
rubyforge_project:
|
122
|
-
rubygems_version: 1.8.
|
122
|
+
rubygems_version: 1.8.23
|
123
123
|
signing_key:
|
124
124
|
specification_version: 3
|
125
125
|
summary: High performance memcached client for Ruby
|