Bug #80412 [Opn->Csd]: PHP-FPM : PDO / Broken Pipe when DBMS ends the connection

From: Date: Thu, 26 Nov 2020 10:59:23 +0000
Subject: Bug #80412 [Opn->Csd]: PHP-FPM : PDO / Broken Pipe when DBMS ends the connection
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-230629@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=80412&edit=1

 ID:                 80412
 User updated by:    heavy-traffic-website at yopmail dot com
 Reported by:        heavy-traffic-website at yopmail dot com
 Summary:            PHP-FPM : PDO / Broken Pipe when DBMS ends the
                     connection
-Status:             Open
+Status:             Closed
 Type:               Bug
 Package:            PDO Core
 Operating System:   Debian 11
 PHP Version:        7.4.12
 Block user comment: N
 Private report:     N

 New Comment:

Ok, so, i now understand, seeing lot of PHP INTERNAL errors, but PDO still works.

nikic@php.net ; I supposed you have right.

Then, this was just surprising me, i thought PDO was not working fine but i seems it is. I will
suppress this error from the logs, and go on like that...

Thank you for support.


Previous Comments:
------------------------------------------------------------------------
[2020-11-26 10:52:45] heavy-traffic-website at yopmail dot com

Actual result:
--------------

string(26) "<BEFORE new PDO(), loop 1>"
string(34) "------------[ ERROR ]-------------"
string(79) "-- errstr: PDO::__construct(): send of 5 bytes failed with errno=32 Broken
pipe"
string(34) "----------------------------------"
string(25) "<AFTER new PDO(), loop 1>"
object(PDO)#2 (0) {
}
int(1)
string(26) "<BEFORE new PDO(), loop 2>"
string(25) "<AFTER new PDO(), loop 2>"
object(PDO)#3 (0) {
}
int(1)
string(26) "<BEFORE new PDO(), loop 3>"
string(25) "<AFTER new PDO(), loop 3>"
object(PDO)#2 (0) {
}
int(1)
string(26) "<BEFORE new PDO(), loop 4>"
string(25) "<AFTER new PDO(), loop 4>"
object(PDO)#3 (0) {
}
int(1)
string(26) "<BEFORE new PDO(), loop 5>"
string(25) "<AFTER new PDO(), loop 5>"
object(PDO)#2 (0) {
}
int(1)
string(26) "<BEFORE new PDO(), loop 6>"
string(25) "<AFTER new PDO(), loop 6>"
object(PDO)#3 (0) {
}
int(1)

------------------------------------------------------------------------
[2020-11-25 20:01:27] heavy-traffic-website at yopmail dot com

Description:
------------
Related to this very old bug reported 10 years ago
=> https://bugs.php.net/bug.php?id=53287

(johannes@php.net) was wrong - the bug still exists and no one fixed it.
--> Please consider the bug onto the PHP side, not the DBMS side.

PHP (php7.4-fpm) have threads with broken persistent connections cached with PDO and this should try
to reconnect a new one connection rather than calling the error handler in the case where the pipe
is already broken, during opening a nwe connection using persistence. Maybe there is a leak, why
don't you free a broken pipe and try to reuse it ?

Also, the PDO::_construct() return a PDO objet (without throwing an Exception), but PHP rises an
E_NOTICE :
string(68) "PDO::__construct(): send of 5 bytes failed with errno=32 Broken pipe"
That seems to deal with a E_CORE_WARNING ; generated by the core of PHP.

Currently, there is NO SOLUTION UNLESS FORGET PERSITANCE, it sucks !


Test script:
---------------

ini_set("error_reporting", (string) E_ALL);
ini_set("display_errors", "On");
ini_set("display_startup_errors", "On");
ini_set("track_errors", "On");

function my_error($errno, $errstr, $errfile, $errline)
{
	var_dump("------------[ ERROR ]-------------"); 
	var_dump("-- errstr: " . $errstr);
	var_dump("----------------------------------");
}

set_error_handler("my_error");

abstract class Database
{
    private static ?PDO $_db = null;
   
    final public static function test() : ?PDO
    {
        for ($i = 1 ; $i <=6 ; ++$i)
        {
            var_dump("<BEFORE new PDO(), loop $i>");
			try {
				self::$_db = new PDO("mysql:host=127.0.0.1;dbname=XXX",
					 "XXX", "XXX", [
							 PDO::ATTR_PERSISTENT => true,
							 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
							]);
			}
			catch (PDOException $e) {
				var_dump("----------[ EXCEPTION ]-----------"); 
				var_dump("-- excstr: " . $e->getMessage());
				var_dump("----------------------------------");	
			}
		
            var_dump("<AFTER new PDO(), loop $i>")
            var_dump(self::$_db);
            var_dump("<PING select, loop $i>");
            var_dump(self::$_db->query("SELECT 1")->rowCount());
        }
        return self::$_db;
    }
}

