How to Low-Level Caching ? Cache key - ruby-on-rails

I want to implement low level cache on my application but I'm having some troubles following the documentation. This is what they have as example:
class Product < ActiveRecord::Base
def competing_price
Rails.cache.fetch("#{cache_key}/competing_price", expires_in: 12.hours) do
Competitor::API.find_price(id)
end
end
end
My questions are:
How am I suppose to get that variable cache_key? Should be given somehow via rails cache or should I have my pre-builded key?
I'm not sure if I clearly understood how this works, confirm if this logic is correct: I set this for example on my controller to generate a ton of variables. And then every time a variable is requested (from view for example), the controller instead of recalculating it every time (long query) will retrieve the pre-made query in case the key haven't changed. If the key has changed it will recalculate again all variables inside the cache block.

ActiveRecord::Integration includes the cache_key method in Rails 4. It should be included by default in the standard Rails configuration. To test this, pop open console, get a record and call cache_key on it.
Reference on the method is available here.
It will usually generate a string similar to "#{record.class.name.underscore}/#{record.to_param}-#{record.updated_at}". This key-based invalidation approach lets you avoid a lot of the effort involved by simply looking for a cache value based on whenever the record was last updated. Old cache values will be ignored because they're not being retrieved.
DHH wrote a great article on the topic here.

Related

Performance - Multiple association call on object

I have a method called on user object which has many documents (associated).
Inside method I had to call documents many places where caller is self by default.
So I was wondering whether it will call documents for user so many times, and thought that I will call once and refer by docs, docs = self.documents or docs = documents and I will use this reference wherever user's documents are needed & thus we can avoid calling association method documents on user object
But does it really call again and again or just cache it for first time when it gets called?
I checked in console, When I call user.documents, it loaded documents (db call) but later for same call It was not loading.
Suggest how it works. Is it good to use reference variable for first call and use it further ?
Rails automatically caches the result of database calls. From the Rails Guides:
Query caching is a Rails feature that caches the result set returned by each query. If Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.
For example:
class ProductsController < ApplicationController
def index
# Run a find query
#products = Product.all
...
# Run the same query again
#products = Product.all
end
end
The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can with low level caching.
My recommendation is not to assign it to a variable because it does nothing to improve the readability of the code and the performance difference is negligible. It could introduce confusion; if I were reading the code and saw someone replaced all calls to documents with docs I would wonder why and would have to take time to understand why.
Ultimately, setting docs = self.documents just tells Ruby "docs should point at the same memory location as self.documents", and regardless of which one you call Ruby will return the same data from the same memory location. There is a performance difference between calling a method and calling a variable, but that performance difference is so minor in comparison to something like the speed of a database call that it can be ignored; there are much better ways to improve the performance of an app than switching method calls to variable calls.
If your concern is that you don't want to type out documents over and over again when you could just type docs, then use alias_method:
class User < ApplicationRecord
has_many :documents
alias_method :docs, :documents
end
Then there is no difference between calling user.documents and user.docs -- they call the same method. But again, does it do anything to improve readability of the code? In my opinion, no.
Stick with calling documents.

Rails short term caching of complex query results

The special_item_id_list method is responsible for returning an array of ids. The query and logic is complicated enough that I only want to have to run it once per any page request, but I'll be utilizing that resulting array of ids in many different places. The idea is to be able to use the is_special? method or the special_items scope freely without worrying about incurring overhead each time they are used, so they rely on the special_item_id_list method to do the heavy lifting and caching.
I don't want the results of this query to persist between page loads, but I'd like the query ran only once per page load. I don't want to use a global variable and thought a class variable on the model might work, however it appears that the class variable does persist between page loads. I'm guessing the Item class is part of the Rails stack and stays in memory.
So where would be the preferred place for storing my id list so that it's rebuilt on each page load?
class Item < ActiveRecord::Base
scope :special_items, lambda { where(:id => special_item_id_list) }
def self.special_item_id_list
#special_item_id_list ||= ... # some complicated queries
end
def is_special?
self.class.special_item_id_list.include?(id)
end
end
UPDATE: What about using Thread? I've done this before for tracking the current user and I think it could be applied here, but I wonder if there's another way? Here's a StackOverflow conversation discussing threads! and also mentions the request_store! gem as possibly a cleaner way of doing so.
This railscast covers what you're looking for. In short, you're going to want to do something like this:
after_commit :flush_cache
def self.cached_special_item_list
Rails.cache.fetch("special_items") do
special_item_id_list
end
end
private
def flush_cache
Rails.cache.delete("special_items")
end
At first I went with a form of Jonathan Bender's suggestion of utilizing Rails.cache (thanks John), but wasn't quite happy with how I was having to expire it. For lack of a better idea I thought it might be better to use Thread after all. I ultimately installed the request_store gem to store the query results. This keeps the data around for the duration I wanted (the lifetime of the request/response) and no longer, without any need for expiration.
Are you really sure this optimisation is necessary? Are you having performance issues because of it? Unless it's actually a problem I would not worry about it.
That said; you could create a new class, make special_item_id_list an instance method on that class and then pass the class around to anything needs to use that expensive-to-calculate data.
Or it might suffice to cache the data on instances of Item (possibly by making special_item_id_list an instance method), and not worry about different instances not being able to share the cache.

Rails Tableless Model

