Skip to content

Commit 7cb7122

Browse files
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.
1 parent 8e5ac74 commit 7cb7122

File tree

1 file changed

+70
-32
lines changed
  • src/backend/access/transam

1 file changed

+70
-32
lines changed

src/backend/access/transam/xact.c

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ typedef enum TBlockState
118118
/* subtransaction states */
119119
TBLOCK_SUBBEGIN, /* starting a subtransaction */
120120
TBLOCK_SUBINPROGRESS, /* live subtransaction */
121-
TBLOCK_SUBEND, /* RELEASE received */
121+
TBLOCK_SUBRELEASE, /* RELEASE received */
122+
TBLOCK_SUBCOMMIT, /* COMMIT received while TBLOCK_SUBINPROGRESS */
122123
TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */
123124
TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
124125
TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
@@ -272,7 +273,7 @@ static TransactionId RecordTransactionAbort(bool isSubXact);
272273
static void StartTransaction(void);
273274

274275
static void StartSubTransaction(void);
275-
static void CommitSubTransaction(void);
276+
static void CommitSubTransaction(bool isTopLevel);
276277
static void AbortSubTransaction(void);
277278
static void CleanupSubTransaction(void);
278279
static void PushTransaction(void);
@@ -2442,7 +2443,8 @@ StartTransactionCommand(void)
24422443
case TBLOCK_BEGIN:
24432444
case TBLOCK_SUBBEGIN:
24442445
case TBLOCK_END:
2445-
case TBLOCK_SUBEND:
2446+
case TBLOCK_SUBRELEASE:
2447+
case TBLOCK_SUBCOMMIT:
24462448
case TBLOCK_ABORT_END:
24472449
case TBLOCK_SUBABORT_END:
24482450
case TBLOCK_ABORT_PENDING:
@@ -2572,17 +2574,32 @@ CommitTransactionCommand(void)
25722574
break;
25732575

25742576
/*
2575-
* We were issued a COMMIT or RELEASE command, so we end the
2577+
* We were issued a RELEASE command, so we end the
25762578
* current subtransaction and return to the parent transaction.
2577-
* The parent might be ended too, so repeat till we are all the
2578-
* way out or find an INPROGRESS transaction.
2579+
* The parent might be ended too, so repeat till we find an
2580+
* INPROGRESS transaction or subtransaction.
25792581
*/
2580-
case TBLOCK_SUBEND:
2582+
case TBLOCK_SUBRELEASE:
25812583
do
25822584
{
2583-
CommitSubTransaction();
2585+
CommitSubTransaction(false);
25842586
s = CurrentTransactionState; /* changed by pop */
2585-
} while (s->blockState == TBLOCK_SUBEND);
2587+
} while (s->blockState == TBLOCK_SUBRELEASE);
2588+
2589+
Assert(s->blockState == TBLOCK_INPROGRESS ||
2590+
s->blockState == TBLOCK_SUBINPROGRESS);
2591+
break;
2592+
2593+
/*
2594+
* We were issued a COMMIT, so we end the current subtransaction
2595+
* hierarchy and perform final commit.
2596+
*/
2597+
case TBLOCK_SUBCOMMIT:
2598+
do
2599+
{
2600+
CommitSubTransaction(true);
2601+
s = CurrentTransactionState; /* changed by pop */
2602+
} while (s->blockState == TBLOCK_SUBCOMMIT);
25862603
/* If we had a COMMIT command, finish off the main xact too */
25872604
if (s->blockState == TBLOCK_END)
25882605
{
@@ -2597,10 +2614,8 @@ CommitTransactionCommand(void)
25972614
s->blockState = TBLOCK_DEFAULT;
25982615
}
25992616
else
2600-
{
2601-
Assert(s->blockState == TBLOCK_INPROGRESS ||
2602-
s->blockState == TBLOCK_SUBINPROGRESS);
2603-
}
2617+
elog(ERROR, "CommitTransactionCommand: unexpected state %s",
2618+
BlockStateAsString(s->blockState));
26042619
break;
26052620

