caching options #34

Closed
fluxsaas opened this Issue Feb 15, 2013 · 10 comments

Comments

Projects
None yet
2 participants

Hey,

i use display case in on of my current projects, but really need some caching :)

so here is what i have so far:

display_case/enumerable_exhibit.rb

require 'base64'
require 'display_case/enumerable_exhibit'

module DisplayCase
  class EnumerableExhibit
    def to_hash_array(options={})
      __getobj__.map do |i|
        DisplayCase::EnumerableExhibit.cache_store.fetch [context.current_account, context.current_user, i] do
          exhibit(i).to_hash
        end
      end
    end
    def to_json(options={})
      DisplayCase::EnumerableExhibit.cache_store.fetch [context.current_account, context.current_user, __getobj__.name.to_s.pluralize.underscore, cache_key, 'to-json'] do
        to_hash_array.to_json
      end
    end

    def self.cache_store
      @@_cs ||= if Rails.configuration.action_controller.perform_caching
        Rails.cache
      else
        ActiveSupport::Cache::MemoryStore.new(size: 0)
      end
    end

    def cache_key
      __getobj__.map &:cache_key
    end

  end
end

which is kind of hacky, because i set the cache keys only globally, it would speed things up if there is some other way of implementing some caching.

It also would feel better if i could use the fragment helper in the exhibit itself:

module Api
  module V2

    class AccountExhibit < DisplayCase::Exhibit

      def self.applicable_to?(object, context)
        object.kind_of? Account
      end

      def to_hash
        to_model.attributes.slice("id", "subdomain").symbolize_keys
      end

      def to_json(options = {})
        cache to_model do
          to_hash.to_json
        end
      end

    end

  end
end
Member

codeodor commented Feb 15, 2013

Cool idea. Give me some time to play around with it. Happy to hear any other ideas you have about this as well.

Member

codeodor commented Feb 15, 2013

Also, a couple of questions:

Can you provide some example code that would benefit from a cache, and talk about where having a cache in display_case would be more useful than caching the exhibited object in the normal Rails way?

by "the normal Rails" way i guess you mean caching on the controller level ? like:

module Api::V2
  class ShiftsController < Api::V2::ApplicationController
    def index
      shifts = account_or_requested_schedule.shifts.accessible_by(current_ability).with_includes
      if stale? etag: shifts.cache_key
        render json: exhibit(shifts)
      end
    end
  end
end

i found this article helpful: http://broadcastingadam.com/2012/07/advanced_caching_part_6-fast_json_apis/

The caching approach from Rabl is pretty good: https://github.com/nesquena/rabl/wiki/Caching-in-RABL

I use Display Case for my new API and Rabl for my old code, Rabl is slower on initial rendering (due to templates/partials,...) but if the cache is warm, Rabl is pretty fast. At some point the string manipulation (to_json) makes it slow (in Rabl and DisplayCase). Caching the Json output makes it a lot faster.

i have a lot of models and nested ressources which i have to transport to the end user (like 100-500 different models in one request).

To make sure the user hits a warm cache, i use some background tasks

class ShiftCacheWarming
  include DisplayCase::ExhibitsHelper
  def perform(schedule)
    exhibit( schedule.shifts, context).to_json
    Rabl::Renderer.new('location/shifts/index', schedule.shifts, view_path: 'app/views').render
  end
end

caching on the display_case level would also help caching associated ressources => aka the rails 4 way / russian doll

a clean and easy cashing strategy would boost my application enormously :) my approach works for now, but i'm sure other would find it helpful too.

Member

codeodor commented Feb 27, 2013

Thanks for the info @fluxsaas. I see your point.

I don't mind working on this, but I'll have to defer to @avdi on whether we want to take it on. @avdi, does this fit into your vision of what concerns display_case should handle?

If so, I'll need to do some research and play around with ideas on different branches under my github account to see how it's going to work before putting it in the official repo.

I'll also update the comments here with where those branches are, in case you're interested in collaborating or following.

sounds good, thanks for the update.

a note for inspiration:

i had a look @ the Railscasts on Active Model Serializers

you can set the serialization_scope to the :view_contextwhich makes the current_user, current_account and other variables/methods available to the Serializers.

you can already access current_user through the context but only in the DisplayCase::EnumerableExhibit

  module DisplayCase
    class EnumerableExhibit
      def to_hash_array(options={})
        __getobj__.map do |i|
          DisplayCase::EnumerableExhibit.cache_store.fetch [context.current_user, i] do
            exhibit(i).to_hash
          end
        end
      end

      ...

    end
  end

....and i just realized you made the context available to DisplayCase::Exhibit itself, awesome :).

Member

codeodor commented Mar 22, 2013

Thanks @fluxsaas. I apologize for not moving quicker on this or even responding for over a week.

I've been behind on work this week, so I just wanted to check in and let you know I'm not ignoring this!

It's still on my list to dig into, but if in the mean time you want to get a pull request started please feel free. =)

hey,
no problem, i have my next performance iteration next week. i guess i gonna have a look into that as well...

Member

codeodor commented Apr 19, 2013

Hi @fluxsaas,

I took a little stab at implementing caching for DisplayCase. See the caching branch

What do you think?

Will this cover the case of EnumerableExhibit too, or do I need to make that a special case?

Thanks for your time and ideas!

Member

codeodor commented May 29, 2013

I just released version 0.0.7 which includes caching support.

@codeodor codeodor closed this May 29, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment