[PHP-DEV] Improving PDO persistent connections and event loop support

Hi. I would like to offer three changes to PDO module.

  1. Cleanup persistent connection before passing it to next request

Documentation on persistent connections [1] says:

Warning
PDO does not perform any cleanup of persistent connections. Temporary tables, locks, transactions and other stateful changes may remain from previous usage of the connection, causing unexpected problems.

This makes persistent connections effectively unusable. I don’t want to obtain dirty connection from previous request. In contrast, mysqli extension do automatic cleanup [2]:

The persistent connection of the mysqli extension however provides built-in cleanup handling code.

Adding cleanup code to PDO will align persisting connection behavior with mysqli and make persistent connections safe to use. This is breaking change.

  1. Add new method to PDO class - reset()

This method will reset connection state. For mysql it will call C-API function mysql_change_user() like mysqli do. Most value this method would bring to event loop environments (more on this below), but also can be non breaking alternative to my first proposal - I just call this method on the very beginning of my script and proceed without fear, that everything may break due to garbage state from previous worker request.

After calling proposed refresh() method: all still alive PDOStatement invalidated, logical db connection refreshed (e.g. mysql_change_user C-API called), underlying TCP/unix-socket/file-pointer connection stay intact.

  1. Add new method to PDO class - close()

PDO does not have method for closing connection. Documentation [1] says:

To close the connection, you need to destroy the object by … assigning null to the variable that holds the object.

Explicit close is not necessary for fpm and mod_php environments, because connection automatically closed at the end of script execution. But in event loops this may be complicated because of internal references to PDO object from PDOStatement objects [1]:

Note: If there are still other references to this PDO instance (such as from a PDOStatement instance, or from other variables referencing the same PDO instance), these have to be removed also (for instance, by assigning null to the variable that references the PDOStatement).

After calling proposed close() method: all still alive PDOStatement objects invalidated, logical db connection closed (e.g. mysql_close C-API called), underlyind TCP/unix-socket/file-pointer connection closed.

Event loop considerations

Event loop based applications (e.g. roadrunner, frankenphp) are main intended users of reset() and close() methods. Event loop may look like:

// Example pseudocode
$ev = setupEventLoopEnvironment();
$app = setupApplication();

while ($request = $ev->waitForRequest()) {
$db = new PDO('dsn', 'user', 'password');
$app->setDb($db);
$app->handle($request);
$db = null;
$app->setDb(null);
}

Such approach may lead to db connection leak because application may unintentionally cache PDO or PDOStatement object that would prevent connection from closing. However, this may be handled by hiding PDO objects behind abstraction, provided by event loop framework. But performance impact cannot be avoided.

More safe and performant variant using reset():

$ev = setupEventLoopEnvironment();
$db = new PDO('dsn', 'user', 'password');
$app = setupApplication();
$app->setDb($db);

while ($request = $ev->waitForRequest()) {
$db->reset();
$app->handle($request);
}

On this mail I focused on mysql. If the community is interested by the proposed changes, I’ll investigate other supported db drivers as well and write RFC.

[1] https://www.php.net/manual/en/pdo.connections.php
[2] https://www.php.net/manual/en/mysqli.persistconns.php

Hi,

1. Automatic cleanup is something that we could add to PDO, at least
for MySQL. I am not sure if other drivers have reset methods. The
cleanup should probably happen when requesting the connection again.

But there is a reason why mysqli allows for this feature to be
switched off in the compilation options. Certain users do not like
this. It is silently discarding errors and data, which could be
disastrous in some situations. If such a feature is added to PDO, it
should be opt-in, so that users aren't automatically enrolled in a
dangerous feature.

2. A reset method sounds really reasonable, even if it is only a
driver-specific method for MySQL. It should probably use
mysql_reset_connection(). If this method is added, then we don't need
to bother adding automatic cleanup, as this would be a far better
option.

PDO does not have method for closing connection.

As I said in an earlier email, this is intentional. Such a method is a bad idea.

But in event loops this may be complicated because of internal references to PDO object from PDOStatement objects

And that's very good. As long as any PDOStatement or PDO objects refer
to the connection, it should NOT be closed. Giving users such an
option would only result in creating crippled objects for no apparent
benefit. I stand by what I said before: in a well-designed
application, there is absolutely no reason to close the connection
explicitly.

Regards,
Kamil