0

I have placed in the site's home, via twig, a views block of nodes with a random sort and obliviously no cache active. Therefore, at each page reload different nodes appear.

The problem is that block get still cached for anonymous users.

For now, we can't yet do performance test of the site, so we'd rather not disable the Internal Page Cache module.

The Internal page documentation says:

Websites that serve personalized content to anonymous users (dynamic, per-session, e.g. a shopping cart) will want to disable the Internal Page Cache module. This module assumes pages are identical for all anonymous users. Those websites can still take advantage of the Dynamic Page Cache module though, or can alternatively do their personalization using JavaScript + AJAX.

So after failing to find anything else useful, I've tried this solution to solve the issue.

Unfortunately I found no module that let "load" the block via ajax, or something similar. For now I've implemented a custom route that render the block view:

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Render\Renderer;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\sdm_user\SdmUserSalesforceClient;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;

/**
 * Provides route responses for the Example module.
 */
class myController extends ControllerBase {

  

  /**
   * @var \Drupal\Core\Render\Renderer
   */
  private  $renderer;

  /**
   * @param \Drupal\Core\Render\Renderer $renderer
   */
  public function __construct(Renderer $renderer){
    $this->renderer = $renderer;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('renderer')
    );
  }

  /**
   * @return array
   *   A simple renderable array.
   */
  public function render($id, $display) {
    $view = \Drupal\views\Views::getView($id);
    $render_array = $view->buildRenderable($display);
    $rendered = $this->renderer->renderRoot($render_array);
    $response = new Response();
    $response->setContent($rendered);
   
    return $response;

  }
}

And then in a custom js, load the necessary block via ajax:

(function ($, Drupal, drupalSettings) {
    Drupal.behaviors.products_in_home = {
        attach: function (context, settings) {
            $(document, context).once('products_in_home').each(function () {
                jQuery.ajax({
                    url: drupalSettings.path.currentLanguage + '/ajax/views/prodotti/products',
                    method: 'GET',
                    success: function (data) {
                        $(".products-container-home").html(data);
                    }
                });
            });
        }
    }
}(jQuery, Drupal, drupalSettings));

This works, but I'm kinda baffled: is this the only approach possible?

I mean, do I need to manually load via ajax every block\thing in the site that will suffer this problem?

Isn't there a module\API\some approach that can generally solve this problem, without custom specific code for every element?

3
  • @miststudent2011 I already found those questions, and didn't work for me. I don't know if I'm using the wrong hook to set the max-age - I'm using hook_views_pre_view for that - or most probably because of the issue linked in the 1st link. The solution relying on tags of the 1st link actually works, but client would like to have that block strictly refresh every reload. That would need to set up an extremely aggressive cron - like every 5s - and still not doing exactly what the client want.
    – Giuseppe
    Apr 8, 2021 at 16:52
  • What I would suggest on the view block, you might add a custom cache tag and invalidate it every 60 seconds on cronjob.
    – Ales
    Apr 8, 2021 at 21:03

1 Answer 1

1

In general the Internal Page Cache is a rather simple solution. If you need more features replace it with Varnish or a CDN. Most of them provide similar features, like for example ESI to serve dynamic blocks if you can't use Javascript. I think most people would use Javascript in this case, though, and there is a fully cacheable solution. You can deliver all eligible node IDs in drupalSettings and then select and fetch a random node in Javascript. Both, the page containing all eligible nodes and the node randomly fetched, are cacheable, so you can provide the dynamic behavior without any performance hit.

2
  • this solution looks nice to me especially because kinda easily replicable. However, in this case I should do N get call for N nodes, while calling the view I should only do once. Do you think anyaway the first solution is better, performance-wise, than the latter?
    – Giuseppe
    Apr 12, 2021 at 15:22
  • When you have the random selection client-side all content built on the server is fully cacheable. In contrast, a View with a random sort is not cacheable at all.
    – 4uk4
    Apr 12, 2021 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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