DEV Community

[Comment from a deleted post]
Collapse
 
zspencer profile image
Zee

Hey Lautaro!

This is a fantastic question! I'm going to re-write your example code to make sure my understanding of what you're saying is accurate:

This example behaves as expected:

Rails.cache.fetch("sah_hi") { "hi" }

This one does not:

Rails.cache.fetch("items/#{xx}") { Item.find(xx) }

The first thing we need to unpack is "what does it mean to work as expected?" My gut says you expect that when you call Rails.cache.fetch("items/#{xx}") { Item.find(xx) } you will see the Item.find hitting the Real Database once, and then following requests will not hit the database.

The questions that come to mind to me are:

  • How do we know that the Item.find is being called again? Is it because we're seeing in our logs the corresponding SELECT query? Or is it because we're not seeing a measurable change in performance?
  • What are the data-structures that Redis is attempting to store when it stores an ActiveRecord model?
  • Is the model wired up correctly so that it can be safely stored within Redis?

When we think about "What is the result of Item.find" it's really easy to think "Oh, it's the particular Item from the database!"

But what we sometimes fail to realize in the moment of problem-solving-frustration, is it is really a representation of the data in the database, with operations and data made accessible through the Item class.

The process of converting the in-ruby representation to a data-structure that is persistable in Redis is known as serialization. You can see an example of this in Luis Osnet's post where he walks through wiring Redis into Rails. Look for the code sample named "snippets_helper.rb"; which I've reproduced here and refactored to use the Rails.cache abstraction:

def fetch_snippets
  snippets = Rails.cache.find("snippets") { Snippet.all.to_json }
  JSON.load(snippets)
end

Can you spot where we're serializing data to go into Redis?

Yep! It's Snippet.all.to_json! The call to to_json takes all the data from the database converts it to a big-old JSON string. Strings are easy for Redis to store and retrieve; so you know that all the data representing the snippets will be available later.

Now, Strings aren't super useful in Ruby land. We prefer Hashes or plain-old Ruby objects. So how do we get that serialized JSON string back out into a useful form?

Well, you guessed it, it must be deserialized! If you're wondering where that's happening, it's in JSON.load(snippets)!

So there you go, you now have a collection of hashes that you can use to render the data out to your users.

But now you have another problem! How do you use that hash in your forms or link builders? You've got a couple options.

Option A is to have a strong enough understanding of what methods you would need to expose for the Rails Form and Link helpers to work, and you could then write a "ViewModel" or a "Representer" that you can treat pretty-much like a real ActiveRecord Model

Option B is to ... reinflate the model! You can probably do this using Item.new(JSON.load(item)); but there may be not super great side-effects because it wasn't loaded via ActiveRecord by default.

Option C is to leave it as a plain old hash, and adjust your user interface to accomodate the fact that your Item is represented by a JSON hash and not a real ActiveRecord object anymore.

Hope this helps!

Zee

Collapse
 
lautarocastillo profile image
Lautaro C

Hi Zee, many thanks for your answer! It was very useful. Now I understood better how redis works.
I finished using the json.cache! Method from Jbuilder since I’m caching my api responses.
Thanks for your amazing explanation

Collapse
 
zspencer profile image
Zee

Perfect! It sounds like you're solving caching at the "post-serialization, pre-transmission" boundary; which makes a ton of sense to me.