In ClickLion, we use Redis heavily to track all kind of stats in real-time such as pageview of an article, number of requests handled for a particular session, number of action users have made during the session and a lot more metrics.

Interacting with Redis is simple but contains a lot of repeated steps. For updating the value, we basically have to do following steps:

  • make connection to Redis
  • decide how to store the stats (key or hash)
  • choose a key to store that value
  • update the value
  • close the connection

For fetching the value, the steps might be:

  • make connection to Redis
  • remember corresponding key of a specific one or more stats
  • get those values from Redis
  • close the connection

Working through all of those steps every time you want to update or fetch a stat would not a good idea and it should be abstracted. lion-attr gem does the abstraction for you.

Install

gem install lion-attr

Usage

class Article
  include Mongoid::Document
  include LionAttr
  field :url, type: String
  field :view, type: Integer

  # field :view will be stored in Redis and saved back to Mongodb later
  live :view
end

# fetch the object from Redis
article = Article.fetch('54d5f10d5675730bd1050000')
# increase its view without database hits
article.incr(:view)
#=> 10

# view is updated without object save, its updated value is available
# even when you query the object from database
article_from_another_session = Article.find('54d5f10d5675730bd1050000')
article_from_another_session.view
#=> 10

Live Attribute

That counter is usually an attribute of a Model object, the difference is that attribute will get the value from Redis instead of the database. We call it live attribute because it tends update its value very frequently.

Cache Object

Including LionAttr will set an after save callback to create/update the object cache in Redis.

LionAttr stores objects as json string in Redis Hash with hash key is the full class name of the object. Objects in the same class will be stored in the same Hash with its identity.

Object identity is object’s id by default but you can tell LionAttr to use different field’s value as object identity.

class Article
  include Mongoid::Document
  include LionAttr
  field :url, type: String
  field :view, type: Integer

  # field :view will be stored in Redis and saved back to Mongodb later
  live :view
  # using :url as the key
  self.live_key = :url
end

# fetch the object using custom key
Article.fetch('http://uniqueurl.com')

LionAttr connections

LionAttr connections figure

Increment

LionAttr focuses on improving the wellknown problem increament operator in web application such as tracking pageviews, actions, impressions, clicks. It provides incr method in both class and object scope.

article.incr(:view)
Article.incr('54d5f10d5675730bd1050000', :view)
# also work with custom key
Article.incr('http://uniqueurl.com', :view)

This operator only interact with Redis. No database hits.

incr class method is very useful when you dont want to fetch the object from databse nor Redis, you just want to increase the counter.

Note: This operator will return the increased value if the field type is Integer or Float, otherwise a warning string will be returned.

Save Back to Database

You might want to save back the value to the database. LionAttr also provides update_db to do so.

article.update_db

It’s good practice if you do this periodly using the gem whenever.

Simple code snippet to save back all object in Article class.

Article.where(published: true).map! &:update_db

Redis Connection Pool

LionAttr uses Connection Pool to keep connections to Redis. Mongoid uses connection-pool too.

Configuration

LionAttr uses Redis as Redis ruby driver.

You can pass your redis configuration to LionAttr.

LionAttr.configure do |config|
  # Tell LionAttr to use Redis db 16
  config.redis_config = { :db => 16 }
end

All redis configuration used in Redis is valid here.