Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Impossible to use lazy sessions #4332

Open
glen-84 opened this Issue · 15 comments

4 participants

@glen-84

I created a lazy session handler (see https://gist.github.com/glen-84/5470169), but it is ineffective, as Zend sessions always store _REQUEST_ACCESS_TIME in the session, even if it is not needed.

Is it possible to make it store this only when required?

Lazy sessions are useful when using a caching HTTP reverse proxy such as Varnish.

@weierophinney

That key is part of how Zend\Session does per session and/or per-namespace expiry; it's kind of difficult to remove. @mwillbanks -- what do you think?

@glen-84

Is it not possible to store it the first time you call setExpirationHops()? I don't know exactly how it all works, but if it can be done in a lazy fashion that would be great.

@mwillbanks
Collaborator

@weierophinney @glen-84

Lazy sessions are semi-possible but you cannot do this from the save handler itself as that is not lazy. _REQUEST_ACCESS_TIME is always required; this is much like how PHP works as well. Session expiration is based on this value as well as container expiration hops and expiration time. The largest issue with this entire area is based on how do you know if the session has changed or not and that you will need to load the session? When you do know you want to start a session you can explicitly call the session manager or do a session type action. Containers will automatically start the session as well.

If you made them lazy the _REQUEST_ACCESS_TIME should not cause you an issue; the issue comes in with things such as HOPS because they require the session or the hops will last x hops when the session is active. I'm not really certain I see the use case here; what are you attempting to save?

@glen-84

@mwillbanks

Did you look at my code? I use almost the same thing with ZF1 and it works just fine as far as I know.

It won't write a session file or set a session cookie if there is no data to store in the session, which allows Varnish to cache the response (since there are no cookies).

@weierophinney

I use almost the same thing with ZF1 and it works just fine as far as I know.

The session component in ZF2 shares absolutely no code with ZF1, @glen-84 ...

@glen-84

@weierophinney

I know, but it still supports things like a flash messenger without this _REQUEST_ACCESS_TIME.

@weierophinney

@glen-84 Again, different architecture, different needs.

@glen-84

@mwillbanks @weierophinney

Is it possible for you to make functionality that depends on sessions (for example flash messages and authentication storage) check for the existence of a session before using it? If there is no session, then it's impossible for there to be any flash messages, or for the user to be authenticated, so there is no reason to start the session in that case (unless I'm forgetting something).

For example, FlashMessenger#hasCurrentMessages() would look like this:

public function hasCurrentMessages()
{
    if ($this->getSessionManager()->sessionExists()) {
        $container = $this->getContainer();
        $namespace = $this->getNamespace();

        return isset($container->{$namespace});
    }

    return false;
}

This is actually a performance optimization as well.

Thoughts?

@weierophinney

@mwillbanks The suggestion seems reasonable; thoughts?

@mwillbanks
Collaborator

@glen-84 @weierophinney i think this is certainly reasonable and perhaps a good addition for 2.3. It does not seem like it would be too difficult to add either. However, I do have quite a few concerns about it:
1. What if the opposite; you expect flash messenger to start the session for you because you put something on it on the previous request?
2. At what point do we throw exceptions vs. silently failing causing people to think it is a bug.
3. Instead to we create the concept of modes?
a. setMode(strict) vs. setMode(lazy) or something to that degree? Strict throws exceptions whereas lazy does not.

@glen-84

@mwillbanks, @weierophinney

Sorry, I wasn't clear, and in fact my code above is flawed – it should be something like this:

public function hasCurrentMessages()
{
    if (isset($_COOKIE[session_name()]) {
        $container = $this->getContainer();
        $namespace = $this->getNamespace();

        return isset($container->{$namespace});
    }

    return false;
}

In other words, if there is no session cookie, a session cannot be continued and therefore you wouldn't be able to access flash messages or authentication storage anyway, so it would be pointless to start the session for this purpose.

Two small discussion points:

  1. This supports cookie-based sessions only, the code would have to be updated to check for URL-based sessions if that is required.
  2. The only "BC break" (if you can call it that), is if someone relies on the fact that usage of such functionality currently causes the session to be started. I would imagine that this is not an issue in most cases, as most developers will either start the session early, or only manipulate the session via containers, in which case the session would again be started automatically.

AbstractContainer could probably also be made to start the session lazily (which would take care of the flash messages and authentication storage indirectly), but this might be more complicated.

@mwillbanks
Collaborator
  1. This will need a bit of investigation; i'm certain that it is possible with already existing functions rather than manually checking the cookie vs. transid

  2. Is really the discussion point that I was raising.

Really it all depends on do we throw an exception or do we silently return. Since checking current messages, there may indeed be a message from previous session. Once a session has been started at all it would always then have to connect to the session.

Now, you might have to fetch it by cookie or by transid (http://us2.php.net/manual/en/session.configuration.php#ini.session.use-trans-sid)

So for instance:

protected hasExistingSession() {
    $name = session_name();
    if (ini_get('session.use_cookies')) {
        return isset($_COOKIE[$name]);
    }
    if (ini_get('session.use_trans_sid')) {
        return isset($_GET[$name]);
    }
    return false;
}

Containers today already automatically will start the session if they are invoked from the following area in the constructor: https://github.com/zendframework/zf2/blob/master/library/Zend/Session/AbstractContainer.php#L78

@mwillbanks
Collaborator

So just to make certain we are on the same page here...
You're saying that once a session has been started for a user it is fine that the session starts for that user whenever something happens with the session correct?

@glen-84

@mwillbanks

Really it all depends on do we throw an exception or do we silently return.

I don't follow – throw an exception when?

Since checking current messages, there may indeed be a message from previous session

Sure, but you have no way of accessing it if the user has no cookie or transid.

Once a session has been started at all it would always then have to connect to the session.

Correct.

Now, you might have to fetch it by cookie or by transid

Sure.

Containers today already automatically will start the session ...

Yes, however, you could theoretically implement lazy sessions from here. i.e. Remove the starting of the session in the constructor, move it into the read/write methods, and only start it within read methods (offsetExists, offsetGet, etc.) if it makes sense to (hasExistingSession). But like I said, this might be more complicated (I don't know enough about it).

You're saying that once a session has been started for a user it is fine that the session starts for that user whenever something happens with the session correct?

Yes, as long as they don't lose their cookie/transid, at which point it is again pointless to start the session.

Quite simply, as long as they have a cookie or transid (and regardless of whether or not it even represents a valid session), the code will continue to start the session and attempt to read the value(s).

This means that as long as a session is not used (no log in, flash messages, etc.) it will not be started unnecessarily and therefore no cookie/transid/session file will be added. This will allow HTTP reverse proxies such as Varnish to easily cache such requests. It will also prevent the creation of a large number of empty session files.

@mwillbanks mwillbanks was assigned
@tfountain

Just wanted to add here that my situation is quite similar to @glen-84 's. I've been trying to port my DB session handler from ZF1 (different architecture, I know, but everything in it works apart from this point). Like glen's, my handler won't store a session if it contains no data. My main use case is to prevent hundreds of empty sessions from being created when a search engine robot crawls a site (which is much more of an issue when using DB sessions than file-based, as empty files take up no space but 'empty' DB rows do).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.