forked from paulcarey/relaxdb
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The cache only handles get requests (so in particular RelaxDB.load %w(lots of ids) is not cached), but it does handle all get requests (so views are cached). The cache uses the etags that couchdb provides to add a If-None-Match header, if the document or view has not changed then couchdb return a 304 response and the data from the cache is returned. This means that there is not a huge gain when loading a single document (other than shifting some work off couchdb) - the cache is most useful for views. This mechanism means that there is no expiry to worry about. Couchdb knows from the etag whether the cached data is up to date or not. The cache is off by default. To enable it pass a cache store object to RelaxDB.configure eg RelaxDB.configure ..., :cache_store => RelaxDB::MemoryStore.new(:size => 200) will tell RelaxDB to use a cache store that stores up to 200 entries in memory. There is also a simple memcache based store, eg RelaxDB.configure ..., :cache_store => RelaxDB::MemcacheStore.new('localhost:11211') which is more useful for a web app environment. All options passed to MemcacheStore.new are passed straight through to memcache-client
- Loading branch information
Showing
4 changed files
with
235 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
gem 'memcache-client' | ||
require 'memcache' | ||
require 'zlib' | ||
module RelaxDB | ||
class MemcacheStore | ||
def initialize(*args_for_memcache) | ||
@cache = MemCache.new *args_for_memcache | ||
end | ||
|
||
def get_etag(cache_key) | ||
@cache.get "etag:#{cache_key}" | ||
end | ||
|
||
def get_data(cache_key) | ||
entry = @cache.get "data:#{cache_key}" | ||
if entry[:deflated] | ||
Zlib::Inflate.inflate(entry[:data]) | ||
else | ||
entry[:data] | ||
end | ||
end | ||
|
||
def store(cache_key, data, etag) | ||
deflated = false | ||
if data.length >= 1024 * 1024 | ||
data = Zlib::Deflate.deflate(data, 1) | ||
deflated = true | ||
end | ||
|
||
if data.size < 1024*1024 #memcache cannot store values over 1mb | ||
@cache.set( "data:#{cache_key}", {:data => data, :deflated => deflated}) | ||
@cache.set( "etag:#{cache_key}", etag) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
require File.dirname(__FILE__) + '/spec_helper.rb' | ||
|
||
describe RelaxDB::MemoryStore do | ||
|
||
describe "cache" do | ||
it "should not store objects bigger than the maximum size" do | ||
@store = RelaxDB::MemoryStore.new :maximum_entry_size => 30 | ||
@store.store 'some_key', 'x' * 30, 'tag' | ||
|
||
@store.get('some_key').should_not be_nil | ||
|
||
@store.store 'some_other_key', 'x' * 31, 'tag' | ||
@store.get('some_other_key').should be_nil | ||
end | ||
|
||
it "should store and retrieve objects" do | ||
@store = RelaxDB::MemoryStore.new | ||
@store.store 'some_key', 'x' * 30, 'tag' | ||
entry = @store.get 'some_key' | ||
entry.data.should == 'x' * 30 | ||
entry.etag.should == 'tag' | ||
end | ||
|
||
it "should remove old items from the cache" do | ||
@store = RelaxDB::MemoryStore.new :size => 2 | ||
|
||
@store.store 'first_key', 'data', 'tag' | ||
@store.store 'second_key', 'data', 'tag' | ||
@store.store 'third_key', 'data', 'tag' | ||
|
||
@store.get('second_key').should_not be_nil | ||
@store.get('third_key').should_not be_nil | ||
@store.get('first_key').should be_nil | ||
end | ||
|
||
it "should not remove items from cache when storing an existing key" do | ||
@store = RelaxDB::MemoryStore.new :size => 2 | ||
@store.store 'first_key', 'data', 'tag' | ||
@store.store 'second_key', 'data', 'tag' | ||
@store.store 'second_key', 'data2', 'tag' | ||
@store.store 'second_key', 'data3', 'tag' | ||
|
||
@store.get('second_key').should_not be_nil | ||
@store.get('first_key').should_not be_nil | ||
end | ||
|
||
it "should push items to the front of the queue" do | ||
@store = RelaxDB::MemoryStore.new :size => 2 | ||
@store.store 'first_key', 'data', 'tag' | ||
@store.store 'second_key', 'data', 'tag' | ||
|
||
@store.get('first_key') | ||
@store.store 'third_key', 'data', 'tag' | ||
|
||
@store.get('second_key').should be_nil | ||
@store.get('third_key').should_not be_nil | ||
@store.get('first_key').should_not be_nil | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters