Remove O(N^2) performance issue with multiple SAVEPOINTs.

Subtransaction locks now released en masse at main commit, rather than
repeatedly re-scanning for locks as we ascend the nested transaction tree.
Split transaction state TBLOCK_SUBEND into two states, TBLOCK_SUBCOMMIT
and TBLOCK_SUBRELEASE to allow the commit path to be optimised using
the existing code in ResourceOwnerRelease() which appears to have been
intended for this usage, judging from comments therein.
This commit is contained in:
Simon Riggs 2011-07-19 17:21:24 +01:00
parent 8e5ac74c12
commit 7cb7122800
1 changed files with 70 additions and 32 deletions

View File

@ -118,7 +118,8 @@ typedef enum TBlockState
/* subtransaction states */ /* subtransaction states */
TBLOCK_SUBBEGIN, /* starting a subtransaction */ TBLOCK_SUBBEGIN, /* starting a subtransaction */
TBLOCK_SUBINPROGRESS, /* live subtransaction */ TBLOCK_SUBINPROGRESS, /* live subtransaction */
TBLOCK_SUBEND, /* RELEASE received */ TBLOCK_SUBRELEASE, /* RELEASE received */
TBLOCK_SUBCOMMIT, /* COMMIT received while TBLOCK_SUBINPROGRESS */
TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */ TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */
TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */ TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */ TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
@ -272,7 +273,7 @@ static TransactionId RecordTransactionAbort(bool isSubXact);
static void StartTransaction(void); static void StartTransaction(void);
static void StartSubTransaction(void); static void StartSubTransaction(void);
static void CommitSubTransaction(void); static void CommitSubTransaction(bool isTopLevel);
static void AbortSubTransaction(void); static void AbortSubTransaction(void);
static void CleanupSubTransaction(void); static void CleanupSubTransaction(void);
static void PushTransaction(void); static void PushTransaction(void);
@ -2442,7 +2443,8 @@ StartTransactionCommand(void)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING: case TBLOCK_ABORT_PENDING:
@ -2572,17 +2574,32 @@ CommitTransactionCommand(void)
break; break;
/* /*
* We were issued a COMMIT or RELEASE command, so we end the * We were issued a RELEASE command, so we end the
* current subtransaction and return to the parent transaction. * current subtransaction and return to the parent transaction.
* The parent might be ended too, so repeat till we are all the * The parent might be ended too, so repeat till we find an
* way out or find an INPROGRESS transaction. * INPROGRESS transaction or subtransaction.
*/ */
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
do do
{ {
CommitSubTransaction(); CommitSubTransaction(false);
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
} while (s->blockState == TBLOCK_SUBEND); } while (s->blockState == TBLOCK_SUBRELEASE);
Assert(s->blockState == TBLOCK_INPROGRESS ||
s->blockState == TBLOCK_SUBINPROGRESS);
break;
/*
* We were issued a COMMIT, so we end the current subtransaction
* hierarchy and perform final commit.
*/
case TBLOCK_SUBCOMMIT:
do
{
CommitSubTransaction(true);
s = CurrentTransactionState; /* changed by pop */
} while (s->blockState == TBLOCK_SUBCOMMIT);
/* If we had a COMMIT command, finish off the main xact too */ /* If we had a COMMIT command, finish off the main xact too */
if (s->blockState == TBLOCK_END) if (s->blockState == TBLOCK_END)
{ {
@ -2597,10 +2614,8 @@ CommitTransactionCommand(void)
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
} }
else else
{ elog(ERROR, "CommitTransactionCommand: unexpected state %s",
Assert(s->blockState == TBLOCK_INPROGRESS || BlockStateAsString(s->blockState));
s->blockState == TBLOCK_SUBINPROGRESS);
}
break; break;
/* /*
@ -2814,7 +2829,8 @@ AbortCurrentTransaction(void)
* applies if we get a failure while ending a subtransaction. * applies if we get a failure while ending a subtransaction.
*/ */
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART: case TBLOCK_SUBRESTART:
AbortSubTransaction(); AbortSubTransaction();
@ -3122,7 +3138,8 @@ BeginTransactionBlock(void)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING: case TBLOCK_ABORT_PENDING:
@ -3232,7 +3249,7 @@ EndTransactionBlock(void)
while (s->parent != NULL) while (s->parent != NULL)
{ {
if (s->blockState == TBLOCK_SUBINPROGRESS) if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBEND; s->blockState = TBLOCK_SUBCOMMIT;
else else
elog(FATAL, "EndTransactionBlock: unexpected state %s", elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
@ -3290,7 +3307,8 @@ EndTransactionBlock(void)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING: case TBLOCK_ABORT_PENDING:
@ -3382,7 +3400,8 @@ UserAbortTransactionBlock(void)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING: case TBLOCK_ABORT_PENDING:
@ -3427,7 +3446,8 @@ DefineSavepoint(char *name)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
@ -3483,7 +3503,8 @@ ReleaseSavepoint(List *options)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
@ -3534,7 +3555,7 @@ ReleaseSavepoint(List *options)
for (;;) for (;;)
{ {
Assert(xact->blockState == TBLOCK_SUBINPROGRESS); Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
xact->blockState = TBLOCK_SUBEND; xact->blockState = TBLOCK_SUBRELEASE;
if (xact == target) if (xact == target)
break; break;
xact = xact->parent; xact = xact->parent;
@ -3583,7 +3604,8 @@ RollbackToSavepoint(List *options)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING: case TBLOCK_ABORT_PENDING:
@ -3691,7 +3713,8 @@ BeginInternalSubTransaction(char *name)
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
@ -3726,7 +3749,7 @@ ReleaseCurrentSubTransaction(void)
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
Assert(s->state == TRANS_INPROGRESS); Assert(s->state == TRANS_INPROGRESS);
MemoryContextSwitchTo(CurTransactionContext); MemoryContextSwitchTo(CurTransactionContext);
CommitSubTransaction(); CommitSubTransaction(false);
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
Assert(s->state == TRANS_INPROGRESS); Assert(s->state == TRANS_INPROGRESS);
} }
@ -3757,7 +3780,8 @@ RollbackAndReleaseCurrentSubTransaction(void)
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_ABORT_END: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END:
@ -3831,7 +3855,8 @@ AbortOutOfAnyTransaction(void)
*/ */
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART: case TBLOCK_SUBRESTART:
AbortSubTransaction(); AbortSubTransaction();
@ -3903,7 +3928,8 @@ TransactionBlockStatusCode(void)
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
case TBLOCK_SUBCOMMIT:
case TBLOCK_PREPARE: case TBLOCK_PREPARE:
return 'T'; /* in transaction */ return 'T'; /* in transaction */
case TBLOCK_ABORT: case TBLOCK_ABORT:
@ -3987,9 +4013,13 @@ StartSubTransaction(void)
* *
* The caller has to make sure to always reassign CurrentTransactionState * The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function. * if it has a local pointer to it after calling this function.
*
* isTopLevel means that this CommitSubTransaction() is being issued as a
* sequence of actions leading directly to a main transaction commit
* allowing some actions to be optimised.
*/ */
static void static void
CommitSubTransaction(void) CommitSubTransaction(bool isTopLevel)
{ {
TransactionState s = CurrentTransactionState; TransactionState s = CurrentTransactionState;
@ -4036,15 +4066,21 @@ CommitSubTransaction(void)
/* /*
* The only lock we actually release here is the subtransaction XID lock. * The only lock we actually release here is the subtransaction XID lock.
* The rest just get transferred to the parent resource owner.
*/ */
CurrentResourceOwner = s->curTransactionOwner; CurrentResourceOwner = s->curTransactionOwner;
if (TransactionIdIsValid(s->transactionId)) if (TransactionIdIsValid(s->transactionId))
XactLockTableDelete(s->transactionId); XactLockTableDelete(s->transactionId);
/*
* Other locks should get transferred to their parent resource owner.
* Doing that is an O(N^2) operation, so if isTopLevel then we can just
* leave the lock records as they are, knowing they will all get released
* by the top level commit using ProcReleaseLocks(). We only optimize
* this for commit; aborts may need to do other cleanup.
*/
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_LOCKS, RESOURCE_RELEASE_LOCKS,
true, false); true, isTopLevel);
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_AFTER_LOCKS, RESOURCE_RELEASE_AFTER_LOCKS,
true, false); true, false);
@ -4398,8 +4434,10 @@ BlockStateAsString(TBlockState blockState)
return "SUB BEGIN"; return "SUB BEGIN";
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
return "SUB INPROGRS"; return "SUB INPROGRS";
case TBLOCK_SUBEND: case TBLOCK_SUBRELEASE:
return "SUB END"; return "SUB RELEASE";
case TBLOCK_SUBCOMMIT:
return "SUB COMMIT";
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
return "SUB ABORT"; return "SUB ABORT";
case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_END: