Skip to content
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

Adding details about the changes to the PdoSessionHandler in 2.6 #4609

Merged
merged 3 commits into from
Dec 29, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 104 additions & 47 deletions cookbook/configuration/pdo_session_storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
How to Use PdoSessionHandler to Store Sessions in the Database
==============================================================

.. caution::

There was a backwards-compatibility break in Symfony 2.6: the database
schema changed slightly. See :ref:`Symfony 2.6 Changes <pdo-session-handle-26-changes>`
for details.

The default Symfony session storage writes the session information to
file(s). Most medium to large websites use a database to store the session
values instead of files, because databases are easier to use and scale in a
Expand All @@ -24,18 +30,11 @@ configuration format of your choice):
# ...
handler_id: session.handler.pdo

parameters:
pdo.db_options:
db_table: session
db_id_col: session_id
db_data_col: session_data
db_time_col: session_time
db_lifetime_col: session_lifetime

services:
pdo:
class: PDO
arguments:
# see below for how to use your existing DB config
dsn: "mysql:dbname=mydatabase"
user: myuser
password: mypassword
Expand All @@ -44,7 +43,7 @@ configuration format of your choice):

session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
arguments: ["@pdo", "%pdo.db_options%"]
arguments: ["@pdo"]

.. code-block:: xml

Expand All @@ -53,16 +52,6 @@ configuration format of your choice):
<framework:session handler-id="session.handler.pdo" cookie-lifetime="3600" auto-start="true"/>
</framework:config>

<parameters>
<parameter key="pdo.db_options" type="collection">
<parameter key="db_table">session</parameter>
<parameter key="db_id_col">session_id</parameter>
<parameter key="db_data_col">session_data</parameter>
<parameter key="db_time_col">session_time</parameter>
<parameter key="db_lifetime_col">session_lifetime</parameter>
</parameter>
</parameters>

<services>
<service id="pdo" class="PDO">
<argument>mysql:dbname=mydatabase</argument>
Expand All @@ -76,7 +65,6 @@ configuration format of your choice):

<service id="session.handler.pdo" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler">
<argument type="service" id="pdo" />
<argument>%pdo.db_options%</argument>
</service>
</services>

Expand All @@ -94,14 +82,6 @@ configuration format of your choice):
),
));

$container->setParameter('pdo.db_options', array(
'db_table' => 'session',
'db_id_col' => 'session_id',
'db_data_col' => 'session_data',
'db_time_col' => 'session_time',
'db_lifetime_col' => 'session_lifetime',
));

$pdoDefinition = new Definition('PDO', array(
'mysql:dbname=mydatabase',
'myuser',
Expand All @@ -112,15 +92,74 @@ configuration format of your choice):

$storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array(
new Reference('pdo'),
'%pdo.db_options%',
));
$container->setDefinition('session.handler.pdo', $storageDefinition);

* ``db_table``: The name of the session table in your database
* ``db_id_col``: The name of the id column in your session table (VARCHAR(128))
* ``db_data_col``: The name of the value column in your session table (BLOB)
* ``db_time_col``: The name of the time column in your session table (INTEGER)
* ``db_lifetime_col``: The name of the lifetime column in your session table (INTEGER)
Configuring the Table and Column Names
--------------------------------------

This will expect a ``sessions`` table with a number of different columns.
The table name, and all of the column names, can be configured by passing
a second array argument to ``PdoSessionHandler``:

.. configuration-block::

.. code-block:: yaml

# app/config/config.yml
services:
# ...
session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
arguments:
- "@pdo"
Copy link
Contributor

Choose a reason for hiding this comment

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

would prefer
- @pdo
instead of
- "@pdo"

Copy link
Member

Choose a reason for hiding this comment

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

Using the @ character in an unquoted string breaks the YAML syntax highlighting in the docs.

Copy link
Member

Choose a reason for hiding this comment

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

to be more precize, as the first character in an unquoted string (since that was a reserved character in Yaml 1.0)

- { 'db_table': 'sessions'}

.. code-block:: xml

<!-- app/config/config.xml -->
<services>
<service id="session.handler.pdo"
class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler">
<argument type="service" id="pdo" />
<argument type="collection">
<argument key="db_table">sessions</argument>
</argument>
</service>
</services>

.. code-block:: php

// app/config/config.php

use Symfony\Component\DependencyInjection\Definition;
// ...

Copy link
Member

Choose a reason for hiding this comment

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

this empty line should be placed between the 2 comment line

$storageDefinition = new Definition(
'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler',
array(
new Reference('pdo'),
array('db_table' => 'session')
)
);
$container->setDefinition('session.handler.pdo', $storageDefinition);

.. versionadded:: 2.6
The ``db_lifetime_col`` was introduced in Symfony 2.6. Prior to 2.6,
this column did not exist.

The following things can be configured:

* ``db_table``: (default ``session``) The name of the session table in your
database;
* ``db_id_col``: (default ``sess_id``) The name of the id column in your
session table (VARCHAR(128));
* ``db_data_col``: (default ``sess_data``) The name of the value column in
your session table (BLOB);
* ``db_time_col``: (default ``sess_time``) The name of the time column in
your session table (INTEGER);
* ``db_lifetime_col``: (default ``sess_lifetime``) The name of the lifetime
column in your session table (INTEGER).
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we move this block above the code? If I didn't want to change the default values, I would probably not be interested in how that would be done.

Copy link
Member

Choose a reason for hiding this comment

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

this block some become a definition list


Sharing your Database Connection Information
Copy link
Member

Choose a reason for hiding this comment

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

The default lock_mode is LOCK_TRANSACTIONAL which, according to the API doc should not be used with the same database as your app. Should we make a note about this or am I misunderstanding?

Copy link
Member Author

Choose a reason for hiding this comment

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

I see that now. Is this what was causing the issue you and I talked about? Or just something you noticed when looking into it?

But yes, we should have a note on this, and the locking strategy in general. Can you start a PR with that?

Copy link
Member

Choose a reason for hiding this comment

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

I think this was actually a misunderstanding on my part, see: symfony/symfony#13411

I think what that means is I can't use the PDO service I created anywhere else in my app.

One thing @Tobion mentioned is I should be using lazy connecting to the db: pass the connection params directly to the PdoSessionHandler instead of a PDO service.

What do you think, want me to make a PR for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Boom, love it:

A) Recommend that people use a separate PDO connection (and not to use that connection from in their app)
B) Change things to pass in the credentials directly to PdoSessionHandler (which will take care of the first part, really).

Thanks - these will be good improvements!

--------------------------------------------
Expand Down Expand Up @@ -163,6 +202,24 @@ of your project's data, you can use the connection settings from the
Example SQL Statements
----------------------

.. _pdo-session-handle-26-changes:

.. sidebar:: Schema Changes needed when Upgrading to Symfony 2.6

If you use the ``PdoSessionHandler`` prior to Symfony 2.6 and upgrade, you'll
need to make a few changes to your session table:

* A new session lifetime (``sess_lifetime`` by default) integer column
needs to be added;
* The data column (``sess_data`` by default) needs to be changed to a
BLOG type.

Check the SQL statements below for more details.
Copy link
Contributor

Choose a reason for hiding this comment

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

couldn't we provide some kind of update sql script?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we could in theory, but it would take a bit of work (reading the config) + reading the DB engine. And it would be a little risky, since an upgrade script implies that it'll work (I could see people running it and expecting with 100% faith that it'll work, when maybe they've made some additional session changes). It's an unfortunate BC break, so I would like to make this change as obvious and easy as possible... but not too easy :)


To keep the old (2.5 and earlier) functionality, change your class name
to use ``LegacyPdoSessionHandler`` instead of ``PdoSessionHandler`` (the
legacy class was added in Symfony 2.6.2).

