redis-store 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of redis-store might be problematic. Click here for more details.

Files changed (39) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +13 -7
  3. data/README.md +11 -1
  4. data/Rakefile +19 -5
  5. data/VERSION +1 -1
  6. data/lib/cache/merb/redis_store.rb +6 -6
  7. data/lib/cache/rails/redis_store.rb +10 -10
  8. data/lib/cache/sinatra/redis_store.rb +14 -11
  9. data/lib/rack/cache/redis_entitystore.rb +2 -2
  10. data/lib/rack/cache/redis_metastore.rb +5 -5
  11. data/lib/rack/session/rails.rb +61 -0
  12. data/lib/rack/session/redis.rb +10 -10
  13. data/lib/redis-store.rb +8 -4
  14. data/lib/redis/distributed_marshaled.rb +18 -0
  15. data/lib/redis/factory.rb +26 -0
  16. data/lib/redis/marshaled_client.rb +54 -0
  17. data/lib/redis/namespace.rb +9 -0
  18. data/redis-store.gemspec +18 -13
  19. data/spec/cache/merb/redis_store_spec.rb +9 -14
  20. data/spec/cache/rails/redis_session_store_spec.rb +77 -0
  21. data/spec/cache/rails/redis_store_spec.rb +13 -17
  22. data/spec/cache/sinatra/redis_store_spec.rb +9 -14
  23. data/spec/config/master.conf +5 -5
  24. data/spec/config/single.conf +5 -5
  25. data/spec/config/slave.conf +6 -6
  26. data/spec/rack/cache/entitystore/redis_spec.rb +21 -11
  27. data/spec/rack/cache/metastore/redis_spec.rb +169 -16
  28. data/spec/rack/session/redis_spec.rb +7 -11
  29. data/spec/redis/distributed_marshaled_redis_spec.rb +9 -11
  30. data/spec/redis/factory_spec.rb +68 -0
  31. data/spec/redis/marshaled_client_spec.rb +54 -0
  32. data/spec/spec_helper.rb +11 -5
  33. data/tasks/redis.tasks.rb +16 -5
  34. metadata +28 -12
  35. data/lib/redis/distributed_marshaled_redis.rb +0 -10
  36. data/lib/redis/marshaled_redis.rb +0 -33
  37. data/lib/redis/redis_factory.rb +0 -27
  38. data/spec/redis/marshaled_redis_spec.rb +0 -54
  39. data/spec/redis/redis_factory_spec.rb +0 -34
@@ -124,10 +124,10 @@ databases 16
124
124
  #
125
125
  # The name of the append only file is "appendonly.log"
126
126
 
127
- appendonly no
127
+ #appendonly no
128
128
 
129
129
  # The fsync() call tells the Operating System to actually write data on disk
130
- # instead to wait for more data in the output buffer. Some OS will really flush
130
+ # instead to wait for more data in the output buffer. Some OS will really flush
131
131
  # data on disk, some other OS will just try to do it ASAP.
132
132
  #
133
133
  # Redis supports three different modes:
@@ -142,7 +142,7 @@ appendonly no
142
142
  # it want, for better performances (but if you can live with the idea of
143
143
  # some data loss consider the default persistence mode that's snapshotting).
144
144
 
145
- appendfsync always
145
+ #appendfsync always
146
146
  # appendfsync everysec
147
147
  # appendfsync no
148
148
 
@@ -167,5 +167,5 @@ glueoutputbuf yes
167
167
  # WARNING: object sharing is experimental, don't enable this feature
168
168
  # in production before of Redis 1.0-stable. Still please try this feature in
169
169
  # your development environment so that we can test it better.
170
- shareobjects no
171
- shareobjectspoolsize 1024
170
+ #shareobjects no
171
+ #shareobjectspoolsize 1024
@@ -124,10 +124,10 @@ databases 16
124
124
  #
125
125
  # The name of the append only file is "appendonly.log"
126
126
 
127
- appendonly no
127
+ #appendonly no
128
128
 
129
129
  # The fsync() call tells the Operating System to actually write data on disk
130
- # instead to wait for more data in the output buffer. Some OS will really flush
130
+ # instead to wait for more data in the output buffer. Some OS will really flush
131
131
  # data on disk, some other OS will just try to do it ASAP.
132
132
  #
