Skip to content

Commit 32846f8

Browse files
committed
Fix TransactionIdIsCurrentTransactionId() to use binary search instead of
linear search when checking child-transaction XIDs. This makes for an important speedup in transactions that have large numbers of children, as in a recent example from Craig Ringer. We can also get rid of an ugly kluge that represented lists of TransactionIds as lists of OIDs. Heikki Linnakangas
1 parent a7c58ab commit 32846f8

File tree

3 files changed

+128
-72
lines changed

3 files changed

+128
-72
lines changed

src/backend/access/transam/twophase.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.39 2008/01/01 19:45:48 momjian Exp $
10+
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.40 2008/03/17 02:18:55 tgl Exp $
1111
*
1212
* NOTES
1313
* Each global transaction is associated with a global transaction
@@ -827,7 +827,6 @@ StartPrepare(GlobalTransaction gxact)
827827
save_state_data(children, hdr.nsubxacts * sizeof(TransactionId));
828828
/* While we have the child-xact data, stuff it in the gxact too */
829829
GXactLoadSubxactData(gxact, hdr.nsubxacts, children);
830-
pfree(children);
831830
}
832831
if (hdr.ncommitrels > 0)
833832
{

src/backend/access/transam/xact.c

Lines changed: 126 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.258 2008/03/04 19:54:06 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.259 2008/03/17 02:18:55 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -130,7 +130,9 @@ typedef struct TransactionStateData
130130
int gucNestLevel; /* GUC context nesting depth */
131131
MemoryContext curTransactionContext; /* my xact-lifetime context */
132132
ResourceOwner curTransactionOwner; /* my query resources */
133-
List *childXids; /* subcommitted child XIDs */
133+
TransactionId *childXids; /* subcommitted child XIDs, in XID order */
134+
int nChildXids; /* # of subcommitted child XIDs */
135+
int maxChildXids; /* allocated size of childXids[] */
134136
Oid prevUser; /* previous CurrentUserId setting */
135137
bool prevSecDefCxt; /* previous SecurityDefinerContext setting */
136138
bool prevXactReadOnly; /* entry-time xact r/o state */
@@ -156,7 +158,9 @@ static TransactionStateData TopTransactionStateData = {
156158
0, /* GUC context nesting depth */
157159
NULL, /* cur transaction context */
158160
NULL, /* cur transaction resource owner */
159-
NIL, /* subcommitted child Xids */
161+
NULL, /* subcommitted child Xids */
162+
0, /* # of subcommitted child Xids */
163+
0, /* allocated size of childXids[] */
160164
InvalidOid, /* previous CurrentUserId setting */
161165
false, /* previous SecurityDefinerContext setting */
162166
false, /* entry-time xact r/o state */
@@ -559,18 +563,30 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
559563
*/
560564
for (s = CurrentTransactionState; s != NULL; s = s->parent)
561565
{
562-
ListCell *cell;
566+
int low, high;
563567

564568
if (s->state == TRANS_ABORT)
565569
continue;
566570
if (!TransactionIdIsValid(s->transactionId))
567571
continue; /* it can't have any child XIDs either */
568572
if (TransactionIdEquals(xid, s->transactionId))
569573
return true;
570-
foreach(cell, s->childXids)
574+
/* As the childXids array is ordered, we can use binary search */
575+
low = 0;
576+
high = s->nChildXids - 1;
577+
while (low <= high)
571578
{
572-
if (TransactionIdEquals(xid, lfirst_xid(cell)))
579+
int middle;
580+
TransactionId probe;
581+
582+
middle = low + (high - low) / 2;
583+
probe = s->childXids[middle];
584+
if (TransactionIdEquals(probe, xid))
573585
return true;
586+
else if (TransactionIdPrecedes(probe, xid))
587+
low = middle + 1;
588+
else
589+
high = middle - 1;
574590
}
575591
}
576592

@@ -985,8 +1001,6 @@ RecordTransactionCommit(void)
9851001
/* Clean up local data */
9861002
if (rels)
9871003
pfree(rels);
988-
if (children)
989-
pfree(children);
9901004

9911005
return latestXid;
9921006
}
@@ -1067,34 +1081,79 @@ static void
10671081
AtSubCommit_childXids(void)
10681082
{
10691083
TransactionState s = CurrentTransactionState;
1070-
MemoryContext old_cxt;
1084+
int new_nChildXids;
10711085

10721086
Assert(s->parent != NULL);
10731087

10741088
/*
1075-
* We keep the child-XID lists in TopTransactionContext; this avoids
1076-
* setting up child-transaction contexts for what might be just a few
1077-
* bytes of grandchild XIDs.
1089+
* The parent childXids array will need to hold my XID and all my
1090+
* childXids, in addition to the XIDs already there.
10781091
*/
1079-
old_cxt = MemoryContextSwitchTo(TopTransactionContext);
1080-
1081-
s->parent->childXids = lappend_xid(s->parent->childXids,
1082-
s->transactionId);
1092+
new_nChildXids = s->parent->nChildXids + s->nChildXids + 1;
10831093

1084-
if (s->childXids != NIL)
1094+
/* Allocate or enlarge the parent array if necessary */
1095+
if (s->parent->maxChildXids < new_nChildXids)
10851096
{
1086-
s->parent->childXids = list_concat(s->parent->childXids,
1087-
s->childXids);
1097+
int new_maxChildXids;
1098+
TransactionId *new_childXids;
10881099

10891100
/*
1090-
* list_concat doesn't free the list header for the second list; do so
1091-
* here to avoid memory leakage (kluge)
1101+
* Make it 2x what's needed right now, to avoid having to enlarge it
1102+
* repeatedly. But we can't go above MaxAllocSize. (The latter
1103+
* limit is what ensures that we don't need to worry about integer
1104+
* overflow here or in the calculation of new_nChildXids.)
10921105
*/
1093-
pfree(s->childXids);
1094-
s->childXids = NIL;
1106+
new_maxChildXids = Min(new_nChildXids * 2,
1107+
(int) (MaxAllocSize / sizeof(TransactionId)));
1108+
1109+
if (new_maxChildXids < new_nChildXids)
1110+
ereport(ERROR,
1111+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1112+
errmsg("maximum number of committed subtransactions (%d) exceeded",
1113+
(int) (MaxAllocSize / sizeof(TransactionId)))));
1114+
1115+
/*
1116+
* We keep the child-XID arrays in TopTransactionContext; this avoids
1117+
* setting up child-transaction contexts for what might be just a few
1118+
* bytes of grandchild XIDs.
1119+
*/
1120+
if (s->parent->childXids == NULL)
1121+
new_childXids =
1122+
MemoryContextAlloc(TopTransactionContext,
1123+
new_maxChildXids * sizeof(TransactionId));
1124+
else
1125+
new_childXids = repalloc(s->parent->childXids,
1126+
new_maxChildXids * sizeof(TransactionId));
1127+
1128+
s->parent->childXids = new_childXids;
1129+
s->parent->maxChildXids = new_maxChildXids;
10951130
}
10961131

1097-
MemoryContextSwitchTo(old_cxt);
1132+
/*
1133+
* Copy all my XIDs to parent's array.
1134+
*
1135+
* Note: We rely on the fact that the XID of a child always follows that
1136+
* of its parent. By copying the XID of this subtransaction before the
1137+
* XIDs of its children, we ensure that the array stays ordered. Likewise,
1138+
* all XIDs already in the array belong to subtransactions started and
1139+
* subcommitted before us, so their XIDs must precede ours.
1140+
*/
1141+
s->parent->childXids[s->parent->nChildXids] = s->transactionId;
1142+
1143+
if (s->nChildXids > 0)
1144+
memcpy(&s->parent->childXids[s->parent->nChildXids + 1],
1145+
s->childXids,
1146+
s->nChildXids * sizeof(TransactionId));
1147+
1148+
s->parent->nChildXids = new_nChildXids;
1149+
1150+
/* Release child's array to avoid leakage */
1151+
if (s->childXids != NULL)
1152+
pfree(s->childXids);
1153+
/* We must reset these to avoid double-free if fail later in commit */
1154+
s->childXids = NULL;
1155+
s->nChildXids = 0;
1156+
s->maxChildXids = 0;
10981157
}
10991158

