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:
parent
90f701be8e
commit
67d9b3afb9
@ -141,10 +141,10 @@ static mysql_sescmd_t* sescmd_cursor_get_command(
|
||||
static bool sescmd_cursor_next(
|
||||
sescmd_cursor_t* scur);
|
||||
|
||||
static bool sescmd_reply_to_client(
|
||||
DCB* client_dcb,
|
||||
mysql_sescmd_t* scmd,
|
||||
GWBUF* writebuf);
|
||||
static GWBUF* sescmd_cursor_process_replies(
|
||||
DCB* client_dcb,
|
||||
GWBUF* replybuf,
|
||||
sescmd_cursor_t* scur);
|
||||
|
||||
static bool cont_exec_sescmd_in_backend(
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
@ -1020,37 +1020,34 @@ static void clientReply(
|
||||
"reply_by_statement",
|
||||
backend_dcb,
|
||||
gwbuf_clone(writebuf)));
|
||||
|
||||
/** Lock router session */
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
/** Log to debug that router was closed */
|
||||
goto lock_failed;
|
||||
}
|
||||
|
||||
scur = rses_get_sescmd_cursor(router_cli_ses, be_type);
|
||||
/**
|
||||
* 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))
|
||||
{
|
||||
mysql_sescmd_t* scmd = sescmd_cursor_get_command(scur);
|
||||
sescmd_reply_to_client(client_dcb, scmd, writebuf);
|
||||
/** When sescmd list is empty set cursor passive. */
|
||||
if (!sescmd_cursor_next(scur))
|
||||
{
|
||||
sescmd_cursor_set_active(scur, false);
|
||||
}
|
||||
/** Unlock router session */
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
writebuf = sescmd_cursor_process_replies(client_dcb,
|
||||
writebuf,
|
||||
scur);
|
||||
|
||||
}
|
||||
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 */
|
||||
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(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [clientReply:rwsplit] client dcb %p, "
|
||||
@ -1059,6 +1056,7 @@ static void clientReply(
|
||||
client_dcb,
|
||||
backend_dcb)));
|
||||
}
|
||||
|
||||
lock_failed:
|
||||
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(
|
||||
rses_property_t* prop)
|
||||
{
|
||||
@ -1377,66 +1378,89 @@ static void mysql_sescmd_done(
|
||||
memset(sescmd, 0, sizeof(mysql_sescmd_t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write session command reply from backend to client if command haven't yet
|
||||
* been replied.
|
||||
* Return true if succeed, false if command was already replied.
|
||||
*
|
||||
* Router session must be locked */
|
||||
static bool sescmd_reply_to_client(
|
||||
DCB* client_dcb,
|
||||
mysql_sescmd_t* scmd,
|
||||
GWBUF* writebuf)
|
||||
/**
|
||||
* All cases where backend message starts at least with one response to session
|
||||
* command are handled here.
|
||||
* Read session commands from property list. If command is already replied,
|
||||
* discard packet. Else send reply to client. In both cases move cursor forward
|
||||
* until all session command replies are handled.
|
||||
*/
|
||||
static GWBUF* sescmd_cursor_process_replies(
|
||||
DCB* client_dcb,
|
||||
GWBUF* replybuf,
|
||||
sescmd_cursor_t* scur)
|
||||
{
|
||||
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);
|
||||
CHK_MYSQL_SESCMD(scmd);
|
||||
CHK_GWBUF(writebuf);
|
||||
ss_dassert(SPINLOCK_IS_LOCKED(
|
||||
&scmd->my_sescmd_prop->rses_prop_rsession->rses_lock));
|
||||
/**
|
||||
* 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'.
|
||||
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
|
||||
|
||||
scmd = sescmd_cursor_get_command(scur);
|
||||
|
||||
CHK_DCB(client_dcb);
|
||||
CHK_GWBUF(replybuf);
|
||||
|
||||
/**
|
||||
* Walk through packets in the message and the list of session
|
||||
*commands.
|
||||
*/
|
||||
if (scmd->my_sescmd_is_replied)
|
||||
{
|
||||
size_t len;
|
||||
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))
|
||||
while (scmd != NULL && replybuf != NULL)
|
||||
{
|
||||
if (scmd->my_sescmd_is_replied)
|
||||
{
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
||||
client_dcb->func.write(client_dcb, writebuf);
|
||||
scmd->my_sescmd_is_replied = true;
|
||||
succp = true;
|
||||
LOGIF(LT, (skygw_log_write_flush(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [sescmd_reply_to_client] Replied cmd %p to client dcb %p.",
|
||||
pthread_self(),
|
||||
scmd,
|
||||
client_dcb)));
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
else
|
||||
{
|
||||
/** Mark the rest session commands as replied */
|
||||
scmd->my_sescmd_is_replied = true;
|
||||
LOGIF(LT, (skygw_log_write_flush(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [sescmd_cursor_process_replies] Marked "
|
||||
"cmd %p to as replied. Left message to %s's "
|
||||
"buffer for reply.",
|
||||
pthread_self(),
|
||||
scmd,
|
||||
STRBETYPE(scur->scmd_cur_be_type))));
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
@ -1445,9 +1469,8 @@ static mysql_sescmd_t* sescmd_cursor_get_command(
|
||||
sescmd_cursor_t* scur)
|
||||
{
|
||||
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);
|
||||
|
||||
CHK_MYSQL_SESCMD(scur->scmd_cur_cmd);
|
||||
@ -1524,7 +1547,7 @@ static bool execute_sescmd_in_backend(
|
||||
bool succp = true;
|
||||
int rc = 0;
|
||||
sescmd_cursor_t* scur;
|
||||
|
||||
|
||||
dcb = rses->rses_dcb[be_type];
|
||||
|
||||
CHK_DCB(dcb);
|
||||
@ -1535,14 +1558,15 @@ static bool execute_sescmd_in_backend(
|
||||
* Get cursor pointer and copy of command buffer to cursor.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
if (!sescmd_cursor_is_active(scur))
|
||||
|
||||
if (!sescmd_cursor_is_active(scur))
|
||||
{
|
||||
/** Cursor is left active when function returns. */
|
||||
sescmd_cursor_set_active(scur, true);
|
||||
@ -1551,6 +1575,7 @@ static bool execute_sescmd_in_backend(
|
||||
"execute_sescmd_in_backend",
|
||||
dcb,
|
||||
sescmd_cursor_clone_querybuf(scur)));
|
||||
|
||||
switch (scur->scmd_cur_cmd->my_sescmd_packet_type) {
|
||||
case COM_CHANGE_USER:
|
||||
rc = dcb->func.auth(
|
||||
@ -1584,57 +1609,10 @@ 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.
|
||||
* 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
|
||||
*/
|
||||
@ -1695,7 +1673,7 @@ static bool sescmd_cursor_next(
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Log error, sescmd shouldn't be NULL */
|
||||
ss_dassert(false); /*< Log error, sescmd shouldn't be NULL */
|
||||
}
|
||||
return_succp:
|
||||
return succp;
|
||||
|
Loading…
x
Reference in New Issue
Block a user