Improving PDO persistent connections and event loop support

php.internals

Unnamed Person

237 days ago
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. 2. 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. 3. 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: ```php // 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()`: ```php $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

Kamil Tekiela

236 days ago
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

Unnamed Person

225 days ago
> 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.
I'm pretty sure this option introduced not by user demand. But because maintainers thought it may be useful. This should be opt in. Because backward compatibility, not because someone disliked it. Can't imagine situations where obtaining dirty connect from previous script execution would be useful. This may be implemented using pdo option: ```php $db = new PDO( 'dsn', 'user', 'password', [ PDO::ATTR_PERSISTENT => true PDO::ATTR_AUTO_RESET_PERSISTENT => true, ] ); ```
> It is silently discarding errors and data, which could be > disastrous in some situations.
That's exactly what I want - not bothering about errors and data left from previous script.
>> 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.
We have mysqli::close and this is ok. But PDO::close is a bad idea? I don't understand.
> I stand by what I said before: in a well-designed > application, there is absolutely no reason to close the connection > explicitly.
Creating well-designed application is hard. As a creator of event loop framework I can't be sure that every user of my framework is a superstar in programming and application design. So I'd prefer to leave my users with "crippled objects" that would fail-fast in the next loop iteration instead of invisible db connection leak. This is also very suitable for True Async RFC [1]. In that case if you want send queries in parallel coroutines, then db connection pool should be used. And for connection pool implementations it's mandatory to have possibility to close db connection explicilty. [1] https://wiki.php.net/rfc/true_async

Edmond Dantes

225 days ago
Hello, Almost all of these changes relate to the connection pool implementation. For it to work correctly, the language must support a concurrent mode of operation with the EventLoop. There’s no way to implement this in isolation from the overall architecture.

Unnamed Person

222 days ago
Mysqli already have all three features mentioned here and works fine without concurrent mode of operation.