@catch at #2702001-12: Inject config factory for BigPipe (including in exception handlers):
Can we open a follow-up, or link to the existing issue if there is one, to something which would allow the message to be printed in the same request? For example could we show error messages in the footer as a final fallback?
@Wim Leers' reply at #13:
You mean a follow-up to #2678568: Ensure good UX & DX even when A) rendering of placeholder fails, B) some response event subscriber fails, not to this, right? This is solely code clean-up. What you describe sounds feasible, but I'm not sure if it's desirable. It'd mean appending HTML to the HTML just before the closing
</body>tag. And that HTML may simply end up not being visible depending on the theme. I think it's good enough as it is.
And @catch's reply to that at #14:
Well I still think the config setting for error display leaking into bigpipe is a bit smelly, I'm not aware of anywhere else we have to deal with that.
| Comment | File | Size | Author |
|---|---|---|---|
| #57 | status_messages_placeholder-2712935-57.patch | 22.88 KB | wim leers |
| #45 | status_messages_placeholder-2712935-45.patch | 21.69 KB | wim leers |
Comments
Comment #2
wim leersSo, to reply to #14: Sure, that's smelly. I dislike it too. But the reason is simple: this is a streamed response, with calculations happening during the streaming. Which means it's too late to allow the default error handler's behavior, which uses
drupal_set_message(). That doesn't work, because messages may already have been sent.Perhaps the better solution then is for BigPipe to override the default error handler on BigPipe responses, to not use
drupal_set_message(), but use what you proposed.I don't know why I didn't think of that before, that seems the perfect solution, doesn't it?
Comment #3
catchApart from error messages, won't this affect any drupal_set_message() call which might happen during rendering of a placeholder?
drupal_set_message() just adds things to session, the real issue seems to me that we have a race condition where drupal_get_messages() can be run considerably earlier on a big pipe request compared to the rest of rendering, when compared to a regular request.
With the js implementation of BigPipe it pught to be feasible to handle messages last - so that the maximum can be pulled from session.
Non-js I think we're stuck with footer rendering. #77245: Provide a common API for displaying JavaScript messages is dealing with a similar problem.
I don't think it's an issue with the error handler as such.
Comment #4
catchRe-titling to reflect what I think the problem is.
Comment #5
wim leersTriaged by @Fabianx and I at DrupalCon New Orleans.
We see basically two options:
#placeholder_optionsin the render system, which then allows a weight to be set, which the BigPipe placeholder strategy (and other placeholder strategies) can then take into account.#placeholder_optionswould then allow scalar values.Wim is not a fan of option 2 because it opens the door to lots of undocumented pieces of metadata that have big consequences, but he also doesn't see any better way.
Option 2 is what Fabianx proposed from the very beginning, but Wim was against because there were no real-world use cases yet, and now there is one. Another possible use case is the ability to specify a name/ID that would be part of the placeholder ID, to simplify debugging.
Comment #7
wim leersPer #2702609-42: Disable placeholdering (and BigPipe) on unsafe requests to help find forms as fast as possible (to allow EnforcedResponses to work), I think this is actually a bug in the render system.
Comment #8
wim leersComment #9
wim leersI started looking into this again because I'm starting to work on this.
I'm still deeply concerned about
#placeholder_options(see #5.2). It can easily become a dumping ground. From experience with weights in the module system (to adjust the hook order), asset system (to adjust asset order in D7), event subscribers (priorities to adjust event subscriber order), we already know that it's a brittle system. That's why the asset system in D8 started relying solely on dependencies. Doing something similar here doesn't make sense though: the "messages" placeholder would simply have to be run after anything else. But it wouldn't know which those other things are. So we can't do something like that.Furthermore, assigning weights to placeholders is antithetical to one of the key design aspects of placeholders: that they are independently renderable. By providing weights, we open the door for placeholders other than messages to start relying on global state, because you (a placeholder developer) can then control the order in which placeholders are rendered. This removes control from placeholder strategies, which should be the ones in charge of determining the placeholder execution order.
Which leads me to the key point: the entire reason we have this problem is because messages rely on global state. And no, I also don't see a way how we can avoid that.
So I think that in this case, it's appropriate to:
Thoughts? Especially @catch.
Comment #10
dawehnerOne thing I was always wondering, why are drupal messages not something which can also be bubbled.
Let me explain what I mean with that. For example each individual block currently can attach its required libraries. Those are then attached to the HTML and are processed by the browser. Messages could be seen sort of similar. When you could create a new message as part of rendering some placeholder, the attachments could contain that message. By that the messages block could be rendered early and then just appended over time. This of course would need some customiziation in ajax.js, but that sounds not totally impossible.
Comment #11
wim leersYep,
#attached[messages]. I'd swear I've written about exactly that before.That still requires it to be processed last, of course. I don't see a way around that.
Yes, but this would not work when JS is turned off. Which means we'd need different rendering of messages based on whether the current request comes from a JS-enabled browser.
Comment #12
wim leersSo, I have the "go" for the direction outlined in #9.
Step 1: regression tests, for both the render system and BigPipe.
Comment #13
catch#attached['messages'] would be good, but not sure it can replace direct writing to $_SESSION - we'll still have the error handler etc. wanting to set messages and that shouldn't rely on the rendering system (or at least makes things complicated if it does). Also there's always the possibility that a message gets set after messages get rendered (via the error handler) and $_SESSION means that they least get rendered on the next request.
Comment #14
fabianx commentedI also thought about bubbling messages via #attached message, but the problem is that we don't want to cache the messages, e.g. an error thrown by a block that is cached.
But maybe we do, if the message itself would always have max-age = 0, so it would never be cached in the first place, but then it would be auto-placeholdered for no reason except that it has a message, which is uhm ...
However that would not solve anything here as the problem is that every placeholder could still set a message.
Therefore:
The messages block must come last (also in core, probably should just change the order in the subscriber) and there is nothing we can do for no-js big pipe if any of the placeholders show a message.
Comment #15
dawehnerWell, the question is whether we can treat messages similar to libraries, which are rendered specially by ajax.js, by aka. making it possible to load them dynamically. Messages could be similar in that sense .
Comment #16
wim leersFirst, here's a test to prove we can reliably measure the current (wrong) behavior.
Comment #17
wim leers#16 should come back green, because it's asserting the wrong behavior. This then, is asserting the right behavior. And hence it should come back red.
(Note that the issue tag must remain, because this patch only contains
render systemtest coverage, notbig_pipe.module.)Comment #18
wim leersAnd here's the fix for
render system.Next:
big_pipe.moduletest coverage.Comment #24
wim leersNow that's interesting! In #16 + #17 + #18, the 8.2 test results always have 127 additional failures compared to the 8.1 test. That suggests Drupal 8.2 HEAD is broken. And https://www.drupal.org/node/3060/qa confirms that.
EDIT: catch reverted the sole 8.2.x commit of today, hopefully that fixed it. Queued all 3 patches for re-testing against 8.2.x.
Comment #26
wim leersAh, now #16 is green on 8.2, #17 is red, #18 is green. This now perfectly matches the 8.1.x results and the expectations.
Expect the
big_pipe.moduletest coverage tomorrow.Comment #27
wim leersAnd here's test coverage for
big_pipe.module. This should fail.Comment #28
wim leersAnd here's the fix for BigPipe. Effectively the same as the fix for the render system. This should pass.
Comment #29
wim leersThe #28 interdiff is wrong. This is the right one.
Comment #30
wim leersFixed nits.
Comment #37
wim leersThis change caused a fail in the existing BigPipe test coverage. Working on a fix.
Comment #38
wim leersAnd here's the fix.
BigPipeTest::testBigPipe()was assuming that placeholders would be streamed in the order that they appeared in the DOM. This is a correct assumption in the current codebase, but this patch changes that. So we need a minor adjustment to the test.This is now ready for final review.
Comment #39
fabianx commentedQuick drive-by review:
I don't really like that.
Can't we at least allow an attribute with an "ID" or "name" for the placeholder so we can easier identify it.
I don't think we should hardcode the combinations here.
That feels really fragile.
Comment #40
wim leersBut that would still mean requiring + allowing
#placeholder_options. Which is problematic, for reasons outlined above.It's not. The accompanying
assert()statements would fail before a patch could be committed to Drupal core that changes this.The only alternative I can think of is to modify
\Drupal\Core\Render\Element\StatusMessagesso that it generates its own custom placeholder. But you'd then still end up with a set of hardcoded strings.Comment #41
catchI think we need profiling to justify the hard-coding, but I don't see a way around hardcoding without doing the generation.
Comment #42
fabianx commented#40 However I can extend the status codes and I can override the block to allow new status codes.
Like 'pink-message' or 'debug' and I have seen such custom message types in the wild.
We might choose to disallow that and the documentation clearly states that is the case (only supported: status, error, warning), but it is still a little BC break for assumed functionality.
But if we don't want to go #placeholder_options: Why not #placeholder_id or #placeholder_name?
An optional name or ID is for other reasons pretty important - especially as seen here for identifying the things.
An alternative would be to look into the #lazy_builder itself and match the LazyBuilder string. That too is less fragile.
Comment #43
wim leers#41: true. But why even do that if we can easily hardcode it and have assertions to prove correctness. It'd be entirely pointless. This is not a premature optimization because it doesn't add complexity.
#42:
No you can't. From
drupal_set_message()'s docblock:It's not a BC break. I've never ever seen anybody assume they can pass custom
$types todrupal_set_message().I don't see why it's important. Even if we did that, then that
#placeholder_namewould have to use a different name for the four possible different arguments. So we still wouldn't be able to hardcode it to a single thing. So you'd still end hardcoding four permutations:<drupal-render-placeholder name="messages-all" token="TOKEN1"></drupal-render-placeholder><drupal-render-placeholder name="messages-status" token="TOKEN2"></drupal-render-placeholder><drupal-render-placeholder name="messages-warning" token="TOKEN3"></drupal-render-placeholder><drupal-render-placeholder name="messages-error" token="TOKEN4"></drupal-render-placeholder>So that'd look like this:
… but that'd still mean you're hardcoding four permutations:
<drupal-status-messages display="all" token="TOKEN1"></drupal-status-messages><drupal-status-messages display="status" token="TOKEN2"></drupal-status-messages><drupal-status-messages display="warning" token="TOKEN3"></drupal-status-messages><drupal-status-messages display="error" token="TOKEN4"></drupal-status-messages>True, and I could live with that if catch +1s. But it makes the code more complex, less greppable for IMO no good reason.
Comment #44
fabianx commentedUhm, no what I meant is an ID that is independent of the placeholder name:
If not, then examining the lazy builder should be done - IMHO.
Comment #45
wim leersI wrote this, but now I see I was wrong. My apologies!
The code is not really more complex, nor less greppable. The complexity is similar, if not less.
Thanks for insisting, @Fabianx! You were right :)
Comment #46
fabianx commentednit - Those are unrelated changes, but I guess they are okay.
RTBC! Thanks for trying it out!
Comment #48
alexpottI think this could be neater as an array_filter() or you could even do the whole thing in one go and just move the message ones to the end. Like this...
Comment #49
alexpottOr maybe even do this...
For even less array messing around.
Comment #50
wim leersGood call, #49 looks much simpler indeed. Done.
Comment #51
alexpottNeeds a reroll - conflcted with #2678662: Ensure BigPipe does not break when HTML document contains CDATA sections or inline scripts matching certain patterns
Comment #52
fabianx commentedComment #53
wim leersRerolling…
Comment #54
wim leersStraight reroll.
Comment #57
wim leersBad reroll, my bad. Merged one thing wrong. Interdiff included.
Comment #62
wim leersUnrelated fail :/
Drupal\config\Tests\ConfigImportAllTest. Retesting. Back to RTBC.Comment #65
wim leersToo. Many. Random. Fails.
Comment #66
alexpottCommitted and pushed 81e51d9f27abf58da5be431b0a4590342aa1880c to 8.2.x and ec674c0 to 8.1.x. Thanks!
Comment #69
wim leersYay! :)
Unblocked #2765385: Three minor bugs in BigPipe test coverage.
And finally created #2775587: Mark BigPipe as beta-level stability (instead of alpha).
Comment #73
berdirSomehow this doesn't seem to be fully fixed yet. We can still have test fails in simplenews related to this problem and can reproduce manually. See also #2773333: Form validation errors, status messages on form submission are shown after page refresh when form is rendered in block, which was originally opened days before this was committed but people still see this problem there as well.