MS 1.6 (current development series, alpha) - transient errors: config, concept and example, stats

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@330540 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Ulf Wendel 2013-06-17 17:39:19 +00:00
parent cf03bfdfb0
commit 5a133dd1d9
4 changed files with 363 additions and 6 deletions

View file

@ -12,6 +12,63 @@
for a complete list of changes.
</para>
<section xml:id="mysqlnd-ms.changes-one-six">
<title xmlns="http://docbook.org/ns/docbook">PECL/mysqlnd_ms 1.6 series</title>
<para>
1.6.0-alpha
<itemizedlist>
<listitem>
<simpara>
Release date: TBD
</simpara>
</listitem>
<listitem>
<simpara>
Motto/theme: Maintenance
</simpara>
</listitem>
</itemizedlist>
</para>
<note>
<para>
This is the current development series. All
features are at an early stage. Changes may happen at any time without
prior notice. Please, do not use this version in production environments.
</para>
<para>
The documentation may not reflect all changes yet.
</para>
</note>
<para>
Bug fixes
<itemizedlist>
<listitem>
<para>
No new bugs reported
</para>
</listitem>
</itemizedlist>
</para>
<para>
Feature changes
<itemizedlist>
<listitem>
<para>
Introduced automatic retry loop for transient errors and
<link linkend="function.mysqlnd-ms-get-stats">corresponding statistic</link>
to count the number of implicit retries. Some distributed
database clusters use transient errors to hint a client to retry
its operation in a bit. Most often, the client is then supposed
to halt execution (sleep) for a short moment before retrying the
desired operation. Immediately failing over to another node
is not necessary in response to the error. Instead, a retry loop
can be performed. Common situation when using MySQL Cluster.
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section xml:id="mysqlnd-ms.changes-one-five">
<title xmlns="http://docbook.org/ns/docbook">PECL/mysqlnd_ms 1.5 series</title>
<para>
@ -31,12 +88,11 @@
</para>
<note>
<para>
This is the current development series. All
features are at an early stage. Changes may happen at any time without
prior notice. Please, do not use this version in production environments.
This is the current release series. Albeit a stable release is missing,
it can be considered stable. Use this version in production environments.
</para>
<para>
The documentation may not reflect all changes yet.
The documentation is complete.
</para>
</note>
<para>
@ -140,7 +196,7 @@
modified to use the new C API calls of the <literal>mysqlnd</literal>
library to begin, commit, and rollback a transaction or savepoint.
If <link linkend="ini.mysqlnd-ms-plugin-config-v2.trx-stickiness">trx_stickiness</link>
is used to enable transaction aware load balancing, the <function>mysqli_begin</function>,
is used to enable transaction aware load balancing, the <function>mysqli_begin</function>,
<function>mysqli_commit</function> and <function>mysqli_rollback</function> functions
will now be monitered by the plugin, to go along with the <function>mysqli_autocommit</function>
function that was already supported. All SQL features to control

View file

@ -658,6 +658,166 @@ version = 5.6.2-m5-log
</para>
</section>
<section xml:id="mysqlnd-ms.transient_errors">
<title>Transient errors</title>
<para>
Some distributed database clusters make use of transient errors. A transient
error is a temporary error that is likely to disappear soon. By definition
it is safe for a client to ignore a transient error and retry the failed
operation on the same database server. The retry is free of side effects.
Clients are not forced to abort their work or to fail over to another database
server immediately. They may enter a retry loop before to wait for the
error to disappear before giving up on the database server.
Transient errors can be seen, for example, when using MySQL Cluster. But they
are not bound to any specific clustering solution per se.
</para>
<para>
<literal>PECL/mysqlnd_ms</literal> can perform an automatic retry loop in
case of a transient error. This increases distribution transparency and thus
makes it easier to migrate an application running on a single database
server to run on a cluster of database servers without having to change
the source of the application.
</para>
<para>
The automatic retry loop will repeat the requested operation up to a user
configurable number of times and pause between the attempts for a configurable
amount of time. If the error disappears during the loop, the application will
never see it. If not, the error is forwarded to the application for handling.
</para>
<para>
In the example below a duplicate key error is provoked to make the plugin
retry the failing query two times before the error is passed to the application.
Between the two attempts the plugin sleeps for 100 milliseconds.
</para>
<para>
<example>
<title>Provoking a transient error</title>
<programlisting role="ini">
<![CDATA[
mysqlnd_ms.enable=1
mysqlnd_ms.collect_statistics=1
]]>
</programlisting>
<programlisting role="ini">
<![CDATA[
{
"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
}
}
}
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Transient error retry loop</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()));
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);
}
/* Retry loop is completely transparent. Checking statistics is
the only way to know about implicit retries */
$stats = mysqlnd_ms_get_stats();
printf("Transient error retries before error: %d\n", $stats['transient_error_retries']);
/* Provoking duplicate key error to see statistics change */
if (!$mysqli->query("INSERT INTO test(id) VALUES (1))")) {
printf("[%d] %s\n", $mysqli->errno, $mysqli->error);
}
$stats = mysqlnd_ms_get_stats();
printf("Transient error retries after error: %d\n", $stats['transient_error_retries']);
$mysqli->close();
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
Transient error retries before error: 0
[1062] Duplicate entry '1' for key 'PRIMARY'
Transient error retries before error: 2
]]>
</screen>
</example>
</para>
<para>
Because the execution of the retry loop is transparent from a users point of
view, the example checks the
<link linkend="function.mysqlnd-ms-get-stats">statistics</link>
provided by the plugin to learn about it.
</para>
<para>
As the example shows, the plugin can be instructed to consider any error
transient regardless of the database servers error semantics. The only error
that a stock MySQL server considers temporary has the error code
<constant>1297</constant>. When configuring other error codes but
<constant>1297</constant> make sure your configuration reflects
the semantics of your clusters error codes.
</para>
<para>
The following mysqlnd C API calls are monitored by the plugin to check
for transient errors: <literal>query()</literal>,
<literal>change_user()</literal>, <literal>select_db()</literal>,
<literal>set_charset()</literal>, <literal>set_server_option()</literal>
<literal>prepare()</literal>, <literal>execute()</literal>,
<literal>set_autocommit()</literal>,
<literal>tx_begin()</literal>, <literal>tx_commit()</literal>,
<literal>tx_rollback()</literal>, <literal>tx_commit_or_rollback()</literal>.
The corresponding user API calls have similar names.
</para>
<para>
The maximum time the plugin may sleep during the retry loop depends on the
function in question. The a retry loop for <literal>query()</literal>,
<literal>prepare()</literal> or <literal>execute()</literal> will sleep for
up to <literal>max_retries * usleep_retry</literal> milliseconds.
</para>
<para>
However, functions that
<link linkend="mysqlnd-ms.pooling">control connection state</link>
are dispatched to all all connections. The retry loop settings are applied
to every connection on which the command is to be run. Thus, such a function
may interrupt program execution for longer than a function that is run
on one server only. For example, <literal>set_autocommit()</literal> is
dispatched to connections and may sleep up to
<literal>(max_retries * usleep_retry) * number_of_open_connections)</literal>
milliseconds. Please, keep this in mind when setting long sleep times
and large retry numbers. Using the default settings of
<literal>max_retries=1</literal>, <literal>usleep_retry=100</literal> and
<literal>lazy_connections=1</literal> it is unlikely that you will
ever see a delay of more than 1 second.
</para>
</section>
<section xml:id="mysqlnd-ms.failover">
<title>Failover</title>
<para>