133
133
  # Redis supports three different modes:
@@ -142,7 +142,7 @@ appendonly no
142
142
  # it want, for better performances (but if you can live with the idea of
143
143
  # some data loss consider the default persistence mode that's snapshotting).
144
144
 
145
- appendfsync always
145
+ #appendfsync always
146
146
  # appendfsync everysec
147
147
  # appendfsync no
148
148
 
@@ -167,5 +167,5 @@ glueoutputbuf yes
167
167
  # WARNING: object sharing is experimental, don't enable this feature
168
168
  # in production before of Redis 1.0-stable. Still please try this feature in
169
169
  # your development environment so that we can test it better.
170
- shareobjects no
171
- shareobjectspoolsize 1024
170
+ #shareobjects no
171
+ #shareobjectspoolsize 1024
@@ -65,7 +65,7 @@ databases 16
65
65
  # so for example it is possible to configure the slave to save the DB with a
66
66
  # different interval, or to listen to another port, and so on.
67
67
 
68
- slaveof localhost 6380
68
+ slaveof localhost 6380
69
69
 
70
70
  ################################## SECURITY ###################################
71
71
 
@@ -124,10 +124,10 @@ slaveof localhost 6380
124
124
  #
125
125
  # The name of the append only file is "appendonly.log"
126
126
 
127
- appendonly no
127
+ #appendonly no
128
128
 
129
129
  # The fsync() call tells the Operating System to actually write data on disk
130
- # instead to wait for more data in the output buffer. Some OS will really flush
130
+ # instead to wait for more data in the output buffer. Some OS will really flush
131
131
  # data on disk, some other OS will just try to do it ASAP.
132
132
  #
133
133
  # Redis supports three different modes:
@@ -142,7 +142,7 @@ appendonly no
142
142
  # it want, for better performances (but if you can live with the idea of
143
143
  # some data loss consider the default persistence mode that's snapshotting).
144
144
 
145
- appendfsync always
145
+ #appendfsync always
146
146
  # appendfsync everysec
147
147
  # appendfsync no
148
148
 
@@ -167,5 +167,5 @@ glueoutputbuf yes
167
167
  # WARNING: object sharing is experimental, don't enable this feature
168
168
  # in production before of Redis 1.0-stable. Still please try this feature in
169
169
  # your development environment so that we can test it better.
170
- shareobjects no
171
- shareobjectspoolsize 1024
170
+ #shareobjects no
171
+ #shareobjectspoolsize 1024
@@ -1,31 +1,40 @@
1
1
  require File.join(File.dirname(__FILE__), "/../../../spec_helper")
2
2
 
3
+ class Object
4
+ def sha_like?
5
+ length == 40 && self =~ /^[0-9a-z]+$/
6
+ end
7
+ end
8
+
3
9
  module Rack
4
10
  module Cache
5
11
  class EntityStore
12
+ # courtesy of http://github.com/rtomayko/rack-cache team
6
13
  describe "Rack::Cache::EntityStore::Redis" do
7
14
  before(:each) do
8
15
  @store = Rack::Cache::EntityStore::Redis.new :host => "localhost"
9
16
  end
10
17
 
18
+ # Redis store specific examples ===========================================
19
+
11
20
  it "should have the class referenced by homonym constant" do
12
21
  Rack::Cache::EntityStore::REDIS.should be(Rack::Cache::EntityStore::Redis)
13
22
  end
14
23
 
15
24
  it "should resolve the connection uri" do
16
25
  cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1")).cache
17
- cache.should be_kind_of(::Redis)
18
- cache.host.should == "127.0.0.1"
19
- cache.port.should == 6379
20
- cache.db.should == 0
26
+ cache.should be_kind_of(::Redis::Client)
27
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
21
28
 
22
29
  cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
23
- cache.port.should == 6380
30
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
24
31
 
25
32
  cache = Rack::Cache::EntityStore::Redis.resolve(uri("redis://127.0.0.1/13")).cache
26
- cache.db.should == 13
33
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 13"
27
34
  end
28
35
 
36
+ # Entity store shared examples ===========================================
37
+
29
38
  it 'responds to all required messages' do
30
39
  %w[read open write exist?].each do |message|
31
40
  @store.should respond_to(message)
@@ -35,7 +44,7 @@ module Rack
35
44
  it 'stores bodies with #write' do