MySQL
~~~~~

Expand All @@ -172,10 +229,10 @@ following (MySQL):
.. code-block:: sql

CREATE TABLE `session` (
`session_id` VARBINARY(128) NOT NULL PRIMARY KEY,
`session_data` BLOB NOT NULL,
`session_time` INTEGER UNSIGNED NOT NULL,
`session_lifetime` MEDIUMINT NOT NULL
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
`sess_data` BLOB NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

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

I just had an issue where BLOB wasn't large enough for my needs. When the size is exceeded, the user is logged out without warning...

I had to change to MEDIUMBLOB. Should we make a note about this?

Copy link
Contributor

Choose a reason for hiding this comment

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

👎 we should notice that huge sessions are bad by design.

Copy link
Member

Choose a reason for hiding this comment

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

It took me quite some time to find the cause of the auto-logout as there is no error. Good or bad design, it still should be noted imo.

Copy link
Member

Choose a reason for hiding this comment

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

What about adding this small note?

.. note::

    A ``BLOB`` column type can store up to 65,535 bytes maximum, which should be
    enough for well-designed applications. However, if you need to store more
    information, consider switching this type to ``MEDIUMBLOB`` for storing up
    to 16,777,215 bytes.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't this to specific to document?

Anyway, I find the proposed text of @javiereguiluz way to long for something as minor and not that related as this. If we want to add something (I'm not sure yet), I would reduce it to just something as "If BLOB can't store large enough data for you, consider user the MEDIUMBLOB type."

Copy link
Member

Choose a reason for hiding this comment

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

I think something should be said about what happens if the data limit is exceeded: the session is reset without warning or error...

Copy link
Member Author

Choose a reason for hiding this comment

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

.. note::

    A ``BLOB`` column type can only store up to 64 kb. If the data stored in
    a user's session exceeds this, the user will be logged out. Consider using
    a ``MEDIUMBLOB`` if you need more space.

Copy link
Member Author

Choose a reason for hiding this comment

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

@kbond if you're making a PR for the other change, maybe you can add this too? Thanks for your help on this.

Copy link
Member

Choose a reason for hiding this comment

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

Sure, I will make a PR. Instead of the user will be logged out. what about the session will be reset.. The user might not be logged in...

Copy link
Member

Choose a reason for hiding this comment

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

I think something should be said about what happens if the data limit is exceeded: the session is reset without warning or error...

This depends of the configuration of your database server actually. If you run MySQL in strict SQL compliance mode, it will return an error when saving the session instead of silently accepting the invalid value and dropping it

`sess_time` INTEGER UNSIGNED NOT NULL,
`sess_lifetime` MEDIUMINT NOT NULL
) COLLATE utf8_bin, ENGINE = InnoDB;

PostgreSQL
Expand All @@ -186,10 +243,10 @@ For PostgreSQL, the statement should look like this:
.. code-block:: sql

CREATE TABLE session (
session_id VARCHAR(128) NOT NULL PRIMARY KEY,
session_data BYTEA NOT NULL,
session_time INTEGER NOT NULL,
session_lifetime INTEGER NOT NULL
sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
sess_data BYTEA NOT NULL,
sess_time INTEGER NOT NULL,
sess_lifetime INTEGER NOT NULL
);

Microsoft SQL Server
Expand All @@ -200,12 +257,12 @@ For MSSQL, the statement might look like the following:
.. code-block:: sql

CREATE TABLE [dbo].[session](
[session_id] [nvarchar](255) NOT NULL,
[session_data] [ntext] NOT NULL,
[session_time] [int] NOT NULL,
[session_lifetime] [int] NOT NULL,
[sess_id] [nvarchar](255) NOT NULL,
[sess_data] [ntext] NOT NULL,
[sess_time] [int] NOT NULL,
[sess_lifetime] [int] NOT NULL,
PRIMARY KEY CLUSTERED(
[session_id] ASC
[sess_id] ASC
) WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
Expand Down