1

I am working on a module that works relatively well on the site, but is failing all functional tests I write, because it keeps serving cached versions of pages.

As far as I can tell I have properly defined cache tags and contexts, so I really would like to learn how Drupal 8 decides it can safely use the 'previous' cached response.

What mechanism is responsible for this decision?

UPDATE

My entity defines defines its tags and contexts like this:

class WhiteLabel extends ContentEntityBase implements WhiteLabelInterface {

  /**
   * {@inheritdoc}
   */
  protected $cacheContexts = ['whitelabel'];

  /**
   * {@inheritdoc}
   *
   * Next to the default entity-type:id cache tag, also invalidate these.
   *
   * whitelabel_view is used to invalidate all white labels on a configuration
   * update.
   *
   * @see: Drupal\whitelabel\EventSubscriber\WhiteLabelCacheConfigInvalidator
   */
  protected $cacheTags = ['whitelabel_view'];

  ...

}

The cache context is defined like this:

class WhiteLabelCacheContext implements CacheContextInterface {

  /**
   * Holds the white label.
   *
   * @var \Drupal\whitelabel\WhiteLabelProviderInterface
   */
  protected $whiteLabelProvider;

  /**
   * WhiteLabelCacheContext constructor.
   *
   * @param \Drupal\whitelabel\WhiteLabelProviderInterface $white_label_provider
   *   The white label provider.
   */
  public function __construct(WhiteLabelProviderInterface $white_label_provider) {
    $this->whiteLabelProvider = $white_label_provider;
  }

  /**
   * {@inheritdoc}
   */
  public static function getLabel() {
    return t('White label');
  }

  /**
   * {@inheritdoc}
   */
  public function getContext() {
    $white_label = $this->whiteLabelProvider->getWhiteLabel();
    return !empty($white_label) ? $white_label->id() : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata() {
    return new CacheableMetadata();
  }

}

I am using the system branding block for testing the overriding of the site name, and I add cacheability data like so:

/**
 * Implements hook_block_view_alter().
 *
 * @see whitelabel_block_view_pre_render()
 */
function whitelabel_block_view_alter(array &$build, BlockPluginInterface $block) {
  $build['#pre_render'][] = 'whitelabel_block_view_pre_render';
}

/**
 * #pre_render callback: Sets white label cacheability metadata.
 *
 * Scans all blocks to see if they depend on the system.site cache tag. If so,
 * also make them depend on the white label cache tag and cache context to make
 * sure we can cache one block for every white label.
 */
function whitelabel_block_view_pre_render(array $build) {
  $cacheable_metadata = CacheableMetadata::createFromRenderArray($build)
    // Make sure there always is a no-whitelabel version as well.
    ->addCacheContexts(['whitelabel']);

  // Add cache contexts for blocks depending on config:system.site cache tags.
  $tags = $cacheable_metadata->getCacheTags();
  if (in_array('config:system.site', $tags)) {
    if ($whitelabel = \Drupal::service('whitelabel.whitelabel_provider')->getWhiteLabel()) {
      // Add white label specific cacheability metadata.
      $cacheable_metadata
        ->addCacheableDependency($whitelabel);
    }
  }

  // Add cacheable metadata.
  $cacheable_metadata
    ->applyTo($build);

  return $build;
}

Finally the test fails at the second assertion, and still serves the no white label version of the page:

class WhiteLabelConfigOverrideCacheTest extends WhiteLabelTestBase {

  /**
   * Holds the second generated white label throughout the different tests.
   *
   * @var \Drupal\whitelabel\WhiteLabelInterface
   */
  private $whiteLabel2;

  private $defaultName;

  /**
   * {@inheritdoc}
   */
  public static $modules = [
    'block',
    'whitelabel_test', //TODO: Overrides the WL path processor so caching by URL is rooted out.
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    parent::setUp();

    // Place branding block with site name and slogan into header region.
    $this->drupalPlaceBlock('system_branding_block', [
      'region' => 'header',
    ]);

    $this->defaultName = $this->config('system.site')->get('name');
  }

  /**
   * Test to see if the system branding block shows the right values.
   */
  public function testWhiteLabelPageCaching() {
    // No white label.
    $page = $this->drupalGet('<front>');
    $this->assertSession()->elementTextNotContains('css', '.block-system-branding-block', $this->whiteLabel->getName());

    // White label 1.
    $this->setCurrentWhiteLabel($this->whiteLabel);
    $page = $this->drupalGet('<front>');
    // Check page title.
    $this->assertSession()->elementTextContains('css', '.block-system-branding-block', $this->whiteLabel->getName());

    ...
  }
}

For more code you might want to look at the repo: http://cgit.drupalcode.org/whitelabel/tree/?h=8.x-2.x-failing-tests

9
  • 1
    "As far as I can tell I have properly defined cache tags and contexts" - then this should be fine if you don't run into a specific issue. Without knowing any details there is one worth mentioning. This is that the Internal Page Cache for anonymous traffic doesn't use cache contexts or max-age. Only invalidating cache tags works.
    – 4uk4
    Commented Aug 31, 2017 at 8:55
  • As far as I can tell I have properly defined cache tags and contexts mind sharing your code on this? Cause maybe you didn't do it properly :p
    – No Sssweat
    Commented Aug 31, 2017 at 9:13
  • @4k4, During testing a user is logged in, so the anonymous page cache should not affect me. I deliberately left out the details as the question started to feel like a bug report (but then again I might be doing something wrong as well). If you have some time and would like to look at some code we could open a chat? Including all code here would make the question hard to read. Commented Aug 31, 2017 at 9:13
  • @NoSssweat, updated. Commented Aug 31, 2017 at 9:27
  • I wouldn't use the branding block for testing, because this is AFAIK the only block in core which doesn't render the build array properly, see drupal.stackexchange.com/questions/205210/….
    – 4uk4
    Commented Aug 31, 2017 at 9:58

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.