Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
As discovered in #2846485: Terrible performance when rendering multi-value fields in views, DateForamtter::format
attempts to load a date format from storage even when a custom date format is passed in. This leads to an unnecessary and expensive storage lookup. We can fix it just by checking of the format type is "custom" before attempting the lookup.
Comment | File | Size | Author |
---|---|---|---|
#20 | interdiff-16-20.txt | 2.26 KB | mpdonadio |
#20 | 2846643-20.patch | 6.04 KB | mpdonadio |
#16 | interdiff-11-16.txt | 6.39 KB | mpdonadio |
#16 | 2846643-16.patch | 5.71 KB | mpdonadio |
#16 | 2846643-should-fail.patch | 6.37 KB | mpdonadio |
Comments
Comment #2
bkosborneAre there no existing unit tests for datetime module?
Comment #3
mpdonadioNice catch; that was one of the thoughts I wanted to look into for the parent problem.
Apparently, there is no direct test coverage of that service (which is really part of core and not the datetime.module, which just provides the fields/widgets/formatters, but that is splitling hairs) :/ The service is indirectly tested in the timestamp tests, and the field tests.
It should have its own test coverage. Given the number of services that get injected, a kernel test (rather than a unit test) would probably be best (and easiest, so we don't need to mock five services).
Comment #4
dawehnerI'm wondering whether on top of that we want to also static cache the result of loading the date format. I guess custom is just particulally horrible because we don't actually have that, so we cannot cache the result of the empty result?
Comment #5
mpdonadioI thought entity loads were already static cached?
Also, DateFormatter::dateFormat() already does static caching by (format,langcode). The docs say it returns the format string, but it looks like is returns the date format entity. I think that can be tightened up to call ->getPattern() that and static caching that instead of the entity, and then getting rid of the ->getPattern calls in dateFormat().
If doing the entity lookup for 'custom' is failing and causing a big performance problem, then this means that we have a problem with searching for config entities by label in general? There really aren't that may default format that the system provides.
Comment #7
mpdonadioThis makes DateFormatter::dateFormat() match the docblock, which should also speed things up.
Adding Needs Tests. We need a kernel test to prevent regression here. Should be able to pull all of the format entities, grab their patterns, render out something, and compare against what date() would do with the same thing (being careful w/ timezones...).
Comment #9
mpdonadio\Drupal\KernelTests\Core\Datetime\FormatDateTest has plenty of explicit coverage for this.
Here is a fix for my last oopsie.
Comment #11
mpdonadioLet's see if this is better.
Comment #12
jhedstromThis looks good to go.
Comment #13
alexpottIf the NULL was the fallback pattern we'd save quite a few method calls.
The changes to where $this->languageManager->setOverrideLanguage() is called don't make sense. If we call it to change the language regardless or not if we have loaded a format we need to set it back afterwards. Obviously we're missing test coverage of that but that's tricky. Also if we were to load the fallback here then the fallback pattern could vary per language - which actually makes sense.
We changing the return type here. This makes this fix unnecessarily not bugfix safe. Also if we decide we only want this to be fixed in a minor (which I'm not sure is right) then we need to update the docs.
Comment #14
alexpottBut to be honest the entire scope of this change could be achieved by just testing what $type is before trying to load the format. Also there is another unintended logic change. In HEAD calling \Drupal\Core\Datetime\DateFormatter::format(0, 'custom', '') whould fallback to the fallback - with the patch in #11 it looks as if that behaviour is broken.
Comment #15
mpdonadioThanks for looking at this.
#13-1, not totally sure what you mean here? That hunk you referenced is mostly an indentation change; but
would probably be better, compared to what is in HEAD:
#13-2, yup. That is borked, and we are missing test coverage somewhere (likely b/c mocking).
#13-3, we are changing the return type, but it matches the advertised API. IOW, in HEAD the docblock says a string is returned but a config entity is actually returned.
#14, also a little confused by this comment, too. The patch does check for `if ($type != 'custom')` before loading the config entity / format. We don't know if the 'type' actually exists until we try to load it.
Comment #16
mpdonadioOK, here is the minimally invasive way to do it to be BC, and a doc fix.
Confused about the 2846643-should-fail.patch b/c commenting out the save/restore of the ConfigOverrideLanguage doesn't seem to make a difference in the test.
Comment #18
jhedstromI'm confused, the should fail patch is failing, but it wasn't expected to, or wasn't locally?
Comment #19
alexpottThis is still an unnecessary behaviour change...
HEAD does this:
With patch...
All that we need to change is to not load the fallback with the $type check...
^^ This bit needs to happen regardless of type.
Comment #20
mpdonadioSorry for being dense here. Update to logic, expanded comment, and explicit assertion for the case you identified.
Comment #21
jhedstromI think #20 has addressed the remaining issues.
Comment #22
alexpottCommitted and pushed 2736deb to 8.4.x and 53b2726 to 8.3.x. Thanks!
Fixed coding standard on commit.
Comment #25
drugan CreditAttribution: drugan as a volunteer commentedWell, seems something is wrong with the 53b27262cec56eaeca70f90204ce9822f87db1a1 commit.
Can't figure out how the patch below could be related with LocaleTranslatedSchemaDefinitionTest and DateFormatter....
https://www.drupal.org/node/2745123#comment-11949523
https://www.drupal.org/pift-ci-job/604934
UPD: somehow the LocaleTranslatedSchemaDefinitionTest takes a system message displayed after the previous test directory was deleted by simpletest_clean_temporary_directories(), specifically this:
'Removed 1 temporary directory.', 'Removed @count temporary directories.'
...and then uses it as a placeholder in DB query.
Is it expected behaviour? OK, but why it takes not only message from HTML markup but placeholders for the message too?
Comment #26
alexpott@drugan 8.3.x HEAD and that test is passing when run locally. If I apply the patch on #2745123: Simpletest module crashes on cleanup, uninstall it fails. Therefore there is a problem with the patch.
Comment #27
drugan CreditAttribution: drugan as a volunteer commentedOK, I'll try to avoid emitting system messages on the patch when test site directory is successfully removed.