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
This commit is contained in:
parent
a7c58abeb1
commit
32846f8152
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.39 2008/01/01 19:45:48 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.40 2008/03/17 02:18:55 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Each global transaction is associated with a global transaction
|
* Each global transaction is associated with a global transaction
|
||||||
|
@ -827,7 +827,6 @@ StartPrepare(GlobalTransaction gxact)
|
||||||
save_state_data(children, hdr.nsubxacts * sizeof(TransactionId));
|
save_state_data(children, hdr.nsubxacts * sizeof(TransactionId));
|
||||||
/* While we have the child-xact data, stuff it in the gxact too */
|
/* While we have the child-xact data, stuff it in the gxact too */
|
||||||
GXactLoadSubxactData(gxact, hdr.nsubxacts, children);
|
GXactLoadSubxactData(gxact, hdr.nsubxacts, children);
|
||||||
pfree(children);
|
|
||||||
}
|
}
|
||||||
if (hdr.ncommitrels > 0)
|
if (hdr.ncommitrels > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.258 2008/03/04 19:54:06 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.259 2008/03/17 02:18:55 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -130,7 +130,9 @@ typedef struct TransactionStateData
|
||||||
int gucNestLevel; /* GUC context nesting depth */
|
int gucNestLevel; /* GUC context nesting depth */
|
||||||
MemoryContext curTransactionContext; /* my xact-lifetime context */
|
MemoryContext curTransactionContext; /* my xact-lifetime context */
|
||||||
ResourceOwner curTransactionOwner; /* my query resources */
|
ResourceOwner curTransactionOwner; /* my query resources */
|
||||||
List *childXids; /* subcommitted child XIDs */
|
TransactionId *childXids; /* subcommitted child XIDs, in XID order */
|
||||||
|
int nChildXids; /* # of subcommitted child XIDs */
|
||||||
|
int maxChildXids; /* allocated size of childXids[] */
|
||||||
Oid prevUser; /* previous CurrentUserId setting */
|
Oid prevUser; /* previous CurrentUserId setting */
|
||||||
bool prevSecDefCxt; /* previous SecurityDefinerContext setting */
|
bool prevSecDefCxt; /* previous SecurityDefinerContext setting */
|
||||||
bool prevXactReadOnly; /* entry-time xact r/o state */
|
bool prevXactReadOnly; /* entry-time xact r/o state */
|
||||||
|
@ -156,7 +158,9 @@ static TransactionStateData TopTransactionStateData = {
|
||||||
0, /* GUC context nesting depth */
|
0, /* GUC context nesting depth */
|
||||||
NULL, /* cur transaction context */
|
NULL, /* cur transaction context */
|
||||||
NULL, /* cur transaction resource owner */
|
NULL, /* cur transaction resource owner */
|
||||||
NIL, /* subcommitted child Xids */
|
NULL, /* subcommitted child Xids */
|
||||||
|
0, /* # of subcommitted child Xids */
|
||||||
|
0, /* allocated size of childXids[] */
|
||||||
InvalidOid, /* previous CurrentUserId setting */
|
InvalidOid, /* previous CurrentUserId setting */
|
||||||
false, /* previous SecurityDefinerContext setting */
|
false, /* previous SecurityDefinerContext setting */
|
||||||
false, /* entry-time xact r/o state */
|
false, /* entry-time xact r/o state */
|
||||||
|
@ -559,7 +563,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
|
||||||
*/
|
*/
|
||||||
for (s = CurrentTransactionState; s != NULL; s = s->parent)
|
for (s = CurrentTransactionState; s != NULL; s = s->parent)
|
||||||
{
|
{
|
||||||
ListCell *cell;
|
int low, high;
|
||||||
|
|
||||||
if (s->state == TRANS_ABORT)
|
if (s->state == TRANS_ABORT)
|
||||||
continue;
|
continue;
|
||||||
|
@ -567,10 +571,22 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
|
||||||
continue; /* it can't have any child XIDs either */
|
continue; /* it can't have any child XIDs either */
|
||||||
if (TransactionIdEquals(xid, s->transactionId))
|
if (TransactionIdEquals(xid, s->transactionId))
|
||||||
return true;
|
return true;
|
||||||
foreach(cell, s->childXids)
|
/* As the childXids array is ordered, we can use binary search */
|
||||||
|
low = 0;
|
||||||
|
high = s->nChildXids - 1;
|
||||||
|
while (low <= high)
|
||||||
{
|
{
|
||||||
if (TransactionIdEquals(xid, lfirst_xid(cell)))
|
int middle;
|
||||||
|
TransactionId probe;
|
||||||
|
|
||||||
|
middle = low + (high - low) / 2;
|
||||||
|
probe = s->childXids[middle];
|
||||||
|
if (TransactionIdEquals(probe, xid))
|
||||||
return true;
|
return true;
|
||||||
|
else if (TransactionIdPrecedes(probe, xid))
|
||||||
|
low = middle + 1;
|
||||||
|
else
|
||||||
|
high = middle - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,8 +1001,6 @@ cleanup:
|
||||||
/* Clean up local data */
|
/* Clean up local data */
|
||||||
if (rels)
|
if (rels)
|
||||||
pfree(rels);
|
pfree(rels);
|
||||||
if (children)
|
|
||||||
pfree(children);
|
|
||||||
|
|
||||||
return latestXid;
|
return latestXid;
|
||||||
}
|
}
|
||||||
|
@ -1067,34 +1081,79 @@ static void
|
||||||
AtSubCommit_childXids(void)
|
AtSubCommit_childXids(void)
|
||||||
{
|
{
|
||||||
TransactionState s = CurrentTransactionState;
|
TransactionState s = CurrentTransactionState;
|
||||||
MemoryContext old_cxt;
|
int new_nChildXids;
|
||||||
|
|
||||||
Assert(s->parent != NULL);
|
Assert(s->parent != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We keep the child-XID lists in TopTransactionContext; this avoids
|
* The parent childXids array will need to hold my XID and all my
|
||||||
|
* childXids, in addition to the XIDs already there.
|
||||||
|
*/
|
||||||
|
new_nChildXids = s->parent->nChildXids + s->nChildXids + 1;
|
||||||
|
|
||||||
|
/* Allocate or enlarge the parent array if necessary */
|
||||||
|
if (s->parent->maxChildXids < new_nChildXids)
|
||||||
|
{
|
||||||
|
int new_maxChildXids;
|
||||||
|
TransactionId *new_childXids;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make it 2x what's needed right now, to avoid having to enlarge it
|
||||||
|
* repeatedly. But we can't go above MaxAllocSize. (The latter
|
||||||
|
* limit is what ensures that we don't need to worry about integer
|
||||||
|
* overflow here or in the calculation of new_nChildXids.)
|
||||||
|
*/
|
||||||
|
new_maxChildXids = Min(new_nChildXids * 2,
|
||||||
|
(int) (MaxAllocSize / sizeof(TransactionId)));
|
||||||
|
|
||||||
|
if (new_maxChildXids < new_nChildXids)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
|
errmsg("maximum number of committed subtransactions (%d) exceeded",
|
||||||
|
(int) (MaxAllocSize / sizeof(TransactionId)))));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We keep the child-XID arrays in TopTransactionContext; this avoids
|
||||||
* setting up child-transaction contexts for what might be just a few
|
* setting up child-transaction contexts for what might be just a few
|
||||||
* bytes of grandchild XIDs.
|
* bytes of grandchild XIDs.
|
||||||
*/
|
*/
|
||||||
old_cxt = MemoryContextSwitchTo(TopTransactionContext);
|
if (s->parent->childXids == NULL)
|
||||||
|
new_childXids =
|
||||||
|
MemoryContextAlloc(TopTransactionContext,
|
||||||
|
new_maxChildXids * sizeof(TransactionId));
|
||||||
|
else
|
||||||
|
new_childXids = repalloc(s->parent->childXids,
|
||||||
|
new_maxChildXids * sizeof(TransactionId));
|
||||||
|
|
||||||
s->parent->childXids = lappend_xid(s->parent->childXids,
|
s->parent->childXids = new_childXids;
|
||||||
s->transactionId);
|
s->parent->maxChildXids = new_maxChildXids;
|
||||||
|
|
||||||
if (s->childXids != NIL)
|
|
||||||
{
|
|
||||||
s->parent->childXids = list_concat(s->parent->childXids,
|
|
||||||
s->childXids);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list_concat doesn't free the list header for the second list; do so
|
|
||||||
* here to avoid memory leakage (kluge)
|
|
||||||
*/
|
|
||||||
pfree(s->childXids);
|
|
||||||
s->childXids = NIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(old_cxt);
|
/*
|
||||||
|
* Copy all my XIDs to parent's array.
|
||||||
|
*
|
||||||
|
* Note: We rely on the fact that the XID of a child always follows that
|
||||||
|
* of its parent. By copying the XID of this subtransaction before the
|
||||||
|
* XIDs of its children, we ensure that the array stays ordered. Likewise,
|
||||||
|
* all XIDs already in the array belong to subtransactions started and
|
||||||
|
* subcommitted before us, so their XIDs must precede ours.
|
||||||
|
*/
|
||||||
|
s->parent->childXids[s->parent->nChildXids] = s->transactionId;
|
||||||
|
|
||||||
|
if (s->nChildXids > 0)
|
||||||
|
memcpy(&s->parent->childXids[s->parent->nChildXids + 1],
|
||||||
|
s->childXids,
|
||||||
|
s->nChildXids * sizeof(TransactionId));
|
||||||
|
|
||||||
|
s->parent->nChildXids = new_nChildXids;
|
||||||
|
|
||||||
|
/* Release child's array to avoid leakage */
|
||||||
|
if (s->childXids != NULL)
|
||||||
|
pfree(s->childXids);
|
||||||
|
/* We must reset these to avoid double-free if fail later in commit */
|
||||||
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1259,8 +1318,6 @@ RecordTransactionAbort(bool isSubXact)
|
||||||
/* And clean up local data */
|
/* And clean up local data */
|
||||||
if (rels)
|
if (rels)
|
||||||
pfree(rels);
|
pfree(rels);
|
||||||
if (children)
|
|
||||||
pfree(children);
|
|
||||||
|
|
||||||
return latestXid;
|
return latestXid;
|
||||||
}
|
}
|
||||||
|
@ -1332,12 +1389,15 @@ AtSubAbort_childXids(void)
|
||||||
TransactionState s = CurrentTransactionState;
|
TransactionState s = CurrentTransactionState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We keep the child-XID lists in TopTransactionContext (see
|
* We keep the child-XID arrays in TopTransactionContext (see
|
||||||
* AtSubCommit_childXids). This means we'd better free the list
|
* AtSubCommit_childXids). This means we'd better free the array
|
||||||
* explicitly at abort to avoid leakage.
|
* explicitly at abort to avoid leakage.
|
||||||
*/
|
*/
|
||||||
list_free(s->childXids);
|
if (s->childXids != NULL)
|
||||||
s->childXids = NIL;
|
pfree(s->childXids);
|
||||||
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -1506,7 +1566,9 @@ StartTransaction(void)
|
||||||
*/
|
*/
|
||||||
s->nestingLevel = 1;
|
s->nestingLevel = 1;
|
||||||
s->gucNestLevel = 1;
|
s->gucNestLevel = 1;
|
||||||
s->childXids = NIL;
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
|
GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
|
||||||
/* SecurityDefinerContext should never be set outside a transaction */
|
/* SecurityDefinerContext should never be set outside a transaction */
|
||||||
Assert(!s->prevSecDefCxt);
|
Assert(!s->prevSecDefCxt);
|
||||||
|
@ -1702,7 +1764,9 @@ CommitTransaction(void)
|
||||||
s->subTransactionId = InvalidSubTransactionId;
|
s->subTransactionId = InvalidSubTransactionId;
|
||||||
s->nestingLevel = 0;
|
s->nestingLevel = 0;
|
||||||
s->gucNestLevel = 0;
|
s->gucNestLevel = 0;
|
||||||
s->childXids = NIL;
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* done with commit processing, set current transaction state back to
|
* done with commit processing, set current transaction state back to
|
||||||
|
@ -1931,7 +1995,9 @@ PrepareTransaction(void)
|
||||||
s->subTransactionId = InvalidSubTransactionId;
|
s->subTransactionId = InvalidSubTransactionId;
|
||||||
s->nestingLevel = 0;
|
s->nestingLevel = 0;
|
||||||
s->gucNestLevel = 0;
|
s->gucNestLevel = 0;
|
||||||
s->childXids = NIL;
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* done with 1st phase commit processing, set current transaction state
|
* done with 1st phase commit processing, set current transaction state
|
||||||
|
@ -2101,7 +2167,9 @@ CleanupTransaction(void)
|
||||||
s->subTransactionId = InvalidSubTransactionId;
|
s->subTransactionId = InvalidSubTransactionId;
|
||||||
s->nestingLevel = 0;
|
s->nestingLevel = 0;
|
||||||
s->gucNestLevel = 0;
|
s->gucNestLevel = 0;
|
||||||
s->childXids = NIL;
|
s->childXids = NULL;
|
||||||
|
s->nChildXids = 0;
|
||||||
|
s->maxChildXids = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* done with abort processing, set current transaction state back to
|
* done with abort processing, set current transaction state back to
|
||||||
|
@ -4051,6 +4119,19 @@ ShowTransactionState(const char *str)
|
||||||
static void
|
static void
|
||||||
ShowTransactionStateRec(TransactionState s)
|
ShowTransactionStateRec(TransactionState s)
|
||||||
{
|
{
|
||||||
|
StringInfoData buf;
|
||||||
|
|
||||||
|
initStringInfo(&buf);
|
||||||
|
|
||||||
|
if (s->nChildXids > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
appendStringInfo(&buf, "%u", s->childXids[0]);
|
||||||
|
for (i = 1; i < s->nChildXids; i++)
|
||||||
|
appendStringInfo(&buf, " %u", s->childXids[i]);
|
||||||
|
}
|
||||||
|
|
||||||
if (s->parent)
|
if (s->parent)
|
||||||
ShowTransactionStateRec(s->parent);
|
ShowTransactionStateRec(s->parent);
|
||||||
|
|
||||||
|
@ -4064,8 +4145,9 @@ ShowTransactionStateRec(TransactionState s)
|
||||||
(unsigned int) s->subTransactionId,
|
(unsigned int) s->subTransactionId,
|
||||||
(unsigned int) currentCommandId,
|
(unsigned int) currentCommandId,
|
||||||
currentCommandIdUsed ? " (used)" : "",
|
currentCommandIdUsed ? " (used)" : "",
|
||||||
s->nestingLevel,
|
s->nestingLevel, buf.data)));
|
||||||
nodeToString(s->childXids))));
|
|
||||||
|
pfree(buf.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4144,36 +4226,22 @@ TransStateAsString(TransState state)
|
||||||
* xactGetCommittedChildren
|
* xactGetCommittedChildren
|
||||||
*
|
*
|
||||||
* Gets the list of committed children of the current transaction. The return
|
* Gets the list of committed children of the current transaction. The return
|
||||||
* value is the number of child transactions. *children is set to point to a
|
* value is the number of child transactions. *ptr is set to point to an
|
||||||
* palloc'd array of TransactionIds. If there are no subxacts, *children is
|
* array of TransactionIds. The array is allocated in TopTransactionContext;
|
||||||
* set to NULL.
|
* the caller should *not* pfree() it (this is a change from pre-8.4 code!).
|
||||||
|
* If there are no subxacts, *ptr is set to NULL.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xactGetCommittedChildren(TransactionId **ptr)
|
xactGetCommittedChildren(TransactionId **ptr)
|
||||||
{
|
{
|
||||||
TransactionState s = CurrentTransactionState;
|
TransactionState s = CurrentTransactionState;
|
||||||
int nchildren;
|
|
||||||
TransactionId *children;
|
|
||||||
ListCell *p;
|
|
||||||
|
|
||||||
nchildren = list_length(s->childXids);
|
if (s->nChildXids == 0)
|
||||||
if (nchildren == 0)
|
|
||||||
{
|
|
||||||
*ptr = NULL;
|
*ptr = NULL;
|
||||||
return 0;
|
else
|
||||||
}
|
*ptr = s->childXids;
|
||||||
|
|
||||||
children = (TransactionId *) palloc(nchildren * sizeof(TransactionId));
|
return s->nChildXids;
|
||||||
*ptr = children;
|
|
||||||
|
|
||||||
foreach(p, s->childXids)
|
|
||||||
{
|
|
||||||
TransactionId child = lfirst_xid(p);
|
|
||||||
|
|
||||||
*children++ = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nchildren;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -26,16 +26,11 @@
|
||||||
* (At the moment, ints and Oids are the same size, but they may not
|
* (At the moment, ints and Oids are the same size, but they may not
|
||||||
* always be so; try to be careful to maintain the distinction.)
|
* always be so; try to be careful to maintain the distinction.)
|
||||||
*
|
*
|
||||||
* There is also limited support for lists of TransactionIds; since these
|
|
||||||
* are used in only one or two places, we don't provide a full implementation,
|
|
||||||
* but map them onto Oid lists. This effectively assumes that TransactionId
|
|
||||||
* is no wider than Oid and both are unsigned types.
|
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.57 2008/01/01 19:45:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.58 2008/03/17 02:18:55 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -159,12 +154,6 @@ extern int list_length(List *l);
|
||||||
#define list_make3_oid(x1,x2,x3) lcons_oid(x1, list_make2_oid(x2, x3))
|
#define list_make3_oid(x1,x2,x3) lcons_oid(x1, list_make2_oid(x2, x3))
|
||||||
#define list_make4_oid(x1,x2,x3,x4) lcons_oid(x1, list_make3_oid(x2, x3, x4))
|
#define list_make4_oid(x1,x2,x3,x4) lcons_oid(x1, list_make3_oid(x2, x3, x4))
|
||||||
|
|
||||||
/*
|
|
||||||
* Limited support for lists of TransactionIds, mapped onto lists of Oids
|
|
||||||
*/
|
|
||||||
#define lfirst_xid(lc) ((TransactionId) lfirst_oid(lc))
|
|
||||||
#define lappend_xid(list, datum) lappend_oid(list, (Oid) (datum))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* foreach -
|
* foreach -
|
||||||
* a convenience macro which loops through the list
|
* a convenience macro which loops through the list
|
||||||
|
|
Loading…
Reference in New Issue