fastaccess - redisfast access on generated content

Many web applications which provide textual content to the user have much more reads than writes. This can mean, that on every 10k reads there is one write (see some popular blogs or newspages).

Often this content is generated, because the input source is some markup format, like markdown. This means, that there is always the need to generate this content into html.

And this is why fastaccess exists. It modifies any given method, which generates content, and stores it in a redis database. If this content solely depends on a model attribute, for example like the body in a blog post, it will be auto updated, if the underlying model attribute changes. Otherwise you can trigger the update manually.

Now lets see how this works:

Using fastaccess

First, of course, you'll need to include this gem in your projects Gemfile.

Since this gem utilizes redis, make sure that this is installed and generate the default initializer with

rails generate fastaccess:initialize

This will create a file in config/initializers/fastaccess.rb which will create an instance of a redis database-connection. If you already have one of those, feel free to replace the new instance with your version.

Now, in your project you will have to use the gem-provided acts_with_fastaccess_on method to mark certain methods as registered with fastaccess for caching.

class Post < ActiveRecord::Base
  attr_accessible :title, :body
  acts_with_fastaccess_on :markdown_body

  def markdown_body
    markdown(self.body)
  end
end

In this example a Model named Post is defined which has title and body attributes. The markdown_body method converts the markdown-body to html. Since this is needed often, it cached via redis.

The previous example utilizes a method, which operates on one of the models attributes. So the redis-content will be updated if the pertaining Model instance is updated. But what if you have a method which uses more input than just specific attributes, which will update the models timestamp?

For this case there is an explicit update method, which allows you to trigger an update.

# app/models/post.rb
class Post < ActiveRecord::Base
  attr_accessible :title, :body, :tags
  has_many :tags
  acts_with_fastaccess_on :tag_list
  include Fastaccess::Mixins

  def tag_list
    self.tags.map(&name)
  end
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def update
    @post = Post.find_by_id(params[:id])
    @post.update_on :tag_list
    respond_to do |format|
      if @post.update_attributes(params[:post])
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end 
  end
end

If you don't want to pollute your model with these mixin-methods you can also call

Fastaccess::Fastaccess.update_content @post, :on => :tag_list, :arguments => []

Configuring Fastaccess

Initializers

Fastaccess comes with a generator for an initializer file. You can run it by calling:

rails generate fastaccess:initialize

This will create config/initializers/fastaccess.rb, which allows you to set the redis-connector instance, which will be used to communicate with redis. If you've just installed redis and are running it with default settings, the defaults from the initializer file should work nicely.

Configure acts_with...

deactivating auto_update

If you need to call update_content manually (either via the mixin version or the global one), you probably don't want the unnecessary updates, which are being triggered by every update on the pertaining record (and the subsequent method-call). You can deactivate it for specific method, by passing it via the options hash.

  acts_with_fastaccess_on :markdown, :auto_update => false

using multiple generated versions

Since the methods you will use fastaccess on, will usually generate content, it might be possible, that you want to use multiple versions of this content frequently without resorting to another method. You can achieve this by setting argument-groups, which is an array in which each element (an argument group) refers to the arguments passed to the method in this version:

  acts_with_fastaccess_on :markdown_with_opts, :versions => [
                                              {prevent_html:true},
                                              {prevent_html:false}
                                              ]

This will allow fastaccess to store both versions in the redis database.

Features

planned features

  • ~~disable auto-update via option setting (planned for 0.0.2)~~ implemented
  • more update flexibility
    • e.g. custom update-constraints instead of calling update_content manually
  • ~~version~~ ~~sometimes parameters passed to the watched method aren't arbitrary, but contain some sort of state. So some output-versions of the generated content are used, with evenly distributed odds. Fastaccess should allow for these versions to exist in memory. This means, that certain sets of arguments are bundling a so called version, which should be accessible via redis for more flexibility.~~ implemented

License

(The MIT License)

Copyright © 2013:

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.