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();
?>
Résultat de l'exemple ci-dessus est 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.