1

My use case is to show a block of recommended nodes on a node page. This should be personalized by IP address, so if someone's IP address indicates they are from x location show them y block and vice versa. Testing this by switching my IP with a VPN is working for me locally. However when I get to production with Varnish + CDN configured it is not working. The first result after a cache clear seems to get cached for all users on subsequent requests regardless of IP. Since I'm using a well known hosting provider who configures all of that for me I'm thinking my custom code is at fault.

My understanding of big_pipe and placeholdering is that the non dynamic parts of the page will be cached by the dynamic_page_cache module and once the initial response has returned that non dynamic content big_pipe swaps out the placeholders for the personalized content. You should be able to see this visually.

My blocks are just standard Drupal content blocks created in the UI and I'm adding the ip cache context and telling Drupal to create a placeholder. I don't specify a lazy builder because I believe the core BlockViewBuilder already has one?

/**
 * Implements hook_preprocess_block().
 */
function product_lists_preprocess_block(&$variables) {
  if (isset($variables['elements']['content']['#block_content'])) {
    $blockContent = $variables['elements']['content']['#block_content'];
    // This ensures that product pages can still be cached and the
    // recommended product lists can be served dynamically via bigPipe
    // based on the users ip address.
    if ($blockContent->bundle() === 'product_list_recommended') {
      $variables['#cache']['contexts'][] = 'ip';
      $variables['#create_placeholder'] = TRUE;
    }
  }
}

When I test this locally it seems like the blocks are just rendered with the rest of the page. You can't see the placeholder being replaced visually. Interestingly, after subsequent reloads of the page the x-drupal-dynamic-cache is always returning MISS never HIT. I tried changing it so that I'm adding those properties on the content element like so:

/**
 * Implements hook_preprocess_block().
 */
function product_lists_preprocess_block(&$variables) {
  if (isset($variables['elements']['content']['#block_content'])) {
    $blockContent = $variables['elements']['content']['#block_content'];
    // This ensures that product pages can still be cached and the
    // recommended product lists can be served dynamically via bigPipe
    // based on the users ip address.
    if ($blockContent->bundle() === 'product_list_recommended') {
      $variables['elements']['content']['#cache']['contexts'][] = 'ip';
      $variables['elements']['content']['#create_placeholder'] = TRUE;
    }
  }
}

This does result in x-drupal-dynamic-cache returning HIT which suggests that's the correct place to put it but you still can't see bigpipe replacing the placeholder visually. It looks like it's still being rendered with the rest of the page.

So then I tried adding ip as one of the auto_placeholder_conditions in services.yml like so:

contexts: ['session', 'user', 'ip']

This also didn't work. The block still looks like it's rendering on the initial load. Then I reverted my preprocess to how it was at the beginning and kept the ip definition in the services.yml file and now I can see the placeholder being visually replaced and weirdly enough x-drupal-dynamic-cache is now returning HIT with the original prepocess method :/ The other thing is, this doesn't seem to work when viewing the page anonymously. Again the block just seems to be rendering with the page.

Digging into the big_pipe module I found the following:

First: the BigPipe placeholder strategy only activates if the current request is associated with a session. Without a session, it is assumed this response is not actually dynamic: if none of the placeholders show session-dependent information, then none of the placeholders are uncacheable or poorly cacheable, which means the Page Cache (for anonymous users) can deal with it. In other words: BigPipe works for all authenticated users and for anonymous users that have a session (typical example: a shopping cart).

Based on that it seems like it's not possible to do what I want, at least not using big_pipe. We don't have authenticated users besides content editors and the IP is detected from the request.

Is what I want to do even possible and if so where am I going wrong? I also would prefer not to add ip as an auto_placeholder_condition ideally but it only works properly when I use that.

I also know there are some issues with the page_cache module and cache_contexts but I'm not using that so we can rule that out.

7
  • The only officially supported place to add cache metadata in a preprocess hook is $variables['#cache']. It's most times too late to change other places because they are already processed. This should explain the behavior you are describing. Bigpipe is only for authenticated users. For anonymous traffic you probably need to solve this in javascript or if this is not possible move this part of your application to the edge. But this is not Drupal specific.
    – 4uk4
    Oct 12, 2023 at 7:56
  • Are trusted proxy configurations set up in settings.php so the true client IP is resolving?
    – cilefen
    Oct 12, 2023 at 11:20
  • Yes I’m using the Fastly-Client-IP header along with the reverse_proxy_header.
    – chap
    Oct 12, 2023 at 11:31
  • But how can you make sure you get every client request when you use a CDN to cache anonymous traffic. This doesn't make any sense. If you use Fastly you probably want to use their geolocation service and put it in the request headers.
    – 4uk4
    Oct 12, 2023 at 15:48
  • Maybe I have misunderstood the documentation but the docs suggest you can cache fragments of the page: “This feature improves performance because it makes it possible to cache pages with dynamic content. Pages requested by users (anonymous or authenticated) are stored the first time they are requested and can then be reused. Personalized parts are excluded: they are turned into placeholders automatically. Depending on your site configuration and the complexity of particular pages, Dynamic Page Cache may significantly increase the speed of your site, even for authenticated users.”
    – chap
    Oct 12, 2023 at 20:48

0

Your Answer

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

Browse other questions tagged or ask your own question.