Related to bug #217, added command 'fail accept <errno>' to debug client's commands.

Reproduce #217 by connecting with telnet to debug client interface and execute command:

fail accept 23

and try to execute read query, for example, with

mysql -h 127.0.0.1 -P 4008 -u maxuser -pmaxpwd -e 'select current_user(), @@server_id'
This commit is contained in:
vraatikka
2013-09-24 15:04:12 +03:00
parent 3b3d3dbfe0
commit a7c3cd5f30
4 changed files with 234 additions and 183 deletions

View File

@ -216,6 +216,8 @@ memset(dcb_fake_write_errno, 0, sizeof(unsigned char)*1024);
memset(dcb_fake_write_ev, 0, sizeof(__int32_t)*1024); memset(dcb_fake_write_ev, 0, sizeof(__int32_t)*1024);
fail_next_backend_fd = false; fail_next_backend_fd = false;
fail_next_client_fd = false; fail_next_client_fd = false;
fail_next_accept = false;
fail_accept_errno = 0;
#endif #endif
l = atexit(skygw_logmanager_exit); l = atexit(skygw_logmanager_exit);

View File

@ -190,6 +190,8 @@ unsigned char dcb_fake_write_errno[1024];
__int32_t dcb_fake_write_ev[1024]; __int32_t dcb_fake_write_ev[1024];
bool fail_next_backend_fd; bool fail_next_backend_fd;
bool fail_next_client_fd; bool fail_next_client_fd;
bool fail_next_accept;
int fail_accept_errno;
#endif #endif
/* A few useful macros */ /* A few useful macros */

View File

