Problem/Motivation

You need to check at least once per request whether the active theme you use is enabled.

Currently this is done by loading all theme info.

Proposed resolution

Use the theme list in the container, this avoids two cache gets on almost every HTML response.

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork drupal-2538970

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

dawehner’s picture

Note, this improves not nothing, but it doesn't change the overall time yet, because #2531958: Try to do less in theme_get_settings() on every request also lists all of it.

Function Name Calls Diff Calls
Diff%
Incl. Wall
Diff
(microsec)
IWall
Diff%
Incl.
MemUse
Diff
(bytes)
IMemUse
Diff%
Incl.
PeakMemUse
Diff
(bytes)
IPeakMemUse
Diff%
Current Function
Drupal\Core\Theme\ThemeAccessCheck::checkAccess 0 0.0% -95 -16.8% -61,816 -108.7% 72 0.1%
Exclusive Metrics Diff for Current Function 6 6.3% -44,400 -71.8% 0 0.0%
Parent function
Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme 0 N/A% -95 -100.0% -61,816 -100.0% 72 100.0%
Child functions
Drupal\Core\Extension\ThemeHandler::listInfo -1 -100.0% -243 -255.8% -76,856 -124.3% -51,896 -72077.8%
Drupal\Core\Theme\ThemeInitialization::getActiveThemeByName 1 100.0% 142 149.5% 58,752 95.0% 51,968 72177.8%
Drupal\Core\Theme\ActiveTheme::getStatus 1 100.0% 0 0.0% 688 1.1% 0 0.0%
fabianx’s picture

Is there a patch to review?

dawehner’s picture

Status: Active » Needs review
StatusFileSize
new3.22 KB

Ehm sure here it is. Ignore the NID of the patch :)

Status: Needs review » Needs work

The last submitted patch, 3: 2538982-1.patch, failed testing.

fabianx’s picture

Looks great, just need to fix the test failures.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new3.85 KB

The problem with the tests is because of the architecture of ThemeInitialization::getActiveThemeByName(). When ThemeInitialization::getActiveThemeByName() tries to return a theme that is not installed he calls ThemeInitialization::getActiveTheme() with a fake theme called core. From my point of view this a bad design but this is not subject of this issue.

claudiu.cristea’s picture

StatusFileSize
new645 bytes

Forgot the interdiff.

dawehner’s picture

Good observation ... I'll dig into that later, but yeah we should fix the design there properly.

dawehner’s picture

The problem with the tests is because of the architecture of ThemeInitialization::getActiveThemeByName(). When ThemeInitialization::getActiveThemeByName() tries to return a theme that is not installed he calls ThemeInitialization::getActiveTheme() with a fake theme called core. From my point of view this a bad design but this is not subject of this issue.

Do you mind opening up an issue for that?

