How to reproduce

On a website with a lot of content (1mil+) and revisions (1k+ revisoins per node) views pages have performance issues.

Cause

layout_builder_additions_entity_operation contains the next code:

...
$route_url = Url::fromRoute('layout_builder.overrides.node.view', ['node' => $nid]);
if ($route_url->access($user_current)) {
...

In case of a views list, this access check if executed on every node.
In the callback trace the very slow method \Drupal\Core\Entity\ContentEntityStorageBase::getLatestRevisionId is called on every entity.

Issue background

Drupal core has performance issues with the \Drupal\Core\Entity\ContentEntityStorageBase::getLatestRevisionId method. On a website with a lot of content and revisions this method has performance issues.

These issue have been partially resolved: #2950869: Entity queries querying the latest revision very slow with lots of revisions, #3548313: Updating to 10.5.3 causes gateway timeouts on revisioned content.

But the \Drupal\Core\Entity\ContentEntityStorageBase::getLatestRevisionId is still slow. Ideally it should not be called at all.

Related comment: https://www.drupal.org/project/drupal/issues/3548313#comment-16325848

Comments

a.milkovsky created an issue.

a.milkovsky’s picture

My current workaround: I removed the layout_builder_additions_entity_operation hook in our ptoject.

rklawson’s picture

Thanks for reporting this issue, @a.milkovsky.

Does it help if the addition of the "Layout" operation only occurs on admin pages or the canonical route for the node (i.e., the node detail page)? Perhaps something like this?

/**
 * Implements hook_entity_operation().
 */
function layout_builder_additions_entity_operation(EntityInterface $entity) {
  $operations = [];

  $entity_type_id = $entity->getEntityTypeId();

  if ($entity->getEntityTypeId() !== 'node') {
    return $operations;
  }

  $route_match = \Drupal::service('current_route_match');
  $route = $route_match->getRouteObject();

  // Add 'Layout' operation for layout-enabled nodes with sufficient privileges in the admin and on canonical routes.
  if ($route && (\Drupal::service('router.admin_context')->isAdminRoute() || $route_match->getRouteName() === 'entity.node.canonical')) {
    $nid = $entity->id();
    $user_current = \Drupal::currentUser();
    $route_url = Url::fromRoute('layout_builder.overrides.node.view', ['node' => $nid]);

    // Check current user for access to the override route for the node.
    if ($route_url->access($user_current)) {
      $operations['layout'] = [
        'title' => t('Layout'),
        'url' => $route_url,
        'weight' => 50,
      ];
    }
  }

  return $operations;
}

I tested in my local with a new D11 install, and it continues to provide the operation at /admin/content and /node/<nid>.

Edit: I'm hoping that despite /admin/content being a view, the performance was more of an issue on the frontend of the site. Another option could be to have configuration that disables the functionality entirely so that the rest of the module is still usable.

a.milkovsky’s picture

Does it help if the addition of the "Layout" operation only occurs on admin pages or the canonical route for the node (i.e., the node detail page)? Perhaps something like this?

In my case the performance issue was at the /admin/content page. So this solution would not fix the issue with slow hook_entity_operation.

Another option could be to have configuration that disables the functionality entirely so that the rest of the module is still usable.

This sounds better.