Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Duplicate Session ID (PDOSessionHandler) #8448

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
jeremylivingston opened this issue Jul 8, 2013 · 31 comments
Closed

Duplicate Session ID (PDOSessionHandler) #8448

jeremylivingston opened this issue Jul 8, 2013 · 31 comments

Comments

@jeremylivingston
Copy link
Contributor

Since upgrading to 2.3, I am now receiving occasional errors where a session record insert is failing because of a duplicate primary key. I have narrowed down the stack trace to the PDOSessionHandler's read() function.

It seems that the session ID doesn't exist at the initial SELECT statement, but does exist by the time the INSERT is attempted. This isn't happening with all users -- only a select few. Could this be because of simultaneous requests being submitted?

I can't seem to figure out any explanation for how this could happen. It seems that the record is being created between the SELECT and INSERT statements. Perhaps this issue always existed, but the addition of the PDO exceptions is now exposing it.

Any suggestions or information is appreciated. Thank you!

@JonathanGuo
Copy link

Yes I have met same issue now.

I have tried to output the count number of the key before insert key into session database, the result is 0 means the session id should be available. But just right after that, the bug occurs. When you checked the database, the record has been inserted. I am not sure if there any chance the "createNewSession" function may be double invoked.

File: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler

@ppamment
Copy link

ppamment commented Oct 7, 2013

I too am experiencing this issue (using Symfony 2.3). I have not yet figured out how to reproduce it, but here is the stack trace:

PDOException was thrown when trying to read the session data: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'sj24dkjhi371g7utplgk2j7696' for key 'PRIMARY'

0 - RuntimeException
Information:

Generated at: 07-10-2013 06:39:52
Class name: RuntimeException
Message: PDOException was thrown when trying to read the session data: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'sj24dkjhi371g7utplgk2j7696' for key 'PRIMARY'
Uri: http://eventstagr.am/play/ira-ellyn-s-big-epic-wedding
Stack Trace