@ -127,7 +127,11 @@ mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mys
affected_rows = in_affected_rows; affected_rows = in_affected_rows;
mysql_payload_size = sizeof(field_count) + sizeof(affected_rows) + sizeof(insert_id) + sizeof(mysql_server_status) + sizeof(mysql_warning_count); mysql_payload_size = sizeof(field_count) +
sizeof(affected_rows) +
sizeof(insert_id) +
sizeof(mysql_server_status) +
sizeof(mysql_warning_count);
if (mysql_message != NULL) { if (mysql_message != NULL) {
mysql_payload_size += strlen(mysql_message); mysql_payload_size += strlen(mysql_message);
@ -513,7 +517,6 @@ int gw_read_client_event(DCB* dcb) {
int rc = 0; int rc = 0;
CHK_DCB(dcb); CHK_DCB(dcb);
protocol = DCB_PROTOCOL(dcb, MySQLProtocol); protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
CHK_PROTOCOL(protocol); CHK_PROTOCOL(protocol);
/** /**
@ -611,7 +614,6 @@ int gw_read_client_event(DCB* dcb) {
* Read all the data that is available into a chain of buffers * Read all the data that is available into a chain of buffers
*/ */
{ {
/* int len; */
GWBUF *queue = NULL; GWBUF *queue = NULL;
GWBUF *gw_buffer = NULL; GWBUF *gw_buffer = NULL;
uint8_t *ptr_buff = NULL; uint8_t *ptr_buff = NULL;
@ -639,34 +641,20 @@ int gw_read_client_event(DCB* dcb) {
/* Now, we are assuming in the first buffer there is /* Now, we are assuming in the first buffer there is
* the information form mysql command */ * the information form mysql command */
queue = gw_buffer; queue = gw_buffer;
/* len = GWBUF_LENGTH(queue); */
ptr_buff = GWBUF_DATA(queue); ptr_buff = GWBUF_DATA(queue);
/* get mysql commang at fifth byte */ /* get mysql commang at fifth byte */
if (ptr_buff) { if (ptr_buff) {
mysql_command = ptr_buff[4]; mysql_command = ptr_buff[4];
} }
if (mysql_command == '\x03') {
/**
* SQL Trace here.
* Length can be calculated and it must be passed as
* argument.
*/
/* this is a standard MySQL query !!!! */
}
/** /**
* Routing Client input to Backend * Without rsession there is no access to backend.
* COM_QUIT : close client dcb
* else : write custom error to client dcb.
*/ */
/* Do not route the query without session! */
if(rsession == NULL) { if(rsession == NULL) {
/** COM_QUIT */
if (mysql_command == '\x01') { if (mysql_command == '\x01') {
/**
* COM_QUIT handling
*
* fprintf(stderr, "COM_QUIT received with
* no connected backends from %i\n", dcb->fd);
*/
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_DEBUG, LOGFILE_DEBUG,
"%lu [gw_read_client_event] Client read " "%lu [gw_read_client_event] Client read "
@ -682,63 +670,52 @@ int gw_read_client_event(DCB* dcb) {
dcb, dcb,
1, 1,
0, 0,
"Connection to backend lost"); "Query routing failed. Connection to "
"backend lost");
protocol->state = MYSQL_IDLE; protocol->state = MYSQL_IDLE;
} }
rc = 1; rc = 1;
goto return_rc; goto return_rc;
} }
/* We can route the query */ /** Route COM_QUIT to backend */
/* COM_QUIT handling */
if (mysql_command == '\x01') { if (mysql_command == '\x01') {
skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Before routeQuery. "
"dcb %p.",
pthread_self(),
dcb);
/**
* fprintf(stderr, "COM_QUIT received from %i and
* passed to backed\n", dcb->fd);
* this will propagate COM_QUIT to backend(s)
* fprintf(stderr, "<<< Routing the COM_QUIT ...\n");
*/
router->routeQuery(router_instance, rsession, queue); router->routeQuery(router_instance, rsession, queue);
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_DEBUG, LOGFILE_DEBUG,
"%lu [gw_read_client_event] After routeQuery. " "%lu [gw_read_client_event] Routed COM_QUIT to "
"dcb %p.", "backend. Close client dcb %p",
pthread_self(), pthread_self(),
dcb); dcb);
/* close client connection */ /** close client connection */
(dcb->func).close(dcb); (dcb->func).close(dcb);
rc = 1; rc = 1;
goto return_rc;
} }
else
/* MySQL Command Routing */ {
protocol->state = MYSQL_ROUTING; /** Route other commands to backend */
protocol->state = MYSQL_ROUTING;
/* writing in the backend buffer queue, via routeQuery */ rc = router->routeQuery(router_instance, rsession, queue);
//fprintf(stderr, "<<< Routing the Query ...\n"); /** succeed */
rc = router->routeQuery(router_instance, rsession, queue); if (rc == 1) {
protocol->state = MYSQL_WAITING_RESULT;
if (rc == 1) { rc = 0; /**< here '0' means success */
protocol->state = MYSQL_WAITING_RESULT; } else {
} else { mysql_send_custom_error(dcb,
mysql_send_custom_error(dcb, 1,
1, 0,
0, "Query routing failed. "
"Connection to backend lost."); "Connection to backend "
protocol->state = MYSQL_IDLE; "lost.");
goto return_rc; protocol->state = MYSQL_IDLE;
}
} }
} goto return_rc;
} /* MYSQL_IDLE, MYSQL_WAITING_RESULT */
break; break;
default: default:
// todo
break; break;
} }
rc = 0; rc = 0;
@ -950,75 +927,84 @@ int gw_MySQLAccept(DCB *listener)
&addrlen); &addrlen);
eno = errno; eno = errno;
#if defined(SS_DEBUG)
if (fail_next_accept) {
c_sock = -1;
eno = fail_accept_errno;
i = 10;
fail_next_accept = false;
fail_accept_errno = 0;
}
#endif /* SS_DEBUG */
if (c_sock == -1) { if (c_sock == -1) {
if (eno == EAGAIN || if (eno == EAGAIN ||
eno == EWOULDBLOCK) eno == EWOULDBLOCK)
{ {
rc = 1; rc = 1;
/* We have processed all incoming connections. */ /* We have processed all incoming connections. */
break; break;
} }
else if (eno == ENFILE) else if (eno == ENFILE)
{ {
/** /**
* Exceeded system's max. number of files limit. * Exceeded system's max. number of files limit.
*/ */
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"%lu [gw_MySQLAccept] Error %d, %s.", "%lu [gw_MySQLAccept] Error %d, %s.",
pthread_self(), pthread_self(),
eno, eno,
strerror(eno)); strerror(eno));
i++; i++;
usleep(100*i*i); usleep(100*i*i);
if (i<10) { if (i<10) {
goto retry_accept; goto retry_accept;
} }
rc = 1; rc = 1;
goto return_rc; goto return_rc;
} }
else if (eno == EMFILE) else if (eno == EMFILE)
{ {
/** /**
* Exceeded processes max. number of files limit. * Exceeded processes max. number of files limit.
*/ */
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"%lu [gw_MySQLAccept] Error %d, %s.", "%lu [gw_MySQLAccept] Error %d, %s.",
pthread_self(), pthread_self(),
eno, eno,
strerror(eno)); strerror(eno));
i++; i++;
usleep(100*i*i); usleep(100*i*i);
if (i<10) { if (i<10) {
goto retry_accept; goto retry_accept;
} }
rc = 1; rc = 1;
goto return_rc; goto return_rc;
} }
else else
{ {
/** /**
* Other error. * Other error.
*/ */
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"%lu [gw_MySQLAccept] Error %d, %s.", "%lu [gw_MySQLAccept] Error %d, %s.",
pthread_self(), pthread_self(),
eno, eno,
strerror(eno)); strerror(eno));
ss_dassert(false); ss_dassert(false);
break; break;
} /* if (eno == ..) */ } /* if (eno == ..) */
} /* if (c_sock == -1) */ } /* if (c_sock == -1) */
/* reset counter */ /* reset counter */
i = 0; i = 0;
listener->stats.n_accepts++; listener->stats.n_accepts++;
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_TRACE, LOGFILE_TRACE,
@ -1027,20 +1013,20 @@ int gw_MySQLAccept(DCB *listener)
c_sock); c_sock);
conn_open[c_sock] = true; conn_open[c_sock] = true;
#endif #endif
fprintf(stderr, fprintf(stderr,
"Processing %i connection fd %i for listener %i\n", "Processing %i connection fd %i for listener %i\n",
listener->stats.n_accepts, listener->stats.n_accepts,
c_sock, c_sock,
listener->fd); listener->fd);
// set nonblocking // set nonblocking
setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen); setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen);
setnonblocking(c_sock); setnonblocking(c_sock);
client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
client_dcb->service = listener->session->service; client_dcb->service = listener->session->service;
client_dcb->fd = c_sock; client_dcb->fd = c_sock;
client_dcb->remote = strdup(inet_ntoa(local.sin_addr)); client_dcb->remote = strdup(inet_ntoa(local.sin_addr));
protocol = mysql_protocol_init(client_dcb, c_sock); protocol = mysql_protocol_init(client_dcb, c_sock);
ss_dassert(protocol != NULL); ss_dassert(protocol != NULL);
@ -1058,46 +1044,46 @@ int gw_MySQLAccept(DCB *listener)
goto return_rc; goto return_rc;
} }
client_dcb->protocol = protocol; client_dcb->protocol = protocol;
// assign function poiters to "func" field // assign function poiters to "func" field
memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL));
//send handshake to the client_dcb //send handshake to the client_dcb
MySQLSendHandshake(client_dcb); MySQLSendHandshake(client_dcb);
// client protocol state change // client protocol state change
protocol->state = MYSQL_AUTH_SENT; protocol->state = MYSQL_AUTH_SENT;
/** /**
* Set new descriptor to event set. At the same time, * Set new descriptor to event set. At the same time,
* change state to DCB_STATE_POLLING so that * change state to DCB_STATE_POLLING so that
* thread which wakes up sees correct state. * thread which wakes up sees correct state.
*/ */
if (poll_add_dcb(client_dcb) == -1) if (poll_add_dcb(client_dcb) == -1)
{ {
/** delete client_dcb */ /** delete client_dcb */
dcb_close(client_dcb); dcb_close(client_dcb);
/** Previous state is recovered in poll_add_dcb. */ /** Previous state is recovered in poll_add_dcb. */
skygw_log_write_flush( skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"%lu [gw_MySQLAccept] Failed to add dcb %p for " "%lu [gw_MySQLAccept] Failed to add dcb %p for "
"fd %d to epoll set.", "fd %d to epoll set.",
pthread_self(), pthread_self(),
client_dcb, client_dcb,
client_dcb->fd); client_dcb->fd);
rc = 1; rc = 1;
goto return_rc; goto return_rc;
} }
else else
{ {
skygw_log_write( skygw_log_write(
LOGFILE_TRACE, LOGFILE_TRACE,
"%lu [gw_MySQLAccept] Added dcb %p for fd " "%lu [gw_MySQLAccept] Added dcb %p for fd "
"%d to epoll set.", "%d to epoll set.",
pthread_self(), pthread_self(),
client_dcb, client_dcb,
client_dcb->fd); client_dcb->fd);
} }
} /**< while 1 */ } /**< while 1 */
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
if (rc == 0) { if (rc == 0) {
CHK_DCB(client_dcb); CHK_DCB(client_dcb);
@ -1105,9 +1091,9 @@ int gw_MySQLAccept(DCB *listener)
CHK_PROTOCOL(protocol); CHK_PROTOCOL(protocol);
} }
#endif #endif
return_rc: return_rc:
return rc; return rc;
} }
/* /*
*/ */

