Rails缓存将阻止客户端'请求他们

时间:2015-07-17 02:36:46

标签: ruby-on-rails ruby-on-rails-4

阻止缓存阻止请求并自动重新生成新缓存

我们可以轻松制作Rails cache,并设置过期时间

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

不知何故,当新请求进入时,会发生过期,请求将被阻止。我的问题是,我怎样才能避免这种情况?基本上,我希望在Rails进行缓存时将先前的缓存提供给客户端的请求。

如预期的行为图所示,第二个请求将获得cache 1但不会获得cache 2,尽管Rails正在为cache 2做出贡献。因此,用户不必花费太多时间来制作新的缓存。那么,如何在没有用户的情况下自动重新生成所有缓存。请求触发它?

预期行为

缓存片段

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

更新

如何在命令中获取所有缓存的密钥?

因为缓存的查询可以通过start_dateend_datedepature_atarrive_at的组合生成。

无法手动使所有缓存密钥无效。

如何获取所有缓存键,然后在Rake任务中刷新

2 个答案:

答案 0 :(得分:3)

使用expiration非常棘手,因为一旦缓存的对象过期,您将无法获取过期的值。

最佳做法是将缓存刷新过程与最终用户流量分离。您需要一个rake任务来填充/刷新缓存并将该任务作为cron运行。如果由于某种原因,作业未运行,缓存将过期,您的用户将需要额外的时间来获取数据。

但是,如果您的数据集太大而无法一次刷新/加载所有数据集,则必须使用不同的缓存过期策略(您可以在每次缓存命中后更新到期时间)。

或者,您可以禁用缓存过期并使用其他指示符(例如时间)来确定缓存中的对象是最新还是失效。如果它是陈旧的,您可以使用异步ActiveJob工作程序对作业进行排队以更新缓存。过时的数据将返回给用户,缓存将在后台更新。

答案 1 :(得分:2)

It seems to be working how caching is designed to work. When the second request comes in, after // Get the dependencies that have already been installed // to ./node_modules with `npm install <dep>`in the root director // of your app var _ = require('underscore'), PDFParser = require('pdf2json'); var pdfParser = new PDFParser(); // Create a function to handle the pdf once it has been parsed. // In this case we cycle through all the pages and extraxt // All the text blocks and print them to console. // If you do `console.log(JSON.stringify(pdf))` you will // see how the parsed pdf is composed. Drill down into it // to find the data you are looking for. var _onPDFBinDataReady = function (pdf) { console.log('Loaded pdf:\n'); for (var i in pdf.data.Pages) { var page = pdf.data.Pages[i]; for (var j in page.Texts) { var text = page.Texts[j]; console.log(text.R[0].T); } } }; // Create an error handling function var _onPDFBinDataError = function (error) { console.log(error); }; // Use underscore to bind the data ready function to the pdfParser // so that when the data ready event is emitted your function will // be called. As opposed to the example, I have used `this` instead // of `self` since self had no meaning in this context pdfParser.on('pdfParser_dataReady', _.bind(_onPDFBinDataReady, this)); // Register error handling function pdfParser.on('pdfParser_dataError', _.bind(_onPDFBinDataError, this)); // Construct the file path of the pdf var pdfFilePath = 'test3.pdf'; // Load the pdf. When it is loaded your data ready function will be called. pdfParser.loadPDF(pdfFilePath); 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 1hr the original expired cached while its working on generating the new cache1.

But think about what you are asking? You are asking it to return something which has expired? How could rails return cache2, 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.