Database::test();


Expected result:
----------------

string(26) "<BEFORE new PDO(), loop 1>"
string(25) "<AFTER new PDO(), loop 1>"
object(PDO)#2 (0) {
}
string(21) "<PING select, loop 1>"
int(1)

string(26) "<BEFORE new PDO(), loop 2>"
string(25) "<AFTER new PDO(), loop 2>"
object(PDO)#4 (0) {
}
string(21) "<PING select, loop 2>"
int(1)

string(26) "<BEFORE new PDO(), loop 3>"
string(25) "<AFTER new PDO(), loop 3>"
object(PDO)#3 (0) {
}
string(21) "<PING select, loop 3>"
int(1)

string(26) "<BEFORE new PDO(), loop 4>"
string(25) "<AFTER new PDO(), loop 4>"
object(PDO)#2 (0) {
}
string(21) "<PING select, loop 4>"
int(1)

string(26) "<BEFORE new PDO(), loop 5>"
string(25) "<AFTER new PDO(), loop 5>"
object(PDO)#4 (0) {
}
string(21) "<PING select, loop 5>"
int(1)

string(26) "<BEFORE new PDO(), loop 6>"
string(25) "<AFTER new PDO(), loop 6>"
object(PDO)#3 (0) {
}
string(21) "<PING select, loop 6>"
int(1)


Actual result:
--------------

< I AM DOING SOME TESTS NOW >
< Please wait for an update >

------------------------------------------------------------------------
[2020-11-25 15:22:18] heavy-traffic-website at yopmail dot com

Some precisons :

ini_set("error_reporting", (string) E_ALL);
ini_set("display_errors", "On");
ini_set("display_startup_errors", "On");
ini_set("track_errors", "On");

set_exception_handler("my_exception");
set_error_handler("my_error");


[ERROR HANDLER]
errno: int(8)
errstr: string(68) "PDO::__construct(): send of 5 bytes failed with errno=32 Broken pipe"
errfile: string(31) "/var/www/XXX/Database.class.php"
errline: int(28)


line 27 --> $db = new PDO("mysql:host=127.0.0.1;dbname=XXX", "XXX",
"XXX",
line 28 --> PDO::ATTR_PERSISTENT => true,
line 29 --> PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);

------------------------------------------------------------------------
[2020-11-25 14:24:37] heavy-traffic-website at yopmail dot com

Thank you for your note.

The debate is not about the log itself, independantly the state of error_reporting is. For
developpment, mine is set to E_ALL and works fine, there is no error implementing that.

PHP can procude any warning that he wants, but fail to __contruct PDO after a broken pipe IS NOT A
NORMAL sence or wanted. You cannot tell each pipe connection will never end, so you made a mistake
in your configuration... or that is a desired effect.

There is no error somewhere due to the other parties. It can happen of a pipe is broken due to long
inactivity ; do network activities and you will know. Softwares have to retry retablishing the pipe
BEFORE throwing any error : it should be the same with PHP/PDO core.

If PHP/PDO fails to send data ; it is because the pipe onto the thread is cached (due to the
persistance), and reused like it should be always be working, it is incorrect.

When PHP/PDO have a "send of 5 bytes failed with errno=32", you should flush the cache
about persistent connexion then RETRYING __construct with persistant connection LIKE it has been a
new thread without cached yet.

If an error had to be throwed, it can be adjusted :
- "send of 5 bytes failed with errno=32", moreover, "server has gone away".
- "send of 5 bytes failed with errno=32", moreover, "[last connection error]".

This error will be consistent. But to let a thread with inconsistent broken pipe cached, is not
consistent neither wanted neither normal. By the way, you can't have 2 identical "Pipe
error" when retrying connection. So, it is logic to retry ONCE a persistent broken pipe.

------------------------------------------------------------------------
[2020-11-25 08:44:13] nikic@php.net

Just a note, if you see that "broken pipe" error, that probably means you have a broken
error handler implementation somewhere. This error is suppressed internally (error_reporting=0), but
an incorrectly implemented error handler that does not check the error_reporting level may make it
visible again.

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=80412


--
Edit this bug report at https://bugs.php.net/bug.php?id=80412&edit=1


Thread (13 messages)

« previous php.bugs (#230629) next »