I'm creating a tableless Rails model, and am a bit stuck on how I should use it.
Basically I'm trying to create a little application using Feedzirra that scans a RSS feed every X seconds, and then sends me an email with only the updates.
I'm actually trying to use it as an activerecord model, and although I can get it to work, it doesn't seem to "hold" data as expected.
As an example, I have an initializer method that parses the feed for the first time. On the next requests, I would like to simply call the get_updates method, which according to feedzirra, is the existing object (created during the initialize) that gets updated with only the differences.
I'm finding it really hard to understand how this all works, as the object created on the initialize method doesn't seem to persist across all the methods on the model.
My code looks something like:
def initialize
feed parse here
end
def get_updates
feedzirra update passing the feed object here
end
Not sure if this is the right way of doing it, but it all seems a bit confusing and not very clear. I could be over or under-doing here, but I'd like your opinion about this approach.
Thanks in advance
Using the singleton design pattern it is possible to keep values in memory between requests in ruby on rails. Rails does not reload all objects on every request, so it is possible to keep an in memory store.
with the following in config/initializers/xxx
require 'singleton'
class PersistanceVariableStore
include Singleton
def set(val)
#myvar = val
end
def get
#myvar
end
end
In a controller for example :
#r = PersistanceVariableStore.instance
#r.set(params[:set]) if params[:set]
Then in a view :
<%= #r.get %>
The value in #r will persist between requests ( unless running in cgi mode ).
Not that I think this is a good idea...
The instance variable will not persist between requests since they are entirely different instances. You will likely want to store the feed data in a database so it can be saved between requests and updated after the next request.

(Rails) Managing USER PREFS with a MASTER PREFS fallback...?

I'm looking for a mechanism by which to facilitate user preferences. I also want to have a set of "master" prefs that are used if the currently logged in user doesn't have a specific pref set. I see several questions similar to this, but they seem to get into theory instead of simply proposing a quality solution.
Basically I'm looking for input on management as well as storage -- models, controllers, etc. Initially I was considering simply going with a normalized table of 50+ columns (for performance etc.). However, I plan on adding various, unknown preferences in the future and, performance aside, I could imagine multiple columns getting out of hand. Thoughts?
If you don't need to manipulate or sort by individual preferences in the database, then you might want to use a single bitmask (integer) column. Basically, a bitmask is a set of on/off switches represented as a binary number. For example, let's say we have three preferences:
view subscriptions
view colors
view full names
Let's say a user has 1 and 3 on and 2 off. Using 1s for on and 0s for off, the bitmask for this is:
101
(on off on)
This gets stored in the database as 5 because 101 is 5 in binary. Bitmasks are easy to store in the database (use a single integer column) and are easy to manipulate once you know the operators (for merging a user's preferences into the site defaults). Ryan Bates has a great tutorial on using bitmasks in Rails: Emmbedded Association. Hopefully that will give you the concrete example you're looking for.
In my mind, the best way to define and use defaults is to add another row in the user preferences table, and load it as a class variable in your model. Then override the accessors to find the defaults if the preference hasn't been found. Something like this:
class UserPreference < ActiveRecord::Base
# load default preferences as a class variable
##defaults ||= find(1).attributes
# redefine accessors or each column to load default if nil
column_names.each do |column|
method_name = "#{column}_with_default".to_sym
send :define_method, method_name do
value = send("#{column_without_default}")
case value
when nil
##defaults[column]
else
value
end
end
alias_method_chain column, :default
end
...
end
Essentially the default preferences (loaded from row 1) are stored in the Model as a class variable. All the accessors are redefined and made part of an alias method chain so that the default would be returned if the returned value was nil. I wanted to use || instead of case, but that would cause problems in the event that the user had set a boolean preference to false.
Edit: N.B. I don't know of a good way to update the defaults in a rails app without restarting the server.

rails: how to link values in data migrations to class/model variables?

my sql DB contains tables "jobs" and "job_categories."
"job_categories" associates job category strings (i.e. "Software Development") with an integer number (i.e. 7).
I need these associations saved into variables in my job controller for various query functions. How can I use rails to dynamically link changes to the job_categories table to variables in my jobs controller? I've worked with RoR for a few weeks now but am still a little fuzzy on how everything interacts. Thank you!
There's one big gotcha with what you're trying to do, but first I'll answer your question as asked.
Create class-level accessors in your JobsController, then write an Observer on the JobCategory class that makes the appropriate changes to the JobsController after save and destroy events.
class JobsController < ActionController::Base
##categories = JobCategory.find(:all)
cattr_accessor :categories
# ...
end
class JobCategoryObserver < ActiveRecord::Observer
def after_save(category)
JobsController.categories[category.name] = category.id
end
def after_destroy(category)
JobsController.categories.delete(category.name)
end
end
You'll need additional logic that removes the old name if you allow for name changes. The methods in ActiveRecord::Dirty will help with that.
So, the gotcha. The problem with an approach like this is that typically you have more than one process serving requests. You can make a change to the job_categories table, but that change only is updated in one process. The others are now stale.
Your job_categories table is likely to be small. If it's accessed with any frequency, it'll be cached in memory, either by the OS or the database server. If you query it enough, the results of that query may even be cached by the database. If you aren't querying it very often, then you shouldn't be bothering with trying to cache inside JobsController anyway.
If you absolutely must cache in memory, you're better off going with memcached. Then you get a single cache that all your Rails processes work against and no stale data.

Resources