mysqlnd-ms-php-errorhandling-4

  • Concepts
  • Error handling

  • Error handling
  • Error handling

    Error handling

    Applications using PECL/mysqlnd_ms should implement
    proper error handling for all user API calls. And because the
    plugin changes the semantics of a connection handle, API calls may
    return unexpected errors. If using the plugin on a connection
    handle that no longer represents an individual network connection,
    but a connection pool, an error code and error message will be set
    on the connection handle whenever an error occurs on any of the
    network connections behind.

    If using lazy connections, which is the default,
    connections are not opened until they are needed for query
    execution. Therefore, an API call for a statement execution may
    return a connection error. In the example below, an error is
    provoked when trying to run a statement on a slave. Opening a slave
    connection fails because the plugin configuration file lists an
    invalid host name for the slave.

    Example #1 Provoking a connection error

    {
        "myapp": {
            "master": {
                "master_0": {
                    "host": "localhost",
                    "socket": "\/tmp\/mysql.sock"
                }
            },
            "slave": {
                "slave_0": {
                    "host": "invalid_host_name",
                }
            },
            "lazy_connections": 1
        }
    }
    

    The explicit activation of lazy connections is for
    demonstration purpose only.

    Example #2 Connection error on query
    execution

    <?php
    $mysqli 
    = new mysqli("myapp""username""password""database");
    if (
    mysqli_connect_errno())
      
    /* Of course, your error handling is nicer... */
      
    die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));

    /* Connection 1, connection bound SQL user variable, no SELECT thus run on master */
    if (!$mysqli->query("SET @myrole='master'")) {
     
    printf("[%d] %s\n"$mysqli->errno$mysqli->error);
    }

    /* Connection 2, run on slave because SELECT, provoke connection error */
    if (!($res $mysqli->query("SELECT @myrole AS _role"))) {
     
    printf("[%d] %s\n"$mysqli->errno$mysqli->error);
    } else {
     
    $row $res->fetch_assoc();
     
    $res->close();
     
    printf("@myrole = '%s'\n"$row['_role']);
    }
    $mysqli->close();
    ?>

    The above example will output something similar to:

    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
    

    Applications are expected to handle possible
    connection errors by implementing proper error handling.

    Depending on the use case, applications may want to
    handle connection errors differently from other errors. Typical
    connection errors are 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)
    and 2005 (CR_UNKNOWN_HOST) – Unknown MySQL
    server host ‘%s’ (%d)
    . For example, the application may test
    for the error codes and manually perform a fail over. The plugins
    philosophy is not to offer automatic fail over, beyond master fail
    over, because fail over is not a transparent operation.

    Example #3 Provoking a connection error

    {
        "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": [
    
                ]
            }
        }
    }
    

    Explicitly activating lazy connections is done for
    demonstration purposes, as is round robin load balancing as opposed
    to the default random once type.

    Example #4 Most basic failover

    <?php
    $mysqli 
    = new mysqli("myapp""username""password""database");
    if (
    mysqli_connect_errno())
      
    /* Of course, your error handling is nicer... */
      
    die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));

    /* Connection 1, connection bound SQL user variable, no SELECT thus run on master */
    if (!$mysqli->query("SET @myrole='master'")) {
     
    printf("[%d] %s\n"$mysqli->errno$mysqli->error);
    }

    /* Connection 2, first slave */
    $res $mysqli->query("SELECT VERSION() AS _version");
    /* Hackish manual fail over */
    if (2002 == $mysqli->errno || 2003 == $mysqli->errno || 2004 == $mysqli->errno) {
      
    /* Connection 3, first slave connection failed, trying next slave */
      
    $res $mysqli->query("SELECT VERSION() AS _version");
    }

    if (!$res) {
      
    printf("ERROR, [%d] '%s'\n"$mysqli->errno$mysqli->error);
    } else {
     
    /* Error messages are taken from connection 3, thus no error */
     
    printf("SUCCESS, [%d] '%s'\n"$mysqli->errno$mysqli->error);
     
    $row $res->fetch_assoc();
     
    $res->close();
     
    printf("version = %s\n"$row['_version']);
    }
    $mysqli->close();
    ?>

    The above example will output something similar to:

    [1045] Access denied for user 'username'@'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
    

    In some cases, it may not be easily possible to
    retrieve all errors that occur on all network connections through a
    connection handle. For example, let’s assume a connection handle
    represents a pool of three open connections. One connection to a
    master and two connections to the slaves. The application changes
    the current database using the user API call mysqli_select_db(), which then calls the
    mysqlnd library function to change the schemata. mysqlnd_ms
    monitors the function, and tries to change the current database on
    all connections to harmonize their state. Now, assume the master
    succeeds in changing the database, and both slaves fail. Upon the
    initial error from the first slave, the plugin will set an
    appropriate error on the connection handle. The same is done when
    the second slave fails to change the database. The error message
    from the first slave is lost.

    Such cases can be debugged by either checking for
    errors of the type E_WARNING (see above) or, if no other
    option, investigation of the mysqlnd_ms debug and trace log.