36
45
  key, size = @store.write(['My wild love went riding,'])
37
46
  key.should_not be_nil
38
- # key.should be_sha_like TODO re-enable
47
+ key.should be_sha_like
39
48
 
40
49
  data = @store.read(key)
41
50
  data.should == 'My wild love went riding,'
@@ -98,10 +107,11 @@ module Rack
98
107
  @store.read(key).should be_nil
99
108
  end
100
109
 
101
- private
102
- def uri(uri)
103
- URI.parse uri
104
- end
110
+ # Helper Methods =============================================================
111
+
112
+ define_method :uri do |uri|
113
+ URI.parse uri
114
+ end
105
115
  end
106
116
  end
107
117
  end
@@ -3,9 +3,18 @@ require File.join(File.dirname(__FILE__), "/../../../spec_helper")
3
3
  module Rack
4
4
  module Cache
5
5
  class MetaStore
6
+ # courtesy of http://github.com/rtomayko/rack-cache team
6
7
  describe "Rack::Cache::MetaStore::Redis" do
7
- before(:each) do
8
- @store = Rack::Cache::MetaStore::Redis.resolve uri("redis://127.0.0.1")
8
+ before :each do
9
+ @store = Rack::Cache::MetaStore::Redis.resolve uri("redis://127.0.0.1")
10
+ @entity_store = Rack::Cache::EntityStore::Redis.resolve uri("redis://127.0.0.1:6380")
11
+ @request = mock_request('/', {})
12
+ @response = mock_response(200, {}, ['hello world'])
13
+ end
14
+
15
+ after :each do
16
+ @store.cache.flushall
17
+ @entity_store.cache.flushall
9
18
  end
10
19
 
11
20
  it "should have the class referenced by homonym constant" do
@@ -14,18 +23,18 @@ module Rack
14
23
 
15
24
  it "should resolve the connection uri" do
16
25
  cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1")).cache
17
- cache.should be_kind_of(::MarshaledRedis)
18
- cache.host.should == "127.0.0.1"
19
- cache.port.should == 6379
20
- cache.db.should == 0
26
+ cache.should be_kind_of(::Redis::MarshaledClient)
27
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
21
28
 
22
29
  cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1:6380")).cache
23
- cache.port.should == 6380
30
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
24
31
 
25
32
  cache = Rack::Cache::MetaStore::Redis.resolve(uri("redis://127.0.0.1/13")).cache
26
- cache.db.should == 13
33
+ cache.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 13"
27
34
  end
28
35
 
36
+ # Low-level implementation methods ===========================================
37
+
29
38
  it 'writes a list of negotation tuples with #write' do
30
39
  lambda { @store.write('/test', [[{}, {}]]) }.should_not raise_error
31
40
  end
@@ -87,15 +96,159 @@ module Rack
87
96
  @store.cache_key(request).should == 'tset/'
88
97
  end
89
98
 
90
- private
91
- def mock_request(uri, opts)
92
- env = Rack::MockRequest.env_for(uri, opts || {})
93
- Rack::Cache::Request.new(env)
94
- end
99
+ # Abstract methods ===========================================================
100
+
101
+ # Stores an entry for the given request args, returns a url encoded cache key
102
+ # for the request.
103
+ define_method :store_simple_entry do |*request_args|
104
+ path, headers = request_args
105
+ @request = mock_request(path || '/test', headers || {})
106
+ @response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test'])
107
+ body = @response.body
108
+ cache_key = @store.store(@request, @response, @entity_store)
109
+ @response.body.should_not equal(body)
110
+ cache_key
111
+ end
95
112
 
