La fermeture des connexions persistantes de postgresql via PHP peut être un problème lorsque la liaison avec la base de données est rompue.
On se retrouve alors avec une erreur de type :
Fatal error: Uncaught PDOException: SQLSTATE[57P01]: Admin shutdown: 7 FATAL: terminating connection due to administrator command server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.
Une solution consiste à un installer un service sur le serveur applicatif pour supprimer les sockets en attente de fermeture.
Lorsque la liaison avec la base de données est rompue, PHP a toujours en cache le pool de connexion PDO vers des sockets en attente de fermeture. Ces dernières sont inutilisables, d'où l'erreur citée plus haut.
On peut consulter la liste des sockets en attente de fermeture à l'aide de la commande (il faut les droits root) :
netstat -anp | grep --regexp="5432" | grep CLOSE_WAIT
Le script PHP suivant permet de repérer ces sockets et de les fermer en tuant le process associé :
#créer le fichier /opt/maarch/killPgCloseWait.php
nano /opt/maarch/killPgCloseWait.php
<?php
$return = killCloseWait(['5432', 'CLOSE_WAIT']);
function killCloseWait ($match = []) {
$matchString = '';
foreach ($match as $aMatch) {
$matchString .= ' grep ' . escapeshellarg($aMatch) . ' | ';
}
if (!empty($matchString)) {
$cmd ='netstat -anp | ' . $matchString . " awk '{print $7}'";
exec($cmd, $output, $return);
if ($return) {
//nothing
} else {
while (list(,$t) = each($output)) {
if (preg_match('/^([0-9]+)/', $t, $r)) {
system('kill '. $r[1], $k);
if(!$k) $killed = 1;
}
}
}
}
}
Il nous reste plus qu'à créer un service exécutant ce code php pour se prémunir des ruptures de liaison avec la base de données lorsque l'on utilise les connexions persistantes de PDO.
nano /lib/systemd/system/killPgCloseWait.service
[Unit]
Description=kill close wait service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/env php /opt/maarch/killPgCloseWait.php
[Install]
WantedBy=multi-user.target
Démarrer le service
systemctl start killPgCloseWait.service
Paramétrer le service pour qu'il se lance au boot de la machine
systemctl enable killPgCloseWait.service
Vérifier le statut du service
systemctl status killPgCloseWait.service
Redémarrer le serveur Postgres
service postgresql restart
Contrôler que toutes les sockets postgresql au statut CLOSE_WAIT sont bien fermées
netstat -anp | grep --regexp="5432" | grep CLOSE_WAIT