Fixed bug in how response messages from backends were handled. Now messages are examined if session command cursor is active

. That means that (at least) the first packet  in the message is response to session command. Then if the response is alread
y forwarded to client it is discarded. Otherwise it is routed to client and the command is marked as responded so that the o
ther backend knows to discard its duplicate response.
This commit is contained in:
VilhoRaatikka
2014-03-17 22:26:32 +02:00
parent 90f701be8e
commit 67d9b3afb9

View File

@ -141,10 +141,10 @@ static mysql_sescmd_t* sescmd_cursor_get_command(
static bool sescmd_cursor_next( static bool sescmd_cursor_next(
sescmd_cursor_t* scur); sescmd_cursor_t* scur);
static bool sescmd_reply_to_client( static GWBUF* sescmd_cursor_process_replies(
DCB* client_dcb, DCB* client_dcb,
mysql_sescmd_t* scmd, GWBUF* replybuf,
GWBUF* writebuf); sescmd_cursor_t* scur);
static bool cont_exec_sescmd_in_backend( static bool cont_exec_sescmd_in_backend(
ROUTER_CLIENT_SES* rses, ROUTER_CLIENT_SES* rses,
@ -1020,37 +1020,34 @@ static void clientReply(
"reply_by_statement", "reply_by_statement",
backend_dcb, backend_dcb,
gwbuf_clone(writebuf))); gwbuf_clone(writebuf)));
/** Lock router session */ /** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses)) if (!rses_begin_locked_router_action(router_cli_ses))
{ {
/** Log to debug that router was closed */ /** Log to debug that router was closed */
goto lock_failed; goto lock_failed;
} }
scur = rses_get_sescmd_cursor(router_cli_ses, be_type); scur = rses_get_sescmd_cursor(router_cli_ses, be_type);
/** /**
* Active cursor means that reply is from session command * Active cursor means that reply is from session command
* execution. * execution. Majority of the time there are no session commands
* being executed.
*/ */
if (sescmd_cursor_is_active(scur)) if (sescmd_cursor_is_active(scur))
{ {
mysql_sescmd_t* scmd = sescmd_cursor_get_command(scur); writebuf = sescmd_cursor_process_replies(client_dcb,
sescmd_reply_to_client(client_dcb, scmd, writebuf); writebuf,
/** When sescmd list is empty set cursor passive. */ scur);
if (!sescmd_cursor_next(scur))
{
sescmd_cursor_set_active(scur, false);
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
} }
else if (client_dcb != NULL) /** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
if (writebuf != NULL && client_dcb != NULL)
{ {
/** Write reply to client DCB */ /** Write reply to client DCB */
client_dcb->func.write(client_dcb, writebuf); client_dcb->func.write(client_dcb, writebuf);
ss_dassert(!sescmd_cursor_is_active(scur));
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
LOGIF(LT, (skygw_log_write_flush( LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE, LOGFILE_TRACE,
"%lu [clientReply:rwsplit] client dcb %p, " "%lu [clientReply:rwsplit] client dcb %p, "
@ -1059,6 +1056,7 @@ static void clientReply(
client_dcb, client_dcb,
backend_dcb))); backend_dcb)));
} }
lock_failed: lock_failed:
return; return;
} }
@ -1307,7 +1305,10 @@ static void rses_property_add(
} }
} }
/** Router session must be locked */ /**
* Router session must be locked.
* Return session command pointer if succeed, NULL if failed.
*/
static mysql_sescmd_t* rses_property_get_sescmd( static mysql_sescmd_t* rses_property_get_sescmd(
rses_property_t* prop) rses_property_t* prop)
{ {
@ -1377,66 +1378,89 @@ static void mysql_sescmd_done(
memset(sescmd, 0, sizeof(mysql_sescmd_t)); memset(sescmd, 0, sizeof(mysql_sescmd_t));
} }
/**
* All cases where backend message starts at least with one response to session
/** * command are handled here.
* Write session command reply from backend to client if command haven't yet * Read session commands from property list. If command is already replied,
* been replied. * discard packet. Else send reply to client. In both cases move cursor forward
* Return true if succeed, false if command was already replied. * until all session command replies are handled.
* */
* Router session must be locked */ static GWBUF* sescmd_cursor_process_replies(
static bool sescmd_reply_to_client( DCB* client_dcb,
DCB* client_dcb, GWBUF* replybuf,
mysql_sescmd_t* scmd, sescmd_cursor_t* scur)
GWBUF* writebuf)
{ {
bool succp = false; const size_t headerlen = 4; /*< mysql packet header */
uint8_t* packet;
size_t packetlen;
mysql_sescmd_t* scmd;
CHK_DCB(client_dcb); ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
CHK_MYSQL_SESCMD(scmd);
CHK_GWBUF(writebuf); scmd = sescmd_cursor_get_command(scur);
ss_dassert(SPINLOCK_IS_LOCKED(
&scmd->my_sescmd_prop->rses_prop_rsession->rses_lock)); CHK_DCB(client_dcb);
/** CHK_GWBUF(replybuf);
* This depends on MySQL protoocl and doesn't work with others.
* TODO: write a function to MySQL protocol module and add /**
* a new protocol function 'discard n first messages'. * Walk through packets in the message and the list of session
*commands.
*/ */
if (scmd->my_sescmd_is_replied) while (scmd != NULL && replybuf != NULL)
{ {
size_t len; if (scmd->my_sescmd_is_replied)
uint8_t* packet;
/**
* Skip reply message because it is duplicate of alredy
* replied message.
*/
packet = (uint8_t*)writebuf->start;
len = packet[0];
len += packet[1]*256;
len += packet[2]*256*256;
writebuf = gwbuf_consume(writebuf, len+4);
if (writebuf == NULL || GWBUF_EMPTY(writebuf))
{ {
succp = true; /**
goto return_succp; * Discard heading packets if their related command is
* already replied.
*/
CHK_GWBUF(replybuf);
packet = (uint8_t *)GWBUF_DATA(replybuf);
packetlen = packet[0]+packet[1]*256+packet[2]*256*256;
replybuf = gwbuf_consume(replybuf, packetlen+headerlen);
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"%lu [sescmd_cursor_process_replies] cmd %p "
"is already replied. Discarded %d bytes from "
"the %s replybuffer.",
pthread_self(),
scmd,
packetlen+headerlen,
STRBETYPE(scur->scmd_cur_be_type))));
} }
} else
{
client_dcb->func.write(client_dcb, writebuf); /** Mark the rest session commands as replied */
scmd->my_sescmd_is_replied = true; scmd->my_sescmd_is_replied = true;
succp = true; LOGIF(LT, (skygw_log_write_flush(
LOGIF(LT, (skygw_log_write_flush( LOGFILE_TRACE,
LOGFILE_TRACE, "%lu [sescmd_cursor_process_replies] Marked "
"%lu [sescmd_reply_to_client] Replied cmd %p to client dcb %p.", "cmd %p to as replied. Left message to %s's "
pthread_self(), "buffer for reply.",
scmd, pthread_self(),
client_dcb))); scmd,
STRBETYPE(scur->scmd_cur_be_type))));
return_succp: }
return succp;
if (sescmd_cursor_next(scur))
{
scmd = sescmd_cursor_get_command(scur);
}
else
{
scmd = NULL;
/** All session commands are replied */
scur->scmd_cur_active = false;
}
}
ss_dassert(replybuf == NULL || *scur->scmd_cur_ptr_property == NULL);
return replybuf;
} }
/** /**
* Get the address of current session command. * Get the address of current session command.
* *
@ -1445,9 +1469,8 @@ static mysql_sescmd_t* sescmd_cursor_get_command(
sescmd_cursor_t* scur) sescmd_cursor_t* scur)
{ {
mysql_sescmd_t* scmd; mysql_sescmd_t* scmd;
ss_dassert(SPINLOCK_IS_LOCKED(&scur->scmd_cur_rses->rses_lock));
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
scur->scmd_cur_cmd = rses_property_get_sescmd(*scur->scmd_cur_ptr_property); scur->scmd_cur_cmd = rses_property_get_sescmd(*scur->scmd_cur_ptr_property);
CHK_MYSQL_SESCMD(scur->scmd_cur_cmd); CHK_MYSQL_SESCMD(scur->scmd_cur_cmd);
@ -1524,7 +1547,7 @@ static bool execute_sescmd_in_backend(
bool succp = true; bool succp = true;
int rc = 0; int rc = 0;
sescmd_cursor_t* scur; sescmd_cursor_t* scur;
dcb = rses->rses_dcb[be_type]; dcb = rses->rses_dcb[be_type];
CHK_DCB(dcb); CHK_DCB(dcb);
@ -1535,14 +1558,15 @@ static bool execute_sescmd_in_backend(
* Get cursor pointer and copy of command buffer to cursor. * Get cursor pointer and copy of command buffer to cursor.
*/ */
scur = rses_get_sescmd_cursor(rses, be_type); scur = rses_get_sescmd_cursor(rses, be_type);
/** Return if there are no pending ses commands */ /** Return if there are no pending ses commands */
if (sescmd_cursor_get_command(scur) == NULL) if (sescmd_cursor_get_command(scur) == NULL)
{ {
succp = false; succp = false;
goto return_succp; goto return_succp;
} }
if (!sescmd_cursor_is_active(scur))
if (!sescmd_cursor_is_active(scur))
{ {
/** Cursor is left active when function returns. */ /** Cursor is left active when function returns. */
sescmd_cursor_set_active(scur, true); sescmd_cursor_set_active(scur, true);
@ -1551,6 +1575,7 @@ static bool execute_sescmd_in_backend(
"execute_sescmd_in_backend", "execute_sescmd_in_backend",
dcb, dcb,
sescmd_cursor_clone_querybuf(scur))); sescmd_cursor_clone_querybuf(scur)));
switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { switch (scur->scmd_cur_cmd->my_sescmd_packet_type) {
case COM_CHANGE_USER: case COM_CHANGE_USER:
rc = dcb->func.auth( rc = dcb->func.auth(
@ -1584,57 +1609,10 @@ return_succp:
return succp; return succp;
} }
/**
* Execute session commands when cursor is already active.
*
* Router session must be locked
*
* Return true if there was pending sescmd and sending command to
* backend server succeed. Otherwise false.
*/
static bool cont_exec_sescmd_in_backend(
ROUTER_CLIENT_SES* rses,
backend_type_t be_type)
{
DCB* dcb;
bool succp = true;
int rc = 0;
sescmd_cursor_t* scur;
dcb = rses->rses_dcb[be_type];
CHK_DCB(dcb);
CHK_CLIENT_RSES(rses);
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
scur = rses_get_sescmd_cursor(rses, be_type);
ss_dassert(sescmd_cursor_is_active(scur));
/** Return if there are no pending ses commands */
if (scur->scmd_cur_cmd == NULL)
{
succp = false;
goto return_succp;
}
LOGIF(LT, tracelog_routed_query(rses,
"cont_exec_sescmd_in_backend",
dcb,
sescmd_cursor_clone_querybuf(scur)));
rc = dcb->func.write(dcb, sescmd_cursor_clone_querybuf(scur));
if (rc != 1)
{
succp = false;
}
return_succp:
return succp;
}
/** /**
* Moves cursor to next property and copied address of its sescmd to cursor. * Moves cursor to next property and copied address of its sescmd to cursor.
* Current propery must be non-null. * Current propery must be non-null.
* If current property is the last on the list, *scur->scmd_ptr_property == NULL
* *
* Router session must be locked * Router session must be locked
*/ */
@ -1695,7 +1673,7 @@ static bool sescmd_cursor_next(
} }
else else
{ {
/** Log error, sescmd shouldn't be NULL */ ss_dassert(false); /*< Log error, sescmd shouldn't be NULL */
} }
return_succp: return_succp:
return succp; return succp;