Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching on heavily-used API calls #3149

Closed
jywarren opened this issue Jul 25, 2018 · 7 comments
Closed

Caching on heavily-used API calls #3149

jywarren opened this issue Jul 25, 2018 · 7 comments
Assignees
Milestone

Comments

@jywarren
Copy link
Member

We had some outages this morning due to an old, unoptimized API endpoint being hit hard by the new @ autocomplete...

https://github.com/monterail/grape-rails-cache

Implementation looks pretty simple! @stefannibrasil @milaaraujo interested in this? As your new code gets relied on more, this seems like a good idea...

modulemodul  MyApi < Grape::API
  format :json

  include Grape::Rails::Cache

  resources :posts do
    desc "Return a post"
    get ":id" do
      post = Post.find(params[:id])

      cache(key: "api:posts:#{post.id}", etag: post.updated_at, expires_in: 2.hours) do # <= 2 hour expiration... we can decide how much is appropriate!
        post # post.extend(PostRepresenter) etc, any code that renders response
      end

    end
  end
end
@jywarren jywarren added this to the API milestone Jul 25, 2018
@jywarren jywarren changed the title Caching on API calls Caching on heavily-used API calls Jul 25, 2018
@stefannibrasil
Copy link

hi, @jywarren yeah, sure!! I'll add it to our Project page, this sounds good!

@jywarren
Copy link
Member Author

@milaaraujo maybe this could be related to or folded into #4561?

@milaaraujo
Copy link
Collaborator

Hey @jywarren, I do not know how to implement this... They do not have good documentation beyond example. Do we need a key and an etag, right? But in our case what could we use since results is a set of Users?

  get :profiles do
        search_request = SearchRequest.fromRequest(params)
        results = Search.execute(:profiles, params)

        if results.present?
          docs = results.map do |model|
            DocResult.new(
              doc_type: 'USERS',
              doc_url: '/profile/' + model.name,
              doc_title: model.username
            )
          end
          DocList.new(docs, search_request)
        else
          DocList.new('', search_request)
        end
      end

@jywarren
Copy link
Member Author

jywarren commented Jan 31, 2019

So, we could build whatever is unique into the key -- what are the parameters that can change, when typing in the autocompletion, for example? Since the user is just typing like @mi @mil @mila @milaa... we're only changing the query, no other params in that case. So to cache for that sort of thing we could do something like:

get :profiles do
  search_request = SearchRequest.fromRequest(params)
  cache(key: "api:profiles:#{params[:query]}", etag: post.updated_at, expires_in: 1.day) do
    results = Search.execute(:profiles, params)
    if results.present?
    ...

  end
  ...
end

What do you think? I'm not sure it's actually params[:query] - maybe it's params[:username] but i'm sure we can figure that out. This way there'll be a unique cache for the total response of the get method, already packed up by DocResult, for each incoming query.

If we have other parameters, we could bake them into the key as well, so that the cache saves each variation. Even though most will be the same if via the autocomplete queries. But just so that other parameters don't match the same cache key, and so actually get fulfilled.

What do you think?

@jywarren
Copy link
Member Author

I'm not sure what the etag is for. Hmm. https://en.wikipedia.org/wiki/HTTP_ETag ?

Perhaps the request can be tailored with etag to bust the cache... seems like a timestamp. I'm not sure -- maybe we could skip that parameter?

@jywarren
Copy link
Member Author

jywarren commented Feb 2, 2019

Also, i wonder if we couldn't just use normal Rails caching, which might mean you can't bust the cache using something like etag, but since we don't know how that works anyways, this could just wrap the above code in an almost identical way:

Rails.cache.fetch("#{period}_stats", expires_in: 1.days) do

@milaaraujo
Copy link
Collaborator

We can skip etag parameter! Take a look at #4763.

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

No branches or pull requests

3 participants