Manejo de errores
Las aplicaciones que usan PECL/mysqlnd_ms deberían implementar el manejo de errores apropiado para todas las llamadas a la API del usuario. Ya que el complemento cambia la semántica de un gestor de conexión, las llamadas a la API pueden devolver errores inesperados. Si se usa el complemento sobre un gestor de conexión que ya no represeta una conexión de red individual, sino una agrupación de conexiones, se establecerá un código de error y un mensaje de error en el gestor de conexión siempre que ocurra un error en cualquier conexión de red subyacente.
Si se usan conexiones retardadas, que es lo predeterminado, las conexiones no son abiertas hasta que sean necesarias para ejecutar consultas. Por lo tanto, una llamada a la API para la ejecución de una senetencia puede devolver un error de conexion. En el ejemplo de abajo, se provoca un error al intentar ejecutar una sentencia en un esclavo. La apertura de la conexión esclava fallará debido a que el fichero de configuración del complemento incluye un nombre de anfitrión no válido para el esclavo.
Ejemplo #1 Provocar un error de conexión
{ "myapp": { "master": { "master_0": { "host": "localhost", "socket": "\/tmp\/mysql.sock" } }, "slave": { "slave_0": { "host": "nombre_host_inváido", } }, "lazy_connections": 1 } }
La activación explícita de conexiones retardadas sólo es con propósito de demostración.
Ejemplo #2 Error de conexión en la ejecución de una consulta
<?php
$mysqli = new mysqli("myapp", "nombre_usuario", "contraseña", "base_datos");
if (mysqli_connect_errno())
/* Por supuesto, su manejo de errores es más agradable... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Conexión 1, la conexión vincula una variable SQL de usuario, no se ejecuta ningún SELECT en el maestro */
if (!$mysqli->query("SET @myrole='master'")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Conexión 2, se ejecuta en el esclavo a causa de SELECT, provoca un error de conexión */
if (!($resultado = $mysqli->query("SELECT @myrole AS _role"))) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
} else {
$fila = $resultado->fetch_assoc();
$resultado->close();
printf("@myrole = '%s'\n", $fila['_role']);
}
$mysqli->close();
?>
El resultado del ejemplo sería algo similar a:
PHP Warning: mysqli::query(): php_network_getaddresses: getaddrinfo failed: Name or service not known in %s on line %d PHP Warning: mysqli::query(): [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (trying to connect via tcp://invalid_host_name:3306) in %s on line %d [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known
Se espera que la aplicaciones manejen los posibles errores de conexión mediante la implementación del manejo de errores apropiado.
Dependiendo del caso en uso, las aplicaciones pueden optar por manejar errores de conexión de forma diferente a otros errores. Los errores de conexión típicos son 2002 (CR_CONNECTION_ERROR) - Can't connect to local MySQL server through socket '%s' (%d), 2003 (CR_CONN_HOST_ERROR) - Can't connect to MySQL server on '%s' (%d) y 2005 (CR_UNKNOWN_HOST) - Unknown MySQL server host '%s' (%d). Por ejemplo, la aplicación podría comprobar los códigos de error y realizar una tolerancia a fallos manual. La filosofía del complemento no ofrece la tolerancia a fallos automática, más allá de la tolerancia a fallos del maestro, ya que no es una operación transaparente.
Ejemplo #3 Provocar un error de conexión
{ "myapp": { "master": { "master_0": { "host": "localhost" } }, "slave": { "slave_0": { "host": "invalid_host_name" }, "slave_1": { "host": "192.168.78.136" } }, "lazy_connections": 1, "filters": { "roundrobin": [ ] } } }
La activación explícita de conexiones retardadas sólo es con propósito de demostración, ya que el equilibrado de carga es de rotación en oposición al tipo predeterminado aleatorio una vez.
Ejemplo #4 Tolerancia a fallos más básica
<?php
$mysqli = new mysqli("myapp", "nombre_usuario", "contraseña", "base_datos");
if (mysqli_connect_errno())
/* Por supuesto, su manejo de errores es más agradable... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));
/* Conexión 1, la conexión vincula una variable SQL de usuario, no se ejecuta ningún SELECT en el maestro */
if (!$mysqli->query("SET @myrole='master'")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
/* Conexión 2, primer esclavo */
$resultado = $mysqli->query("SELECT VERSION() AS _version");
/* Tolerancia a fallos manual */
if (2002 == $mysqli->errno || 2003 == $mysqli->errno || 2004 == $mysqli->errno) {
/* Conexión 3, falló la conexión al primer esclavo, se intenta con el siguiente */
$resultado = $mysqli->query("SELECT VERSION() AS _version");
}
if (!$resultado) {
printf("ERROR, [%d] '%s'\n", $mysqli->errno, $mysqli->error);
} else {
/* Los mensajes de error se toman de la conexión 3, por lo que no hay errores */
printf("SUCCESS, [%d] '%s'\n", $mysqli->errno, $mysqli->error);
$fila = $resultado->fetch_assoc();
$resultado->close();
printf("version = %s\n", $fila['_version']);
}
$mysqli->close();
?>
El resultado del ejemplo sería algo similar a:
[1045] Access denied for user 'nombre_usuario'@'localhost' (using password: YES) PHP Warning: mysqli::query(): php_network_getaddresses: getaddrinfo failed: Name or service not known in %s on line %d PHP Warning: mysqli::query(): [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (trying to connect via tcp://invalid_host_name:3306) in %s on line %d SUCCESS, [0] '' version = 5.6.2-m5-log
En algunos casos, puede no ser posible la recuperación de forma sencilla de todos los errores que ocurran en todas las conexiones de red a través de un gestor de conexión. Por ejemplo, se asume que un gestor de conexión representa una agrupación de tres conexiones abiertas. Una conexión a un maestro y dos conexiones a los esclavos. La aplicación cambia la base de datos actual usando la llamada a la API de usuario mysqli_select_db(), la cual luego llama a la función de la biblioteca mysqlnd para cambiar el esquema. mysqlnd_ms monitoriza la función e intenta cambiar la base de datos ctual en todas las conexiones para armonizar sus estados. Ahora, se asume que el maestro tiene éxito al cambiar la base de datos, y ambos esclavos fallan. Durante el error inicial del primer esclavo, el complemento establecerá un error apropiado sobre en el gestor de conexión. Y lo mismo se hace cuando falla el segundo esclavo al cambiar la base de datos. El mensaje de error del primer esclavo se pierde.