View file

@ -442,6 +442,18 @@
</entry>
<entry>Since 1.2.0.</entry>
</row>
<row>
<entry>
<literal>transient_error_retries</literal>
</entry>
<entry>
How often an operation has been retried when a transient error was
detected. See also,
<link linkend="ini.mysqlnd-ms-plugin-config-v2.transient_error"><literal>transient_error</literal></link>
plugin configuration file setting.
</entry>
<entry>Since 1.6.0.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
@ -519,6 +531,8 @@ array(26) {
string(1) "0"
["gtid_implicit_commit_injections_failure"]=>
string(1) "0"
["transient_error_retries"]=>
string(1) "0"
}
]]>
</screen>

View file

@ -2399,9 +2399,136 @@ $mysqli->set_charset("latin1");
</para>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.mysqlnd-ms-plugin-config-v2.transient_error">
<term>
<parameter>transient_error</parameter>
<type>object</type>
</term>
<listitem>
<para>
The setting has been introduced in 1.6.0.
</para>
<para>
A database cluster node may reply a transient error to a client. The client
can then repeat the operation on the same node, fail over to a different node
or abort the operation. Per definition is it safe for a client to
retry the same operation on the same node before giving up.
</para>
<para>
<literal>PECL/mysqlnd_ms</literal> can perform the retry
loop on behalf of the application.
By configuring <literal>transient_error</literal> the plugin can be
instructed to repeat operations failing with a certain error code for
a certain maximum number of times with a pause between the retries.
If the transient error disappears during loop execution, it is
hidden from the application. Otherwise, the error is
forwarded to the application by the end of the loop.
</para>
<para>
<example>
<title>Retry loop for transient errors</title>
<programlisting role="ini">
<![CDATA[
{
"myapp": {
"master": {
"master_0": {
"host": "localhost"
}
},
"slave": {
"slave_0": {
"host": "192.168.78.136",
"port": "3306"
}
},
"transient_error": {
"mysql_error_codes": [
1297
],
"max_retries": 2,
"usleep_retry": 100
}
}
}
]]>
</programlisting>
</example>
</para>
<informaltable>
<tgroup cols="3">
<colspec colwidth="1*"/>
<colspec colwidth="7*"/>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Keyword</entry>
<entry>Description</entry>
<entry>Version</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>mysql_error_codes</literal>
</entry>
<entry>
<para>
List of transient error codes. You may add any MySQL error code
to the list. It is possible to consider any error as transient
not only <literal>1297</literal>
(<literal>HY000 (ER_GET_TEMPORARY_ERRMSG),
Message: Got temporary error %d '%s' from %s</literal>).
Before adding other codes but <literal>1297</literal> to the list,
make sure your cluster supports a new attempt without impacting
the state of your application.
</para>
</entry>
<entry>Since 1.6.0.</entry>
</row>
<row>
<entry>
<literal>max_retries</literal>
</entry>
<entry>
<para>
How often to retry an operation which
fails with a transient error before forwarding the
failure to the user.
</para>
<para>
Default: <literal>1</literal>
</para>
</entry>
<entry>Since 1.6.0.</entry>
</row>
<row>
<entry>
<literal>usleep_retry</literal>
</entry>
<entry>
<para>
Milliseconds to sleep between transient error retries.
The value is passed to the C function <function>usleep</function>,
hence the name.
</para>
<para>
Default: <literal>100</literal>
</para>
</entry>
<entry>Since 1.6.0.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
</section>
<section xml:id="mysqlnd-ms.plugin-ini-v1">
<title xmlns="http://docbook.org/ns/docbook">Plugin configuration file (&lt;= 1.0.x)</title>