View File

@ -43,6 +43,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <service.h> #include <service.h>
#include <session.h> #include <session.h>
#include <router.h> #include <router.h>
@ -189,10 +190,21 @@ static void disable_log_action(DCB *, char *);
* * The subcommands of the enable command * * The subcommands of the enable command
* */ * */
struct subcommand enableoptions[] = { struct subcommand enableoptions[] = {
{ "log", 1, enable_log_action, "Enable Log options for MaxScale, options trace | error | message E.g. enable log message.", {
{ARG_TYPE_STRING, 0, 0} }, "log",
{ NULL, 0, NULL, NULL, 1,
{0, 0, 0} } enable_log_action,
"Enable Log options for MaxScale, options trace | error | "
"message E.g. enable log message.",
{ARG_TYPE_STRING, 0, 0}
},
{
NULL,
0,
NULL,
NULL,
{0, 0, 0}
}
}; };
@ -200,17 +212,28 @@ struct subcommand enableoptions[] = {
* * The subcommands of the disable command * * The subcommands of the disable command
* */ * */
struct subcommand disableoptions[] = { struct subcommand disableoptions[] = {
{ "log", 1, disable_log_action, "Disable Log for MaxScale, Options: trace | error | message E.g. disable log trace", {
{ARG_TYPE_STRING, 0, 0} }, "log",
{ NULL, 0, NULL, NULL, 1,
{0, 0, 0} } disable_log_action,
"Disable Log for MaxScale, Options: trace | error | message E.g. "
"disable log trace",
{ARG_TYPE_STRING, 0, 0}
},
{
NULL,
0,
NULL,
NULL,
{0, 0, 0}
}
}; };
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
static void fail_backendfd(void); static void fail_backendfd(void);
static void fail_clientfd(void); static void fail_clientfd(void);
static void fail_accept(DCB* dcb, char* arg1);
/** /**
* * The subcommands of the fail command * * The subcommands of the fail command
* */ * */
@ -229,6 +252,13 @@ struct subcommand failoptions[] = {
"Fail client socket for next operation.", "Fail client socket for next operation.",
{ARG_TYPE_STRING, 0, 0} {ARG_TYPE_STRING, 0, 0}
}, },
{
"accept",
1,
fail_accept,
"Fail to accept next client connection.",
{ARG_TYPE_STRING, 0, 0}
},
{ {
NULL, NULL,
0, 0,
@ -735,11 +765,42 @@ static void disable_log_action(DCB *dcb, char *arg1) {
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
static void fail_backendfd(void) static void fail_backendfd(void)
{ {
fail_next_backend_fd = TRUE; fail_next_backend_fd = true;
} }
static void fail_clientfd(void) static void fail_clientfd(void)
{ {
fail_next_client_fd = TRUE; fail_next_client_fd = true;
}
static void fail_accept(
DCB* dcb,
char* arg1)
{
fail_accept_errno = atoi(arg1);
switch(fail_accept_errno) {
case EAGAIN:
// case EWOULDBLOCK:
case EBADF:
case EINTR:
case EINVAL:
case EMFILE:
case ENFILE:
case ENOTSOCK:
case EOPNOTSUPP:
case ENOBUFS:
case ENOMEM:
case EPROTO:
fail_next_accept = true;
break;
default:
dcb_printf(dcb,
"[%d, %s] is not valid errno for accept.\n",
fail_accept_errno,
strerror(fail_accept_errno));
return ;
}
} }
#endif #endif