dalli 0.9.8 → 0.9.9
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/History.md +17 -0
- data/README.md +12 -0
- data/TODO.md +1 -3
- data/lib/action_controller/session/dalli_store.rb +57 -0
- data/lib/action_dispatch/middleware/session/dalli_store.rb +5 -11
- data/lib/dalli/client.rb +13 -1
- data/lib/dalli/ring.rb +1 -2
- data/lib/dalli/server.rb +18 -8
- data/lib/dalli/version.rb +1 -1
- data/test/benchmark_test.rb +37 -0
- data/test/test_dalli.rb +24 -0
- metadata +4 -3
data/History.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
0.9.9
|
5
|
+
----
|
6
|
+
|
7
|
+
- Add support for *_multi operations for add, set, replace and delete. This implements
|
8
|
+
pipelined network operations; Dalli disables network replies so we're not limited by
|
9
|
+
latency, allowing for much higher throughput.
|
10
|
+
|
11
|
+
dc = Dalli::Client.new
|
12
|
+
dc.multi do
|
13
|
+
dc.set 'a', 1
|
14
|
+
dc.set 'b', 2
|
15
|
+
dc.set 'c', 3
|
16
|
+
dc.delete 'd'
|
17
|
+
end
|
18
|
+
- Minor fix to set the continuum sorted by value.
|
19
|
+
- Implement session store with Rails 2.3. Update docs.
|
20
|
+
|
4
21
|
0.9.8
|
5
22
|
-----
|
6
23
|
|
data/README.md
CHANGED
@@ -61,6 +61,11 @@ A more comprehensive example (note that we are setting a reasonable default for
|
|
61
61
|
config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com',
|
62
62
|
:namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true, :compress_threshold => 64.kilobytes
|
63
63
|
|
64
|
+
In `config/initializers/session_store.rb`:
|
65
|
+
|
66
|
+
require 'action_dispatch/middleware/session/dalli_store'
|
67
|
+
Rails.application.config.session_store :dalli_store, :key => ...
|
68
|
+
|
64
69
|
|
65
70
|
Usage with Rails 2.3.x
|
66
71
|
----------------------------
|
@@ -71,9 +76,16 @@ In `config/environment.rb`:
|
|
71
76
|
|
72
77
|
In `config/environments/production.rb`:
|
73
78
|
|
79
|
+
# Object cache
|
74
80
|
require 'active_support/cache/dalli_store23'
|
75
81
|
config.cache_store = :dalli_store
|
76
82
|
|
83
|
+
In `config/initializers/session_store.rb`:
|
84
|
+
|
85
|
+
# Session cache
|
86
|
+
require 'action_controller/session/dalli_store'
|
87
|
+
ActionController::Base.session_store = :dalli_store
|
88
|
+
|
77
89
|
|
78
90
|
Usage with Passenger
|
79
91
|
------------------------
|
data/TODO.md
CHANGED
@@ -2,6 +2,4 @@ TODO
|
|
2
2
|
========
|
3
3
|
|
4
4
|
* More of the memcached instruction set. This will be done based on user demand. Email me if the API is missing a feature you'd like to use.
|
5
|
-
* Better API documentation
|
6
|
-
* Add noreply support for all commands
|
7
|
-
* Better pipelining support
|
5
|
+
* Better API documentation
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Session store for Rails 2.3.x
|
2
|
+
# Tested against 2.3.9.
|
3
|
+
begin
|
4
|
+
require_library_or_gem 'dalli'
|
5
|
+
|
6
|
+
module ActionController
|
7
|
+
module Session
|
8
|
+
class DalliStore < AbstractStore
|
9
|
+
def initialize(app, options = {})
|
10
|
+
# Support old :expires option
|
11
|
+
options[:expire_after] ||= options[:expires]
|
12
|
+
|
13
|
+
super
|
14
|
+
|
15
|
+
@default_options = {
|
16
|
+
:namespace => 'rack:session',
|
17
|
+
:memcache_server => 'localhost:11211'
|
18
|
+
}.merge(@default_options)
|
19
|
+
|
20
|
+
@pool = Dalli::Client.new(@default_options[:memcache_server], @default_options)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def get_session(env, sid)
|
26
|
+
sid ||= generate_sid
|
27
|
+
begin
|
28
|
+
session = @pool.get(sid) || {}
|
29
|
+
rescue Dalli::DalliError
|
30
|
+
session = {}
|
31
|
+
end
|
32
|
+
[sid, session]
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_session(env, sid, session_data)
|
36
|
+
options = env['rack.session.options']
|
37
|
+
expiry = options[:expire_after] || 0
|
38
|
+
@pool.set(sid, session_data, expiry)
|
39
|
+
return true
|
40
|
+
rescue Dalli::DalliError
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy(env)
|
45
|
+
if sid = current_session_id(env)
|
46
|
+
@pool.delete(sid)
|
47
|
+
end
|
48
|
+
rescue Dalli::DalliError
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue LoadError
|
56
|
+
# Dalli wasn't available so neither can the store be
|
57
|
+
end
|
@@ -18,7 +18,6 @@ module ActionDispatch
|
|
18
18
|
@default_options = {
|
19
19
|
:namespace => 'rack:session',
|
20
20
|
:memcache_server => 'localhost:11211',
|
21
|
-
:expires_in => options[:expire_after]
|
22
21
|
}.merge(@default_options)
|
23
22
|
|
24
23
|
@pool = options[:cache] || begin
|
@@ -36,16 +35,11 @@ module ActionDispatch
|
|
36
35
|
|
37
36
|
private
|
38
37
|
|
39
|
-
def session_key(sid)
|
40
|
-
# Dalli does not support namespaces directly so we have
|
41
|
-
# to roll our own.
|
42
|
-
@namespace ? "#{@namespace}:#{sid}" : sid
|
43
|
-
end
|
44
|
-
|
45
38
|
def get_session(env, sid)
|
39
|
+
sid ||= generate_sid
|
46
40
|
begin
|
47
|
-
session = @pool.get(
|
48
|
-
rescue Dalli::DalliError
|
41
|
+
session = @pool.get(sid) || {}
|
42
|
+
rescue Dalli::DalliError
|
49
43
|
Rails.logger.warn("Session::DalliStore: #{$!.message}")
|
50
44
|
session = {}
|
51
45
|
end
|
@@ -55,7 +49,7 @@ module ActionDispatch
|
|
55
49
|
def set_session(env, sid, session_data)
|
56
50
|
options = env['rack.session.options']
|
57
51
|
expiry = options[:expire_after] || 0
|
58
|
-
@pool.set(
|
52
|
+
@pool.set(sid, session_data, expiry)
|
59
53
|
sid
|
60
54
|
rescue Dalli::DalliError
|
61
55
|
Rails.logger.warn("Session::DalliStore: #{$!.message}")
|
@@ -64,7 +58,7 @@ module ActionDispatch
|
|
64
58
|
|
65
59
|
def destroy(env)
|
66
60
|
if sid = current_session_id(env)
|
67
|
-
@pool.delete(
|
61
|
+
@pool.delete(sid)
|
68
62
|
end
|
69
63
|
rescue Dalli::DalliError
|
70
64
|
Rails.logger.warn("Session::DalliStore: #{$!.message}")
|
data/lib/dalli/client.rb
CHANGED
@@ -24,6 +24,18 @@ module Dalli
|
|
24
24
|
# The standard memcached instruction set
|
25
25
|
#
|
26
26
|
|
27
|
+
##
|
28
|
+
# Turn on quiet aka noreply support.
|
29
|
+
# All relevant operations within this block with be effectively
|
30
|
+
# pipelined as Dalli will use 'quiet' operations where possible.
|
31
|
+
# Currently supports the set, add, replace and delete operations.
|
32
|
+
def multi
|
33
|
+
Thread.current[:multi] = true
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
Thread.current[:multi] = nil
|
37
|
+
end
|
38
|
+
|
27
39
|
def get(key, options=nil)
|
28
40
|
resp = perform(:get, key)
|
29
41
|
(!resp || resp == 'Not found') ? nil : deserialize(resp, options)
|
@@ -163,7 +175,7 @@ module Dalli
|
|
163
175
|
# Chokepoint method for instrumentation
|
164
176
|
def perform(op, *args)
|
165
177
|
key = args.first
|
166
|
-
if key.is_a?(
|
178
|
+
if !key.is_a?(String)
|
167
179
|
key = key.to_s
|
168
180
|
args[0] = key
|
169
181
|
end
|
data/lib/dalli/ring.rb
CHANGED
@@ -20,8 +20,7 @@ module Dalli
|
|
20
20
|
continuum << Dalli::Ring::Entry.new(value, server)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
continuum.sort { |a, b| a.value <=> b.value }
|
24
|
-
@continuum = continuum
|
23
|
+
@continuum = continuum.sort { |a, b| a.value <=> b.value }
|
25
24
|
end
|
26
25
|
|
27
26
|
threadsafe! unless options[:threadsafe] == false
|
data/lib/dalli/server.rb
CHANGED
@@ -73,6 +73,10 @@ module Dalli
|
|
73
73
|
nil
|
74
74
|
end
|
75
75
|
|
76
|
+
def multi?
|
77
|
+
Thread.current[:multi]
|
78
|
+
end
|
79
|
+
|
76
80
|
ONE_MB = 1024 * 1024
|
77
81
|
|
78
82
|
def get(key)
|
@@ -89,9 +93,9 @@ module Dalli
|
|
89
93
|
def set(key, value, ttl)
|
90
94
|
raise Dalli::DalliError, "Value too large, memcached can only store 1MB of data per key" if value.size > ONE_MB
|
91
95
|
|
92
|
-
req = [REQUEST, OPCODES[:set], key.size, 8, 0, 0, value.size + key.size + 8, 0, 0, 0, ttl, key, value].pack(FORMAT[:set])
|
96
|
+
req = [REQUEST, OPCODES[multi? ? :setq : :set], key.size, 8, 0, 0, value.size + key.size + 8, 0, 0, 0, ttl, key, value].pack(FORMAT[:set])
|
93
97
|
write(req)
|
94
|
-
generic_response
|
98
|
+
generic_response unless multi?
|
95
99
|
end
|
96
100
|
|
97
101
|
def flush(ttl)
|
@@ -103,9 +107,9 @@ module Dalli
|
|
103
107
|
def add(key, value, ttl, cas)
|
104
108
|
raise Dalli::DalliError, "Value too large, memcached can only store 1MB of data per key" if value.size > ONE_MB
|
105
109
|
|
106
|
-
req = [REQUEST, OPCODES[:add], key.size, 8, 0, 0, value.size + key.size + 8, 0, cas, 0, ttl, key, value].pack(FORMAT[:add])
|
110
|
+
req = [REQUEST, OPCODES[multi? ? :addq : :add], key.size, 8, 0, 0, value.size + key.size + 8, 0, cas, 0, ttl, key, value].pack(FORMAT[:add])
|
107
111
|
write(req)
|
108
|
-
generic_response
|
112
|
+
generic_response unless multi?
|
109
113
|
end
|
110
114
|
|
111
115
|
def append(key, value)
|
@@ -115,9 +119,9 @@ module Dalli
|
|
115
119
|
end
|
116
120
|
|
117
121
|
def delete(key)
|
118
|
-
req = [REQUEST, OPCODES[:delete], key.size, 0, 0, 0, key.size, 0, 0, key].pack(FORMAT[:delete])
|
122
|
+
req = [REQUEST, OPCODES[multi? ? :deleteq : :delete], key.size, 0, 0, 0, key.size, 0, 0, key].pack(FORMAT[:delete])
|
119
123
|
write(req)
|
120
|
-
generic_response
|
124
|
+
generic_response unless multi?
|
121
125
|
end
|
122
126
|
|
123
127
|
def decr(key, count, ttl, default)
|
@@ -157,9 +161,9 @@ module Dalli
|
|
157
161
|
end
|
158
162
|
|
159
163
|
def replace(key, value, ttl)
|
160
|
-
req = [REQUEST, OPCODES[:replace], key.size, 8, 0, 0, value.size + key.size + 8, 0, 0, 0, ttl, key, value].pack(FORMAT[:replace])
|
164
|
+
req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.size, 8, 0, 0, value.size + key.size + 8, 0, 0, 0, ttl, key, value].pack(FORMAT[:replace])
|
161
165
|
write(req)
|
162
|
-
generic_response
|
166
|
+
generic_response unless multi?
|
163
167
|
end
|
164
168
|
|
165
169
|
def stats(info='')
|
@@ -351,6 +355,12 @@ module Dalli
|
|
351
355
|
:append => 0x0E,
|
352
356
|
:prepend => 0x0F,
|
353
357
|
:stat => 0x10,
|
358
|
+
:setq => 0x11,
|
359
|
+
:addq => 0x12,
|
360
|
+
:replaceq => 0x13,
|
361
|
+
:deleteq => 0x14,
|
362
|
+
:incrq => 0x15,
|
363
|
+
:decrq => 0x16,
|
354
364
|
:auth_negotiation => 0x20,
|
355
365
|
:auth_request => 0x21,
|
356
366
|
:auth_continue => 0x22,
|
data/lib/dalli/version.rb
CHANGED
data/test/benchmark_test.rb
CHANGED
@@ -40,6 +40,20 @@ class TestBenchmark < Test::Unit::TestCase
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
@m = Dalli::Client.new(@servers)
|
44
|
+
x.report("setq:plain:dalli") do
|
45
|
+
@m.multi do
|
46
|
+
n.times do
|
47
|
+
@m.set @key1, @marshalled, 0, :raw => true
|
48
|
+
@m.set @key2, @marshalled, 0, :raw => true
|
49
|
+
@m.set @key3, @marshalled, 0, :raw => true
|
50
|
+
@m.set @key1, @marshalled, 0, :raw => true
|
51
|
+
@m.set @key2, @marshalled, 0, :raw => true
|
52
|
+
@m.set @key3, @marshalled, 0, :raw => true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
43
57
|
@m = Dalli::Client.new(@servers)
|
44
58
|
x.report("set:ruby:dalli") do
|
45
59
|
n.times do
|
@@ -114,6 +128,29 @@ class TestBenchmark < Test::Unit::TestCase
|
|
114
128
|
end
|
115
129
|
end
|
116
130
|
|
131
|
+
@m = Dalli::Client.new(@servers)
|
132
|
+
x.report("mixedq:ruby:dalli") do
|
133
|
+
@m.multi do
|
134
|
+
n.times do
|
135
|
+
@m.set @key1, @value
|
136
|
+
@m.set @key2, @value
|
137
|
+
@m.set @key3, @value
|
138
|
+
@m.get @key1
|
139
|
+
@m.get @key2
|
140
|
+
@m.get @key3
|
141
|
+
@m.set @key1, @value
|
142
|
+
@m.get @key1
|
143
|
+
@m.set @key2, @value
|
144
|
+
@m.replace @key2, @value
|
145
|
+
@m.delete @key3
|
146
|
+
@m.add @key3, @value
|
147
|
+
@m.get @key2
|
148
|
+
@m.set @key3, @value
|
149
|
+
@m.get @key3
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
117
154
|
@m = Dalli::Client.new(@servers)
|
118
155
|
x.report("incr:ruby:dalli") do
|
119
156
|
n.times do
|
data/test/test_dalli.rb
CHANGED
@@ -26,6 +26,17 @@ class TestDalli < Test::Unit::TestCase
|
|
26
26
|
assert_equal s2, s3
|
27
27
|
end
|
28
28
|
|
29
|
+
should "have the continuum sorted by value" do
|
30
|
+
servers = [stub(:hostname => "localhost", :port => "11211", :weight => 1),
|
31
|
+
stub(:hostname => "localhost", :port => "9500", :weight => 1)]
|
32
|
+
ring = Dalli::Ring.new(servers, {})
|
33
|
+
previous_value = 0
|
34
|
+
ring.continuum.each do |entry|
|
35
|
+
assert entry.value > previous_value
|
36
|
+
previous_value = entry.value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
29
40
|
context 'using a live server' do
|
30
41
|
|
31
42
|
should "support get/set" do
|
@@ -110,6 +121,19 @@ class TestDalli < Test::Unit::TestCase
|
|
110
121
|
dc.set('c', %w(a b c))
|
111
122
|
resp = dc.get_multi(%w(a b c d e f))
|
112
123
|
assert_equal({ 'a' => 'foo', 'b' => 123, 'c' => %w(a b c) }, resp)
|
124
|
+
|
125
|
+
# Perform a huge multi-get with 10,000 elements.
|
126
|
+
arr = []
|
127
|
+
dc.multi do
|
128
|
+
10_000.times do |idx|
|
129
|
+
dc.set idx, idx
|
130
|
+
arr << idx
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
result = dc.get_multi(arr)
|
135
|
+
assert_equal(10_000, result.size)
|
136
|
+
assert_equal(1000, result['1000'])
|
113
137
|
end
|
114
138
|
end
|
115
139
|
|
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
|
+
- 9
|
9
|
+
version: 0.9.9
|
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-10-06 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -82,6 +82,7 @@ extensions: []
|
|
82
82
|
extra_rdoc_files: []
|
83
83
|
|
84
84
|
files:
|
85
|
+
- lib/action_controller/session/dalli_store.rb
|
85
86
|
- lib/action_dispatch/middleware/session/dalli_store.rb
|
86
87
|
- lib/active_support/cache/dalli_store.rb
|
87
88
|
- lib/active_support/cache/dalli_store23.rb
|