96
- def uri(uri)
97
- URI.parse uri
98
- end
113
+ it 'stores a cache entry' do
114
+ cache_key = store_simple_entry
115
+ @store.read(cache_key).should_not be_empty
116
+ end
117
+
118
+ it 'sets the X-Content-Digest response header before storing' do
119
+ cache_key = store_simple_entry
120
+ req, res = @store.read(cache_key).first
121
+ res['X-Content-Digest'].should == 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'
122
+ end
123
+
124
+ it 'finds a stored entry with #lookup' do
125
+ store_simple_entry
126
+ response = @store.lookup(@request, @entity_store)
127
+ response.should_not be_nil
128
+ response.should be_kind_of(Rack::Cache::Response)
129
+ end
130
+
131
+ it 'does not find an entry with #lookup when none exists' do
132
+ req = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
133
+ @store.lookup(req, @entity_store).should be_nil
134
+ end
135
+
136
+ it "canonizes urls for cache keys" do
137
+ store_simple_entry(path='/test?x=y&p=q')
138
+
139
+ hits_req = mock_request(path, {})
140
+ miss_req = mock_request('/test?p=x', {})
141
+
142
+ @store.lookup(hits_req, @entity_store).should_not be_nil
143
+ @store.lookup(miss_req, @entity_store).should be_nil
144
+ end
145
+
146
+ it 'does not find an entry with #lookup when the body does not exist' do
147
+ store_simple_entry
148
+ @response.headers['X-Content-Digest'].should_not be_nil
149
+ @entity_store.purge(@response.headers['X-Content-Digest'])
150
+ @store.lookup(@request, @entity_store).should be_nil
151
+ end
152
+
153
+ it 'restores response headers properly with #lookup' do
154
+ store_simple_entry
155
+ response = @store.lookup(@request, @entity_store)
156
+ response.headers.should == @response.headers.merge('Content-Length' => '4')
157
+ end
158
+
159
+ it 'restores response body from entity store with #lookup' do
160
+ store_simple_entry
161
+ response = @store.lookup(@request, @entity_store)
162
+ body = '' ; response.body.each {|p| body << p}
163
+ body.should == 'test'
164
+ end
165
+
166
+ it 'invalidates meta and entity store entries with #invalidate' do
167
+ store_simple_entry
168
+ @store.invalidate(@request, @entity_store)
169
+ response = @store.lookup(@request, @entity_store)
170
+ response.should be_kind_of(Rack::Cache::Response)
171
+ response.should_not be_fresh
172
+ end
173
+
174
+ it 'succeeds quietly when #invalidate called with no matching entries' do
175
+ req = mock_request('/test', {})
176
+ @store.invalidate(req, @entity_store)
177
+ @store.lookup(@request, @entity_store).should be_nil
178
+ end
179
+
180
+ # Vary =======================================================================
181
+
182
+ it 'does not return entries that Vary with #lookup' do
183
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
184
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
185
+ res = mock_response(200, {'Vary' => 'Foo Bar'}, ['test'])
186
+ @store.store(req1, res, @entity_store)
187
+
188
+ @store.lookup(req2, @entity_store).should be_nil
189
+ end
190
+
191
+ it 'stores multiple responses for each Vary combination' do
192
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
193
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
194
+ key = @store.store(req1, res1, @entity_store)
195
+
196
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
197
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
198
+ @store.store(req2, res2, @entity_store)
199
+
200
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom'})
201
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
202
+ @store.store(req3, res3, @entity_store)
203
+
204
+ slurp(@store.lookup(req3, @entity_store).body).should == 'test 3'
205
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
206
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
207
+
208
+ @store.read(key).length.should == 3
209
+ end
210
+
211
+ it 'overwrites non-varying responses with #store' do
212
+ req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
213
+ res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
214
+ key = @store.store(req1, res1, @entity_store)
215
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 1'
216
+
217
+ req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
218
+ res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
219
+ @store.store(req2, res2, @entity_store)
220
+ slurp(@store.lookup(req2, @entity_store).body).should == 'test 2'
221
+
222
+ req3 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
223
+ res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
224
+ @store.store(req3, res3, @entity_store)
225
+ slurp(@store.lookup(req1, @entity_store).body).should == 'test 3'
226
+
227
+ @store.read(key).length.should == 2
228
+ end
229
+
230
+ # Helper Methods =============================================================
231
+
232
+ define_method :mock_request do |uri,opts|
233
+ env = Rack::MockRequest.env_for(uri, opts || {})
234
+ Rack::Cache::Request.new(env)
235
+ end
236
+
237
+ define_method :mock_response do |status,headers,body|
238
+ headers ||= {}
239
+ body = Array(body).compact
240
+ Rack::Cache::Response.new(status, headers, body)
241
+ end
242
+
243
+ define_method :slurp do |body|
244
+ buf = ''
245
+ body.each {|part| buf << part }
246
+ buf
247
+ end
248
+
249
+ define_method :uri do |uri|
250
+ URI.parse uri
251
+ end
99
252
  end
