23
votes

I am using Symfony's Custom Session handler. It works fine when I set the session for the first time but when I refresh the page. The page will keep on loading and after 30 seconds which is max_execution_time defined in php.ini, the errors are shown. To setup custom save handler, I created a table sessions in ratchet database. The structure and the data in the table:

Data and Structure of Sessions Table

Now the script, on the top of page looks like this to initialize custom save handler:

<?php 

ini_set('session.auto_start', false);

use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;

    require dirname(__DIR__) . '/vendor/autoload.php';

    $pdo = new \PDO('mysql:host=localhost;dbname=ratchet', 'root', '');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $storage = new NativeSessionStorage(array(), new PdoSessionHandler($pdo)); // Here when PdoSessionHandler class is initialized again on refreshing it locks the page there
    $session = new Session($storage);

    $session->start();

    $username = $session->get('username');

The errors thrown by PHP script for Line 153:

Errors

Line 153 is a statement in the following method which executes and reads a session value from database.

private function doRead($sessionId)
{
    $this->sessionExpired = false;

    if (self::LOCK_ADVISORY === $this->lockMode) {
        $this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
    }

    $selectSql = $this->getSelectSql();
    $selectStmt = $this->pdo->prepare($selectSql);
    $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
    $selectStmt->execute(); // Line 153

Is something broken in the framework's component or am I missing something? I have followed the documentation. I can't find anything that is new or has been added by myself.

Addition

In addition to initializing session in the web app, I initialize symfony session in my server script as well because it is required in order to attach the same session object to each connection. The points to be noted:

  1. The application works fine for the first attempt.
  2. Chat server works as intended. It never throws an error.
  3. When the page in which I connect to the websocket server is refreshed, it remains in the loading state for 30 seconds. Then the errors occur.
  4. Same happens in other browsers. Like it will let the connection establish but when I will refresh ........ See above :D
2
Do you test it on local machine?Yang
@DavidY Yeah, ofc. Why would I put it online on a real server?Muhammad Talha Akbar

2 Answers

7
votes

You mentioned that:

I initialize symfony session in my server script as well

Does this also call new NativeSessionStorage?

What I think is happening is that you are creating essentially two session handlers but PHP only knows about one and thus only closes the database connection / lifts the database lock for one of them. Let me see if I can explain clearly. Every time you instantiate NativeSessionStorage the class registers the sessionHandler (the pdo session handler in your case) with php via session_set_save_handler which you can see in the NativeSessionStorage code here.

When PHP stops execution it calls the PdoSessionHandler->close() method. However since you have two instances and php only has one registered as the session handler, its closing one but not the other. Which keeps the database locked. Based on your description I think this may be your problem. You should be able to easily test this by explicitly calling Session->save() try doing this explicitly in your app and server code to see if you don't get the locked error anymore.

Hope this helps!

5
votes

Is the session closing when the websocket disconnects? If not, it is keeping the lock on the session record and not allowing the incoming request to continue past starting the session. The initial page load works because the websocket picks up the session when the page load is complete.