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.

CommentFileSizeAuthor
#2 2597520-2.patch793 byteswim leers

Comments

Wim Leers created an issue. See original summary.

wim leers’s picture

Assigned: wim leers » Unassigned
Status: Active » Needs review
StatusFileSize
new793 bytes
znerol’s picture

Please 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 request response was sent should just reopen it again.

Status: Needs review » Needs work

The last submitted patch, 2: 2597520-2.patch, failed testing.

cpj’s picture

Just 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 ?

wim leers’s picture

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 response was sent should just reopen it again.

Oh, interesting! I'll do that then.


So this means that this comment is wrong/misleading?

    /**
     * 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();
wim leers’s picture

Status: Needs work » Closed (works as designed)
znerol’s picture

Re #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.

cpj’s picture

@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.

wim leers’s picture

In 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?

xjm’s picture

Issue tags: -rc target triage