Problem/Motivation
The messages block/element always uses a placeholder, this is because the messages to be rendered can be unique.
This is needed to allow dynamic page cache to work, but there is not really a performance benefit from rendering the messages themselves via bigpipe because it's fast, and usually there are no messages.
After #3493911: Add a CachedPlaceholderStrategy to optimize render cache hits and reduce layout shift from big pipe this is the only big pipe placeholder on every page for regular authenticated users out of the box. If we bypass bigpipe, we can avoid loading big pipe js (and in turn ajax.js and jQuery). This saves around 115kb of JavaScript for auth users with the standard profile.
Steps to reproduce
Install the standard profile, create an authenticated user, log in as that user, visit the front page - note that ajax.js and big_pipe.js are loaded.
Then apply the MR, clear caches and refresh - big_pipe.js and ajax.js should no longer be loaded.
Proposed resolution
Add a new #placeholder_strategy_denylist render array key, this can be used to communicate hints to the ChainedPlaceholderStrategy that it should ignore particular strategies for particular placeholders.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #19 | 3508299-nr-bot.txt | 91 bytes | needs-review-queue-bot |
Issue fork drupal-3508299
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:
- 3508299-messages
changes, plain diff MR !11263
Comments
Comment #3
catchComment #4
catchStandardPerformanceTest is missing an assertion on ScriptBytes for authenticated users. Added that and ran the tests only job:
https://git.drupalcode.org/project/drupal/-/jobs/4436719
115kb of JavaScript reduction for authenticated users out of the box.
Comment #5
catch@fabianx suggested '#placeholder_strategy => ['single_flush'] as a generic way to avoid bigpipe for a placeholder. That would allow other blocks/elements to opt out of bigpipe too which could be a good thing.
I couldn't see a straightforward way to allow a specific placeholder to target a specific placeholder_strategy - because placeholder strategies don't have IDs or anything, they're just tagged services, and they could theoretically be removed by contrib etc.
However, if we add support in big_pipe specifically for 'no_big_pipe', and allow #placeholder_strategy to be used in general, then that gives a way for different placeholder implementations to support different kinds of placeholder_strategy hints. This might be overly flexible but also it was very, very easy to implement and doesn't require any API changes.
Comment #6
catchThis is working.
The main remaining test failures are because big pipe's test coverage heavily relies on messages being placeholdered by big pipe, which they no longer are even on cold caches.
The messages placeholder is handy for testing because it's easy to put arbitrary content in it, behaves differently depending session etc. so I can see why it was used in the first place. Easiest way to address this was to add an element info alter to big_pipe_test module to remove the #placeholder_strategy. Only alternative I could think of that wouldn't result in less test coverage was subclassing or copying the element over, or coming up with something superficially different but similar.
Probably still a couple of test failures left but getting close.
Will need a change record to document the new capability so tagging for that, would be nice to get a review before that though.
Comment #7
catchComment #8
catchNeeded to split the messages alter out to a dedicated module because big_pipe_test adds an http header for every placeholder and that is fun (as in 502 error fun) in BigPipeRegressionTest which creates 2000 placeholders.
Tests should be green now.
Comment #9
catchI think this will allow us to do #3508508: Allow big pipe to run for session-less users so went ahead and opened it.
Comment #10
supriyagupta@catch I think we can use page_attachments hook to remove the js file based on condition and based on user role. So that it will work proper according to our requirement.
Comment #11
catch@supriyagupta that's not necessary because the big pipe js is already added conditionally (and when it's added it's needed). This issue is just reducing some of the conditions under which it gets added.
Comment #12
kristiaanvandeneyndeI like the approach and that we no longer use magic strings. Left some remarks, but nothing too drastic.
Comment #13
catchI think I was able to address all the remarks.
Comment #14
kristiaanvandeneyndeLooks good to me, tests go green too. Just some small doc mistakes that can be fixed upon commit for all I care. Holding off on RTBC as the issue is still marked as "Needs change record".
Comment #15
catchAdded a change record.
In the process of writing it, I realised we should specify the CachedStrategy for the messages element even though it won't be used (the block is cached with its own keys, the placeholder element is not) - because we only want to prevent big pipe from kicking in, not anything else.
Comment #16
kristiaanvandeneyndeNeeds to merge in origin/11.x again for the performance tests and needs to update docs. The only thing that bothers me about both the MR and CR is that it's now painfully obvious that an allowlist can be limiting. We only want to turn off BigPipe and have to specify all other strategies. If contrib introduces another strategy, it won't apply to the messages block.
Doesn't a denylist make more sense here? You opt into all strategies out of the box, so the only possible change you'd want to make here is opt out of some of them, meaning a denylist makes more sense.
Comment #17
catchYeah it does, I started out with that with the no_big_pipe magic string but went to an allowlist when we added the classnames.
I didn't fully reallise that ClassName::class works whether or not the class is defined or not, so that should actually be fine to just reverse the logic and still use classnames.
Comment #18
catchRebased and switched to a denylist.
Comment #19
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. 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.
Comment #20
catchRebased.
Comment #21
kristiaanvandeneyndeDug up the ::class docs for people stumbling upon this issue: https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basi...
Found some. minor things but looks good to me otherwise
Comment #22
catchThink I got to all the feedback, agreed on the more general assertion message.
Comment #23
kristiaanvandeneyndeChainedPlaceholderStrategyTest is choking on the new assert, I think because of a small mistake.
😂
Comment #24
catchI thought I'd missed the exclamation mark when you pointed this out, but it was actually fine, the problem was in the test.
The unit test sets up a 'placeholder removing strategy' that returns an empty array. It is not possible to do this, you can ignore placeholders, you can't remove them. So I just removed this from the data provider, it's testing something that's not supported and pretty sure the unit test would have choked on the new assertion if it was added in isolation without the rest of the patch.
Comment #25
catchComment #26
catchComment #27
kristiaanvandeneyndeIf CachedStrategy does not find any hits, it will also return an empty array. Isn't that the same as what the test strategy did? BigPipeStrategy can also return an empty array. The only difference I see is that the test did not have SingleFlushStrategy as a fallback, which ends up returning all placeholders anyway.
Might be my sickness talking but if I read ChainedPlaceholderStrategy::processPlaceholders() correctly, we reduce the set of $placeholders whenever a strategy returned some of them back. At the end of the loop we want $placeholders to be empty, no? So does that not mean that the check has to be !empty() to figure out that something was, in fact, not replaced?
Comment #28
catchYes exactly - SingleFlushStrategy always replaces the remaining placeholders, which is why we can do the assert() to make sure there's none left to replace.
The unit test has no fallback, just a no-op strategy, and in that situation the 'removed' placeholder is not actually removed, but just ignored and not replaced, which we are now validating should never be allowed to happen.
No because the failure condition for an assert() call is when it returns FALSE.
https://www.php.net/manual/en/function.assert.php
Comment #29
kristiaanvandeneyndeOh wow, for a second there my brain processed the assert as an if-statement. Left one more suggestion, but that can be fixed upon commit.
Comment #30
catchComment #31
larowlanLeft one question on the MR about adding the key and value to
$pre_bubbling_elementsComment #32
catchAdded to $pre_bubbling_elements - it won't make any difference in practice due to CachedPlaceholderStrategy but Renderer itself doesn't know this, so we should do it. Also applied the other suggestions and added the extra comment to the test pre_render.
Comment #33
catchSince these are all minor, going to self-RTBC for the changes.
Comment #37
larowlanCommitted to 11.x - thanks!
Published the change record
Comment #39
cilefen commentedA dev says this causes a regression.