Skip to main content

How do I cache a complex JSON response (with a lot of belongs_to associations) to be used by Vue.js in the frontend?

Rails • Asked by TL

Thanks to Chris nice tutorials, we just rewrote a slow interface in our app to use Vue.js. Now Rails is just generating the JSON to be consumed by Vue.js in the view.

What I'm trying to achieve is to generate a cached JSON response using read_multi or fetch_multi, so all Conversations (there are 20 on each pagination) are loaded on a single call to memcached.

Le'ts say I have a model called Conversation, that belongs to an user, to a tourist, to a property, and it also has many replies.

class Conversation
  belongs_to :user
  belongs_to :tourist
  belongs_to :property
  has_many :replies
    has_many :holidays

    def generate_json
        {

        tourist: {
          name: tourist.name,
          email: tourist.email,
        property: {
          id: property.id,
          image_url: property.pictures.first.try(:image).try(:url,:thumb),
          property_code: property.property_code,
          title: property.title_br,
        },
        recent_replies: replies.last(5).map(&:generate_json) 
      }
    end


end

For the has_many associations, I simply use touch: true in the other side of the association, so model Reply is a belongs_to :conversation, touch: true, which takes care of updating Conversation's updated_at if it changes and the cache is automatically updated.

But every time I try to cache a model like this (wether in a view fragment or, in this case, in a JSON response) I end up with a huge and complex cache key due, mainly, to the belongs_to association on the cached model. Something like this for example:

Rails.cache.fetch(self.cache_key, self.user.cache_key, self.tourist.cache_key, self.property.cache_key) do
   ... generates the JSON for this model
end

Justin Weiss has a nice tutorial (altough a bit complex) of how to cache complicated JSON responses (https://www.justinweiss.com/articles/a-faster-way-to-cache-complicated-data-models/) but as far as I could understand he's using manually expiring cache keys.

I'm a little bit lost at this point. How would you guys implement caching for this type of JSON structure?

One idea is to use just the self.cache_key in the cache key for the Conversation, (1) rely on touch: true for the has_many associations and (2) write manual after_commit :touch_conversations for the belongs_to associations, something like this:

model Tourist

    has_many :conversations
    after_commit :touch_conversations

    def touch_conversations
        self.conversations.update_all(updated_at: Time.now)
    end

end

The problem with this approach is that some users already got + 200k conversations for example. Updating them all takes at least 4 seconds in my tests, and I'm afraid interfaces will start to get sluggish over time.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.