26062621
/*
@@ -2814,7 +2829,8 @@ AbortCurrentTransaction(void)
28142829
* applies if we get a failure while ending a subtransaction.
28152830
*/
28162831
case TBLOCK_SUBBEGIN:
2817-
case TBLOCK_SUBEND:
2832+
case TBLOCK_SUBRELEASE:
2833+
case TBLOCK_SUBCOMMIT:
28182834
case TBLOCK_SUBABORT_PENDING:
28192835
case TBLOCK_SUBRESTART:
28202836
AbortSubTransaction();
@@ -3122,7 +3138,8 @@ BeginTransactionBlock(void)
31223138
case TBLOCK_BEGIN:
31233139
case TBLOCK_SUBBEGIN:
31243140
case TBLOCK_END:
3125-
case TBLOCK_SUBEND:
3141+
case TBLOCK_SUBRELEASE:
3142+
case TBLOCK_SUBCOMMIT:
31263143
case TBLOCK_ABORT_END:
31273144
case TBLOCK_SUBABORT_END:
31283145
case TBLOCK_ABORT_PENDING:
@@ -3232,7 +3249,7 @@ EndTransactionBlock(void)
32323249
while (s->parent != NULL)
32333250
{
32343251
if (s->blockState == TBLOCK_SUBINPROGRESS)
3235-
s->blockState = TBLOCK_SUBEND;
3252+
s->blockState = TBLOCK_SUBCOMMIT;
32363253
else
32373254
elog(FATAL, "EndTransactionBlock: unexpected state %s",
32383255
BlockStateAsString(s->blockState));
@@ -3290,7 +3307,8 @@ EndTransactionBlock(void)
32903307
case TBLOCK_BEGIN:
32913308
case TBLOCK_SUBBEGIN:
32923309
case TBLOCK_END:
3293-
case TBLOCK_SUBEND:
3310+
case TBLOCK_SUBRELEASE:
3311+
case TBLOCK_SUBCOMMIT:
32943312
case TBLOCK_ABORT_END:
32953313
case TBLOCK_SUBABORT_END:
32963314
case TBLOCK_ABORT_PENDING:
@@ -3382,7 +3400,8 @@ UserAbortTransactionBlock(void)
33823400
case TBLOCK_BEGIN:
33833401
case TBLOCK_SUBBEGIN:
33843402
case TBLOCK_END:
3385-
case TBLOCK_SUBEND:
3403+
case TBLOCK_SUBRELEASE:
3404+
case TBLOCK_SUBCOMMIT:
33863405
case TBLOCK_ABORT_END:
33873406
case TBLOCK_SUBABORT_END:
33883407
case TBLOCK_ABORT_PENDING:
@@ -3427,7 +3446,8 @@ DefineSavepoint(char *name)
34273446
case TBLOCK_BEGIN:
34283447
case TBLOCK_SUBBEGIN:
34293448
case TBLOCK_END:
3430-
case TBLOCK_SUBEND:
3449+
case TBLOCK_SUBRELEASE:
3450+
case TBLOCK_SUBCOMMIT:
34313451
case TBLOCK_ABORT:
34323452
case TBLOCK_SUBABORT:
34333453
case TBLOCK_ABORT_END:
@@ -3483,7 +3503,8 @@ ReleaseSavepoint(List *options)
34833503
case TBLOCK_BEGIN:
34843504
case TBLOCK_SUBBEGIN:
34853505
case TBLOCK_END:
3486-
case TBLOCK_SUBEND:
3506+
case TBLOCK_SUBRELEASE:
3507+
case TBLOCK_SUBCOMMIT:
34873508
case TBLOCK_ABORT:
34883509
case TBLOCK_SUBABORT:
34893510
case TBLOCK_ABORT_END:
@@ -3534,7 +3555,7 @@ ReleaseSavepoint(List *options)
35343555
for (;;)
35353556
{
35363557
Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
3537-
xact->blockState = TBLOCK_SUBEND;
3558+
xact->blockState = TBLOCK_SUBRELEASE;
35383559
if (xact == target)
35393560
break;
35403561
xact = xact->parent;
@@ -3583,7 +3604,8 @@ RollbackToSavepoint(List *options)
35833604
case TBLOCK_BEGIN:
35843605
case TBLOCK_SUBBEGIN:
35853606
case TBLOCK_END:
3586-
case TBLOCK_SUBEND:
3607+
case TBLOCK_SUBRELEASE:
3608+
case TBLOCK_SUBCOMMIT:
35873609
case TBLOCK_ABORT_END:
35883610
case TBLOCK_SUBABORT_END:
35893611
case TBLOCK_ABORT_PENDING:
@@ -3691,7 +3713,8 @@ BeginInternalSubTransaction(char *name)
36913713
case TBLOCK_DEFAULT:
36923714
case TBLOCK_BEGIN:
36933715
case TBLOCK_SUBBEGIN:
3694-
case TBLOCK_SUBEND:
3716+
case TBLOCK_SUBRELEASE:
3717+
case TBLOCK_SUBCOMMIT:
36953718
case TBLOCK_ABORT:
36963719
case TBLOCK_SUBABORT:
36973720
case TBLOCK_ABORT_END:
@@ -3726,7 +3749,7 @@ ReleaseCurrentSubTransaction(void)
37263749
BlockStateAsString(s->blockState));
37273750
Assert(s->state == TRANS_INPROGRESS);
37283751
MemoryContextSwitchTo(CurTransactionContext);
3729-
CommitSubTransaction();
3752+
CommitSubTransaction(false);
37303753
s = CurrentTransactionState; /* changed by pop */
37313754
Assert(s->state == TRANS_INPROGRESS);
37323755
}
@@ -3757,7 +3780,8 @@ RollbackAndReleaseCurrentSubTransaction(void)
37573780
case TBLOCK_SUBBEGIN:
37583781
case TBLOCK_INPROGRESS:
37593782
case TBLOCK_END:
3760-
case TBLOCK_SUBEND:
3783+
case TBLOCK_SUBRELEASE:
3784+
case TBLOCK_SUBCOMMIT:
37613785
case TBLOCK_ABORT:
37623786
case TBLOCK_ABORT_END:
37633787
case TBLOCK_SUBABORT_END:
@@ -3831,7 +3855,8 @@ AbortOutOfAnyTransaction(void)
38313855
*/
38323856
case TBLOCK_SUBBEGIN:
38333857
case TBLOCK_SUBINPROGRESS:
3834-
case TBLOCK_SUBEND:
3858+
case TBLOCK_SUBRELEASE:
3859+
case TBLOCK_SUBCOMMIT:
38353860
case TBLOCK_SUBABORT_PENDING:
38363861
case TBLOCK_SUBRESTART:
38373862
AbortSubTransaction();
@@ -3903,7 +3928,8 @@ TransactionBlockStatusCode(void)
39033928
case TBLOCK_INPROGRESS:
39043929
case TBLOCK_SUBINPROGRESS:
39053930
case TBLOCK_END:
3906-
case TBLOCK_SUBEND:
3931+
case TBLOCK_SUBRELEASE:
3932+
case TBLOCK_SUBCOMMIT:
39073933
case TBLOCK_PREPARE:
39083934
return 'T'; /* in transaction */
39093935
case TBLOCK_ABORT:
@@ -3987,9 +4013,13 @@ StartSubTransaction(void)
39874013
*
39884014
* The caller has to make sure to always reassign CurrentTransactionState
39894015
* if it has a local pointer to it after calling this function.
4016+
*
4017+
* isTopLevel means that this CommitSubTransaction() is being issued as a
4018+
* sequence of actions leading directly to a main transaction commit
4019+
* allowing some actions to be optimised.
39904020
*/
39914021
static void
3992-
CommitSubTransaction(void)
4022+
CommitSubTransaction(bool isTopLevel)
39934023
{
39944024
TransactionState s = CurrentTransactionState;
39954025

@@ -4036,15 +4066,21 @@ CommitSubTransaction(void)
40364066

40374067
/*
40384068
* The only lock we actually release here is the subtransaction XID lock.
4039-
* The rest just get transferred to the parent resource owner.
40404069
*/
40414070
CurrentResourceOwner = s->curTransactionOwner;
40424071
if (TransactionIdIsValid(s->transactionId))
40434072
XactLockTableDelete(s->transactionId);
40444073

4074+
/*
4075+
* Other locks should get transferred to their parent resource owner.
4076+
* Doing that is an O(N^2) operation, so if isTopLevel then we can just
4077+
* leave the lock records as they are, knowing they will all get released
4078+
* by the top level commit using ProcReleaseLocks(). We only optimize
4079+
* this for commit; aborts may need to do other cleanup.
4080+
*/
40454081
ResourceOwnerRelease(s->curTransactionOwner,
40464082
RESOURCE_RELEASE_LOCKS,
4047-
true, false);
4083+
true, isTopLevel);
40484084
ResourceOwnerRelease(s->curTransactionOwner,
40494085
RESOURCE_RELEASE_AFTER_LOCKS,
40504086
true, false);
@@ -4398,8 +4434,10 @@ BlockStateAsString(TBlockState blockState)
43984434
return "SUB BEGIN";
43994435
case TBLOCK_SUBINPROGRESS:
44004436
return "SUB INPROGRS";
4401-
case TBLOCK_SUBEND:
4402-
return "SUB END";
4437+
case TBLOCK_SUBRELEASE:
4438+
return "SUB RELEASE";
4439+
case TBLOCK_SUBCOMMIT:
4440+
return "SUB COMMIT";
44034441
case TBLOCK_SUBABORT:
44044442
return "SUB ABORT";
44054443
case TBLOCK_SUBABORT_END:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy