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__ */
}