100
253
  end
101
254
  end
@@ -27,13 +27,11 @@ module Rack
27
27
 
28
28
  it "should specify connection params" do
29
29
  pool = Rack::Session::Redis.new(@incrementor, :redis_server => "localhost:6380/1").pool
30
- pool.should be_kind_of(MarshaledRedis)
31
- pool.host.should == "localhost"
32
- pool.port.should == 6380
33
- pool.db.should == 1
30
+ pool.should be_kind_of(::Redis::MarshaledClient)
31
+ pool.to_s.should == "Redis Client connected to localhost:6380 against DB 1"
34
32
 
35
33
  pool = Rack::Session::Redis.new(@incrementor, :redis_server => ["localhost:6379", "localhost:6380"]).pool
36
- pool.should be_kind_of(DistributedMarshaledRedis)
34
+ pool.should be_kind_of(::Redis::DistributedMarshaled)
37
35
  end
38
36
 
39
37
  it "creates a new cookie" do
@@ -48,12 +46,10 @@ module Rack
48
46
  req = Rack::MockRequest.new(pool)
49
47
  res = req.get("/")
50
48
  cookie = res["Set-Cookie"]
51
- req.get("/", "HTTP_COOKIE" => cookie).
52
- body.should == '{"counter"=>2}'
53
- req.get("/", "HTTP_COOKIE" => cookie).
54
- body.should == '{"counter"=>3}'
49
+ req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>2}'
50
+ req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>3}'
55
51
  end
56
-
52
+
57
53
  it "survives nonexistant cookies" do
58
54
  bad_cookie = "rack.session=blarghfasel"
59
55
  pool = Rack::Session::Redis.new(@incrementor)
@@ -211,7 +207,7 @@ module Rack
211
207
  session['counter'].should == 3
212
208
 
213
209
  drop_counter = proc do |env|
214
- env['rack.session'].delete 'counter'
210
+ env['rack.session'].del 'counter'
215
211
  env['rack.session']['foo'] = 'bar'
216
212
  [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
217
213
  end
@@ -1,35 +1,33 @@
1
1
  require File.join(File.dirname(__FILE__), "/../spec_helper")
2
2
 
3
- describe "DistributedMarshaledRedis" do
3
+ describe "Redis::DistributedMarshaled" do
4
4
  before(:each) do
5
- @dmr = DistributedMarshaledRedis.new [
5
+ @dmr = Redis::DistributedMarshaled.new [
6
6
  {:host => "localhost", :port => "6380", :db => 0},
7
7
  {:host => "localhost", :port => "6381", :db => 0}
8
8
  ]
9
9
  @rabbit = OpenStruct.new :name => "bunny"
10
10
  @white_rabbit = OpenStruct.new :color => "white"
11
- @dmr.set "rabbit", @rabbit
11
+ @dmr.marshalled_set "rabbit", @rabbit
12
12
  end
13
13
 
14
14
  after(:all) do
15
- @dmr.ring.nodes.each { |server| server.flush_db }
15
+ @dmr.ring.nodes.each { |server| server.flushdb }
16
16
  end
17
17
 
18
18
  it "should accept connection params" do
19
- dmr = DistributedMarshaledRedis.new [ :host => "localhost", :port => "6380", :db => "1" ]
19
+ dmr = Redis::DistributedMarshaled.new [ :host => "localhost", :port => "6380", :db => "1" ]
20
20
  dmr.ring.should have(1).node
21
21
  mr = dmr.ring.nodes.first
22
- mr.host.should == "localhost"
23
- mr.port.should == 6380
24
- mr.db.should == 1
22
+ mr.to_s.should == "Redis Client connected to localhost:6380 against DB 1"
25
23
  end
26
24
 
27
25
  it "should set an object" do
28
- @dmr.set "rabbit", @white_rabbit
29
- @dmr.get("rabbit").should == @white_rabbit
26
+ @dmr.marshalled_set "rabbit", @white_rabbit
27
+ @dmr.marshalled_get("rabbit").should == @white_rabbit
30
28
  end
31
29
 
32
30
  it "should get an object" do
33
- @dmr.get("rabbit").should == @rabbit
31
+ @dmr.marshalled_get("rabbit").should == @rabbit
34
32
  end
35
33
  end