RedisStorageMethods
RedisStorageMethods allows you to include a MixIn to your models that will let you easily create copies in a redis store when you create actual objects with no extra logic, and expose methods that enable you to find in redis store, not in the database, to protect yourself from the massive time costs in querying with SQL compared to Redis.
Installation
Add this line to your application's Gemfile:
gem 'redis_storage_methods'
And then execute:
$ bundle
Or install it yourself as:
$ gem install redis_storage_methods
Usage
What this Gem does is provides a framework mixin for your models that exposes new methods that allow you to save to redis when you save to the database, and let you get/update redis without getting/updating the database until later(maybe under a cron job or something.) This is great if you want all the speed of redis but the querying reporting ability of SQL.
When you create, normally you'll be passing hashes into your create method, most likely from form submission. Now you will need to pass them into your
create_with_associations_and_redis(params)
method. I didn't overwrite the create method, because I wanted to differentiate between the two: This method will take your object and create it, and then store it in redis as a hash. It will not store it's associations unless you instruct it to do so, as will be discussed further in depth below. This is because redis has no ability to store foreign keyed objects, if a Battle has a Soldier in it, you can really only store it in a redis hash for Battle like this:
{ battle_name battle_time soldier0hp soldier0stamina soldier1hp soldier1stamina }
You can see how this is a limitation of redis - you can clearly see there are two soldiers in this battle hash but it only allows storing of hashes at the top level. You cannot store hashes in hashes, so you have to use this method. Because of this limitation, when I designed this mixin, I made it non-implementation specific for pushing associations with your model.
Pretty easy to use, just do:
class Battle < ActiveRecord::Base
include RedisStorageMethods
has_many :soldiers
...
end
Once you've done this, you'll need to add a few methods to Battle that the Mixin requires if you want to store nested objects, like Soldiers for a battle. I am going to provide an example set of methods below assuming Battle has a nested object called Soldier.
#accepts the part of the form hash with nested atributes(but with attributes taken off keys), like { "soldiers" => {"0" => {field => value}}}
#and then creates models. Because we don't support nested creation at the meta level, must be handled locally.
def create_associations_from_params(params)
params["soldiers"].andand.each do |index, values|
self.soldiers << Soldier.create(values.merge("battle_id" => self.id))
end
end
#No nesting in Redis, no methods for listing associations on DataMapper/AR, This is custom, returns nested objects for use in storage
#with redis. Because I dont want to write metacode that detects nested objects within my own nested objects, I'm just leaving it
# to each model to implement this in a custom fashion.
def add_key_value_pairs_of_associations_for_redis!(array)
i=0
soldiers.each do |video|
Soldier.properties.map { |prop| prop.name.to_s }.each do |p|
array<<"soldier#{i}#{p}"
array<< soldier.send(p)
end
i+=1
end
array
end
#expects to get hash of things like video0video_id => "555" from redis, needs to create associations from it. This is used when we GET from Redis, and need to reconstruct a model.
def populate_associations_from_redis(redis_hash)
construct_low_level_model_from_redis_hash(redis_hash, "soldiers")
#bonus, this method above is included in RedisStorageMethods for simple models that have no
#other associations and only attribute values. It will take fields like "soldier0hp", realize
#it's part of a soldier object, the zeroth in the index, and make that soldier.
end
#if you have sets stored outside the redis hash, need to make a call to get them, haven't you?
# you can do that here, or leave this a nil method. It won't hurt you.
def populate_custom_fields_from_extraneous_redis_calls
end
#method to sync object in db with redis. You may want to set this up on a cron script to keep
#the databases in sync. A lot of developers don't want to call the DB for PUT requests, they
#just find & change the redis object instead. This is where you reconcile
def sync_with_redis
me_fake = Battle.find_with_redis(self.id)
self.soldiers.each do |soldier|
me_fake.soldiers.each do |f_s|
soldier.hp = f_s.hp if(soldier.id == f_s.id)
end
end
self.save
end
#create the custom fields you need like lists for arrays etc, artist:name => id references,
#stuff like that.
def create_custom_redis_fields
end
#after hook for after you put hash in redis.
def after_redis_create_do
end
Copy and paste these bad boys into the Battle class. You should be aware that nested associations are not stored in redis by default, most of these methods involve implementing them to allow for nesting.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request