Problem/Motivation
In the BigPipe D8.1 core issue, @dawehner said this at #2469431-212: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts:
+++ b/core/modules/big_pipe/src/Render/BigPipe.php @@ -0,0 +1,432 @@ + $fake_request = $this->requestStack->getMasterRequest()->duplicate(); + $this->requestStack->push($fake_request); + $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $html_response); + $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event); + $this->requestStack->pop();I'm wondering whether we need to try catch here and reset the request stack in case some exceptions are thrown inside.
The obvious conclusion to jump to after reading that is: when something goes wrong when rendering placeholders, things will break badly
. But that's actually not what that code does, it doesn't render placeholders. That happens earlier! So we should also wrap the code responsible for doing that in a try…catch.
The code that @dawehner quotes actually can only be broken if some response event subscriber throws an exception. This of course should also be tested.
Broken logic to render a placeholder is probably 100 times more common than a broken response event subscriber. So, let's also address that here too.
Proposed resolution
- Broken logic to render a placeholder
-
- Add test case that explicitly throws an exception while rendering the placeholder.
- Verify that in production (non-verbose error logging), the exception is caught and not shown to the end user, that placeholder is then simply not shown
- Verify that in development (verbose error logging), the exception is not caught and shown to the end user, to not get in the way of the developer doing debugging
- Broken response event subscriber
- Same, but for an exception thrown in a response event subscriber, not while rendering a placeholder.
Remaining tasks
Handle broken placeholder renderingHandle broken response event subscriber- Reviews
- Commit
User interface changes
When
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #38 | 2678568-38.patch | 31.29 KB | wim leers |
Comments
Comment #2
wim leersComment #3
wim leersComment #4
wim leersClean-up.
Comment #6
wim leersAs of this reroll, the logic is in place to handle broken response event subscribers.
This issue is now blocked on reviews.
Comment #7
wim leersComment #8
wim leersRerolled now that BigPipe is in core.
Comment #9
fabianx commentedRTBC - looks good to me.
Comment #11
wim leersComment #14
wim leersStraight reroll.
Comment #15
alexpottNeed to test that this is empty before simulating development otherwise throwing this away here might hide errors.
Comment #16
alexpottHmmm this is not the only test that does this (as pointed out by @Wim Leers on IRC)... I guess we just have to live with it atm... see \Drupal\system\Tests\Ajax\FormValuesTest for example. So back to rtbc for further committer review.
Comment #18
wim leersStraight reroll. Conflicted with #2670444: Embedded AJAX responses aren't application/json, but application/vnd.drupal-ajax.
Comment #19
catchThis doesn't look right to me - it's eating the exception entirely when error verbose is off.
Could we either:
1. trigger_error() all the time, that'll go to the UI and/or the log, would also mean less logic since the error handler handles checking config etc.
2. trigger_error() in the else case if we think rethrowing the exception is important otherwise.
If we go for #2 I wonder if there needs to be some kind of helper for that.
Comment #20
wim leersDiscussed with catch to get on the same page. Went with #2, because otherwise debugging is still significantly impaired, which itself already largely goes against what the issue title says.
Writing test coverage to verify that errors do in fact get logged was extremely painful, but I managed to do it.
Comment #21
fabianx commentedBack to RTBC, looks great to me.
Comment #22
catchCommitted/pushed to 8.2.x and cherry-picked to 8.1.x. Thanks!
Comment #24
wim leersAlso committed & pushed to the contrib module for Drupal 8.0: http://drupalcode.org/project/big_pipe.git/commit/50e0fb6
Comment #25
alexpottHow come we didn't inject the config service here? Should we open a follow-up to do this?
Comment #26
wim leers#25: because this is for handling uncaught exceptions. i.e. when something fails: when code is broken.
Injecting this service means it's always initialized, and therefore always slows down this code. We only need that service in case of broken code, so there's no need for that service to be injected always.
Comment #27
alexpottBut the config factory will be initialised.
Comment #28
catchTo be honest my preference would be for trigger_error() here generally, then we don't have to copy what the error handler does, and don't have to inject anything or call out to \Drupal.
Comment #29
wim leersBut not rethrowing the exception will still mean it's painful to debug. The whole point of this issue is to ensure it's equally easy to debug with BigPipe as it is without.
Or am I missing something here?
Comment #30
catchThe trigger_error() will still show the exception in the UI during development, so it's not really much more painful than throwing the exception itself is it?
Comment #32
alexpottUnfortunately this patch introduced a PHP7 fail - probably due to the differences in error handling.
Comment #33
alexpottSee https://www.drupal.org/pift-ci-job/233033 for the fails.
Comment #34
wim leersNo, that doesn't work, because:
trigger_error()only allows forE_USER_*errors to be triggered_drupal_error_handler_real()treats allE_USER_*errors (except if thrown from a__toString()method) as non-fatal errorsTherefore you will only get to see this
trigger_error()-triggered error message on the next page you visit!The consequence of doing #30 is therefore that the DX is worse than it is today in HEAD, and that only UX improves.
Thoughts/ideas?
Comment #35
dawehnerAt least this is logged by default as well, I don't see an issue though:
... this is the only sane configuration for people which can deal with that message.
Comment #36
wim leersTo be clear, this is what AFAICT @catch is asking for in #30, and I explained in #34 why that does not work.
Comment #37
wim leers@dawehner in IRC agrees with me:
I'm +100 for that, but that's a much bigger undertaking. I'd like to see BigPipe's DX fixed before Drupal 8.1.0 is released.
So, in light of #34–#36, I think the most sensible thing for me to do is to figure out why the patch in #20 that was committed and then reverted is failing for PHP7. This is the patch in #20, plus some debug output to figure that out. I suspect PHP7 simply logs things differently.
Comment #38
wim leersSo, #37 clearly shows the difference between PHP 5 and PHP 7. The failed assertionsare:
i.e. the expected strings are:
exception 'Exception' with message 'Oh noes!'exception 'Exception' with message 'You are not allowed to say llamas are not cool!'These expectations are met on PHP 5. But on PHP 7, the resulting strings are:
Exception: Oh noes!Exception: You are not allowed to say llamas are not cool!So, rather than asserting the presence of the exact entire error log string generated in PHP 5, we can just assert on the presence of the custom message. This then is still testing what we care about: that the actual expected exceptions are logged correctly. We don't care about the exact format that PHP 5 or 7 log them in.
The net difference between the patch here and #20 is then (and this is the entire interdiff):
That's it. And just for PHP 7 compatibility. I believe that makes it okay for me to re-RTBC this patch, since the changes are so utterly trivial.
Comment #39
fabianx commentedRTBC + 1
Comment #40
xjm@alexpott, @Cottser, @effulgentsia, and I agreed that this issue is valuable to add before 8.1.0 since it will affect the initial developer experience with BigPipe. Since it is an experimental module, the disruption from this change is still acceptable. Tagging as an RC target.
Comment #41
wim leersTo be more accurate, there is no disruption. It does not break anything. It does not change any APIs.
Also note that 80% of this patch is test coverage.
Comment #42
xjm@Wim Leers, I guess I should have said "any potential for disruption", but it adds a new behavior for an exception being thrown/error being triggered. That type of change does add potential disruption in general (there were some really nasty criticals in D7 related to that kind of a change), and so for a stable module, we would at least be very cautious about committing it for a patch release. As an experimental module, Big Pipe is not subject to such restrictions, so it could go in during 8.1.x anyway, but better to get it in during RC for the initial developer experience with the module.
Comment #43
wim leers+1 — thanks for all the detailed insight into your considerations!
Comment #44
alexpottStill not super keen on not injecting this config, but I think it is better to proceed here as the improvements are tangible. I do think it is better to code for a day when \Drupal and the service location anti pattern is not possible.
Committed 653a363 and pushed to 8.1.x and 8.2.x. Thanks!
Comment #46
wim leersFair enough. Thanks for committing it so that the improvements will definitely be available in 8.1.0. I filed #2702001: Inject config factory for BigPipe (including in exception handlers) to address that.
Comment #47
wim leersAlso committed & pushed to the contrib module for Drupal 8.0: http://drupalcode.org/project/big_pipe.git/commit/07ebd76