11001159
/*
@@ -1259,8 +1318,6 @@ RecordTransactionAbort(bool isSubXact)
12591318
/* And clean up local data */
12601319
if (rels)
12611320
pfree(rels);
1262-
if (children)
1263-
pfree(children);
12641321

12651322
return latestXid;
12661323
}
@@ -1332,12 +1389,15 @@ AtSubAbort_childXids(void)
13321389
TransactionState s = CurrentTransactionState;
13331390

13341391
/*
1335-
* We keep the child-XID lists in TopTransactionContext (see
1336-
* AtSubCommit_childXids). This means we'd better free the list
1392+
* We keep the child-XID arrays in TopTransactionContext (see
1393+
* AtSubCommit_childXids). This means we'd better free the array
13371394
* explicitly at abort to avoid leakage.
13381395
*/
1339-
list_free(s->childXids);
1340-
s->childXids = NIL;
1396+
if (s->childXids != NULL)
1397+
pfree(s->childXids);
1398+
s->childXids = NULL;
1399+
s->nChildXids = 0;
1400+
s->maxChildXids = 0;
13411401
}
13421402

13431403
/* ----------------------------------------------------------------
@@ -1506,7 +1566,9 @@ StartTransaction(void)
15061566
*/
15071567
s->nestingLevel = 1;
15081568
s->gucNestLevel = 1;
1509-
s->childXids = NIL;
1569+
s->childXids = NULL;
1570+
s->nChildXids = 0;
1571+
s->maxChildXids = 0;
15101572
GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
15111573
/* SecurityDefinerContext should never be set outside a transaction */
15121574
Assert(!s->prevSecDefCxt);
@@ -1702,7 +1764,9 @@ CommitTransaction(void)
17021764
s->subTransactionId = InvalidSubTransactionId;
17031765
s->nestingLevel = 0;
17041766
s->gucNestLevel = 0;
1705-
s->childXids = NIL;
1767+
s->childXids = NULL;
1768+
s->nChildXids = 0;
1769+
s->maxChildXids = 0;
17061770

