Problem/Motivation
I’ve identified a significant performance issue when accessing the Extend page (/admin/modules) on a production site with approximately 300 installed modules, including the Workflow module.
System details:
- Drupal version: 10.5
- PHP version: 8.2
- Database: MySQL 8.0
- Workflow module version: 2.1.6
Workflow statistics:
- Total workflows created: 8
- Total states: 50
- Total transitions: 442
When accessing the /admin/modules page, the Time To First Byte (TTFB) is approximately 50 seconds.
After some investigation, I discovered that by commenting out the permission_callbacks directive in the workflow.permissions.yml file, clearing the cache, and reloading the page, the TTFB drops to about 6 seconds.
Digging deeper, I found that Drupal\workflow\WorkflowPermissions::getPermissions() is being executed as many times as there are installed modules — as far as I can tell, this is expected core behavior.
However, the real performance bottleneck seems to come from the Drupal\workflow\Entity\Workflow::postLoad() method, which loads all states and transitions for each workflow entity.
/**
* {@inheritdoc}
*/
public static function postLoad(EntityStorageInterface $storage, array &$entities) {
/** @var \Drupal\workflow\Entity\WorkflowInterface $workflow */
foreach ($entities as &$workflow) {
// Better performance, together with Annotation static_cache = TRUE.
// Load the states, and set the creation state.
$workflow->getStates(); // <--- Load all related States
$workflow->getTransitions(); // <--- Load all related Transitions
}
}
That said, it seems that in Drupal\workflow\Entity\WorkflowPermissions::buildPermissions(), there is no need to load the full state and transition data in order to generate the permission list.
Would it be possible to load only the necessary workflow information (such as ID and label), without loading the complete state and transition entities, which are not needed in this context?
What would be the best way to optimize this process?
Comments
Comment #3
johnvThanks for your report. This performance problem most likely occurs on many pages.
The problem occurs because the Workflow::load() and Workflow:loadMultiple() functions do not use the same cache.
The above patch fixes that by routing the load() via the loadMultiple() function.
So, yes, all states and transitions are still loaded (postloaded), but only once.
Comment #5
johnvComment #6
lolgm commented@johnv Thank you for looking into this issue.
I tested the scenario described in the issue with the changes from commit
afd4836f, and I can confirm that the TTFB decreased from about 50s to ~7s.If you consider that the information I provided was useful in resolving this problem, would it be possible to attribute credit to me?
Comment #7
johnvIndeed, thanks for your information.
I credited the Issue, which will reflect on your user page. It is too late for the commit.
Comment #8
lolgm commented@johnv Thanks!
Comment #9
johnvRelease workflow 2.1.7 has been created.