Problem/Motivation
\Drupal\Core\DrupalKernel::preHandle currently does a config get, really early in the bootstrap,
for something which is not even configurable via the UI and is quite low level and is not changed really on deployment
What you see in there is the time spend in the preHandle function, which for example consists of ~0.5 % inside the config get
+ the container->get() call for the first initialized config factory, so solving this issue would solve that 0.5% + move the config get call more away from early bootstrap.
| Function Name | Calls | Calls% | Incl. Wall Time (microsec) |
IWall% | Incl. MemUse (bytes) |
IMemUse% | Incl. PeakMemUse (bytes) |
IPeakMemUse% |
|---|---|---|---|---|---|---|---|---|
| Current Function | ||||||||
| Drupal\Core\DrupalKernel::preHandle | 1 | 4.3% | 3,859 | 4.1% | 1,032,104 | 6.2% | 1,032,160 | 6.2% |
| Exclusive Metrics for Current Function | 30 | 0.8% | 4,864 | 0.5% | 4,672 | 0.5% | ||
| Parent function | ||||||||
| Drupal\Core\StackMiddleware\KernelPreHandle::handle | 1 | 100.0% | 3,859 | 100.0% | 1,032,104 | 100.0% | 1,032,160 | 100.0% |
| Child functions | ||||||||
| Symfony\Component\DependencyInjection\Container::get | 4 | 26.7% | 1,942 | 50.3% | 632,728 | 61.3% | 650,840 | 63.1% |
| Drupal\Core\Extension\ModuleHandler::loadAll | 1 | 6.7% | 788 | 20.4% | 111,840 | 10.8% | 95,920 | 9.3% |
| Drupal\Core\Config\ConfigFactory::get | 1 | 6.7% | 557 | 14.4% | 143,600 | 13.9% | 144,768 | 14.0% |
| Drupal\Core\DrupalKernel::loadLegacyIncludes | 1 | 6.7% | 239 | 6.2% | 47,352 | 4.6% | 47,008 | 4.6% |
| Drupal\Core\File\MimeType\MimeTypeGuesser::registerWithSymfonyGuesser | 1 | 6.7% | 143 | 3.7% | 42,440 | 4.1% | 41,568 | 4.0% |
| Drupal\Core\DrupalKernel::initializeRequestGlobals | 1 | 6.7% | 68 | 1.8% | 19,008 | 1.8% | 17,384 | 1.7% |
| spl_autoload_call | 2 | 13.3% | 58 | 1.5% | 17,992 | 1.7% | 19,040 | 1.8% |
| Drupal\Core\StreamWrapper\StreamWrapperManager::register | 1 | 6.7% | 24 | 0.6% | 7,344 | 0.7% | 6,184 | 0.6% |
| Drupal\Core\Config\Config::get | 1 | 6.7% | 8 | 0.2% | 3,744 | 0.4% | 3,552 | 0.3% |
| Symfony\Component\HttpFoundation\RequestStack::push | 1 | 6.7% | 1 | 0.0% | 992 | 0.1% | 848 | 0.1% |
| Drupal\Component\Utility\UrlHelper::setAllowedProtocols | 1 | 6.7% | 1 | 0.0% | 200 | 0.0% | 376 | 0.0% |
Proposed resolution
Move it to a container parameter to avoid early config instantiation.
This has the advantage of being much faster than a configuration that early in the bootstrap. In an ideal world together with smart cache pages should be potentially
able to serve without invoking config. This issue is a small step in that direction.
Remaining tasks
User interface changes
API changes
Data model changes
Beta phase evaluation
| Issue category | Task, because nothing is broken. |
|---|---|
| Issue priority | Major because it is in the critical performance path. |
| Prioritized changes | The main goal of this issue is performance. |
| Disruption | Potentially disruptive for existing sites that had customized this configuration. Usually this was set via settings.php in D7 and given there is no UI a config override is way more likely, so it is easy to move over to settings.yml |
| Comment | File | Size | Author |
|---|---|---|---|
| #35 | 2529514-35.patch | 13.63 KB | dawehner |
| #32 | interdiff.txt | 2.8 KB | dawehner |
| #32 | 2529514-31.patch | 14.03 KB | dawehner |
| #28 | interdiff.txt | 1.54 KB | dawehner |
| #28 | 2529514-28.patch | 14.29 KB | dawehner |
Comments
Comment #1
fabianx commentedComment #2
dawehnerJust a start.
Comment #4
dawehnerAdded a profiling of HEAD into the issue summary.
Comment #5
dawehnerSome work on it.
Comment #7
dawehnerUps.
Comment #9
dawehner.
Comment #11
dawehnerI think
Drupal\migrate_drupal\Tests\d6\MigrateSystemFilterTestis kinda wrong, as it just migrates the default variables ...so even if it should not pass, it does now.
ideally we would have a container parameter migration destination.
Comment #13
wim leersJust a migrate test failure. Easy fix? :)
s/url/URL/
Comment #14
dawehnerWell, we just need to write a migration destination for services.yml files.
Comment #15
berdirAs mentioned, I think we should simply drop that from the migration. We did that as well for the private file path for example which is now in $settings.php
Comment #16
dawehnerSure let's do that.
@chx recommended to to add it to https://www.drupal.org/node/2167633, so this is done.
Comment #18
dawehnerUps, let's fix the merge error
Comment #20
dawehnerMeh.
Comment #21
dawehnerImproved the IS a bit
Comment #22
wim leersThis looks great!
Just one last question before this is RTBC:
Should we document why we need the fallback here?
Comment #23
dawehnerWell, in cases like tests, not sure what exactly we would gain. Those tests would fail then, but for runtime this doesn't matter.
Comment #24
dawehnerWrote a change record.
Comment #25
wim leersI personally think it'd be great if #23 would be added as a comment to the patch, to explain the need for #22. But since @dawehner doesn't feel the same way AFAICT, let's see what a committer thinks. Because other than that, this is ready.
Comment #26
dawehnerMh, I see your point, I just can't form some proper sentence which makes sense.
Comment #27
fabianx commentedRTBC + 1, I think it is fine to have a safe fallback.
Comment #28
dawehnerHa, that triggered something in me :)
Comment #29
fabianx commented#26: What about:
During tests the ContainerParameter filter.protocols might not be available, in that case use a fallback.
However that is wrong, too as getParameter() throws an Exception when a parameter cannot be found, so either we write a wrapper that works like variable_get() / config() with default values or we catch the Exception ...
(See: https://github.com/symfony/DependencyInjection/blob/master/ParameterBag/...)
So currently the fallback should never be used anyway ...
Edit: X-post with #28
Comment #30
dawehnerOne the the test failures on https://qa.drupal.org/pifr/test/1094313 caused me to add it. But well, in general think being always able to render HTTP/HTTPs links isn't such a bad idea to start with.
Comment #31
fabianx commentedDiscussed in IRC:
CNW as resolveValue() / getParameter() of ParameterBag clearly throws an Exception and that parameter should never be empty.
If we wanted a fallback, then we should add a generic Drupal::getParameter() function or a parameter service or such, that allows to specify a default value as fallback.
But then we are again way on our way to re-invent config, so probably not worth it ...
Comment #32
dawehnerLet's see whether it still passes. I might have changed UrlGenerator before moving the information for
default.services.ymltocore.services.ymlComment #33
fabianx commentedThanks, back to RTBC
Comment #35
dawehnerJust an ordinary reroll.
Comment #36
alexpottThis issue is a major task that will improve performance significantly and the disruption it introduces is limited. Per https://www.drupal.org/core/beta-changes, this is a good change to complete during the Drupal 8 beta phase. Committed 0613685 and pushed to 8.0.x. Thanks!
Fixed the mode change on commit.
Comment #38
yched commentedThe code comment still talks about the *config* possibly not existing yet.
Is that "if" case still relevant ? (the parameter not existing in $this->container ?) Not sure about the installer, but probably not true for update.php now that it's a regular route & controller ?
Comment #39
dawehnerWell it is relevant for sites which did not had yet a container rebuild, as they will temporarily not have this parameter in the container, so you really want to ensure that at least simple links always work, as otherwise you might have problems for itself.
Comment #40
fabianx commented#38 The parameter not existing would be an Exception, so the only case when it could be NULL or FALSE or '' is if its NULL or FALSE or '' in the container.
Not sure if that can happen during installation.
Comment #41
yched commented@FabianX is right, looks like that snippet had a straighforward conversion from "if (!config->get())" to "if (!container->getParameter())", but both sources don't behave the same on "doesn't exist" (and that "doesn't exist" doesn't occur on the same cases regarding install.php / update.php)
If I get the current situation right, It seems this "if (not exists)" is currently dead code, since the "not exists" case would throw an exception one line above, which doesn't seem to happen since we're currently green ?
Comment #42
fabianx commented#41: That is correct.
Comment #43
yched commentedPosted a patch in #2541560: Followup for [#2529514] - remove dead code then
Comment #44
neclimdulThis removed a migration. I don't see anyone from the migration team weighing in here so should there be a follow up to make sure this is addressed or are we just not going to support upgrading them?
Comment #45
dawehnerLet me quote myself earlier in the issue: