PHPWales 2020 - June 3rd to June 4th

Les erreurs passagères

Quelques clusters de bases de données utilisent les erreurs passagères. Une erreur passagère est une erreur temporaire qui normalement doit disparaître rapidement. Par définition, il est sécurisé pour un client d'ignorer une erreur passagère et de re-tenter l'opération en échec sur le même serveur de base de données. La nouvelle tentative n'a aucun effet secondaire. Les clients ne sont pas obligés de stopper leurs travaux ou de basculer sur un autre serveur de base de données immédiatement. Ils doivent plutôt entrer dans une boucle de tentatives pour attendre que l'erreur disparaisse avant d'abandonner le serveur de base de données au profit d'un autre. Les erreurs passagères peuvent être observées, par exemple, lors de l'utilisation d'un cluster MySQL. Mais elles ne sont pas liées à une solution de clusters spécifique.

PECL/mysqlnd_ms peut lancer une boucle de tentatives automatiquement dans le cas d'erreurs passagères. Ce mécanisme permet de rendre encore plus transparent la distribution, et ainsi, rend simple la migration d'une application fonctionnant sur un seul serveur de base de données sur un cluster de serveurs de bases de données sans avoir à changer le source de l'application.

La boucle de tentatives automatique va répéter l'opération demandée autant de fois que le nombre configuré, et effectuera une pause entre les tentatives pendant une durée configurée. Si l'erreur disparaît pendant la boucle, l'application ne la verra jamais. Sinon, l'erreur sera envoyée à l'application afin qu'elle la gère.

Dans l'exemple ci-dessous, une erreur concernant une clé dupliquée est provoquée pour que le plugin entre dans un cycle de deux nouvelles tentatives avant de passer l'erreur à l'application. Entre les deux tentatives, le plugin va attendre 100 millisecondes.

Exemple #1 Provoque une erreur passagère

mysqlnd_ms.enable=1
mysqlnd_ms.collect_statistics=1
{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
       },
       "transient_error": {
          "mysql_error_codes": [
            1062
          ],
          "max_retries": 2,
          "usleep_retry": 100
       }
    }
}

Exemple #2 Boucle de tentative pour une erreur passagère

<?php
$mysqli 
= new mysqli("myapp""username""password""database");
if (
mysqli_connect_errno())
  
/* Bien évidemment, votre gestionnaire d'erreurs est bien meilleur... */
  
die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));

if (!
$mysqli->query("DROP TABLE IF EXISTS test") ||
    !
$mysqli->query("CREATE TABLE test(id INT PRIMARY KEY)") ||
    !
$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
  
printf("[%d] %s\n"$mysqli->errno$mysqli->error);
}

/* La boucle de tentatives est totalement transparente.
   La vérification des statistiques est la seule façon de
   voir les tentatives implicites */
$stats mysqlnd_ms_get_stats();
printf("Tentatives sur l'erreur passagère avant de lancer l'erreur : %d\n"$stats['transient_error_retries']);

/* Provoque une erreur concernant une clé dupliquée pour voir les statistiques changer */
if (!$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
  
printf("[%d] %s\n"$mysqli->errno$mysqli->error);
}

$stats mysqlnd_ms_get_stats();
printf("Tentatives sur l'erreur passagère après le lancement de l'erreur : %d\n"$stats['transient_error_retries']);

$mysqli->close();
?>

L'exemple ci-dessus va afficher quelque chose de similaire à :

Tentatives sur l'erreur passagère avant de lancer l'erreur : 0
[1062] Duplicate entry '1' for key 'PRIMARY'
Tentatives sur l'erreur passagère après le lancement de l'erreur : 2

En raison du fait de l'exécution transparente d'un point de vue utilisateur de la boucle de tentatives, l'exemple vérifie les statistiques fournies par le plugin pour en savoir plus.

Comme l'exemple le montre, le plugin peut considérer toutes les erreurs comme passagères au regard de la sémantique des erreurs des serveurs de base de données. La seule erreur que le serveur MySQL considère comme temporaire est l'erreur code 1297. Lorsque vous configurez d'autres codes erreurs autre que la 1297, assurez-vous que votre configuration reflète la sémantique des codes erreurs de votre cluster.

Les appels mysqlnd C API suivants sont surveillés par le plugin pour vérifier les erreurs passagères : query(), change_user(), select_db(), set_charset(), set_server_option() prepare(), execute(), set_autocommit(), tx_begin(), tx_commit(), tx_rollback(), tx_commit_or_rollback(). Les appels API utilisateur ont des noms similaires.

La durée d'attente du plugin entre plusieurs tentatives dans la boucle dépend de la fonction en question. Dans une boucle de tentative pour query(), prepare() ou execute(), la durée d'attente sera max_retries * usleep_retry millisecondes.

Cependant, les fonctions qui contrôle le statut de la connexion sont dispatchées pour toutes les connexions. La configuration de la boucle de tentative est appliquée à chaque connexion sur laquelle la commande est exécutée. Aussi, une fonction peut interrompre l'exécution du programme plus longtemps qu'une fonction qui est exécutée sur un seul serveur. Par exemple, set_autocommit() est dispatché sur l'ensemble des connexions, et peut attendre (max_retries * usleep_retry) * nombre_de_connexions_ouvertes) millisecondes. Veuillez garder cela à l'esprit lorsque vous définissez une durée élevée ainsi qu'un grand nombre de tentatives. L'utilisation de la configuration par défaut (max_retries=1, usleep_retry=100 et lazy_connections=1) est vivement conseillé, car vous n'aurez jamais de délai supérieur à 1 seconde.

add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top