Problem/Motivation
The BigPipe patch until this issue was unable to deliver the "messages" block — or really, anything that depends on the session. This hugely limits what BigPipe (and other placeholder strategies) can do.
The cause:
the 'messages' block does not play nicely with the session handler in the stage where BigPipe runs
And from IRC:
[2015-09-01 11:23:48] <WimLeers> The messages block works fine with BigPipe? I don't see why it wouldn't.
[2015-09-01 12:19:52] <Fabianx-screen> WimLeers Messges block won't work with BigPipe as the session is shutdown already - somehow - likely bug in session handler.
I just found the root cause of that bug.
The Session middleware does this after it has passed on the request to the HTTP kernel:
if ($type === self::MASTER_REQUEST && $request->hasSession()) {
$request->getSession()->save();
}
Note that this happens before $response->send() happens, and thus before BigPipe streams the response. Which means that any changes to the session (such as messages having been shown and therefore removed from the session's data) that happen while BigPipe is rendering placeholders are lost.
Now, \Symfony\Component\HttpFoundation\Session\SessionInterface::save() has the following documentation:
/**
* Force the session to be saved and closed.
*
* This method is generally not required for real sessions as
* the session will be automatically saved at the end of
* code execution.
*/
public function save();
Note how it says it's not required to be called.
Then if we do some archaeology to figure out why this is being called in the Session middleware, then we find #2229145: Register symfony session components in the DIC and inject the session service into the request object introduced this and at #30.5 it was asked to document why we're only saving the session for the master request, to which the answer was:
Well, I frankly do not know. This is what Symfony does in its SessionListener. Session is as global as something can be in PHP, so there is not much of a point dragging that through the request stack?
Therefore we can simply remove this.
Proposed resolution
Remove the Session::save() call in the Session middleware.
Remaining tasks
Review.
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | 2597520-2.patch | 793 bytes | wim leers |
Comments
Comment #2
wim leersComment #3
znerol commentedPlease note that we must close the session before the response is sent, otherwise lazy session loading and empty session removal does not work. Session cookies cannot be created deleted after headers were sent.
There is no limit on how many times a session can be opened/closed during a request. Anything which requires session data after the initial
requestresponse was sent should just reopen it again.Comment #5
cpj commentedJust to underline what @znerol is saying, it is essential to explicitly close the session for all sorts of reasons. For example, the actual mechanics and sequence of the behind-the-scenes session & cookie processes in PHP are not 100% clear and have not remained fully consistent version to version, and we should not assume that future versions of PHP don't change things again. For that reason, in our pure Symfony work, we close the session as soon as possible in the request processing cycle, and then explicitly re-open/re-close it if we need to.
@Wim Leers, I am not familiar with the latest caching code. Could you point out where it interacts with the session ?
Comment #6
wim leersOh, interesting! I'll do that then.
So this means that this comment is wrong/misleading?
Comment #7
wim leersThanks for the feedback!
Incorporated it: #2469431-152: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts. Closing this issue now :)
Comment #8
znerol commentedRe #6, the comment is correct for Symfony. However, we have features in our session management which are not present in Symfony. For those features it is essential, that we run some session-related code at a point in time when headers still can be sent.
Comment #9
cpj commented@znerol, actually the Symfony comment in #6 is only correct for fairly trivial Symfony session scenarios. Symfony 2 has had several changes to it's session handling (in 2.3 and 2.4 I think) to improve it's robustness, but one still needs to pay close attention to make sure the session is opened & closed correctly if you're using the session for your own variables.
Comment #10
wim leersIn other words: Symfony's comment is highly misleading and only true for the simplest applications, and Drupal definitely qualify as "simple".
Would either of you want to fix that upstream?
Comment #11
xjm