Description:
The options MYSQL_OPT_READ_TIMEOUT and MYSQL_OPT_WRITE_TIMEOUT have been added to the mysql_options() function in version 4.1.1 of the libmysqlclient. However, for now they're available for windows only.
I think a timeout is very useful for a program using the MySQL client library. It prevents the application from hanging on an I/O operation when the MySQL server does not respond (heavy load, hardware problem, etc.) That's why I tried to make timeout options available on systems other than windows.
How to repeat:
It's possible to simulate a MySQL server which doesn't answer by running iptables after the connection with the server has been established.
# iptables -A INPUT -p tcp --dport 3306 -j DROP
Suggested fix:
Following patch was made against the latest nightly snapshot available on dev.mysql.com. It works on Linux, and should work on other POSIX systems.
diff -u -r mysql-4.1.13-nightly-20050706.orig/include/violite.h mysql-4.1.13-nightly-20050706/include/violite.h
--- mysql-4.1.13-nightly-20050706.orig/include/violite.h 2005-07-06 13:50:35.176417456 +0200
+++ mysql-4.1.13-nightly-20050706/include/violite.h 2005-07-06 13:51:13.854537488 +0200
@@ -37,6 +37,12 @@
VIO_TYPE_SSL, VIO_TYPE_SHARED_MEMORY
};
+typedef enum enum_timeout_type
+{
+ VIO_READ_TIMEOUT,
+ VIO_WRITE_TIMEOUT
+} timeout_type;
+
Vio* vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost);
#ifdef __WIN__
Vio* vio_new_win32pipe(HANDLE hPipe);
@@ -81,7 +87,7 @@
/* Remotes in_addr */
void vio_in_addr(Vio *vio, struct in_addr *in);
my_bool vio_poll_read(Vio *vio,uint timeout);
-void vio_timeout(Vio *vio,uint which, uint timeout);
+void vio_timeout(Vio *vio, timeout_type which, uint milliseconds);
#ifdef HAVE_OPENSSL
#include <openssl/opensslv.h>
@@ -149,7 +155,7 @@
#define vio_close(vio) ((vio)->vioclose)(vio)
#define vio_peer_addr(vio, buf, prt) (vio)->peer_addr(vio, buf, prt)
#define vio_in_addr(vio, in) (vio)->in_addr(vio, in)
-#define vio_timeout(vio, which, seconds) (vio)->timeout(vio, which, seconds)
+#define vio_timeout(vio, which, milliseconds) (vio)->timeout((vio), (which), (milliseconds))
#endif /* defined(HAVE_VIO) && !defined(DONT_MAP_VIO) */
/* This enumerator is used in parser - should be always visible */
@@ -189,7 +195,7 @@
void (*in_addr)(Vio*, struct in_addr*);
my_bool (*should_retry)(Vio*);
int (*vioclose)(Vio*);
- void (*timeout)(Vio*, unsigned int which, unsigned int timeout);
+ void (*timeout)(Vio*, timeout_type which, uint milliseconds);
void *ssl_arg;
#ifdef HAVE_SMEM
HANDLE handle_file_map;
diff -u -r mysql-4.1.13-nightly-20050706.orig/sql/net_serv.cc mysql-4.1.13-nightly-20050706/sql/net_serv.cc
--- mysql-4.1.13-nightly-20050706.orig/sql/net_serv.cc 2005-07-06 13:50:39.429770848 +0200
+++ mysql-4.1.13-nightly-20050706/sql/net_serv.cc 2005-07-06 13:51:13.855537336 +0200
@@ -485,13 +485,10 @@
#endif /* HAVE_COMPRESS */
/* DBUG_DUMP("net",packet,len); */
-#ifndef NO_ALARM
thr_alarm_init(&alarmed);
+#ifndef NO_ALARM
if (net_blocking)
thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff);
-#else
- alarmed=0;
- vio_timeout(net->vio, 1, net->write_timeout);
#endif /* NO_ALARM */
pos=(char*) packet; end=pos+len;
@@ -506,9 +503,10 @@
if (!thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff))
{ /* Always true for client */
my_bool old_mode;
+ uint retry_fcntl = 0;
while (vio_blocking(net->vio, TRUE, &old_mode) < 0)
{
- if (vio_should_retry(net->vio) && retry_count++ < net->retry_count)
+ if (vio_should_retry(net->vio) && retry_fcntl++ < net->retry_count)
continue;
#ifdef EXTRA_DEBUG
fprintf(stderr,
@@ -522,8 +520,8 @@
net->report_error= 1;
goto end;
}
- retry_count=0;
- continue;
+ if (retry_count++ < net->retry_count)
+ continue;
}
}
else
@@ -539,7 +537,7 @@
#endif /* EXTRA_DEBUG */
}
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
- if (vio_errno(net->vio) == SOCKET_EINTR)
+ if (interrupted && retry_count++ < net->retry_count)
{
DBUG_PRINT("warning",("Interrupted write. Retrying..."));
continue;
@@ -683,8 +681,6 @@
#ifndef NO_ALARM
if (net_blocking)
thr_alarm(&alarmed,net->read_timeout,&alarm_buff);
-#else
- vio_timeout(net->vio, 0, net->read_timeout);
#endif /* NO_ALARM */
pos = net->buff + net->where_b; /* net->packet -4 */
@@ -710,10 +706,11 @@
if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */
{
my_bool old_mode;
+ uint retry_fcntl = 0;
while (vio_blocking(net->vio, TRUE, &old_mode) < 0)
{
if (vio_should_retry(net->vio) &&
- retry_count++ < net->retry_count)
+ retry_fcntl++ < net->retry_count)
continue;
DBUG_PRINT("error",
("fcntl returned error %d, aborting thread",
@@ -731,8 +728,8 @@
#endif
goto end;
}
- retry_count=0;
- continue;
+ if (retry_count++ < net->retry_count)
+ continue;
}
}
#endif /* (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER) */
@@ -747,7 +744,7 @@
#endif /* EXTRA_DEBUG */
}
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
- if (vio_should_retry(net->vio))
+ if (interrupted && retry_count++ < net->retry_count)
{
DBUG_PRINT("warning",("Interrupted read. Retrying..."));
continue;
diff -u -r mysql-4.1.13-nightly-20050706.orig/sql-common/client.c mysql-4.1.13-nightly-20050706/sql-common/client.c
--- mysql-4.1.13-nightly-20050706.orig/sql-common/client.c 2005-07-06 13:50:37.151117256 +0200
+++ mysql-4.1.13-nightly-20050706/sql-common/client.c 2005-07-06 13:51:13.857537032 +0200
@@ -1800,9 +1800,17 @@
vio_keepalive(net->vio,TRUE);
/* Override local client variables */
if (mysql->options.read_timeout)
- net->read_timeout= mysql->options.read_timeout;
+ {
+ net->read_timeout = mysql->options.read_timeout;
+ vio_timeout(net->vio, VIO_READ_TIMEOUT,
+ net->read_timeout * 1000 / (net->retry_count + 1));
+ }
if (mysql->options.write_timeout)
- net->write_timeout= mysql->options.write_timeout;
+ {
+ net->write_timeout = mysql->options.write_timeout;
+ vio_timeout(net->vio, VIO_WRITE_TIMEOUT,
+ net->write_timeout * 1000 / (net->retry_count + 1));
+ }
if (mysql->options.max_allowed_packet)
net->max_packet_size= mysql->options.max_allowed_packet;
diff -u -r mysql-4.1.13-nightly-20050706.orig/vio/viosocket.c mysql-4.1.13-nightly-20050706/vio/viosocket.c
--- mysql-4.1.13-nightly-20050706.orig/vio/viosocket.c 2005-07-06 13:50:39.336784984 +0200
+++ mysql-4.1.13-nightly-20050706/vio/viosocket.c 2005-07-06 13:51:13.857537032 +0200
@@ -34,13 +34,7 @@
int r;
DBUG_ENTER("vio_read");
DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size));
-
-#ifdef __WIN__
- r = recv(vio->sd, buf, size,0);
-#else
- errno=0; /* For linux */
- r = read(vio->sd, buf, size);
-#endif /* __WIN__ */
+ r = recv(vio->sd, buf, size, 0);
#ifndef DBUG_OFF
if (r < 0)
{
@@ -57,11 +51,7 @@
int r;
DBUG_ENTER("vio_write");
DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size));
-#ifdef __WIN__
- r = send(vio->sd, buf, size,0);
-#else
- r = write(vio->sd, buf, size);
-#endif /* __WIN__ */
+ r = send(vio->sd, buf, size, 0);
#ifndef DBUG_OFF
if (r < 0)
{
@@ -317,15 +307,33 @@
}
-void vio_timeout(Vio *vio __attribute__((unused)),
- uint which __attribute__((unused)),
- uint timeout __attribute__((unused)))
+void vio_timeout(Vio *vio, timeout_type which, uint milliseconds)
{
#ifdef __WIN__
- ulong wait_timeout= (ulong) timeout * 1000;
- (void) setsockopt(vio->sd, SOL_SOCKET,
- which ? SO_SNDTIMEO : SO_RCVTIMEO, (char*) &wait_timeout,
- sizeof(wait_timeout));
+ ulong wait_timeout;
+#else
+ struct timeval tv;
+#endif /* __WIN__ */
+ int optname;
+ switch (which)
+ {
+ case VIO_READ_TIMEOUT:
+ optname = SO_RCVTIMEO;
+ break;
+ case VIO_WRITE_TIMEOUT:
+ optname = SO_SNDTIMEO;
+ break;
+ default:
+ return;
+ }
+#ifdef __WIN__
+ wait_timeout= (ulong) milliseconds;
+ setsockopt(vio->sd, SOL_SOCKET, optname, (char*) &wait_timeout,
+ sizeof(wait_timeout));
+#else
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = milliseconds % 1000 * 1000;
+ setsockopt(vio->sd, SOL_SOCKET, optname, &tv, sizeof(tv));
#endif /* __WIN__ */
}
Description: The options MYSQL_OPT_READ_TIMEOUT and MYSQL_OPT_WRITE_TIMEOUT have been added to the mysql_options() function in version 4.1.1 of the libmysqlclient. However, for now they're available for windows only. I think a timeout is very useful for a program using the MySQL client library. It prevents the application from hanging on an I/O operation when the MySQL server does not respond (heavy load, hardware problem, etc.) That's why I tried to make timeout options available on systems other than windows. How to repeat: It's possible to simulate a MySQL server which doesn't answer by running iptables after the connection with the server has been established. # iptables -A INPUT -p tcp --dport 3306 -j DROP Suggested fix: Following patch was made against the latest nightly snapshot available on dev.mysql.com. It works on Linux, and should work on other POSIX systems. diff -u -r mysql-4.1.13-nightly-20050706.orig/include/violite.h mysql-4.1.13-nightly-20050706/include/violite.h --- mysql-4.1.13-nightly-20050706.orig/include/violite.h 2005-07-06 13:50:35.176417456 +0200 +++ mysql-4.1.13-nightly-20050706/include/violite.h 2005-07-06 13:51:13.854537488 +0200 @@ -37,6 +37,12 @@ VIO_TYPE_SSL, VIO_TYPE_SHARED_MEMORY }; +typedef enum enum_timeout_type +{ + VIO_READ_TIMEOUT, + VIO_WRITE_TIMEOUT +} timeout_type; + Vio* vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost); #ifdef __WIN__ Vio* vio_new_win32pipe(HANDLE hPipe); @@ -81,7 +87,7 @@ /* Remotes in_addr */ void vio_in_addr(Vio *vio, struct in_addr *in); my_bool vio_poll_read(Vio *vio,uint timeout); -void vio_timeout(Vio *vio,uint which, uint timeout); +void vio_timeout(Vio *vio, timeout_type which, uint milliseconds); #ifdef HAVE_OPENSSL #include <openssl/opensslv.h> @@ -149,7 +155,7 @@ #define vio_close(vio) ((vio)->vioclose)(vio) #define vio_peer_addr(vio, buf, prt) (vio)->peer_addr(vio, buf, prt) #define vio_in_addr(vio, in) (vio)->in_addr(vio, in) -#define vio_timeout(vio, which, seconds) (vio)->timeout(vio, which, seconds) +#define vio_timeout(vio, which, milliseconds) (vio)->timeout((vio), (which), (milliseconds)) #endif /* defined(HAVE_VIO) && !defined(DONT_MAP_VIO) */ /* This enumerator is used in parser - should be always visible */ @@ -189,7 +195,7 @@ void (*in_addr)(Vio*, struct in_addr*); my_bool (*should_retry)(Vio*); int (*vioclose)(Vio*); - void (*timeout)(Vio*, unsigned int which, unsigned int timeout); + void (*timeout)(Vio*, timeout_type which, uint milliseconds); void *ssl_arg; #ifdef HAVE_SMEM HANDLE handle_file_map; diff -u -r mysql-4.1.13-nightly-20050706.orig/sql/net_serv.cc mysql-4.1.13-nightly-20050706/sql/net_serv.cc --- mysql-4.1.13-nightly-20050706.orig/sql/net_serv.cc 2005-07-06 13:50:39.429770848 +0200 +++ mysql-4.1.13-nightly-20050706/sql/net_serv.cc 2005-07-06 13:51:13.855537336 +0200 @@ -485,13 +485,10 @@ #endif /* HAVE_COMPRESS */ /* DBUG_DUMP("net",packet,len); */ -#ifndef NO_ALARM thr_alarm_init(&alarmed); +#ifndef NO_ALARM if (net_blocking) thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff); -#else - alarmed=0; - vio_timeout(net->vio, 1, net->write_timeout); #endif /* NO_ALARM */ pos=(char*) packet; end=pos+len; @@ -506,9 +503,10 @@ if (!thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff)) { /* Always true for client */ my_bool old_mode; + uint retry_fcntl = 0; while (vio_blocking(net->vio, TRUE, &old_mode) < 0) { - if (vio_should_retry(net->vio) && retry_count++ < net->retry_count) + if (vio_should_retry(net->vio) && retry_fcntl++ < net->retry_count) continue; #ifdef EXTRA_DEBUG fprintf(stderr, @@ -522,8 +520,8 @@ net->report_error= 1; goto end; } - retry_count=0; - continue; + if (retry_count++ < net->retry_count) + continue; } } else @@ -539,7 +537,7 @@ #endif /* EXTRA_DEBUG */ } #if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_errno(net->vio) == SOCKET_EINTR) + if (interrupted && retry_count++ < net->retry_count) { DBUG_PRINT("warning",("Interrupted write. Retrying...")); continue; @@ -683,8 +681,6 @@ #ifndef NO_ALARM if (net_blocking) thr_alarm(&alarmed,net->read_timeout,&alarm_buff); -#else - vio_timeout(net->vio, 0, net->read_timeout); #endif /* NO_ALARM */ pos = net->buff + net->where_b; /* net->packet -4 */ @@ -710,10 +706,11 @@ if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */ { my_bool old_mode; + uint retry_fcntl = 0; while (vio_blocking(net->vio, TRUE, &old_mode) < 0) { if (vio_should_retry(net->vio) && - retry_count++ < net->retry_count) + retry_fcntl++ < net->retry_count) continue; DBUG_PRINT("error", ("fcntl returned error %d, aborting thread", @@ -731,8 +728,8 @@ #endif goto end; } - retry_count=0; - continue; + if (retry_count++ < net->retry_count) + continue; } } #endif /* (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER) */ @@ -747,7 +744,7 @@ #endif /* EXTRA_DEBUG */ } #if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_should_retry(net->vio)) + if (interrupted && retry_count++ < net->retry_count) { DBUG_PRINT("warning",("Interrupted read. Retrying...")); continue; diff -u -r mysql-4.1.13-nightly-20050706.orig/sql-common/client.c mysql-4.1.13-nightly-20050706/sql-common/client.c --- mysql-4.1.13-nightly-20050706.orig/sql-common/client.c 2005-07-06 13:50:37.151117256 +0200 +++ mysql-4.1.13-nightly-20050706/sql-common/client.c 2005-07-06 13:51:13.857537032 +0200 @@ -1800,9 +1800,17 @@ vio_keepalive(net->vio,TRUE); /* Override local client variables */ if (mysql->options.read_timeout) - net->read_timeout= mysql->options.read_timeout; + { + net->read_timeout = mysql->options.read_timeout; + vio_timeout(net->vio, VIO_READ_TIMEOUT, + net->read_timeout * 1000 / (net->retry_count + 1)); + } if (mysql->options.write_timeout) - net->write_timeout= mysql->options.write_timeout; + { + net->write_timeout = mysql->options.write_timeout; + vio_timeout(net->vio, VIO_WRITE_TIMEOUT, + net->write_timeout * 1000 / (net->retry_count + 1)); + } if (mysql->options.max_allowed_packet) net->max_packet_size= mysql->options.max_allowed_packet; diff -u -r mysql-4.1.13-nightly-20050706.orig/vio/viosocket.c mysql-4.1.13-nightly-20050706/vio/viosocket.c --- mysql-4.1.13-nightly-20050706.orig/vio/viosocket.c 2005-07-06 13:50:39.336784984 +0200 +++ mysql-4.1.13-nightly-20050706/vio/viosocket.c 2005-07-06 13:51:13.857537032 +0200 @@ -34,13 +34,7 @@ int r; DBUG_ENTER("vio_read"); DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size)); - -#ifdef __WIN__ - r = recv(vio->sd, buf, size,0); -#else - errno=0; /* For linux */ - r = read(vio->sd, buf, size); -#endif /* __WIN__ */ + r = recv(vio->sd, buf, size, 0); #ifndef DBUG_OFF if (r < 0) { @@ -57,11 +51,7 @@ int r; DBUG_ENTER("vio_write"); DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size)); -#ifdef __WIN__ - r = send(vio->sd, buf, size,0); -#else - r = write(vio->sd, buf, size); -#endif /* __WIN__ */ + r = send(vio->sd, buf, size, 0); #ifndef DBUG_OFF if (r < 0) { @@ -317,15 +307,33 @@ } -void vio_timeout(Vio *vio __attribute__((unused)), - uint which __attribute__((unused)), - uint timeout __attribute__((unused))) +void vio_timeout(Vio *vio, timeout_type which, uint milliseconds) { #ifdef __WIN__ - ulong wait_timeout= (ulong) timeout * 1000; - (void) setsockopt(vio->sd, SOL_SOCKET, - which ? SO_SNDTIMEO : SO_RCVTIMEO, (char*) &wait_timeout, - sizeof(wait_timeout)); + ulong wait_timeout; +#else + struct timeval tv; +#endif /* __WIN__ */ + int optname; + switch (which) + { + case VIO_READ_TIMEOUT: + optname = SO_RCVTIMEO; + break; + case VIO_WRITE_TIMEOUT: + optname = SO_SNDTIMEO; + break; + default: + return; + } +#ifdef __WIN__ + wait_timeout= (ulong) milliseconds; + setsockopt(vio->sd, SOL_SOCKET, optname, (char*) &wait_timeout, + sizeof(wait_timeout)); +#else + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = milliseconds % 1000 * 1000; + setsockopt(vio->sd, SOL_SOCKET, optname, &tv, sizeof(tv)); #endif /* __WIN__ */ }