Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[HttpFoundation] MongoDbSessionHandler::read() now checks for valid session age #12516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

bzikarsky
Copy link

Q A
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? no
Fixed tickets -
License MIT
Doc PR -

As discussed with @jmikola:

  • It's a bugfix - expired sessions should never be read
  • Therefore should be merged into 2.3 LTS
  • TODO: Modify the behavior to match the PDO adapter
  • TODO: Add a precise description of the behavior to the manual

-- old description below --

This patch can be seen as either bugfix or feature, though it's probably more on the feature-side.

Currently a session will be returned by read() even if it's already older than gc_maxlifetime. If gc_maxlifetime is used for timing out sessions and a low gc-ratio is in place there is a high chance the session stays alive for a longer time than wanted. The manual describes the current limitations of gc_maxlifetime correctly at http://symfony.com/doc/current/components/http_foundation/session_configuration.html#session-lifetime.

But since the modification timestamp is stored in MongoDB anway, we can actually use it to only return "valid" session-data as does the PdoSessionHandler

@pborreli
Copy link
Contributor

could this addition affect performance ? did you compare with just testing the date after fetching the record ? ping @jmikola

@bzikarsky bzikarsky force-pushed the mongodb-session-safe-read branch from 51a3db1 to 3f6372d Compare November 19, 2014 14:21
@bzikarsky
Copy link
Author

Initially I only did some testing regarding the scanning-behavior: Mongo hits the indexed _id field, and scans all matching documents (1 😄) for the $gt condition.

Since you asked I threw together a quick benchmark: https://gist.github.com/bzikarsky/26706edc07d5e4cceb05

The results are inconclusive. (Ran it on a rather slow single core VPS)

n_sess n_read t_mongo t_php
1000 1000 0.11557793617249s 0.11554098129272s
10000 1000 0.11895108222961s 0.12062788009644s
100000 1000 0.12337493896484s 0.12323498725891s
100000 10000 1.2311668395996s 1.1953091621399s
1000000 100000 13.634443998337s 12.997648954391s

I assume the db tuning makes all the difference between PHP/Mongo. I think it's simpler to push the condition into the query, but if the majority prefers a PHP condition, I'm happy to update the PR accordingly 😄

@pborreli
Copy link
Contributor

nice, thank you very much for this benchmark ! did you ran it against a large session dataset ?

@bzikarsky
Copy link
Author

Well, 1.000.000 artificial sessions with 100.000 reads seemed big enough for me, or do you mean something else?

@pborreli
Copy link
Contributor

no it's great already, it gives us a very good information about how it affects performance.
Let's see others feedback on this PR, thanks @bzikarsky

@bzikarsky bzikarsky changed the title MongoDbSessionHandler::read() now checks for valid session age [HttpFoundation] MongoDbSessionHandler::read() now checks for valid session age Nov 20, 2014
@stof
Copy link
Member

stof commented Nov 28, 2014

@Drak does this makes sense ?

@@ -159,8 +159,11 @@ public function write($sessionId, $data)
*/
public function read($sessionId)
{
$timeoutThreshold = new \MongoDate(time() - (int) ini_get('session.gc_maxlifetime'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there is a bug with the current code where read() may return an expired session (if gc() is not invoked).

If we compare this to PdoSessionHandler, its doRead() method checks the timestamp and lifetime values after selecting the record from the database. If the INI value is changed, I think the correct behavior would be to respect the original lifetime from the session. I suppose it would be updated when we next wrote the session back to the database.

Currently, the MongoDB handler only stores lifetime indirectly in a calculated expiry field (useful for the TTL indexing). To properly fix this, I think we may need to store the lifetime in a new field at all times.

@jmikola
Copy link
Contributor

jmikola commented Nov 29, 2014

@Drak: Speaking to @bzikarsky in IRC, we noted that the documentation doesn't really discuss how sessions are timed out. Perhaps this is only an issue for the database handlers, where this kind of expiration logic is implemented in PHP (in contrast to Memcache using its own TTL, or the filesystem handler). Any thoughts on that?

@bzikarsky
Copy link
Author

That would break BC for configurations relying on TTL indices though. In the gc-method we have a check that tests for !== false and skips garbage-collection for truthy values.

a) We remove the gc-optimization. This only introduces an additional query for people with TTL indices every $gcProbability requests and can be avoided by setting gc-probability to 0. (My preference)

b) We introduce another option for TTL-indices (actual BC break)

c) We use a separate option for the new expiry-field, but this gets messy really fast (I can't even think of a good name for this option...)

PR is now at an implementation of a). I moved all inline documentation regarding the TTL collections to the constructor-docblock. We probably should update the documentation with some of the insights.

