Rack::Cache on Google App Engine

It’s been a few weeks now since Google announced Java support on their App Engine infrastructure. Since Java support also adds support for JRuby, it is now possible to deploy Rails or Sinatra or any other Rack based applications to Google. In my case, I am playing with Sinatra 🙂 This is an excelent step-by-step tutorial on how to get started with Sinatra App. If you running your applications on App Engine, you might be interested (at least I was) in reducing your application load by using some sort of page cache. Since Google won’t allow you to write out files, I figured using their MemCachedService might be pretty nice way to work around this issue. Ryan Tomayko has written a very nice Rack middle-ware, Rack::Cache, which provides support for cache control and validation and looked to me like a very nice solution. Following is my extension to Ryan’s work, which will allow to use Rack::Cache with Google Memcache as a meta-data and entity storage.
I have the following code in ./lib/cache.rb file:

require 'java'
require 'rack/cache'
require 'yaml'

module Cache

module MC
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.memcache.Stats;

Service = MemcacheServiceFactory.getMemcacheService
end

module ClassMethods
def clear
MC::Service.clearAll
end

def exists?(key)
MC::Service.contains(key)
end

alias_method :contains?, :exists?

def get(key)
value = MC::Service.get(key)
YAML.load(value) if value
end

def put(key, value, ttl = nil)
expiration = ttl ? MC::Expiration.byDeltaSeconds(ttl) : nil
MC::Service.put(key, value.to_yaml, expiration)
end

def namespace
MC::Service.getNamespace
end

def namespace=(value)
MC::Service.setNamespace(value.to_s)
end

def delete(key)
MC::Service.delete(key)
end
end

module Service
extend Cache::ClassMethods
end

end

module Rack::Cache

class MetaStore

public

class GAEStore < MetaStore
attr_reader :cache

def initialize(options = {})
@cache = Cache::Service
@cache.namespace = options[:namespace] if options[:namespace]
end

def read(key)
key = hexdigest(key)
@cache.get(key) || []
end

def write(key, entries)
key = hexdigest(key)
@cache.put(key, entries)
end

def purge(key)
key = hexdigest(key)
@cache.delete(key)
end

def self.resolve(uri)
self.new(:namespace => uri.host)
end

end

GAECACHE = GAEStore
GAE = GAEStore
end

class EntityStore

public

class GAEStore < EntityStore
attr_reader :cache

def initialize(options = {})
@cache = Cache::Service
@cache.namespace = options[:namespace] if options[:namespace]
end

def exist?(key)
@cache.exists?(key)
end

def read(key)
result = @cache.get(key)
result
end

def open(key)
if data = read(key)
[data]
else
nil
end
end

def write(body)
buf = StringIO.new
key, size = slurp(body){|part| buf.write(part) }
@cache.put(key, buf.string)
[key, size]
end

def self.resolve(uri)
self.new(:namespace => uri.host)
end
end

GAECACHE = GAEStore
GAE = GAEStore

end
end

Add the following into config.ru:

……..

require ‘application’

use Rack::Cache, {:metastore => ‘gae://namespace‘, :entitystore => ‘gae://namespace‘}

run Sinatra::Application

In order to make it work properly, read Ryan’s documentation on all supported options.

I am hoping this is helpful 🙂

Advertisements

About Wondering Bear
39 years old male bear.

3 Responses to Rack::Cache on Google App Engine

  1. Ryan Tomayko says:

    Let’s get this into rack-cache proper. Care to take a crack at a patch? http://github.com/rtomayko/rack-cache/

  2. Wondering Bear says:

    Latest release on rack-cache (0.5) has integrated GAE storage. See http://github.com/rtomayko/rack-cache/blob/0.5.0/CHANGES for details.

    Big Thanks to Ryan Tomayko!!!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: