dalli 1.0.5 → 1.1.1
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 +5 -1
- data/History.md +11 -0
- data/Performance.md +15 -7
- data/README.md +9 -28
- data/Rakefile +10 -5
- data/dalli.gemspec +1 -1
- data/lib/action_dispatch/middleware/session/dalli_store.rb +9 -5
- data/lib/dalli.rb +4 -0
- data/lib/dalli/client.rb +20 -8
- data/lib/dalli/compatibility.rb +1 -1
- data/lib/dalli/server.rb +25 -18
- data/lib/dalli/socket.rb +33 -1
- data/lib/dalli/version.rb +2 -2
- data/test/abstract_unit.rb +4 -6
- data/test/helper.rb +7 -22
- data/test/memcached_mock.rb +9 -3
- data/test/test_active_support.rb +20 -41
- data/test/test_dalli.rb +5 -7
- data/test/test_failover.rb +6 -7
- data/test/test_sasl.rb +9 -5
- data/test/test_session_store.rb +14 -19
- data/test/test_synchrony.rb +175 -0
- metadata +8 -5
- data/lib/action_controller/session/dalli_store.rb +0 -62
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
1.1.1
|
5
|
+
=======
|
6
|
+
|
7
|
+
v1.1.0 was a bad release. Yanked.
|
8
|
+
|
4
9
|
1.1.0
|
5
10
|
=======
|
6
11
|
|
7
12
|
- Remove support for Rails 2.3, add support for Rails 3.1
|
8
13
|
- Fix socket failure retry logic, now you can restart memcached and Dalli won't complain!
|
14
|
+
- Add support for fibered operation via em-synchrony (eliaslevy)
|
15
|
+
- Gracefully handle write timeouts, GH-99
|
16
|
+
- Only issue bug warning for unexpected StandardErrors, GH-102
|
17
|
+
- Add travis-ci build support (ryanlecompte)
|
18
|
+
- Gracefully handle errors in get_multi (michaelfairley)
|
19
|
+
- Misc fixes from crash2burn, fphilipe, igreg, raggi
|
9
20
|
|
10
21
|
1.0.5
|
11
22
|
=======
|
data/Performance.md
CHANGED
@@ -6,9 +6,11 @@ Times are from a Unibody MBP 2.4Ghz Core i5 running Snow Leopard.
|
|
6
6
|
|
7
7
|
You can optionally use kgio to give Dalli a small, 10-20% performance boost: gem install kgio.
|
8
8
|
|
9
|
-
|
9
|
+
memcache-client
|
10
|
+
---------------
|
11
|
+
|
12
|
+
Testing 1.8.5 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
10
13
|
|
11
|
-
Testing 1.8.5 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
12
14
|
user system total real
|
13
15
|
set:plain:memcache 2.070000 0.340000 2.410000 ( 2.611709)
|
14
16
|
set:ruby:memcache 1.990000 0.330000 2.320000 ( 2.869653)
|
@@ -19,9 +21,11 @@ You can optionally use kgio to give Dalli a small, 10-20% performance boost: gem
|
|
19
21
|
mixed:ruby:memcache 4.390000 0.670000 5.060000 ( 5.721000)
|
20
22
|
incr:ruby:memcache 0.700000 0.120000 0.820000 ( 0.842896)
|
21
23
|
|
22
|
-
|
24
|
+
libmemcached
|
25
|
+
------------
|
26
|
+
|
27
|
+
Testing 1.1.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
23
28
|
|
24
|
-
Testing 1.1.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
25
29
|
user system total real
|
26
30
|
set:plain:libm 0.120000 0.220000 0.340000 ( 0.847521)
|
27
31
|
setq:plain:libm 0.030000 0.000000 0.030000 ( 0.126944)
|
@@ -34,10 +38,12 @@ You can optionally use kgio to give Dalli a small, 10-20% performance boost: gem
|
|
34
38
|
mixedq:ruby:libm 0.410000 0.360000 0.770000 ( 1.516718)
|
35
39
|
incr:ruby:libm 0.080000 0.340000 0.420000 ( 1.685931)
|
36
40
|
|
37
|
-
|
41
|
+
dalli
|
42
|
+
-----
|
43
|
+
|
44
|
+
Using kgio socket IO
|
45
|
+
Testing 1.0.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
38
46
|
|
39
|
-
Using kgio socket IO
|
40
|
-
Testing 1.0.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
|
41
47
|
user system total real
|
42
48
|
set:plain:dalli 0.850000 0.320000 1.170000 ( 1.691393)
|
43
49
|
setq:plain:dalli 0.500000 0.130000 0.630000 ( 0.651227)
|
@@ -51,6 +57,7 @@ You can optionally use kgio to give Dalli a small, 10-20% performance boost: gem
|
|
51
57
|
incr:ruby:dalli 0.310000 0.120000 0.430000 ( 0.616465)
|
52
58
|
|
53
59
|
Testing 1.0.2 with rubinius 1.3.0dev (1.8.7 382e813f xxxx-xx-xx JI) [x86_64-apple-darwin10.6.0]
|
60
|
+
|
54
61
|
user system total real
|
55
62
|
set:plain:dalli 2.800581 0.329360 3.129941 ( 5.186546)
|
56
63
|
setq:plain:dalli 1.064253 0.138044 1.202297 ( 1.280355)
|
@@ -64,6 +71,7 @@ Testing 1.0.2 with rubinius 1.3.0dev (1.8.7 382e813f xxxx-xx-xx JI) [x86_64-appl
|
|
64
71
|
incr:ruby:dalli 0.691467 0.091475 0.782942 ( 0.931674)
|
65
72
|
|
66
73
|
Testing 1.0.2 with rubinius 1.2.0 (1.8.7 release 2010-12-21 JI) [x86_64-apple-darwin10.6.0]
|
74
|
+
|
67
75
|
user system total real
|
68
76
|
set:plain:dalli 6.586927 0.331545 6.918472 ( 4.628652)
|
69
77
|
setq:plain:dalli 0.930905 0.129008 1.059913 ( 1.016105)
|
data/README.md
CHANGED
@@ -26,10 +26,9 @@ So a few notes. Dalli:
|
|
26
26
|
1. is approximately 20% faster than memcache-client (which itself was heavily optimized) in Ruby 1.9.2.
|
27
27
|
2. contains explicit "chokepoint" methods which handle all requests; these can be hooked into by monitoring tools (NewRelic, Rack::Bug, etc) to track memcached usage.
|
28
28
|
3. comes with hooks to replace memcache-client in Rails.
|
29
|
-
4.
|
30
|
-
5.
|
31
|
-
6.
|
32
|
-
7. has a backwards-compatibility mode for people migrating from memcache-client (see Upgrade.md).
|
29
|
+
4. supports SASL for use in managed environments, e.g. Heroku.
|
30
|
+
5. provides proper failover with recovery and adjustable timeouts
|
31
|
+
6. has a backwards-compatibility mode for people migrating from memcache-client (see Upgrade.md).
|
33
32
|
|
34
33
|
|
35
34
|
Installation and Usage
|
@@ -50,7 +49,7 @@ Dalli has no runtime dependencies and never will. You can optionally install th
|
|
50
49
|
give Dalli a 10-20% performance boost.
|
51
50
|
|
52
51
|
|
53
|
-
Usage with Rails 3.
|
52
|
+
Usage with Rails 3.x
|
54
53
|
---------------------------
|
55
54
|
|
56
55
|
In your Gemfile:
|
@@ -75,29 +74,7 @@ To use Dalli for Rails session storage, in `config/initializers/session_store.rb
|
|
75
74
|
Usage with Rails 2.3.x
|
76
75
|
----------------------------
|
77
76
|
|
78
|
-
|
79
|
-
|
80
|
-
config.gem 'dalli'
|
81
|
-
|
82
|
-
In `config/environments/production.rb`:
|
83
|
-
|
84
|
-
# Object cache
|
85
|
-
require 'active_support/cache/dalli_store23'
|
86
|
-
config.cache_store = :dalli_store
|
87
|
-
|
88
|
-
In `config/initializers/session_store.rb`:
|
89
|
-
|
90
|
-
# Session cache
|
91
|
-
ActionController::Base.session = {
|
92
|
-
:namespace => 'sessions',
|
93
|
-
:expire_after => 20.minutes.to_i,
|
94
|
-
:memcache_server => ['server-1:11211', 'server-2:11211'],
|
95
|
-
:key => ...,
|
96
|
-
:secret => ...
|
97
|
-
}
|
98
|
-
|
99
|
-
require 'action_controller/session/dalli_store'
|
100
|
-
ActionController::Base.session_store = :dalli_store
|
77
|
+
Dalli v1.1+ does not support Rails 2.3. Please use an earlier version: gem install dalli -v "~> 1.0.4"
|
101
78
|
|
102
79
|
|
103
80
|
Usage with Passenger
|
@@ -137,6 +114,8 @@ Dalli::Client accepts the following options. All times are in seconds.
|
|
137
114
|
|
138
115
|
**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.
|
139
116
|
|
117
|
+
**async**: Boolean, if true Dalli will assume its running inside the EventMachine reactor and use EM through the em-synchrony gem. Currently disables the socket_timeout option. Default is false.
|
118
|
+
|
140
119
|
Features and Changes
|
141
120
|
------------------------
|
142
121
|
|
@@ -144,6 +123,8 @@ Dalli is **NOT** 100% API compatible with memcache-client. If you have code whi
|
|
144
123
|
|
145
124
|
By default, Dalli is thread-safe. Disable thread-safety at your own peril.
|
146
125
|
|
126
|
+
Multi-threaded use will fail if used with EventMachine.
|
127
|
+
|
147
128
|
Note that Dalli does not require ActiveSupport or Rails. You can safely use it in your own Ruby projects.
|
148
129
|
|
149
130
|
|
data/Rakefile
CHANGED
@@ -20,15 +20,20 @@ end
|
|
20
20
|
task :default => :test
|
21
21
|
|
22
22
|
task :test_all do
|
23
|
-
system('rake test RAILS_VERSION="~> 2.3.0"')
|
24
23
|
system('rake test RAILS_VERSION="~> 3.0.0"')
|
24
|
+
system('rake test RAILS_VERSION=">= 3.0.0"')
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
# 'gem install rdoc' to upgrade RDoc if this is giving you errors
|
28
|
+
begin
|
29
|
+
require 'rdoc/task'
|
30
|
+
RDoc::Task.new do |rd|
|
31
|
+
rd.rdoc_files.include("lib/**/*.rb")
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
puts "Unable to load rdoc, run 'gem install rdoc' to fix this."
|
30
35
|
end
|
31
36
|
|
32
37
|
require 'rake/clean'
|
33
38
|
CLEAN.include "**/*.rbc"
|
34
|
-
CLEAN.include "**/.DS_Store"
|
39
|
+
CLEAN.include "**/.DS_Store"
|
data/dalli.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.test_files = Dir.glob("test/**/*")
|
26
26
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
27
27
|
s.add_development_dependency(%q<mocha>, [">= 0"])
|
28
|
-
s.add_development_dependency(%q<rails>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<rails>, [">= 3.0.0"])
|
29
29
|
s.add_development_dependency(%q<memcache-client>, [">= 1.8.5"])
|
30
30
|
end
|
31
31
|
|
@@ -12,10 +12,7 @@ module ActionDispatch
|
|
12
12
|
|
13
13
|
super
|
14
14
|
|
15
|
-
@default_options = {
|
16
|
-
:namespace => 'rack:session',
|
17
|
-
:memcache_server => 'localhost:11211',
|
18
|
-
}.merge(@default_options)
|
15
|
+
@default_options = { :namespace => 'rack:session' }.merge(@default_options)
|
19
16
|
|
20
17
|
@pool = options[:cache] || begin
|
21
18
|
Dalli::Client.new(
|
@@ -53,12 +50,19 @@ module ActionDispatch
|
|
53
50
|
false
|
54
51
|
end
|
55
52
|
|
53
|
+
def destroy_session(env, session_id, options)
|
54
|
+
@pool.delete(session_id)
|
55
|
+
rescue Dalli::DalliError
|
56
|
+
Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
56
60
|
def destroy(env)
|
57
61
|
if sid = current_session_id(env)
|
58
62
|
@pool.delete(sid)
|
59
63
|
end
|
60
64
|
rescue Dalli::DalliError
|
61
|
-
Rails.logger.warn("Session::DalliStore#
|
65
|
+
Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
|
62
66
|
false
|
63
67
|
end
|
64
68
|
|
data/lib/dalli.rb
CHANGED
data/lib/dalli/client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: ascii
|
2
2
|
module Dalli
|
3
3
|
class Client
|
4
|
-
|
4
|
+
|
5
5
|
##
|
6
6
|
# Dalli::Client is the main class which developers will use to interact with
|
7
7
|
# the memcached server. Usage:
|
@@ -20,6 +20,7 @@ module Dalli
|
|
20
20
|
# - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
|
21
21
|
# - :compression - defaults to false, if true Dalli will compress values larger than 100 bytes before
|
22
22
|
# sending them to memcached.
|
23
|
+
# - :async - assume its running inside the EM reactor. Requires em-synchrony to be installed. Default: false.
|
23
24
|
#
|
24
25
|
def initialize(servers=nil, options={})
|
25
26
|
@servers = env_servers || servers || 'localhost:11211'
|
@@ -39,14 +40,14 @@ module Dalli
|
|
39
40
|
require 'dalli/compatibility'
|
40
41
|
@compatibility_mode = compatibility_mode
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
#
|
44
45
|
# The standard memcached instruction set
|
45
46
|
#
|
46
47
|
|
47
48
|
##
|
48
49
|
# Turn on quiet aka noreply support.
|
49
|
-
# All relevant operations within this block
|
50
|
+
# All relevant operations within this block will be effectively
|
50
51
|
# pipelined as Dalli will use 'quiet' operations where possible.
|
51
52
|
# Currently supports the set, add, replace and delete operations.
|
52
53
|
def multi
|
@@ -70,7 +71,12 @@ module Dalli
|
|
70
71
|
options = keys.pop if keys.last.is_a?(Hash) || keys.last.nil?
|
71
72
|
ring.lock do
|
72
73
|
keys.flatten.each do |key|
|
73
|
-
|
74
|
+
begin
|
75
|
+
perform(:getkq, key)
|
76
|
+
rescue DalliError, NetworkError => e
|
77
|
+
Dalli.logger.debug { e.message }
|
78
|
+
Dalli.logger.debug { "unable to get key #{key}" }
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
82
|
values = {}
|
@@ -80,7 +86,7 @@ module Dalli
|
|
80
86
|
server.request(:noop).each_pair do |key, value|
|
81
87
|
values[key_without_namespace(key)] = value
|
82
88
|
end
|
83
|
-
rescue NetworkError => e
|
89
|
+
rescue DalliError, NetworkError => e
|
84
90
|
Dalli.logger.debug { e.message }
|
85
91
|
Dalli.logger.debug { "results from this server will be missing" }
|
86
92
|
end
|
@@ -116,14 +122,14 @@ module Dalli
|
|
116
122
|
value = (!value || value == 'Not found') ? nil : value
|
117
123
|
if value
|
118
124
|
newvalue = block.call(value)
|
119
|
-
perform(:
|
125
|
+
perform(:set, key, newvalue, ttl, cas, options)
|
120
126
|
end
|
121
127
|
end
|
122
128
|
|
123
129
|
def set(key, value, ttl=nil, options=nil)
|
124
130
|
raise "Invalid API usage, please require 'dalli/memcache-client' for compatibility, see Upgrade.md" if options == true
|
125
131
|
ttl ||= @options[:expires_in]
|
126
|
-
perform(:set, key, value, ttl, options)
|
132
|
+
perform(:set, key, value, ttl, 0, options)
|
127
133
|
end
|
128
134
|
|
129
135
|
##
|
@@ -131,7 +137,7 @@ module Dalli
|
|
131
137
|
# on the server. Returns true if the operation succeeded.
|
132
138
|
def add(key, value, ttl=nil, options=nil)
|
133
139
|
ttl ||= @options[:expires_in]
|
134
|
-
perform(:add, key, value, ttl,
|
140
|
+
perform(:add, key, value, ttl, options)
|
135
141
|
end
|
136
142
|
|
137
143
|
##
|
@@ -146,10 +152,16 @@ module Dalli
|
|
146
152
|
perform(:delete, key)
|
147
153
|
end
|
148
154
|
|
155
|
+
##
|
156
|
+
# Append value to the value already stored on the server for 'key'.
|
157
|
+
# Appending only works for values stored with :raw => true.
|
149
158
|
def append(key, value)
|
150
159
|
perform(:append, key, value.to_s)
|
151
160
|
end
|
152
161
|
|
162
|
+
##
|
163
|
+
# Prepend value to the value already stored on the server for 'key'.
|
164
|
+
# Prepending only works for values stored with :raw => true.
|
153
165
|
def prepend(key, value)
|
154
166
|
perform(:prepend, key, value.to_s)
|
155
167
|
end
|
data/lib/dalli/compatibility.rb
CHANGED
@@ -38,7 +38,7 @@ class Dalli::Client
|
|
38
38
|
def get(key, options = nil)
|
39
39
|
value = super(key, options)
|
40
40
|
if value && value.is_a?(String) && !options && value.size > 2 &&
|
41
|
-
bytes = value.unpack('cc') && bytes[0] == 4 && bytes[1] == 8
|
41
|
+
(bytes = value.unpack('cc')) && bytes[0] == 4 && bytes[1] == 8
|
42
42
|
return Marshal.load(value) rescue value
|
43
43
|
end
|
44
44
|
value
|
data/lib/dalli/server.rb
CHANGED
@@ -8,7 +8,7 @@ module Dalli
|
|
8
8
|
attr_accessor :port
|
9
9
|
attr_accessor :weight
|
10
10
|
attr_accessor :options
|
11
|
-
|
11
|
+
|
12
12
|
DEFAULTS = {
|
13
13
|
# seconds between trying to contact a remote server
|
14
14
|
:down_retry_delay => 1,
|
@@ -22,6 +22,7 @@ module Dalli
|
|
22
22
|
:value_max_bytes => 1024 * 1024,
|
23
23
|
:username => nil,
|
24
24
|
:password => nil,
|
25
|
+
:async => false,
|
25
26
|
}
|
26
27
|
|
27
28
|
def initialize(attribs, options = {})
|
@@ -37,7 +38,7 @@ module Dalli
|
|
37
38
|
@sock = nil
|
38
39
|
@msg = nil
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
# Chokepoint method for instrumentation
|
42
43
|
def request(op, *args)
|
43
44
|
raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}" unless alive?
|
@@ -52,7 +53,7 @@ module Dalli
|
|
52
53
|
false
|
53
54
|
rescue Dalli::DalliError
|
54
55
|
raise
|
55
|
-
rescue
|
56
|
+
rescue => ex
|
56
57
|
Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
|
57
58
|
Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
|
58
59
|
Dalli.logger.error ex.backtrace.join("\n\t")
|
@@ -103,7 +104,7 @@ module Dalli
|
|
103
104
|
raise Dalli::NetworkError, "Socket operation failed, retrying..."
|
104
105
|
end
|
105
106
|
end
|
106
|
-
|
107
|
+
|
107
108
|
def down!
|
108
109
|
close
|
109
110
|
|
@@ -150,22 +151,22 @@ module Dalli
|
|
150
151
|
write(req)
|
151
152
|
end
|
152
153
|
|
153
|
-
def set(key, value, ttl, options)
|
154
|
+
def set(key, value, ttl, cas, options)
|
154
155
|
(value, flags) = serialize(key, value, options)
|
155
156
|
|
156
|
-
req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0,
|
157
|
+
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])
|
157
158
|
write(req)
|
158
159
|
generic_response unless multi?
|
159
160
|
end
|
160
161
|
|
161
|
-
def add(key, value, ttl,
|
162
|
+
def add(key, value, ttl, options)
|
162
163
|
(value, flags) = serialize(key, value, options)
|
163
164
|
|
164
|
-
req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0,
|
165
|
+
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])
|
165
166
|
write(req)
|
166
167
|
generic_response unless multi?
|
167
168
|
end
|
168
|
-
|
169
|
+
|
169
170
|
def replace(key, value, ttl, options)
|
170
171
|
(value, flags) = serialize(key, value, options)
|
171
172
|
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])
|
@@ -195,7 +196,7 @@ module Dalli
|
|
195
196
|
body = generic_response
|
196
197
|
body ? longlong(*body.unpack('NN')) : body
|
197
198
|
end
|
198
|
-
|
199
|
+
|
199
200
|
def incr(key, count, ttl, default)
|
200
201
|
expiry = default ? ttl : 0xFFFFFFFF
|
201
202
|
default ||= 0
|
@@ -206,7 +207,7 @@ module Dalli
|
|
206
207
|
body = generic_response
|
207
208
|
body ? longlong(*body.unpack('NN')) : body
|
208
209
|
end
|
209
|
-
|
210
|
+
|
210
211
|
# Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
|
211
212
|
# We need to read all the responses at once.
|
212
213
|
def noop
|
@@ -362,7 +363,7 @@ module Dalli
|
|
362
363
|
def write(bytes)
|
363
364
|
begin
|
364
365
|
@sock.write(bytes)
|
365
|
-
rescue SystemCallError
|
366
|
+
rescue SystemCallError, Timeout::Error
|
366
367
|
failure!
|
367
368
|
retry
|
368
369
|
end
|
@@ -381,13 +382,19 @@ module Dalli
|
|
381
382
|
Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
|
382
383
|
|
383
384
|
begin
|
384
|
-
|
385
|
+
if options[:async]
|
386
|
+
raise Dalli::DalliError, "EM support not enabled, as em-synchrony is not installed." if not defined?(AsyncSocket)
|
387
|
+
@sock = AsyncSocket.open(hostname, port, :timeout => options[:socket_timeout])
|
388
|
+
else
|
389
|
+
@sock = KSocket.open(hostname, port, :timeout => options[:socket_timeout])
|
390
|
+
end
|
385
391
|
@version = version # trigger actual connect
|
386
392
|
sasl_authentication if need_auth?
|
387
393
|
up!
|
388
394
|
rescue Dalli::DalliError # SASL auth failure
|
389
395
|
raise
|
390
|
-
rescue SystemCallError, Timeout::Error, EOFError
|
396
|
+
rescue SystemCallError, Timeout::Error, EOFError, SocketError
|
397
|
+
# SocketError = DNS resolution failure
|
391
398
|
failure!
|
392
399
|
retry
|
393
400
|
end
|
@@ -403,7 +410,7 @@ module Dalli
|
|
403
410
|
|
404
411
|
REQUEST = 0x80
|
405
412
|
RESPONSE = 0x81
|
406
|
-
|
413
|
+
|
407
414
|
RESPONSE_CODES = {
|
408
415
|
0 => 'No error',
|
409
416
|
1 => 'Key not found',
|
@@ -416,7 +423,7 @@ module Dalli
|
|
416
423
|
0x81 => 'Unknown command',
|
417
424
|
0x82 => 'Out of memory',
|
418
425
|
}
|
419
|
-
|
426
|
+
|
420
427
|
OPCODES = {
|
421
428
|
:get => 0x00,
|
422
429
|
:set => 0x01,
|
@@ -442,7 +449,7 @@ module Dalli
|
|
442
449
|
:auth_request => 0x21,
|
443
450
|
:auth_continue => 0x22,
|
444
451
|
}
|
445
|
-
|
452
|
+
|
446
453
|
HEADER = "CCnCCnNNQ"
|
447
454
|
OP_FORMAT = {
|
448
455
|
:get => 'a*',
|
@@ -472,7 +479,7 @@ module Dalli
|
|
472
479
|
def need_auth?
|
473
480
|
@options[:username] || ENV['MEMCACHE_USERNAME']
|
474
481
|
end
|
475
|
-
|
482
|
+
|
476
483
|
def username
|
477
484
|
@options[:username] || ENV['MEMCACHE_USERNAME']
|
478
485
|
end
|
data/lib/dalli/socket.rb
CHANGED
@@ -4,7 +4,7 @@ begin
|
|
4
4
|
|
5
5
|
class Dalli::Server::KSocket < Kgio::Socket
|
6
6
|
attr_accessor :options
|
7
|
-
|
7
|
+
|
8
8
|
def kgio_wait_readable
|
9
9
|
IO.select([self], nil, nil, options[:timeout]) || raise(Timeout::Error, "IO timeout")
|
10
10
|
end
|
@@ -113,5 +113,37 @@ rescue LoadError
|
|
113
113
|
end
|
114
114
|
|
115
115
|
end
|
116
|
+
end
|
117
|
+
|
118
|
+
begin
|
119
|
+
require 'em-synchrony'
|
120
|
+
puts "Defining alternate em-synchrony socket IO" if defined?($TESTING) && $TESTING
|
121
|
+
|
122
|
+
class Dalli::Server::AsyncSocket < EventMachine::Synchrony::TCPSocket
|
123
|
+
# XXX. need to somehow implement the timeout option.
|
124
|
+
# EM::Synchrony::TCPsocket does give you any timeouts. To implement it we'd have
|
125
|
+
# to change that class to take a timeout parameter that it would then set on
|
126
|
+
# the Deferrable objects it waits on.
|
127
|
+
attr_accessor :options
|
116
128
|
|
129
|
+
def self.open(host, port, options = {})
|
130
|
+
sock = new(host, port)
|
131
|
+
sock.options = { :host => host, :port => port }.merge(options)
|
132
|
+
sock
|
133
|
+
end
|
134
|
+
|
135
|
+
def readfull(count)
|
136
|
+
value = ''
|
137
|
+
loop do
|
138
|
+
value << read(count - value.bytesize)
|
139
|
+
break if value.bytesize == count
|
140
|
+
end
|
141
|
+
value
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
rescue LoadError
|
147
|
+
puts "Could not define alternate em-synchrony socket IO" if defined?($TESTING) && $TESTING
|
117
148
|
end
|
149
|
+
|
data/lib/dalli/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Dalli
|
2
|
-
VERSION = '1.
|
3
|
-
end
|
2
|
+
VERSION = '1.1.1'
|
3
|
+
end
|
data/test/abstract_unit.rb
CHANGED
@@ -93,14 +93,12 @@ module ActiveSupport
|
|
93
93
|
# Hold off drawing routes until all the possible controller classes
|
94
94
|
# have been loaded.
|
95
95
|
setup_once do
|
96
|
-
SharedTestRoutes.draw do
|
97
|
-
|
98
|
-
map.connect ':controller/:action/:id'
|
96
|
+
SharedTestRoutes.draw do
|
97
|
+
match ':controller(/:action(/:id))'
|
99
98
|
end
|
100
99
|
|
101
|
-
ActionController::IntegrationTest.app.routes.draw do
|
102
|
-
|
103
|
-
map.connect ':controller/:action/:id'
|
100
|
+
ActionController::IntegrationTest.app.routes.draw do
|
101
|
+
match ':controller(/:action(/:id))'
|
104
102
|
end
|
105
103
|
end
|
106
104
|
end
|
data/test/helper.rb
CHANGED
@@ -2,9 +2,10 @@ $TESTING = true
|
|
2
2
|
require 'rubygems'
|
3
3
|
# require 'simplecov'
|
4
4
|
# SimpleCov.start
|
5
|
-
|
6
|
-
|
7
|
-
|
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}"
|
8
9
|
|
9
10
|
require 'test/unit'
|
10
11
|
require 'shoulda'
|
@@ -20,35 +21,19 @@ Dalli.logger.level = Logger::ERROR
|
|
20
21
|
class Test::Unit::TestCase
|
21
22
|
include MemcachedMock::Helper
|
22
23
|
|
23
|
-
def rails3?
|
24
|
-
RAILS_VERSION =~ /3\.0\./
|
25
|
-
end
|
26
|
-
|
27
24
|
def assert_error(error, regexp=nil, &block)
|
28
25
|
ex = assert_raise(error, &block)
|
29
26
|
assert_match(regexp, ex.message, "#{ex.class.name}: #{ex.message}\n#{ex.backtrace.join("\n\t")}")
|
30
27
|
end
|
31
28
|
|
32
29
|
def with_activesupport
|
33
|
-
|
34
|
-
when rails3?
|
35
|
-
require 'active_support/all'
|
36
|
-
else
|
37
|
-
require 'active_support'
|
38
|
-
require 'active_support/cache/dalli_store23'
|
39
|
-
end
|
30
|
+
require 'active_support/all'
|
40
31
|
yield
|
41
32
|
end
|
42
33
|
|
43
34
|
def with_actionpack
|
44
|
-
|
45
|
-
|
46
|
-
require 'action_dispatch'
|
47
|
-
require 'action_controller'
|
48
|
-
# when '2.3.0'
|
49
|
-
# raise NotImplementedError
|
50
|
-
end
|
35
|
+
require 'action_dispatch'
|
36
|
+
require 'action_controller'
|
51
37
|
yield
|
52
38
|
end
|
53
|
-
|
54
39
|
end
|
data/test/memcached_mock.rb
CHANGED
@@ -24,6 +24,7 @@ module MemcachedMock
|
|
24
24
|
# end
|
25
25
|
#
|
26
26
|
def memcached_mock(proc, meth = :start)
|
27
|
+
return unless supports_fork?
|
27
28
|
begin
|
28
29
|
pid = fork do
|
29
30
|
trap("TERM") { exit }
|
@@ -65,7 +66,8 @@ module MemcachedMock
|
|
65
66
|
nil
|
66
67
|
end
|
67
68
|
|
68
|
-
def memcached(port=19122, args='')
|
69
|
+
def memcached(port=19122, args='', options={})
|
70
|
+
return unless supports_fork?
|
69
71
|
Memcached.path ||= find_memcached
|
70
72
|
cmd = "#{Memcached.path}memcached #{args} -p #{port}"
|
71
73
|
$started[port] ||= begin
|
@@ -82,9 +84,13 @@ module MemcachedMock
|
|
82
84
|
pid
|
83
85
|
end
|
84
86
|
|
85
|
-
yield Dalli::Client.new(["localhost:#{port}", "127.0.0.1:#{port}"])
|
87
|
+
yield Dalli::Client.new(["localhost:#{port}", "127.0.0.1:#{port}"], options)
|
86
88
|
end
|
87
|
-
|
89
|
+
|
90
|
+
def supports_fork?
|
91
|
+
RUBY_ENGINE != 'jruby'
|
92
|
+
end
|
93
|
+
|
88
94
|
def memcached_kill(port)
|
89
95
|
pid = $started.delete(port)
|
90
96
|
if pid
|
data/test/test_active_support.rb
CHANGED
@@ -32,22 +32,12 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
32
32
|
with_activesupport do
|
33
33
|
memcached do
|
34
34
|
connect
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
mvalue = @dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
|
39
|
-
assert_equal mvalue, dvalue
|
40
|
-
else
|
41
|
-
assert_raises ArgumentError do
|
42
|
-
@mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
|
43
|
-
end
|
44
|
-
assert_raises ArgumentError do
|
45
|
-
@dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
|
46
|
-
end
|
47
|
-
end
|
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
|
48
38
|
end
|
49
39
|
end
|
50
|
-
end
|
40
|
+
end
|
51
41
|
|
52
42
|
should 'support read_multi' do
|
53
43
|
with_activesupport do
|
@@ -66,24 +56,21 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
66
56
|
end
|
67
57
|
end
|
68
58
|
end
|
69
|
-
|
59
|
+
|
70
60
|
should 'support read_multi with an array' do
|
71
61
|
with_activesupport do
|
72
62
|
memcached do
|
73
63
|
connect
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
assert_equal({ x => '123', y => 123 }, @dalli.read_multi([x, y]))
|
85
|
-
assert_equal({}, @mc.read_multi([x,y]))
|
86
|
-
end
|
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]))
|
87
74
|
end
|
88
75
|
end
|
89
76
|
end
|
@@ -94,18 +81,10 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
94
81
|
connect
|
95
82
|
@mc.write("abc", 5, :raw => true)
|
96
83
|
@mc.write("cba", 5, :raw => true)
|
97
|
-
|
98
|
-
assert_raise ArgumentError do
|
99
|
-
@mc.read_multi("abc", "cba")
|
100
|
-
end
|
101
|
-
else
|
102
|
-
assert_equal({'abc' => '5', 'cba' => '5' }, @mc.read_multi("abc", "cba"))
|
103
|
-
end
|
84
|
+
assert_equal({'abc' => '5', 'cba' => '5' }, @mc.read_multi("abc", "cba"))
|
104
85
|
|
105
86
|
@dalli.write("abc", 5, :raw => true)
|
106
87
|
@dalli.write("cba", 5, :raw => true)
|
107
|
-
# XXX: API difference between m-c and dalli. Dalli is smarter about
|
108
|
-
# what it needs to unmarshal.
|
109
88
|
assert_equal({'abc' => '5', 'cba' => '5' }, @dalli.read_multi("abc", "cba"))
|
110
89
|
end
|
111
90
|
end
|
@@ -127,7 +106,7 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
127
106
|
dres = @dalli.read(y)
|
128
107
|
assert_equal mres, dres
|
129
108
|
assert_equal 123, dres
|
130
|
-
|
109
|
+
|
131
110
|
mres = @mc.delete(x)
|
132
111
|
dres = @dalli.delete(y)
|
133
112
|
assert_equal mres, dres
|
@@ -135,7 +114,7 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
135
114
|
end
|
136
115
|
end
|
137
116
|
end
|
138
|
-
|
117
|
+
|
139
118
|
should 'support increment/decrement commands' do
|
140
119
|
with_activesupport do
|
141
120
|
memcached do
|
@@ -200,7 +179,7 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
200
179
|
end
|
201
180
|
end
|
202
181
|
end
|
203
|
-
|
182
|
+
|
204
183
|
def connect
|
205
184
|
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122', :expires_in => 10.seconds, :namespace => 'x')
|
206
185
|
@mc = ActiveSupport::Cache.lookup_store(:mem_cache_store, 'localhost:19122', :expires_in => 10.seconds, :namespace => 'a')
|
@@ -210,4 +189,4 @@ class TestActiveSupport < Test::Unit::TestCase
|
|
210
189
|
def rand_key
|
211
190
|
rand(1_000_000_000)
|
212
191
|
end
|
213
|
-
end
|
192
|
+
end
|
data/test/test_dalli.rb
CHANGED
@@ -43,7 +43,7 @@ class TestDalli < Test::Unit::TestCase
|
|
43
43
|
dc.set('a', val1)
|
44
44
|
val2 = dc.get('a')
|
45
45
|
assert_equal val1, val2
|
46
|
-
|
46
|
+
|
47
47
|
assert_equal true, dc.set('a', nil)
|
48
48
|
assert_nil dc.get('a')
|
49
49
|
end
|
@@ -90,11 +90,9 @@ class TestDalli < Test::Unit::TestCase
|
|
90
90
|
mutated
|
91
91
|
end
|
92
92
|
assert_equal true, resp
|
93
|
-
|
93
|
+
|
94
94
|
resp = dc.get('cas_key')
|
95
95
|
assert_equal mutated, resp
|
96
|
-
|
97
|
-
# TODO Need to verify failure when value is mutated between get and add.
|
98
96
|
end
|
99
97
|
end
|
100
98
|
|
@@ -269,7 +267,7 @@ class TestDalli < Test::Unit::TestCase
|
|
269
267
|
dc.close
|
270
268
|
end
|
271
269
|
end
|
272
|
-
|
270
|
+
|
273
271
|
should "support multithreaded access" do
|
274
272
|
memcached do |cache|
|
275
273
|
cache.flush
|
@@ -366,7 +364,7 @@ class TestDalli < Test::Unit::TestCase
|
|
366
364
|
assert_equal true, dc.set(idx, value)
|
367
365
|
rescue Dalli::DalliError
|
368
366
|
failed = true
|
369
|
-
assert((800..
|
367
|
+
assert((800..960).include?(idx), "unexpected failure on iteration #{idx}")
|
370
368
|
break
|
371
369
|
end
|
372
370
|
end
|
@@ -384,7 +382,7 @@ class TestDalli < Test::Unit::TestCase
|
|
384
382
|
assert_equal true, dalli.set(idx, value)
|
385
383
|
rescue Dalli::DalliError
|
386
384
|
failed = true
|
387
|
-
assert((6000..
|
385
|
+
assert((6000..7800).include?(idx), "unexpected failure on iteration #{idx}")
|
388
386
|
break
|
389
387
|
end
|
390
388
|
end
|
data/test/test_failover.rb
CHANGED
@@ -28,7 +28,7 @@ class TestFailover < Test::Unit::TestCase
|
|
28
28
|
dc.set 'foo', 'bar'
|
29
29
|
foo = dc.get 'foo'
|
30
30
|
assert_equal foo, 'bar'
|
31
|
-
|
31
|
+
|
32
32
|
memcached_kill(29125)
|
33
33
|
|
34
34
|
dc.set 'foo', 'bar'
|
@@ -51,7 +51,7 @@ class TestFailover < Test::Unit::TestCase
|
|
51
51
|
dc.set 'a', 'a1'
|
52
52
|
result = dc.get_multi ['a']
|
53
53
|
assert_equal result, {'a' => 'a1'}
|
54
|
-
|
54
|
+
|
55
55
|
memcached_kill(29125)
|
56
56
|
|
57
57
|
result = dc.get_multi ['a']
|
@@ -68,7 +68,7 @@ class TestFailover < Test::Unit::TestCase
|
|
68
68
|
dc.set 'bar', 'bar1'
|
69
69
|
result = dc.get_multi ['foo', 'bar']
|
70
70
|
assert_equal result, {'foo' => 'foo1', 'bar' => 'bar1'}
|
71
|
-
|
71
|
+
|
72
72
|
memcached_kill(29125)
|
73
73
|
|
74
74
|
dc.set 'foo', 'foo1'
|
@@ -78,9 +78,8 @@ class TestFailover < Test::Unit::TestCase
|
|
78
78
|
|
79
79
|
memcached_kill(29126)
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
end
|
81
|
+
result = dc.get_multi ['foo', 'bar']
|
82
|
+
assert_equal result, {}
|
84
83
|
end
|
85
84
|
end
|
86
85
|
end
|
@@ -92,7 +91,7 @@ class TestFailover < Test::Unit::TestCase
|
|
92
91
|
result = dc.stats
|
93
92
|
assert_instance_of Hash, result['localhost:29125']
|
94
93
|
assert_instance_of Hash, result['localhost:29126']
|
95
|
-
|
94
|
+
|
96
95
|
memcached_kill(29125)
|
97
96
|
|
98
97
|
dc = Dalli::Client.new ['localhost:29125', 'localhost:29126']
|
data/test/test_sasl.rb
CHANGED
@@ -15,7 +15,11 @@ class TestSasl < Test::Unit::TestCase
|
|
15
15
|
ENV['MEMCACHE_PASSWORD'] = nil
|
16
16
|
end
|
17
17
|
|
18
|
-
should '
|
18
|
+
should 'provide one test that passes' do
|
19
|
+
assert true
|
20
|
+
end
|
21
|
+
|
22
|
+
should_eventually 'gracefully handle authentication failures' do
|
19
23
|
memcached(19124, '-S') do |dc|
|
20
24
|
assert_raise Dalli::DalliError, /32/ do
|
21
25
|
dc.set('abc', 123)
|
@@ -24,7 +28,7 @@ class TestSasl < Test::Unit::TestCase
|
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
27
|
-
|
31
|
+
should_eventually 'fail SASL authentication with wrong options' do
|
28
32
|
memcached(19124, '-S') do |dc|
|
29
33
|
dc = Dalli::Client.new('localhost:19124', :username => 'foo', :password => 'wrongpwd')
|
30
34
|
assert_raise Dalli::DalliError, /32/ do
|
@@ -49,7 +53,7 @@ class TestSasl < Test::Unit::TestCase
|
|
49
53
|
ENV['MEMCACHE_PASSWORD'] = nil
|
50
54
|
end
|
51
55
|
|
52
|
-
|
56
|
+
should_eventually 'pass SASL authentication' do
|
53
57
|
memcached(19124, '-S') do |dc|
|
54
58
|
# I get "Dalli::DalliError: Error authenticating: 32" in OSX
|
55
59
|
# but SASL works on Heroku servers. YMMV.
|
@@ -62,7 +66,7 @@ class TestSasl < Test::Unit::TestCase
|
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
65
|
-
|
69
|
+
should_eventually 'pass SASL authentication with options' do
|
66
70
|
memcached(19124, '-S') do |dc|
|
67
71
|
dc = Dalli::Client.new('localhost:19124', :username => 'testuser', :password => 'testtest')
|
68
72
|
# I get "Dalli::DalliError: Error authenticating: 32" in OSX
|
@@ -76,4 +80,4 @@ class TestSasl < Test::Unit::TestCase
|
|
76
80
|
end
|
77
81
|
|
78
82
|
end
|
79
|
-
end
|
83
|
+
end
|
data/test/test_session_store.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
require 'helper'
|
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
|
-
|
7
2
|
require 'abstract_unit'
|
8
3
|
require 'action_dispatch/middleware/session/dalli_store'
|
9
4
|
|
@@ -207,24 +202,24 @@ class TestSessionStore < ActionController::IntegrationTest
|
|
207
202
|
end
|
208
203
|
end
|
209
204
|
rescue LoadError, RuntimeError
|
210
|
-
$stderr.puts "Skipping
|
205
|
+
$stderr.puts "Skipping SessionStore tests. Start memcached and try again: #{$!.message}"
|
211
206
|
end
|
212
207
|
|
213
208
|
private
|
214
|
-
def with_test_route_set(options = {})
|
215
|
-
options = {:key => '_session_id', :memcache_server => '127.0.0.1:11211'}.merge(options)
|
216
|
-
with_routing do |set|
|
217
|
-
set.draw do |map|
|
218
|
-
match ':action', :to => ::TestSessionStore::TestController
|
219
|
-
end
|
220
209
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
210
|
+
def with_test_route_set(options = {})
|
211
|
+
options = {:key => '_session_id', :memcache_server => '127.0.0.1:11211'}.merge(options)
|
212
|
+
with_routing do |set|
|
213
|
+
set.draw do
|
214
|
+
match ':action', :to => ::TestSessionStore::TestController
|
215
|
+
end
|
225
216
|
|
226
|
-
|
217
|
+
@app = self.class.build_app(set) do |middleware|
|
218
|
+
middleware.use ActionDispatch::Session::DalliStore, options
|
219
|
+
middleware.delete "ActionDispatch::ShowExceptions"
|
227
220
|
end
|
221
|
+
|
222
|
+
yield
|
228
223
|
end
|
229
|
-
end
|
230
|
-
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'memcached_mock'
|
3
|
+
if RUBY_ENGINE != 'jruby'
|
4
|
+
begin
|
5
|
+
require 'em-spec/test'
|
6
|
+
|
7
|
+
class TestSynchrony < Test::Unit::TestCase
|
8
|
+
include EM::TestHelper
|
9
|
+
|
10
|
+
context 'using a live server' do
|
11
|
+
|
12
|
+
should "support get/set" do
|
13
|
+
em do
|
14
|
+
memcached(19122,'',:async => true) do |dc|
|
15
|
+
dc.flush
|
16
|
+
|
17
|
+
val1 = "1234567890"*105000
|
18
|
+
assert_error Dalli::DalliError, /too large/ do
|
19
|
+
dc.set('a', val1)
|
20
|
+
val2 = dc.get('a')
|
21
|
+
assert_equal val1, val2
|
22
|
+
end
|
23
|
+
|
24
|
+
val1 = "1234567890"*100000
|
25
|
+
dc.set('a', val1)
|
26
|
+
val2 = dc.get('a')
|
27
|
+
assert_equal val1, val2
|
28
|
+
|
29
|
+
assert_equal true, dc.set('a', nil)
|
30
|
+
assert_nil dc.get('a')
|
31
|
+
|
32
|
+
done
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
should "support the fetch operation" do
|
38
|
+
em do
|
39
|
+
memcached(19122,'',:async => true) do |dc|
|
40
|
+
dc.flush
|
41
|
+
|
42
|
+
expected = { 'blah' => 'blerg!' }
|
43
|
+
executed = false
|
44
|
+
value = dc.fetch('fetch_key') do
|
45
|
+
executed = true
|
46
|
+
expected
|
47
|
+
end
|
48
|
+
assert_equal expected, value
|
49
|
+
assert_equal true, executed
|
50
|
+
|
51
|
+
executed = false
|
52
|
+
value = dc.fetch('fetch_key') do
|
53
|
+
executed = true
|
54
|
+
expected
|
55
|
+
end
|
56
|
+
assert_equal expected, value
|
57
|
+
assert_equal false, executed
|
58
|
+
|
59
|
+
done
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
should "support multi-get" do
|
65
|
+
em do
|
66
|
+
memcached(19122,'',:async => true) do |dc|
|
67
|
+
dc.close
|
68
|
+
dc.flush
|
69
|
+
resp = dc.get_multi(%w(a b c d e f))
|
70
|
+
assert_equal({}, resp)
|
71
|
+
|
72
|
+
dc.set('a', 'foo')
|
73
|
+
dc.set('b', 123)
|
74
|
+
dc.set('c', %w(a b c))
|
75
|
+
resp = dc.get_multi(%w(a b c d e f))
|
76
|
+
assert_equal({ 'a' => 'foo', 'b' => 123, 'c' => %w(a b c) }, resp)
|
77
|
+
|
78
|
+
# Perform a huge multi-get with 10,000 elements.
|
79
|
+
arr = []
|
80
|
+
dc.multi do
|
81
|
+
10_000.times do |idx|
|
82
|
+
dc.set idx, idx
|
83
|
+
arr << idx
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
result = dc.get_multi(arr)
|
88
|
+
assert_equal(10_000, result.size)
|
89
|
+
assert_equal(1000, result['1000'])
|
90
|
+
|
91
|
+
done
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
should "pass a simple smoke test" do
|
97
|
+
em do
|
98
|
+
memcached(19122,'',:async => true) do |dc|
|
99
|
+
resp = dc.flush
|
100
|
+
assert_not_nil resp
|
101
|
+
assert_equal [true, true], resp
|
102
|
+
|
103
|
+
assert_equal true, dc.set(:foo, 'bar')
|
104
|
+
assert_equal 'bar', dc.get(:foo)
|
105
|
+
|
106
|
+
resp = dc.get('123')
|
107
|
+
assert_equal nil, resp
|
108
|
+
|
109
|
+
resp = dc.set('123', 'xyz')
|
110
|
+
assert_equal true, resp
|
111
|
+
|
112
|
+
resp = dc.get('123')
|
113
|
+
assert_equal 'xyz', resp
|
114
|
+
|
115
|
+
resp = dc.set('123', 'abc')
|
116
|
+
assert_equal true, resp
|
117
|
+
|
118
|
+
dc.prepend('123', '0')
|
119
|
+
dc.append('123', '0')
|
120
|
+
|
121
|
+
assert_raises Dalli::DalliError do
|
122
|
+
resp = dc.get('123')
|
123
|
+
end
|
124
|
+
|
125
|
+
dc.close
|
126
|
+
dc = nil
|
127
|
+
|
128
|
+
dc = Dalli::Client.new('localhost:19122', :async => true)
|
129
|
+
|
130
|
+
resp = dc.set('456', 'xyz', 0, :raw => true)
|
131
|
+
assert_equal true, resp
|
132
|
+
|
133
|
+
resp = dc.prepend '456', '0'
|
134
|
+
assert_equal true, resp
|
135
|
+
|
136
|
+
resp = dc.append '456', '9'
|
137
|
+
assert_equal true, resp
|
138
|
+
|
139
|
+
resp = dc.get('456', :raw => true)
|
140
|
+
assert_equal '0xyz9', resp
|
141
|
+
|
142
|
+
resp = dc.stats
|
143
|
+
assert_equal Hash, resp.class
|
144
|
+
|
145
|
+
dc.close
|
146
|
+
|
147
|
+
done
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'with compression' do
|
153
|
+
should 'allow large values' do
|
154
|
+
em do
|
155
|
+
memcached(19122,'',:async => true) do |dc|
|
156
|
+
dalli = Dalli::Client.new(dc.instance_variable_get(:@servers), :compression => true, :async => true)
|
157
|
+
|
158
|
+
value = "0"*1024*1024
|
159
|
+
assert_raise Dalli::DalliError, /too large/ do
|
160
|
+
dc.set('verylarge', value)
|
161
|
+
end
|
162
|
+
dalli.set('verylarge', value)
|
163
|
+
end
|
164
|
+
|
165
|
+
done
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
rescue LoadError
|
173
|
+
puts "Skipping em-synchrony tests"
|
174
|
+
end
|
175
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: dalli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.
|
5
|
+
version: 1.1.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mike Perham
|
@@ -10,7 +10,8 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-08-31 00:00:00 -07:00
|
14
|
+
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: shoulda
|
@@ -42,7 +43,7 @@ dependencies:
|
|
42
43
|
requirements:
|
43
44
|
- - ">="
|
44
45
|
- !ruby/object:Gem::Version
|
45
|
-
version:
|
46
|
+
version: 3.0.0
|
46
47
|
type: :development
|
47
48
|
version_requirements: *id003
|
48
49
|
- !ruby/object:Gem::Dependency
|
@@ -65,7 +66,6 @@ extensions: []
|
|
65
66
|
extra_rdoc_files: []
|
66
67
|
|
67
68
|
files:
|
68
|
-
- lib/action_controller/session/dalli_store.rb
|
69
69
|
- lib/action_dispatch/middleware/session/dalli_store.rb
|
70
70
|
- lib/active_support/cache/dalli_store.rb
|
71
71
|
- lib/active_support/cache/dalli_store23.rb
|
@@ -99,6 +99,8 @@ files:
|
|
99
99
|
- test/test_ring.rb
|
100
100
|
- test/test_sasl.rb
|
101
101
|
- test/test_session_store.rb
|
102
|
+
- test/test_synchrony.rb
|
103
|
+
has_rdoc: true
|
102
104
|
homepage: http://github.com/mperham/dalli
|
103
105
|
licenses: []
|
104
106
|
|
@@ -122,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
124
|
requirements: []
|
123
125
|
|
124
126
|
rubyforge_project:
|
125
|
-
rubygems_version: 1.
|
127
|
+
rubygems_version: 1.5.2
|
126
128
|
signing_key:
|
127
129
|
specification_version: 3
|
128
130
|
summary: High performance memcached client for Ruby
|
@@ -140,3 +142,4 @@ test_files:
|
|
140
142
|
- test/test_ring.rb
|
141
143
|
- test/test_sasl.rb
|
142
144
|
- test/test_session_store.rb
|
145
|
+
- test/test_synchrony.rb
|
@@ -1,62 +0,0 @@
|
|
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
|
-
Rails.logger.debug("Using Dalli #{Dalli::VERSION} for session store at #{@default_options[:memcache_server].inspect}")
|
21
|
-
|
22
|
-
@pool = Dalli::Client.new(@default_options[:memcache_server], @default_options)
|
23
|
-
super
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
def get_session(env, sid)
|
28
|
-
sid ||= generate_sid
|
29
|
-
begin
|
30
|
-
session = @pool.get(sid) || {}
|
31
|
-
rescue Dalli::DalliError
|
32
|
-
Rails.logger.warn("Session::DalliStore#get: #{$!.message}")
|
33
|
-
session = {}
|
34
|
-
end
|
35
|
-
[sid, session]
|
36
|
-
end
|
37
|
-
|
38
|
-
def set_session(env, sid, session_data)
|
39
|
-
options = env['rack.session.options']
|
40
|
-
expiry = options[:expire_after]
|
41
|
-
@pool.set(sid, session_data, expiry)
|
42
|
-
return true
|
43
|
-
rescue Dalli::DalliError
|
44
|
-
Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
|
45
|
-
return false
|
46
|
-
end
|
47
|
-
|
48
|
-
def destroy(env)
|
49
|
-
if sid = current_session_id(env)
|
50
|
-
@pool.delete(sid)
|
51
|
-
end
|
52
|
-
rescue Dalli::DalliError
|
53
|
-
Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
|
54
|
-
false
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
rescue LoadError
|
61
|
-
# Dalli wasn't available so neither can the store be
|
62
|
-
end
|