Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails cache will block the clients' request while they are making

Prevent requests blocked by caching and auto regenerate fresh caches

We can easily make Rails cache, and set the expires time like that

Rails.cache.fetch(cache_key, expires_in: 1.minute) do
    `fetch_data_from_mongoDB_with_complex_query`
end

Somehow, when the new request comes in, expiration happens, and the request will block. My question is, how can I avoid that kind of situation? Basically, I want to give the previous cache to client's request while Rails is making cache.

As shown in the the expected behaviour diagram, the second request will get the cache 1 but not cache 2, although the Rails is making for the cache 2. Therefore, the user won't have to spend much time on making new cache. So, how can I automatically regenerate all the caches without users' request to trigger it?

Expected behaviour

Cache snippet

     cache_key = "#{__callee__}"
     Rails.cache.fetch(cache_key, expires_in: 1.hour) do
       all.order_by(updated_at: -1).limit(max_rtn_count)
     end

Update

How could I get all the cached keys in a command ?

Because the cached query can be generate by the composition of start_date, end_date, depature_at, arrive_at.

It's not possible to invalidate all the cached keys manually.

How could I get the all cache keys, then refresh then in Rake task

like image 486
newBike Avatar asked Jul 17 '15 02:07

newBike


People also ask

What does Rails cache do?

1.1 Page Caching Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the web server (i.e. Apache or NGINX) without having to go through the entire Rails stack. While this is super fast it can't be applied to every situation (such as pages that need authentication).

Is Rails cache thread safe?

Rails and its included gems have been declared thread-safe since 2.2, i.e. since 2008. This alone, however, does not automatically make your app as a whole so. Your own app code and all the gems you use need to be thread-safe as well.

What is a cache and what does it do?

In computing, a cache is a high-speed data storage layer which stores a subset of data, typically transient in nature, so that future requests for that data are served up faster than is possible by accessing the data's primary storage location.

Where is Rails cache stored?

By default, the page cache directory is set to Rails. public_path (which is usually set to the public folder) and this can be configured by changing the configuration setting config. action_controller. page_cache_directory.


2 Answers

Using expiration is tricky as once the cached object expires, you won't be able to fetch the expired value.

The best practice would be to decouple your cache-refreshing process from the end user traffic. You would need a rake task that populates/refreshes your cache and run that task as a cron. If for some reason, the job doesn't run, the cache would expire and your users will incur the additional time to fetch the data.

However, if your dataset is too large to refresh/load all of it at once, you'd have to use a different cache-expiration policy (You could update the expiration time after every cache-hit).

Alternatively, you could disable cache expiration and use a different indicator (such as time) to determine if the object in the cache is up to date or stale. If it is stale, you could use an asynchronous ActiveJob worker to queue a job to update the cache. The stale data will be returned to the user and the cache will be updated in the background.

like image 84
Musannif Zahir Avatar answered Sep 21 '22 08:09

Musannif Zahir


It seems to be working how caching is designed to work. When the second request comes in, after 1hr in your case, the query re-runs and blocks the request while the query is running.

What you want is for Rails to return a cache1 the original expired cached while its working on generating the new cache2.

But think about what you are asking? You are asking it to return something which has expired? How could rails return cache1, when it has expired. This happened because you explicitly set the cache to expire in exactly 1 hour.

There could be many ways to achieve what you want, here is one solution which comes immediately to mind:

  1. Run your long query and put its result along with current timestamp in your cache. Don't put any expiry on it.
  2. Schedule a background job to re-run the above long query and update cache every 1 hour. Try to make the update atomic.
  3. Now whenever a browser request comes in just return what's in the cache.
like image 33
user566245 Avatar answered Sep 18 '22 08:09

user566245