at in /var/www/eventstagram/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php at line 154
return '';
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
at Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler ->read 'sj24dkjhi371g7utplgk2j7696'
in kernel.root_dir/cache/prod/classes.php at line 418
}
public function read($id)
{
return (string) $this->handler->read($id);
}
public function write($id, $data)
{
at Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy ->read 'sj24dkjhi371g7utplgk2j7696'
at session_start
in kernel.root_dir/cache/prod/classes.php at line 107
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
}
if (!session_start()) {
throw new \RuntimeException('Failed to start the session');
}
$this->loadSession();
at Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage ->start
in kernel.root_dir/cache/prod/classes.php at line 184
if ($this->saveHandler->isActive() && !$this->started) {
$this->loadSession();
} elseif (!$this->started) {
$this->start();
}
return $this->bags[$name];
}
at Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage ->getBag 'attributes'
in kernel.root_dir/cache/prod/classes.php at line 494
}
public function get($name, $default = null)
{
return $this->storage->getBag($this->attributeName)->get($name, $default);
}
public function set($name, $value)
{
at Symfony\Component\HttpFoundation\Session\Session ->get '_security_public'
in /var/www/eventstagram/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ContextListener.php at line 77
$request = $event->getRequest();
$session = $request->hasPreviousSession() ? $request->getSession() : null;
if (null === $session || null === $token = $session->get('security'.$this->contextKey)) {
$this->context->setToken(null);
return;

@kbond
Copy link
Member

kbond commented Nov 8, 2013

I can also confirm this issue.

@jenkoian
Copy link
Contributor

We too are experiencing this issue, the only additional information I could possibly offer is that the page where we are seeing this has a number of ESI requests on. Not sure if that helps at all.

@nmcilree
Copy link

Just for the record I am getting the same issue. I have found that periodically clearing down the database seem to reduce the number of occurrences, but obviously this is not a particularly good solution. Well actually not a solution at all - but hopefully someone can shed some light on it as I do not want ot be messing around with Symfony internals.

@nmcilree
Copy link

I may be way off mark but could it have something to do with the environment being used i.e. multiple processes calling session ids from the same source . . . I am using nginx with php-fpm.

@jeremylivingston
Copy link
Contributor Author

Using Apache when I encounter the issue.

@netmikey
Copy link

Same problem here. We're using lighttpd / php / fastcgi.

@nmcilree
Copy link

What database are you using - we are using Postgres, was wondering if this was not happening in MySQL as the MySQL session handler uses insert on duplicate key update rather that a separate insert statement. We have been unable to get any progress on this issue and are moving to memcache to store session data.

@richsage
Copy link
Contributor

We're using MySQL here, and we're experiencing the issue (Symfony 2.3). Nginx and php-fpm, 2 x webservers behind LB. It's extremely intermittent however.

@nmcilree
Copy link

Could the common factor be php-fpm?

@jeremylivingston
Copy link
Contributor Author

This issue still occurred for me when I wasn't using php-fpm.

@nmcilree
Copy link

Then I am stumped - it surely can't be affecting many users or no one would use Symfony - but I just can't seem to track down why it is happening. What versions of Linux are people using?

@jeremylivingston
Copy link
Contributor Author

The issue is intermittent for me. I see errors only every few days on a relatively high traffic site. Using CentOS 6.4 in this particular case.

@netmikey
Copy link

I agree with @jeremylivingston: it happens rarely, but it happens. I use to stalk the logfiles for a couple of days when I make bigger system changes ;) I noticed it a couple of times now. I guess people who don't actively monitor their logs will not even notice it.

We're on Debian Squeeze here 2x lighttpd with DNS-RR.

How about stuff like remember-me (which we're using)? In addition to regular Username/Password login, we also have a custom facebook authentication mechanism (if that's of any help...).

@nmcilree
Copy link

My system only uses built in Symfony authentication - and we are on Wheezy. It is a also intermittent, though regular enough to be of concern to me.

@kellewic
Copy link

I had to just disable it since it was suddenly preventing users from logging in or out due to duplicate keys. I could reproduce it every time upon logout and then it started happening every time on login. We have two web servers pushing to one MySQL server. It wasn't happening yesterday, but today more users are coming in and it seems the problem gets worse. Using Symfony 2.3.9.

@ruudk
Copy link
Contributor

ruudk commented Apr 7, 2014

Just enabled it on our website and am seeing the same errors too.

@Tobion
Copy link
Contributor

Tobion commented Apr 7, 2014

Just looking at the code, https://github.com/symfony/symfony/blob/2.2/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php#L221 is not written for high concurrency. Writing database queries for high concurrency seems to be a common problem.
When I got some time I can fix it.

But this does not seem to be a problem introduced in symfony 2.3. It was already there before from what I can see.

@nmcilree
Copy link

nmcilree commented Apr 8, 2014

Hi Tobion, does this mean that Symfony is currently not suitable for database session storage in high concurrency environments? We are currently in the process of switching our site to using memcache for session storage in response to this problem. However, if the Symfony projects wants to position itself as a framework suitable for large scale production do you think that perhaps this issue needs to be flagged as priority.

As an afterthought this problem seems to be happening mainly at login, could the problem relate to this rather than general session handling?

@jeremylivingston
Copy link
Contributor Author

For what it's worth, I had no problems with this issue prior to Symfony 2.3.

I would highly advise against using Memcache for session storage. Memcache is built to purge records when it reaches capacity. Restarting an instance would also cause you to lose all of its session data. I use Memcache only as a convenience mechanism, not as a persistence layer.

@mvrhov
Copy link

mvrhov commented Apr 8, 2014

IMO it's better to use Redis instead. It will auto purge the expired sessions.

@nmcilree
Copy link

nmcilree commented Apr 8, 2014

Thanks Jeremy and Miha - I will take your suggestions on board.

@ChristianRiesen
Copy link

Session is temporary persistence, if you require more permanent stuff, you should attach it db wise to your user record (which should already be unique). Moving on: Using DB for this is not the best use case. An alternative is redis.io for session storage which comes with clustering out of the box and even some permanence to the storage if need be.

DB and high concurrency is always tricky to sy the least. Tobion and I have very personal experiences with it :)