17071771
/*
17081772
* done with commit processing, set current transaction state back to
@@ -1931,7 +1995,9 @@ PrepareTransaction(void)
19311995
s->subTransactionId = InvalidSubTransactionId;
19321996
s->nestingLevel = 0;
19331997
s->gucNestLevel = 0;
1934-
s->childXids = NIL;
1998+
s->childXids = NULL;
1999+
s->nChildXids = 0;
2000+
s->maxChildXids = 0;
19352001

19362002
/*
19372003
* done with 1st phase commit processing, set current transaction state
@@ -2101,7 +2167,9 @@ CleanupTransaction(void)
21012167
s->subTransactionId = InvalidSubTransactionId;
21022168
s->nestingLevel = 0;
21032169
s->gucNestLevel = 0;
2104-
s->childXids = NIL;
2170+
s->childXids = NULL;
2171+
s->nChildXids = 0;
2172+
s->maxChildXids = 0;
21052173

21062174
/*
21072175
* done with abort processing, set current transaction state back to
@@ -4051,6 +4119,19 @@ ShowTransactionState(const char *str)
40514119
static void
40524120
ShowTransactionStateRec(TransactionState s)
40534121
{
4122+
StringInfoData buf;
4123+
4124+
initStringInfo(&buf);
4125+
4126+
if (s->nChildXids > 0)
4127+
{
4128+
int i;
4129+
4130+
appendStringInfo(&buf, "%u", s->childXids[0]);
4131+
for (i = 1; i < s->nChildXids; i++)
4132+
appendStringInfo(&buf, " %u", s->childXids[i]);
4133+
}
4134+
40544135
if (s->parent)
40554136
ShowTransactionStateRec(s->parent);
40564137

@@ -4064,8 +4145,9 @@ ShowTransactionStateRec(TransactionState s)
40644145
(unsigned int) s->subTransactionId,
40654146
(unsigned int) currentCommandId,
40664147
currentCommandIdUsed ? " (used)" : "",
4067-
s->nestingLevel,
4068-
nodeToString(s->childXids))));
4148+
s->nestingLevel, buf.data)));
4149+
4150+
pfree(buf.data);
40694151
}
40704152

40714153
/*
@@ -4144,36 +4226,22 @@ TransStateAsString(TransState state)
41444226
* xactGetCommittedChildren
41454227
*
41464228
* Gets the list of committed children of the current transaction. The return
4147-
* value is the number of child transactions. *children is set to point to a
4148-
* palloc'd array of TransactionIds. If there are no subxacts, *children is
4149-
* set to NULL.
4229+
* value is the number of child transactions. *ptr is set to point to an
4230+
* array of TransactionIds. The array is allocated in TopTransactionContext;
4231+
* the caller should *not* pfree() it (this is a change from pre-8.4 code!).
4232+
* If there are no subxacts, *ptr is set to NULL.
41504233
*/
41514234
int
41524235
xactGetCommittedChildren(TransactionId **ptr)
41534236
{
41544237
TransactionState s = CurrentTransactionState;
4155-
int nchildren;
4156-
TransactionId *children;
4157-
ListCell *p;
41584238

4159-
nchildren = list_length(s->childXids);
4160-
if (nchildren == 0)
4161-
{
4239+
if (s->nChildXids == 0)
41624240
*ptr = NULL;
4163-
return 0;
4164-
}
4165-
4166-
children = (TransactionId *) palloc(nchildren * sizeof(TransactionId));
4167-
*ptr = children;
4168-
4169-
foreach(p, s->childXids)
4170-
{
4171-
TransactionId child = lfirst_xid(p);
4172-
4173-
*children++ = child;
4174-
}
4241+
else
4242+
*ptr = s->childXids;
41754243

4176-
return nchildren;
4244+
return s->nChildXids;
41774245
}
41784246

