2

Drupal 8 page has a day specific element which is valid for current day. I would like to add a cache tag so that it can be purged in varnish/fastly periodically. Is there a way to invalidate cache tag periodically on Drupal8?

Here is few FAQs/As:

  1. Why can't use max-age?

max-age can be used, but it's page level header which can get quite confusing when more than one component try to set it. Cache tag is kind of self-contained to the component.

  1. Why don't run cron?

Trying to avoid explicit cron/job to do this as it's environment specific.

  • Notice that you are merely stating facts, without asking any explicit question. The question is then about code, but you aren't showing any tentative to resolve the problem. This is valid also for self-answered questions, for which the closing reasons are still valid. – kiamlaluno Dec 29 '16 at 13:14
  • You can use the max-age #cache which is in seconds so you have have a pre/process function on the element that would compute the time left in current day. Or you can create cache context for current day as well. – user21641 Dec 29 '16 at 13:47
  • @kiamlaluno Thanks for quick review. updated to reflect my question. – vijaycs85 Dec 29 '16 at 14:22
  • I don't really understand why max-age is not a solution either. If you want to say that having a high max-age results in caching within browser that you can't invalidate then that is correct, but there are pretty easy ways to avoid that, for example, you can use the Surrogate-Control header with Fastly: docs.fastly.com/guides/tutorials/cache-control-tutorial (I'm sure a custom varnish configuration can be configured to use a similar header) – Berdir Dec 29 '16 at 15:38
  • @Berdir, I might be wrong here, but this is one element (i.e. a block) in almost all pages and we don't want to flush the whole page just to refresh this one component? – vijaycs85 Dec 29 '16 at 17:07
4

Here is the code that flush cache on first hit of a day:

  public function build() {
    ...
    ...
    ...
    $this->handleCacheTag($build['#cache']['tags']);
  }


  protected function handleCacheTag(&$tags) {
    // Get the current date number.
    $current_date_number = date('z');
    $last_date_number = \Drupal::state()->get('module_name.foo.date_counter', $current_date_number);
    // If last date in settings is different from current day, invalidate
    // tags.
    if ($last_date_number < $current_date_number) {
      $invalid_tags = range($last_date_number, $current_date_number);
      array_walk($invalid_tags, function(&$invalid_tag){
        $invalid_tag = 'foo:date.' . $invalid_tag;
      });
      Cache::invalidateTags($invalid_tags);
    }
    \Drupal::state()->set('module_name.foo.date_counter', $current_date_number);
    $tags[] = 'foo:date.' . $current_date_number;
  }
  • Thanks for sharing your code. I found that this didn't exactly worked as I had hoped/expected. When you enable all of Drupal 8's caching, the build process of a page controller is often bypassed because the result has been cached. This means the invalidation logic is skipped too. I ended up putting the invalidation logic in a hook_cron implementation. Another detail I changed in my implementation, is using date('Yz') to prevent problems around new years day. – marcvangend Oct 23 '17 at 15:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.