Algunos clústeres de bases de datos distribuidas hacen usan errores transitorios. Un error transitorio es un error temporal que es probable que dessaparezca pronto. Por definición, es seguro para un cliente el ignorar un error transitorio y reintentar la operación fallida en el mismo servidor de bases de datos. El reintento está libre de efectos segundarios. Los clientes no están forzados a abortar su trabajo o recurrir a otro servidor de bases de datos de forma inmediata. Podrían entrar en un bucle de reintentos antes de esperar a que el error desaparezca antes de dejar el servidor de bases de datos. Los errores transitorios pueden ser vistos, por ejemplo, al usar MySQL Cluster. Aunque no están limitados a ninguan solución de clústeres específica per se.
PECL/mysqlnd_ms
puede realizar un bucle de reintentos automático en
caso de darse un error transitorio. Esto aumento la transparencia de la distribución y, por lo tanto,
hace más sencillo migrar una aplicación ejecutando en un único servidor de bases
de datos para ejecutar un clúster de servidores de bases de datos sin tener que cambiar
el origen de la aplicación.
El bucle de reintentos automáticos repetirá la operación solicitada hasta un número de veces configurable por el usuario y hará una pausa entre intentos por una cantidad de tiempo configurable. Si el error desaparece durante el bucle, la aplicación nunca lo verá. Si no, el error es reenviado a al aplicación para su manejo.
En el ejemplo de abajo se provoca un error de clave duplicada para hacer que el complemento reintente la consulta fallida dos vedes antes de que el error sea pasado a la aplicación. Entre los dos intentos, el complemento duerme durante 100 milisegundos.
Ejemplo #1 Provocar un error transitorio
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 } } }
Ejemplo #2 Bucle de reintentos del error transitorio
<?php
$mysqli = new mysqli("myapp", "username", "password", "database");
if (mysqli_connect_errno())
/* Por supuesto, su manejo de errores es mejor... */
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);
}
/* El bucle de reintentos es completamente transaparente. Verificar las estadísticas es
la única manera de conocer algo sobre los reintentos implícitos */
$stats = mysqlnd_ms_get_stats();
printf("Intentos del error transitorio antes del error: %d\n", $stats['transient_error_retries']);
/* Provocar un error de clave duplicada para ver el cambio en las estadísticas */
if (!$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
$stats = mysqlnd_ms_get_stats();
printf("Intentos del error transitorio después del error: %d\n", $stats['transient_error_retries']);
$mysqli->close();
?>
El resultado del ejemplo sería algo similar a:
Intentos del error transitorio antes del error: 0 [1062] Duplicate entry '1' for key 'PRIMARY' Intentos del error transitorio después del error: 2
Ya que la ejecución del bucle de reintentos es transparente desde el punt de vista de los usuarios, el ejemplo comprueba las estadísticas proporcionadas por el complemento para aprender de ellas.
Como muetra el ejemplo, el complemento puede ser instruido para considerar cualquier error
transitorio a pesar de la semámtica de los errores de los servidores de bases de datos. El único error
que cosidera temporal un stock de servidor MySQL tiene el código de error
1297
. Al configurar otror códigos de errores excepto el
1297
, asegúrese de que la configuración refleja
la semántica de los códigos de error del clúster.
Las siguientes llamadas a las API en C de mysqlnd están monitorizadas por el complemento para
comprobar errores transitorios: query()
,
change_user()
, select_db()
,
set_charset()
, set_server_option()
prepare()
, execute()
,
set_autocommit()
,
tx_begin()
, tx_commit()
,
tx_rollback()
, tx_commit_or_rollback()
.
Las llamadas a la API de usuario correspondientes tienen nombres similares.
El tiemp máximo que el complemento puede dormir durante el bucle de reintentos depende de la
función en cuestión. Un bucle de reintentos para query()
,
prepare()
o execute()
dormirá
hasta max_retries * usleep_retry
milisegundos.
Sin embargo, las funciones que
controlan el estado de conexión
son despachadas para todas las conexiones. Los ajustes del bucle de reintentos están aplicados
a cada conexión sobre la cual el comando se está ejecutando. Así, tales funciones
podrían interrumpir la ejecución del programa durante más tiempo que una función que se ejecute
sobre únicamente un servidor. Por ejemplo, set_autocommit()
es
despachada para conexiones y podría dormir hasta
(max_retries * usleep_retry) * number_of_open_connections)
milisegundos. Por favor, mantenga esto en mento al configurar tiempos de dormir largos
y números de reintentos grandes. Con los ajustes preestablecidos de
max_retries=1
, usleep_retry=100
y
lazy_connections=1
es improbable que vea
un retarno de más de un segundo.