Many things happen during a Drupal cache clear. Rebuilding routing and menu caches is one of them. When there are thousands of menu items this process can become resource intensive, to the point where the process may exceed memory limits causing deployments to fail. However, a small detail in how menu items are stored can completely change the outcome.
Menu Link Storage
The menu_link_content_data
table has two columns, link__uri
and rediscover
that are of interest.
| id | bundle | title | link__uri | rediscover | | --- | | --- | --- | --- | --- | | 30 | menu_link_content | Directory | internal:/directories | 1 | | 31 | menu_link_content | About Us | entity:node/23 | 0 |
Menu items saved with a link__uri
such as internal:/directories
will be marked for rediscovery. Menu items with a link__uri
such as entity:node/26
will not be marked for rediscovery.
This differentiation is done via PHP's parse_url()
function, specifically the value of the scheme
data which informs the rediscovery state as seen below:
Drupal\menu_link_content\Entity\MenuLinkContent::preSave()
if (parse_url($this->link->uri, PHP_URL_SCHEME) === 'internal') {
$this->setRequiresRediscovery(TRUE);
}
else {
$this->setRequiresRediscovery(FALSE);
}
This rediscovery value comes into play in the following code that is called during the cache rebuild process:
Drupal\menu_link_content\Plugin\Deriver\MenuLinkContentDeriver::getDerivativeDefinitions()
// Get all custom menu links which should be rediscovered.
$entity_ids = $this->entityTypeManager->getStorage('menu_link_content')->getQuery()
->condition('rediscover', TRUE)
->execute();
Updating Menu Links
With this knowledge, I could begin crafting a solution that would significantly reduce memory usage and speed up our deployment process. The key was to convert as many menu paths as possible to the entity:node/123
format.
First, I found all menu items with the internal:/node/%
format and fixed those.
$query->condition('link.uri', 'internal:/node/%', 'LIKE');
I followed that up with a query to find the remaining aliased items.
$query->condition('link.uri', 'internal:/%', 'LIKE');
I placed these queries into some update hooks, ran the database updates, and the deployment issue was resolved. Deployments were now speedy and efficient (memory usage went from ~750mB to ~115mB), and I learned something new about Drupal along the way.
(I created a gist with the complete update hook code.)