41794247
/*

src/include/nodes/pg_list.h

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,11 @@
2626
* (At the moment, ints and Oids are the same size, but they may not
2727
* always be so; try to be careful to maintain the distinction.)
2828
*
29-
* There is also limited support for lists of TransactionIds; since these
30-
* are used in only one or two places, we don't provide a full implementation,
31-
* but map them onto Oid lists. This effectively assumes that TransactionId
32-
* is no wider than Oid and both are unsigned types.
33-
*
3429
*
3530
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3631
* Portions Copyright (c) 1994, Regents of the University of California
3732
*
38-
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.57 2008/01/01 19:45:58 momjian Exp $
33+
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.58 2008/03/17 02:18:55 tgl Exp $
3934
*
4035
*-------------------------------------------------------------------------
4136
*/
@@ -159,12 +154,6 @@ extern int list_length(List *l);
159154
#define list_make3_oid(x1,x2,x3) lcons_oid(x1, list_make2_oid(x2, x3))
160155
#define list_make4_oid(x1,x2,x3,x4) lcons_oid(x1, list_make3_oid(x2, x3, x4))
161156

162-
/*
163-
* Limited support for lists of TransactionIds, mapped onto lists of Oids
164-
*/
165-
#define lfirst_xid(lc) ((TransactionId) lfirst_oid(lc))
166-
#define lappend_xid(list, datum) lappend_oid(list, (Oid) (datum))
167-
168157
/*
169158
* foreach -
170159
* a convenience macro which loops through the list

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