cachecataz
Cachecataz is for namespace expiry in a cache where the cache provider does not enable namespace based expiry (like memcached).
Cachecataz creates a namespace key based on a cache_scope defined for the class. Each cache namespace can be expired through an instance or the class.
Problem
Cache servers like Memcache only provide expiry for cache keys based on a unique value. Don’t allow you to specify an entire namespace to expire based on changes in the underlying data.
Solution
Store a value in the cache that determines the namespace of the cache key and increment the value of the namespace key each time the namespace needs to be expired.
Using Cachecataz
Install it:
gem install cachecataz
Include it and define your scopes:
class Element < ActiveRecord::Base
include Cachecataz
cache_scope :name, [:user_id]
end
Enable it:
# ** production.rb **
Name::Application.configure do
#....... bunch of stuff
config.action_controller.perform_caching = true
config.cache_store = :mem_cache_store, 'localhost:11211'
# .... more stuff
config.after_initialize do
Cachecataz.enable = true
Cachecataz.provider = Rails.cache
end
end
Cachecataz is disabled by default. If you use it outside of Rails you need to enable it through the configuration options. Inside Rails this is done in your environment files under your Rails.root/config/environments/ dir.
A cache_scope is the mechanism to create a unique scope for a namespace. The namespace key will be comprised of the cache_scope name (in this example “name”) and the runtime state of any value passed in the Array of symbols that make up a unique scope.
For the example above the namespace key would be “name:#elementelement.user_id”, which allows us to have a unique namespace for each “name” key scoped by the user_id.
Example (in Rails)
Basic premise: An Element belongs_to a User and a Widget belongs_to a User as well. The “something” partial displays data primarily related to the Element but also displays data from the related Widget. I want to have a sweeper that observes changes in the Widget model and expires the namespace for the Element. (can be kinda confusing, but makes sense in the context of a cache that is model dependent.)
Generate a cache key:
# ** Model **
class Element < ActiveRecord::Base
include Cachecataz
cache_scope :user, [:user_id]
end
** View **
<% cache(@element.cache_key(:user, :id)) do %>
<% render :partial => "something", :locals => { :widget => @element.widget } %>
<% end %>
# ** Observer **
class WidgetObserver < ActiveRecord::Observer
after_update(widget)
Element.expire_namespace(:user, widget.attributes) # can also just pass {:user_id => widget.user_id} as the scope_hash, scope requires :user_id
end
end
# ** Another Observer ** Why expire_fragment is available? not important
class ElementObserver < CacheSweepingObserver
after_update(element)
expire_fragment(element.cache_key(:user, :id)) # Will only expire a single key "0:user:1/12" if user_id == 1 and id == 12
end
after_create(element)
element.expire_namespace(:user) # will expire the entire namespace for cache_scope :user for user 1 if user_id ==1
end
end
# by default the scope_hash on any expire_namespace call for an instance is self.attributes
TODO
post more info on how to use cachecataz with a cache that is not Rails.cache (it’s very simple), but wanted to get the basics released
Contributing to cachecataz
-
Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet
-
Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it
-
Fork the project
-
Start a feature/bugfix branch
-
Commit and push until you are happy with your contribution
-
Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.
-
Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
Copyright
Copyright © 2011 Brandon Dewitt. See LICENSE.txt for further details.