Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mongodb EntityStore #49

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions Gemfile.lock

This file was deleted.

92 changes: 92 additions & 0 deletions lib/rack/cache/entitystore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,98 @@ def self.resolve(uri)
DISK = Disk
FILE = Disk

# Base class for mongo entity stores.
class Mongodb < EntityStore

attr_reader :cache

extend Rack::Utils

def open(key)
data = read(key)
data && [data]
end

# Resolving the URI is almost the same as Memcached.
# other than needing a database to connect to. Mongo
# also needs a connection name, this is currently hard
# coded as 'entitystore' but should probably be an ENV
# variable.
def self.resolve(uri)
if uri.respond_to?(:scheme)
server = uri.to_s
options = parse_query(uri.query)
options.keys.each do |key|
value =
case value = options.delete(key)
when 'true' ; true
when 'false' ; false
else value.to_sym
end
options[key.to_sym] = value
end
options[:database] = uri.path.sub(/^\//, '')
new server, options
else
# if the object provided is not a URI, pass it straight through
# to the underlying implementation.
new uri
end
end

def initialize(server="localhost:27017", options={})
@cache =
if server.respond_to?(:collection)
server['entitystore']
else
# use from_uri to make connecting to the DB simpler. Probably should support
# connection pooling and stuff in here too.
require 'mongo'
::Mongo::Connection.from_uri(server, options)[options[:database]]['entitystore']
end
end

def exist?(key)
# use find_one to return a single result and close the cursor
!cache.find_one({:key => key}).nil?
end

def read(key)
# as all data is being written in binary mode, check if nil, and
# return nil if so before converting to a useful format if there
# is a record returned.
data = cache.find_one({:key => key})
if data.nil?
nil
else
# need to call `.to_s` on a mongo binary field to make it useable.
data = data['string'].to_s if data
data.force_encoding('BINARY') if data.respond_to?(:force_encoding)
end
end

def write(body, ttl=nil)
buf = StringIO.new
key, size = slurp(body){|part| buf.write(part) }
# essentially the same as memcached so far, but encode all cached resources
# as BSON::Binary instances to ensure they don't break the db encoding
string = ::BSON::Binary.new(buf.string)
# Call the indexing facility to run in the background every 5 minutes - multiple
# calls to this do nothing, so it is safe to call on each write.
cache.ensure_index([['key', ::Mongo::ASCENDING]], {background:true})
# using `:upsert => true` allows us to override any existing record with that key
[key, size] if cache.update({:key => key}, {:key => key, :string => string, :expires => ttl}, {:upsert => true})
end

def purge(key)
cache.remove({:key => key})
nil
end
end

MONGO = Mongodb
MONGODB = Mongodb

# Base class for memcached entity stores.
class MemCacheBase < EntityStore
# The underlying Memcached instance used to communicate with the
Expand Down
2 changes: 2 additions & 0 deletions lib/rack/cache/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def create_store(type, uri)
case
when defined?(::Dalli) && uri.kind_of?(::Dalli::Client)
type.const_get(:Dalli).resolve(uri)
when defined?(::Mongo) && uri.kind_of?(::Mongo::DB)
type.const_get(:Mongodb).resolve(uri)
when defined?(::Memcached) && uri.respond_to?(:stats)
type.const_get(:MemCached).resolve(uri)
else
Expand Down
1 change: 1 addition & 0 deletions rack-cache.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'bacon'
s.add_development_dependency 'memcached'
s.add_development_dependency 'dalli'
s.add_development_dependency 'mongo'

s.has_rdoc = true
s.homepage = "http://tomayko.com/src/rack-cache/"
Expand Down
14 changes: 14 additions & 0 deletions test/entitystore_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding: utf-8
require "#{File.dirname(__FILE__)}/spec_setup"
require 'rack/cache/entitystore'
require 'rack/cache/metastore'

class Object
def sha_like?
Expand Down Expand Up @@ -211,6 +212,19 @@ def sha_like?
end
end

need_mongodb 'entity store tests' do
describe 'Mongodb' do
before do
@store = Rack::Cache::EntityStore::Mongodb.new($mongodb)
end
after do
@store = nil
end
behaves_like 'A Rack::Cache::EntityStore Implementation'
end

end


need_dalli 'entity store tests' do
describe 'Dalli' do
Expand Down
25 changes: 25 additions & 0 deletions test/spec_setup.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'pp'
require 'tmpdir'
require 'stringio'
require 'uri'

[STDOUT, STDERR].each { |io| io.sync = true }

Expand All @@ -14,8 +15,28 @@
# Set the MEMCACHED environment variable as follows to enable testing
# of the MemCached meta and entity stores.
ENV['MEMCACHED'] ||= 'localhost:11211'
ENV['MONGODB'] ||= 'localhost:27017'
$memcached = nil
$dalli = nil
$mongodb = nil

def have_mongodb?(server=ENV['MONGODB'])
return $mongodb unless $mongodb.nil?
require 'mongo'
$mongodb = Mongo::Connection.from_uri("mongodb://" + server)['rackcache']
$mongodb['test'].insert({:ping => ' '})
true
rescue LoadError => boom
warn "mongo library not available. related tests will be skipped."
$mongodb = false
false
rescue => boom
warn "mongo not working. related tests will be skipped."
$mongodb = false
false
end

have_mongodb?

def have_memcached?(server=ENV['MEMCACHED'])
return $memcached unless $memcached.nil?
Expand Down Expand Up @@ -61,6 +82,10 @@ def have_dalli?(server=ENV['MEMCACHED'])

have_dalli?

def need_mongodb(forwhat)
yield if have_mongodb?
end

def need_dalli(forwhat)
yield if have_dalli?
end
Expand Down
14 changes: 14 additions & 0 deletions test/storage_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,18 @@

end

if have_mongodb?

describe 'Mongo Store URIs' do
%w[mongodb:].each do |scheme|
it "resolves #{scheme} entity store URIs" do
uri = scheme + '//' + ENV['MONGODB'] + "/rackcache"
@storage.resolve_entitystore_uri(uri).
should.be.kind_of Rack::Cache::EntityStore
end
end
end

end

end