We have a site where the homepage is not being cached and contains the headers:
x-cache: MISS, MISS
x-cache-hits: 0, 0
x-content-type-options: nosniff
x-drupal-dynamic-cache: UNCACHEABLE
I narrowed this down to the content regions contents, and disabled the "Main page content" for the front page. This then gave me a cache HIT, and no longer responded as UNCACHEABLE. From there, I narrowed it down to a field formatter being used on a paragraph. We have a custom one that extends the normal entity render formatter.
If I swap it back to the original "Render entity" formatter, everything is fine. So then, it must be something we are doing in this custom formatter causing the issue.
I can see when I follow with xdebug that shouldCacheResponse of DynamicPageCacheSubscriber returns FALSE, because something is setting max-age to 0 (not by code). It looks like calls to addCacheableDependency may be triggering this behavior in the formatter:
Essentially, the formatter adds cache data to the render so if any of its referenced items are updated, the cache should be invalidated for that host paragraph so it re-renders:
$view_builder = \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId());
$elements[$delta] = $view_builder->view($entity, $view_mode, $entity->language()->getId());
try {
$parent = $items->getParent();
$parent_entity = $parent->getValue();
$elements[$delta]['#cache']['keys'][] = $parent_entity->id();
$elements[$delta]['#cache']['keys'][] = $parent_entity->bundle();
$elements[$delta]['#cache']['keys'][] = $parent_entity->getEntityTypeId();
$elements[$delta]['#cache']['keys'][] = 'delta_' . $delta;
$elements[$delta]['#cache']['keys'][] = 'context_aware';
$this->renderer->addCacheableDependency($elements[$delta], $parent);
if ($entity->hasField('field_author')) {
$child = $entity->field_author->entity;
if (isset($child)) {
$this->renderer->addCacheableDependency($elements[$delta], $child);
}
}
// similar statements with addCacheableDependency
If I comment out this initial line:
$this->renderer->addCacheableDependency($elements[$delta], $parent);
Then I get a cacheable response. This looks to be because the $parent item (even though it is a node or paragraph or media entity) triggers this:
/**
* Creates a CacheableMetadata object from a depended object.
*
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
* The object whose cacheability metadata to retrieve. If it implements
* CacheableDependencyInterface, its cacheability metadata will be used,
* otherwise, the passed in object must be assumed to be uncacheable, so
* max-age 0 is set.
*
* @return static
*/
public static function createFromObject($object) {
if ($object instanceof CacheableDependencyInterface) {
$meta = new static();
$meta->cacheContexts = $object->getCacheContexts();
$meta->cacheTags = $object->getCacheTags();
$meta->cacheMaxAge = $object->getCacheMaxAge();
return $meta;
}
// Objects that don't implement CacheableDependencyInterface must be assumed
// to be uncacheable, so set max-age 0.
$meta = new static();
$meta->cacheMaxAge = 0;
return $meta;
}
Setting cacheMaxAge to 0 because it is not an instance of CacheableDependencyInterface.
If I am already setting the cache keys, is this line even needed:
$this->renderer->addCacheableDependency($elements[$delta], $parent);
If I remove that, will there be an adverse effect (like render displays not re-rendering when referenced items are saved)?