@kellewic
Copy link

kellewic commented Apr 8, 2014

For me, it's not a matter of permanence, it's a matter of sharing between multiple web servers.

The issue as near as I can tell is that my setup doesn't stick a user to one web server for all their requests and one user transaction is typically numerous requests. This means when the PDO read happens, it sees no session and creates it, but at the same time another part of the login occurred on a different server and sees no session and tries to create it... in the meantime the first attempt succeeds and the last attempt throws a PRIMARY KEY error.

I tested this on my staging servers by setting the load balancer to stick a user to one server (based on remote IP) and no errors occur. In production, it is set up more round robin so I see the errors quite often.

I'm not sure how easily something like that could be fixed in PDOSession itself, and it probably shouldn't be; it was an architectural decision on my part to allow better use of my server's resources.

I suspect the same thing would happen with memcache or redis, but maybe they don't care about uniqueness like a database does. I'm not really familiar with either except I believe they are basically a key/value store of data and typically just overwrite whatever was in the key before.

Will have to check one of them out; I just hope I don't need to set up 2 more servers to handle it and can just run them on the web servers (cost issue).

@ChristianRiesen
Copy link

Redis does exactly that for you, even if you use multiple redis instances. And yes they are "dumber" but that's essentially what you want in a session storage.

The pain you are feeling is a problem I come across a lot in many different places. Using DB as session storage is a stop gap measure at most, and can be reasonably ok with the right configuration (sticky sessions like you describe in the staging setup). Ultimatevely it's the wrong tool for the job. If you do anything else with your DB, then you just put load on it for sessions, making other operations slow in the process. I had a case where an app's DB load was 75% just from sessions...

Either you setup sticky sessions and dedicated DB servers (can't use the memory storage though, that will fall through quickly, look up limits of storage there), or you go with something better. I suggest Redis because session storage was one of the base concepts when it was created (unlike memcached) and I have made a lot of positive experiences with it.

@jeremylivingston
Copy link
Contributor Author

+1 for pursuing Redis for session storage. I will be going this route for future projects.

@Tobion
Copy link
Contributor

Tobion commented Apr 8, 2014

Btw, I'm working on a fix for this issue.

@Tobion
Copy link
Contributor

Tobion commented Apr 8, 2014

See #10652. Would be good if you can test it.

@vitorbrandao
Copy link

Having this issue even with a low traffic site, so rolled out a DoctrineSessionHandler using a try {} catch {} for inserts/updates and it is working fine. Might submit a PR if you guys find this useful.

@kellewic
Copy link

Initial testing of #10652 seems promising; haven't noticed any errors yet, but the day is still young :)

Will let you know.

fabpot added a commit that referenced this issue Apr 11, 2014
…rency (Tobion)

This PR was merged into the 2.3 branch.

Discussion
----------

[HttpFoundation] fix PDO session handler under high concurrency

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #8448 and http://trac.symfony-project.org/ticket/4777 (which was never really fixed as you can see here)
| License       | MIT

- The first commit fixes PDO session handler under high concurrency.
- The second commit uses MERGE SQL for MS SQL Server. Tested with http://sqlfiddle.com/#!6/66b6d/14
- The third commit uses INSERT OR REPLACE for sqlite session handler http://sqlfiddle.com/#!7/e6707/3

What I find rather bad with the class design is that it depends on the table definition, but it's not part of the class. Also it doesn't make use of open() and close() which could be used to make the database connection lazy instead of having is open all the time when not needed. Doctrine also only lazy connects, but we use PDO directly here.
Furthermore, the session handlers should not throw exceptions, from what I read, but return false when an error occurs. This is not followed in this class. Maybe @Drak knows how php session management behaves when the session handlers return false?

Commits
-------

5c08e29 [HttpFoundation] use insert or replace for sqlite session handler
05ea19a [HttpFoundation] use MERGE SQL for MS SQL Server session storage
e58d7cf [HttpFoundation] fix PDO session handler under high concurrency
@fabpot fabpot closed this as completed Apr 11, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests