Addition of error handling instructions to concepts section. Assorted other minor edits.

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@317722 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Ulf Wendel 2011-10-04 13:34:45 +00:00
parent 3fab8aa041
commit d30ee932d6
4 changed files with 266 additions and 18 deletions

View file

@ -47,6 +47,9 @@
</note>
<section xml:id="mysqlnd-ms.key_features">
<title>Key Features</title>
<para>
The key features of PECL/mysqlnd_ms are as follows.
</para>
<para>
<itemizedlist>
<listitem>
@ -166,7 +169,12 @@
<para>
The read/write splitter is not aware of multi-statements. Multi-statements
are considered as one statement. The decision of where to run the statement
will be based on the beginning of the statement string.
will be based on the beginning of the statement string. For example, if
using <link linkend="mysqli.multi-query"><function>mysqli_multi_query</function></link>
to execute the multi-statement <literal>SELECT id FROM test ; INSERT INTO test(id) VALUES (1)</literal>,
the statement will be redirected to a slave server because it begins with
<literal>SELECT</literal>. The <literal>INSERT</literal> statement, which is
also part of the multi-statement, will not be redirected to a master server.
</para>
<para>
Prior to version 1.1.0-beta the plugin did not support native prepared statements.

View file

@ -4,7 +4,7 @@
<chapter xml:id="mysqlnd-ms.concepts" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Concepts</title>
<para>
The concept section explains the overall architecture and important concepts
The concepts section explains the overall architecture and important concepts
of the plugin. The materials aim to help you understanding the impact of
MySQL replication and using the plugin for your development tasks.
Any application using MySQL replication must take care of certain tasks that
@ -34,18 +34,25 @@
the SQL hint <literal>/*ms=slave*/</literal> or a slave had been choose for
running the previous query and the query starts with the SQL hint
<literal>/*ms=last_used*/</literal>. In all other cases the query will
be sent to the MySQL replication master server.
be sent to the MySQL replication master server. Applications are requested
to use the <constant>MYSQLND_MS_MASTER_SWITCH</constant>,
<constant>MYSQLND_MS_SLAVE_SWITCH</constant> and
<constant>MYSQLND_MS_LAST_USED_SWITCH</constant>
<link linkend="mysqlnd-ms.constants">predefined constants</link>
instead of their literal values,
such as <literal>/*ms=slave*/</literal>,
for better portability.
</para>
<para>
The plugin takes care internally of opening and closing the database connections
to the master server and the slave servers. From an application
point of view there continues to be only one connection handle. However,
internally, this one public connection handle represents a pool of
internal connections managed by the plugin. The plugin proxies queries
network connections managed by the plugin. The plugin proxies queries
to the master server and the slave ones using multiple connections.
</para>
<para>
Database connections have a state consisting, for example, transaction
Database connections have a state consisting of, for example, transaction
status, transaction settings, character set settings, temporary tables.
The plugin will try to maintain the same state among all internal
connections, whenever this can be done in an automatic and transparent way.
@ -84,7 +91,7 @@
At some point in time one and the same PHP connection handle
may point to the MySQL master server. Later on, it may point
to one of the slave servers or still the master. Manipulating
and replacing the wire connection referenced by a PHP MySQL
and replacing the network connection referenced by a PHP MySQL
connection handle is not a transparent operation.
</para>
<para>
@ -121,7 +128,7 @@
</listitem>
<listitem>
<simpara>
The current database set using <literal>USE</literal>
The current database set using <literal>USE</literal> and other state chaning SQL commands
</simpara>
</listitem>
<listitem>
@ -155,7 +162,7 @@
<para>
The plugins philosophy is to align the state of connections in the
pool only if the state is under full control of the plugin, or if it is
necessary for security reasons. Just a few actions that change the
necessary for security reasons. Just a few actions that change the
state of the connection fall into this category.
</para>
<para>
@ -348,7 +355,7 @@
<para>
Please note that the connection state is not only changed by API calls. Thus,
even if PECL mysqlnd_ms monitors all API calls, the application still needs
to take care. Ultimately, it is in the application developers reposibility
to take care. Ultimately, it is in the applications reposibility
to maintain connection state, if needed.
</para>
</section>
@ -395,6 +402,213 @@
</para>
</section>
<section xml:id="mysqlnd-ms.errorhandling">
<title>Error handling</title>
<para>
Applications using PECL/mysqlnd_ms should to proper error handling
of any user API call. Due to the fact that the plugin changes the semantics
of a connection handle, API calls may return unexpected errors. If using
the plugin a connection handle 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.
</para>
<para>
If using lazy connections, which is a default, connections are not
opened until needed for query execution. Therefore, it can happen that
an API call for statement execution returns a connection error.
In the example an error is provoked when trying to run a statement on a slave.
Opening a slave connection fails because the plugins configuration file
lists an invalid host name of the slave.
</para>
<para>
<example>
<title>Provoking a connection error</title>
<programlisting role="ini">
<![CDATA[
{
"myapp": {
"master": {
"master_0": {
"host": "localhost",
"socket": "\/tmp\/mysql.sock"
}
},
"slave": {
"slave_0": {
"host": "invalid_host_name",
}
},
"lazy_connections": 1
}
}
]]>
</programlisting>
</example>
</para>
<para>
Explicitly activating lazy connections is done for demonstration purpose.
</para>
<para>
<example>
<title>Connection error on query execution</title>
<programlisting role="php">
<![CDATA[
<?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();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
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
]]>
</screen>
</example>
</para>
<para>
Applications are expected to be able to handle the connection error. In
most cases this will not require code changes, if the application does
proper error handling already.
</para>
<para>
Depending on the use case applications may want to handle connection errors
differently from other errors. Typical connection errors are
<literal>2002 (CR_CONNECTION_ERROR) - Can't connect to local MySQL server through socket '%s' (%d)</literal>,
<literal>2003 (CR_CONN_HOST_ERROR) - Can't connect to MySQL server on '%s' (%d)</literal> and
<literal>2005 (CR_UNKNOWN_HOST) - Unknown MySQL server host '%s' (%d)</literal>.
For example, the application may test for the error codes and manually
perform fail over. The plugins philosophy is not to offer automatic fail over
beyond master fail over because fail over is not a transparent operation.
</para>
<para>
<example>
<title>Provoking a connection error</title>
<programlisting role="ini">
<![CDATA[
{
"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": [
]
}
}
}
]]>
</programlisting>
</example>
</para>
<para>
Explicitly activating lazy connections is done for demonstration purpose.
Please, note that round robin load balancing is used instead of the default
random once.
</para>
<para>
<example>
<title>Most basic failover</title>
<programlisting role="php">
<![CDATA[
<?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();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
[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
]]>
</screen>
</example>
</para>
<para>
In some cases, it may not be easily possible to retrieve all errors that
occur on all network connections through a connection handle. 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 <function>mysqli_select_db</function>,
which in turn calls the mysqlnd library function to change the schemata.
PECL/mysqlnd_ms monitors the function and tries to change the current
database on all connections.to harmonize their state. Let the master succeed
in changing the database and let both slaves fail. Upon the first 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.
</para>
<para>
Such cases can be debugged by either checking for errors of the type
<literal>E_WARNING</literal> (see above) or, if no other option, investigation
of the <link linkend="mysqlnd-ms.debugging">debug and trace log</link>.
</para>
</section>
<section xml:id="mysqlnd-ms.failover">
<title>Failover</title>
<para>
@ -408,8 +622,8 @@
<para>
It is up to
the application to handle the error and, if need be, re-issue the query to
trigger selection of another slave server for statement execution. The plugin
will make no attempts to failover automatically because the plugin
trigger selection of another slave server for statement execution.
The plugin will make no attempts to failover automatically because the plugin
cannot ensure that an automatic failover will not change the state of
the connection. For example, the application may have issued a query
which depends on SQL user variables which are bound to a specific connection.
@ -429,6 +643,10 @@
<literal><link linkend="ini.mysqlnd-ms-plugin-config-v2.failover">failover</link></literal>
configuration directive.
</para>
<para>
A most basic manual failover example is given under
<link linkend="mysqlnd-ms.errorhandling">error handling</link>.
</para>
</section>
<section xml:id="mysqlnd-ms.loadbalancing">
@ -488,7 +706,7 @@
<para>
A user-defined read-write splitter can ask the built-in logic to make
a proposal where to sent a statement by invoking
<function>mysqlnd_ms_is_select</function>.
<link linkend="function.mysqlnd-ms-query-is-select"><function>mysqlnd_ms_is_select</function></link>.
</para>
<note>
<para>

View file

@ -60,7 +60,6 @@ mysqlnd_ms.ini_file=/path/to/mysqlnd_ms_plugin.ini
Create a plugin-specific configuration file. Save the file to the path
set by the PHP configuration directive
<link linkend="ini.mysqlnd-ms.ini-file">mysqlnd_ms.ini_file</link>.
The plugin-specific file must be readable by PHP.
</para>
<para>
The plugins <link linkend="mysqlnd-ms.plugin-ini-json">configuration file</link>
@ -104,7 +103,7 @@ mysqlnd_ms.ini_file=/path/to/mysqlnd_ms_plugin.ini
It is not allowed to omit the MySQL slave server list. However,
an empty list may be configured. Doing so is not recommended.
You should always configure at least one slave server as well.
The slave server list may consist of one or more servers.
The slave server list, if not empty, may consist of one or more servers.
</para>
<para>
Server lists can use <link linkend="mysqlnd-ms.plugin-ini-json.server_list_syntax">
@ -265,10 +264,18 @@ $mysql = mysql_connect("myapp", "username", "password");
<literal>password</literal> to connect to any of the MySQL servers listed in
the section <literal>myapp</literal> of the plugins configuration file. Upon
connect, the plugin will select <literal>database</literal> as the current
schemata. The username, password and schema name are taken from the connect
schemata.
</para>
<para>
The username, password and schema name are taken from the connect
API calls and used for all servers. In other words: you must use the same
username and password for every MySQL server listed in a plugin configuration
file section.
file section. The is not a general limitation. As of PECL/mysqlnd_ms 1.1.0,
it is possible to set the
<link linkend="mysqlnd-ms.plugin-ini-json.server_config_keywords">username</link> and
<link linkend="mysqlnd-ms.plugin-ini-json.server_config_keywords">password</link> for any server in the
plugins configuration file to be used instead of the credentials passed
to the API call.
</para>
<para>
The plugin does not change the API for running statements.
@ -284,7 +291,7 @@ $mysql = mysql_connect("myapp", "username", "password");
<?php
/* Load balanced following "myapp" section rules from the plugins config file */
$mysqli = new mysqli("myapp", "username", "password", "database");
if (!$mysqli)
if (mysqli_connect_errno())
/* Of course, your error handling is nicer... */
die(sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()));

View file

@ -1774,7 +1774,11 @@ mysqlnd.debug=d:t:x:O,/tmp/mysqlnd.trace
The debug log is not only useful for plugin developers but also to find the
cause of user errors. For example, if your application does not do proper
error handling and fails to record error messages, checking the debug
and trace log may help finding the cause.
and trace log may help finding the cause. Use of the debug log
to debug application issues should be considered only if no other
option is available. Writing the debug log to disk is a slow
operation and may have negative impact on the application
performance.
</para>
<para>
Example excerpt from the debug log (connection failure):
@ -1836,6 +1840,17 @@ mysqlnd.debug=d:t:x:O,/tmp/mysqlnd.trace
]]>
</programlisting>
</para>
<para>
In this case the statement <literal>DROP TABLE IF EXISTS test</literal> has been
executed. Note that the statement string is shown in the log file. You may want
to take measures to restrict access to the log for security considerations.
</para>
<para>
The statement has been load balanced using round robin policy,
as you can easily guess from the functions name <literal>>mysqlnd_ms_choose_connection_rr</literal>.
It has been sent to a master server running on
<literal>host=localhost user=root db=test port=3306 flags=131072 persistent=0 state=0</literal>.
</para>
</section>
</chapter>