+++ b/core/lib/Drupal/Core/Theme/ThemeInitialization.php
@@ -160,6 +160,7 @@ public function getActiveTheme(Extension $theme, array $base_themes = []) {
+    $values['status'] = $values['name'] != 'core';

So if its core its disabled? We should have a least some form of documentation

claudiu.cristea’s picture

StatusFileSize
new4.09 KB
new4.09 KB
claudiu.cristea’s picture

StatusFileSize
new818 bytes

Ouch!

Status: Needs review » Needs work

The last submitted patch, 10: 2538970-10.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review

Accidentally I added the same patch twice in #10. Back to NR.

dawehner’s picture

This looks alright for me now, but yeah I fear we need to provide some benchmarking + beta evaluation to judge whether this is useful.
Note: if you have more themes enabled, (which is the workflow if you have the themekey module, you end up in those kind of problems

claudiu.cristea’s picture

@dawehner, can you point me to some docs, best practices, writeups on how to provide an acceptable benchmark?

claudiu.cristea’s picture

Issue tags: +needs profiling
wim leers’s picture

Issue tags: -needs profiling
StatusFileSize
new9.61 KB
new9.29 KB

I'm afraid this is a dead end :(

EDIT: tested with 1000 requests to /contact, as the anon users, with page cache off.

Before
Requests per second:    14.11 [#/sec] (mean)
Time per request:       70.888 [ms] (mean)
Time per request:       70.888 [ms] (mean, across all concurrent requests)
Transfer rate:          195.59 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    68   71   1.2     71      76
Waiting:       60   64   1.2     63      69
Total:         68   71   1.2     71      76

Percentage of the requests served within a certain time (ms)
  50%     71
  66%     71
  75%     71
  80%     72
  90%     72
  95%     73
  98%     74
  99%     75
 100%     76 (longest request)
After
Requests per second:    13.93 [#/sec] (mean)
Time per request:       71.765 [ms] (mean)
Time per request:       71.765 [ms] (mean, across all concurrent requests)
Transfer rate:          193.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    67   72   1.3     71      78
Waiting:       60   64   1.3     64      71
Total:         67   72   1.3     72      78

Percentage of the requests served within a certain time (ms)
  50%     72
  66%     72
  75%     72
  80%     73
  90%     73
  95%     74
  98%     75
  99%     76
 100%     78 (longest request)

dawehner’s picture

a) to be clear #2531958: Try to do less in theme_get_settings() on every request still has a listInfo call, so this is why things are a bit slower here
b) This doesn't tell us any statistically relevant data, see standard derivation. We need to have more iterations in order to tell something properly

joelpittet’s picture

Here's what I got with the /contact as the front page. xdebug and all caching turned off. Twig debug turned off and twig caching still on.

Also I enabled all the modules and a few contrib modules too but still accessing that page anonymous so it shouldn't make I difference I'd expect.

=== 8.0.x..8.0.x compared (5659123816918..565914310b034):

ct  : 170,827|170,827|0|0.0%
wt  : 632,030|633,399|1,369|0.2%
mu  : 33,371,040|33,373,152|2,112|0.0%
pmu : 35,105,000|35,107,632|2,632|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=5659123816918&...

=== 8.0.x..themeaccesscheck compared (5659123816918..5659177493c27):

ct  : 170,827|170,851|24|0.0%
wt  : 632,030|640,893|8,863|1.4%
mu  : 33,371,040|33,387,336|16,296|0.0%
pmu : 35,105,000|35,121,464|16,464|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=5659123816918&...

=== SUM: 8_0_x-summary..themeaccesscheck-summary compared (565918537d922..5659186b7d6e0):

ct  : 17,091,157|17,093,710|2,553|0.0%
wt  : 69,237,937|69,699,950|462,013|0.7%
mu  : 3,337,669,336|3,339,222,864|1,553,528|0.0%
pmu : 3,511,345,104|3,512,874,752|1,529,648|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=565918537d922&... 8_0_x-summary..themeaccesscheck-summary

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joelpittet’s picture

Status: Needs review » Needs work

@claudiu.cristea is there something else we could test directly because page hits aren't significantly different it seems from the tests above.

Setting to NW for update to profiling scenario/instructions.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

catch’s picture

This is still valid, but we have a container parameter with a list of themes now, so I can't see a reason not to use that.

catch’s picture

Status: Needs work » Needs review
StatusFileSize
new290.67 KB
new301.83 KB

Attaching xhprof (xhgui) screenshots. This appears to save about 1ms from each request, the call chain is via the theme cache context.

longwave’s picture

Status: Needs review » Needs work

One of the Nightwatch tests consistently fails while trying to install a theme, so I wonder if this has a subtle side effect somewhere?

catch’s picture

Status: Needs work » Needs review

The nightwatch failure is because the nightwatch install theme command relies on a controller that changes the default/admin theme in a GET request then immediately renders a response - the active theme is determined after the new theme is installed, but the ThemeAccess service isn't refreshed with the new container parameter afaik.

I can't see any obvious way to force this to be refreshed, or even to workaround it in the test, so for now removed the dependency injection so that the list of themes is got from the container at the very last minute.

godotislate’s picture

I wonder that if #3481903: Support hooks (Hook attribute) in any registered service can land (though there are some challenges), then services can implement hook_modules_installed() and/or hook_themes_installed() and refresh their properties from the new container there.

longwave’s picture

We don't need an entire support module to handle changing the theme for exactly three Nightwatch tests. MR!15405 removes the module and just uses /admin/appearance to change the theme.

Claude Code wrote the change to drupalEnableTheme.js.

catch’s picture

OK better explanation (mostly in a code comment now).

Access checkers all get constructed when the event dispatcher needs them - this happens before the controller method is called, e.g. when checking access to the route or similar.

The controller method installs a theme then immediately sets it as the admin theme - and the controller is an admin page.

Theme negotiation happens after the method is called, but the access check service has already been constructed before the controller method is called. This means that the theme isn't in the list of themes passed to the access checker.

This all means we can workaround the bug in the nightwatch test module, I find it very unlikely that any equivalent code exists anywhere else.

There are probably cleaner ways to fix it though:

1. In the theme negotiatior, instead of injecting the access checker service, we could inline the logic - the actual logic is one line. This assumes the theme negotiator will be fresh from the container, which it might not.

2. To be honest I don't understand the point of this access check during theme negotiation altogether - how does an uninstalled theme get passed in the first place? Going to open an issue to look into removing it.

catch’s picture

Still had the tab open and crossposted with #43.

Dropping the support module seems fine too, although I managed to get things isolated to the support module now so that could also be a follow-up. We have two issues open to replace these nightwatch tests anyway.

Going to open the follow-up to see whether the theme access check is really necessary too.

catch’s picture

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new893 bytes

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Issue summary: View changes
Status: Needs work » Needs review
catch’s picture

Issue summary: View changes
berdir’s picture

How do you suggest we proceed with this in light of #3584064: Check whether ThemeAccess check during theme negotiation is necessary? if we remove the need for it entirely or put it in an asser(), do we still do this too?

catch’s picture

We need to figure out whether we need bc or not in that issue, so for me I'd go ahead here since it's a simple change, then keep going over there. If we go with an assert() in the other issue, it would be better to assert the simple check added here rather than the logic in HEAD too.

berdir’s picture

Status: Needs review » Needs work

After thinking about it, I think it makes sense to do this as well. Especially if we only make it an assert() then it would mean that performance tests would have different results depending on whether or not assert is enabled and profiling is often done with assert on and then could lead people down a wrong path.

Added one question about BC on the constructor. I'd suggest doing this, not really because of possible subclasses but more so for handling updates with an existing container. This would probably break pretty hard when trying to access the site without a cache clear.

catch’s picture

Status: Needs work » Needs review

I don't think we need to provide bc for subclasses, but handling the stale container sounds good. While core updates usually for a container rebuild immediately via the container cache key, some sites set manual deployment identifiers and might forget. Added a commit for that.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.32 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Status: Needs work » Needs review
berdir’s picture

Status: Needs review » Reviewed & tested by the community

Thanks. For constructor parameters with BC, I prefer not promoting them directly, this means we can't set the type on that because of BC, but I think it's OK either way.

catch’s picture

Yeah I went for 'least amount of change', technically we could use a union type in the constructor, but then the property itself would have the union type so not sure that's much better either. Given the constructor is internal and so is the class (even though it's injected in core, it's a tagged service really) not sure it matters much.

catch changed the visibility of the branch 2538970-theme-access-check-nightwatch-fix to hidden.

catch’s picture

I've hidden @longwave's branch with the nightwatch refactor but I added a note on #3553673: Migrate from Nightwatch to Playwright (and phpunit Axe) tests that the playwright conversion should tackle it.

  • godotislate committed 7a031e39 on main
    refactor: #2538970 Improve the speed of \Drupal\Core\Theme\...

  • godotislate committed d3126dbc on 11.x
    refactor: #2538970 Improve the speed of \Drupal\Core\Theme\...
godotislate’s picture

Status: Reviewed & tested by the community » Fixed

Made two suggestions and applied them myself because they were minor changes to comments.
Committed and pushed 7a031e3 to main and d3126db to 11.x. Thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

godotislate’s picture

Version: main » 11.x-dev