@bzikarsky bzikarsky force-pushed the mongodb-session-safe-read branch from 52cb779 to a3fbf34 Compare November 29, 2014 17:19
@@ -127,24 +132,14 @@ public function gc($maxlifetime)
*/
public function write($sessionId, $data)
{
$expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime'));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should try to inject time() also. correct me if i am wrong, but this assumes that your webserver and your database server are sharing the same timezome?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been this way. I think this should be a separate issue?

Edit: MongoDate is not timezone aware anyway. So this is a non-issue. It's always based on PHP's current default-timezone. It's the driver's responsibility to "translate"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction, it's based on the timezone of PHP's execution context, see: https://gist.github.com/bzikarsky/c309a55fefbaf9bdaa5d

Anyhow: ext-mongo uses the system's timezone to create correct Isodate-instances in the database. The same is valid for reading.

Ignoring PHP's timezone is probably a bug. But that is located in ext-mongo.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i looked at the other session handlers, this issue is related to most of them. to solve this issue symfony should provide a service that is able to return date / time informations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring PHP's timezone is probably a bug. But that is located in ext-mongo.

I would disagree. The BSON date type is specified as:

UTC datetime - The int64 is UTC milliseconds since the Unix epoch.

In your gist, strtotime() happens to respect PHP's timezone, as does date('c') when you print out the dates later; however, when printing the date on the way out of the database, you're ignoring the original timezone from the tz field. PHP will simply use the UNIX timestamp from the MongoDate and render it in the default timezone. This is why the time offsets of the three records are still staggered (0, +2, and +1 respectively). Does that make sense?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to avoid any more confusion: I'm fine with the output of my exmaple-script. This is what I expected. My comment about there being a bug in ext-mongo was related to me falling back to the usage of TZ=/usr/share/zoneinfo.... But I must have tricked myself somewhere.

I was under the impression that ext-mongo converts the UNIX timestamps to Isodates (in UTC) based on the TZ process context, and ignores PHP's internal date.timezone. That's the reason why I went for a bash-script instead of a PHP script in the beginning.

But I ran a 2nd test today morning, and this time it worked flawlessly. Strange. Modified Gist at https://gist.github.com/bzikarsky/4044d1fa37e32e5c6996

Anyhow I think everything is fine - no bugs anywhere to be seen. 😉

@bzikarsky
Copy link
Author

How do we continue from here on? With the current solution I propose several changes to the manual:

  • Clarify behavior for "timed out" (older than gc_maxlifetime) session on read - maybe push a strict "Session older than gc_maxlifetime are never read"-phrase; we need to check the other session ahndlers before though
  • Clarify session lifetime behavior regarding changes to gc_maxlifetime when sessions already exist:
    • Sessions are updated every request with the new gc.max_lifetime
    • Idle sessions (as in: inactive) are not updated to the new lifetime, and times out according to the old lifetime
  • Add a section to the MongoSessionHandler-documentation about proper index-configuration with and without TTL indices.

(And I don't know how to handle the test-failures which are unrelated to my changes - do I rebase on 2.7 when those are fixed?)

@jmikola
Copy link
Contributor

jmikola commented Dec 1, 2014

Add a section to the MongoSessionHandler-documentation about proper index-configuration with and without TTL indices.

AFAIK, the documentation doesn't have specific pages on these session hander implementations. They are only mentioned in passing on the Session Configuration page within the HttpFoundation chapter of the book.

It looks like we could easily create a new documentation page. Instead of linking to the API documentation for each provider, we could create new pages as needed. MongoDB's might be the first.

@fabpot
Copy link
Member

fabpot commented Jan 3, 2015

What's the status of this PR?

@bzikarsky
Copy link
Author

It's idle. I was waiting on feedback for the proposed manual changes - especially regarding the "Sessions older than gc_maxlifetime are never read"-stance - and then forgot about it.

If the general direction of this PR and the proposed documentation-changes is approved, I 'll prepare a PR on documentation and check behaviour of the other session-handlers.

@jmikola
Copy link
Contributor

jmikola commented Jan 7, 2015

@fabpot, @bzikarsky: I concur that this is just waiting on a documentation PR. I concur with the documentation todo items in this comment. The generic topics about "timed out" sessions being read and session lifetimes being updated on each request can appear within Session Configuration.

Personally, I'm not sure why Session Cookie Lifetime and Session Lifetime are split (Configuring Garbage Collection appears between them). Both sections talk about session cookies. I do think that Session Lifetime is the better place to discuss the topics relevant to this PR.

As for documenting suggested MongoDB indexes, that does belong on a new page, which should be linked to from Custom Save Handlers.

@fabpot
Copy link
Member

fabpot commented Feb 5, 2015

I'm going to merge it as is then.

@bzikarsky Can you submit a PR for the docs?

@fabpot
Copy link
Member

fabpot commented Feb 5, 2015

I've just noticed that this is a bug fix that needs to be merged in 2.3. Can you submit a PR for that branch?

bzikarsky pushed a commit to bzikarsky/symfony that referenced this pull request Mar 12, 2015
Initial PR was on 2.7:

Conflicts:
	src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php
	src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
@bzikarsky
Copy link
Author

I'll close this in favour for the new PR (based on 2.3) - #13911

@bzikarsky bzikarsky closed this Mar 12, 2015
fabpot added a commit that referenced this pull request Mar 12, 2015
…for valid session age (bzikarsky)

This PR was squashed before being merged into the 2.3 branch (closes #13911).

Discussion
----------

[HttpFoundation] MongoDbSessionHandler::read() now checks for valid session age

This PR is a follow-up to #12516 and replaces the old one.

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | no
| Fixed tickets | -
| License       | MIT
| Doc PR        | TODO

As discussed there: Sessions which are older than GC age should never be read.
This PR adds the expiry-datetime on session-write and changes session-read and session-gc accordingly.

We still need to update the documentation with some clarifications, as described here:
- #12516 (comment)
- #12516 (comment)

My experience with the Symfony Docs from a developer perspective is very limited, so help would be very appreciated.

Commits
-------

8289ec3 [HttpFoundation] MongoDbSessionHandler::read() now checks for valid session age
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.