mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Bug#28323: Server crashed in xid cache operations
The problem was that the server did not robustly handle a unilateral roll back issued by the Resource Manager (RM) due to a resource deadlock within the transaction branch. By not acknowledging the roll back, the server (TM) would eventually corrupt the XA transaction state and crash. The solution is to mark the transaction as rollback-only if the RM indicates that it rolled back its branch of the transaction.
This commit is contained in:
@ -90,9 +90,57 @@ const char *command_name[]={
|
||||
};
|
||||
|
||||
const char *xa_state_names[]={
|
||||
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
|
||||
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
|
||||
};
|
||||
|
||||
/**
|
||||
Mark a XA transaction as rollback-only if the RM unilaterally
|
||||
rolled back the transaction branch.
|
||||
|
||||
@note If a rollback was requested by the RM, this function sets
|
||||
the appropriate rollback error code and transits the state
|
||||
to XA_ROLLBACK_ONLY.
|
||||
|
||||
@return TRUE if transaction was rolled back or if the transaction
|
||||
state is XA_ROLLBACK_ONLY. FALSE otherwise.
|
||||
*/
|
||||
static bool xa_trans_rolled_back(XID_STATE *xid_state)
|
||||
{
|
||||
if (xid_state->rm_error)
|
||||
{
|
||||
switch (xid_state->rm_error) {
|
||||
case ER_LOCK_WAIT_TIMEOUT:
|
||||
my_error(ER_XA_RBTIMEOUT, MYF(0));
|
||||
break;
|
||||
case ER_LOCK_DEADLOCK:
|
||||
my_error(ER_XA_RBDEADLOCK, MYF(0));
|
||||
break;
|
||||
default:
|
||||
my_error(ER_XA_RBROLLBACK, MYF(0));
|
||||
}
|
||||
xid_state->xa_state= XA_ROLLBACK_ONLY;
|
||||
}
|
||||
|
||||
return (xid_state->xa_state == XA_ROLLBACK_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
Rollback work done on behalf of at ransaction branch.
|
||||
*/
|
||||
static bool xa_trans_rollback(THD *thd)
|
||||
{
|
||||
bool status= test(ha_rollback(thd));
|
||||
|
||||
thd->options&= ~(ulong) OPTION_BEGIN;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||
xid_cache_delete(&thd->transaction.xid_state);
|
||||
thd->transaction.xid_state.xa_state= XA_NOTR;
|
||||
thd->transaction.xid_state.rm_error= 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
static bool do_command(THD *thd);
|
||||
#endif // EMBEDDED_LIBRARY
|
||||
@ -5070,6 +5118,7 @@ create_sp_error:
|
||||
}
|
||||
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
|
||||
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
||||
thd->transaction.xid_state.rm_error= 0;
|
||||
thd->transaction.xid_state.xid.set(thd->lex->xid);
|
||||
xid_cache_insert(&thd->transaction.xid_state);
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
@ -5095,6 +5144,8 @@ create_sp_error:
|
||||
my_error(ER_XAER_NOTA, MYF(0));
|
||||
break;
|
||||
}
|
||||
if (xa_trans_rolled_back(&thd->transaction.xid_state))
|
||||
break;
|
||||
thd->transaction.xid_state.xa_state=XA_IDLE;
|
||||
send_ok(thd);
|
||||
break;
|
||||
@ -5126,6 +5177,12 @@ create_sp_error:
|
||||
XID_STATE *xs=xid_cache_search(thd->lex->xid);
|
||||
if (!xs || xs->in_thd)
|
||||
my_error(ER_XAER_NOTA, MYF(0));
|
||||
else if (xa_trans_rolled_back(xs))
|
||||
{
|
||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
||||
xid_cache_delete(xs);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
|
||||
@ -5134,6 +5191,11 @@ create_sp_error:
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (xa_trans_rolled_back(&thd->transaction.xid_state))
|
||||
{
|
||||
xa_trans_rollback(thd);
|
||||
break;
|
||||
}
|
||||
if (thd->transaction.xid_state.xa_state == XA_IDLE &&
|
||||
thd->lex->xa_opt == XA_ONE_PHASE)
|
||||
{
|
||||
@ -5180,28 +5242,26 @@ create_sp_error:
|
||||
my_error(ER_XAER_NOTA, MYF(0));
|
||||
else
|
||||
{
|
||||
bool ok= !xa_trans_rolled_back(xs);
|
||||
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
||||
xid_cache_delete(xs);
|
||||
send_ok(thd);
|
||||
if (ok)
|
||||
send_ok(thd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (thd->transaction.xid_state.xa_state != XA_IDLE &&
|
||||
thd->transaction.xid_state.xa_state != XA_PREPARED)
|
||||
thd->transaction.xid_state.xa_state != XA_PREPARED &&
|
||||
thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY)
|
||||
{
|
||||
my_error(ER_XAER_RMFAIL, MYF(0),
|
||||
xa_state_names[thd->transaction.xid_state.xa_state]);
|
||||
break;
|
||||
}
|
||||
if (ha_rollback(thd))
|
||||
if (xa_trans_rollback(thd))
|
||||
my_error(ER_XAER_RMERR, MYF(0));
|
||||
else
|
||||
send_ok(thd);
|
||||
thd->options&= ~(ulong) OPTION_BEGIN;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||
xid_cache_delete(&thd->transaction.xid_state);
|
||||
thd->transaction.xid_state.xa_state=XA_NOTR;
|
||||
break;
|
||||
case SQLCOM_XA_RECOVER:
|
||||
res= mysql_xa_recover(thd);
|
||||
|
Reference in New Issue
Block a user