Rails Devise: How does the cache (mem) design database queries for a user object?

Every time I get to the authentication page, I notice that the developer issues an SQL statement:

User load (0.2ms) SELECT users . * FROM users WHERE ( users . id = 1) LIMIT 1

(I use Rails 3 btw .. therefore cache_money seems like a solution, and despite a lot of searches, I did not find a replacement).

I tried many overrides in the user model and only called find_by_sql. To whom the string of the entire SQL statement is passed. Something intuitive like find_by_id or find does not seem to be called. I can "override this method and get the user ID and make a reasonable caching system out of it, but it's pretty ugly.

I also tried overriding authenticate_user, which I can intercept one SQL attempt, but then calls to current_user seem to try again.

It’s just that my user objects rarely change, and his sad state continues to beat db for this instead of the memcache solution. (suppose that I am ready to accept all responsibility for the invalidity of the specified cache: after_save as part, but not all of this solution)

+4
source share
3 answers

The following code will cache the user by their identifier and invalidate the cache after each modification.

 class User < ActiveRecord::Base after_save :invalidate_cache def self.serialize_from_session(key, salt) single_key = key.is_a?(Array) ? key.first : key user = Rails.cache.fetch("user:#{single_key}") do User.where(:id => single_key).entries.first end # validate user against stored salt in the session return user if user && user.authenticatable_salt == salt # fallback to devise default method if user is blank or invalid super end private def invalidate_cache Rails.cache.delete("user:#{id}") end end 
+12
source

WARNING: There is most likely a better / smarter way to do this.

I pursued this problem a few months ago. I found - or at least I think I found - where Devise loads the user object here: https://github.com/plataformatec/devise/blob/master/lib/devise/rails/warden_compat.rb#L31

I created a monkey patch for this deserialized method in /initializers/warden.rb to fetch the cache instead of get. It was dirty and wrong, but it worked.

+1
source

I am also struggling with this.

A less complicated way to do this is to add this class method to the user model:

 def self.serialize_from_session(key, salt) single_key = key.is_a?(Array) ? key.first : key Rails.cache.fetch("user:#{single_key}") { User.find(single_key) } end 

Please note that I am adding the model name to the identifier of the object, which is transferred to store / retrieve the object from the cache; You can use any scheme that suits your needs.

The only thing to worry about, of course, is the invalidation of the user in the cache when something changes. It would be nice to save the user in the cache using the session identifier as part of the key, but the session is not available in the model class and is not passed to this Devise method.

+1
source

All Articles