/*
* Record the parent of a subtransaction in the subtrans log.
- *
- * In some cases we may need to overwrite an existing value.
*/
void
-SubTransSetParent(TransactionId xid, TransactionId parent, bool overwriteOK)
+SubTransSetParent(TransactionId xid, TransactionId parent)
{
int pageno = TransactionIdToPage(xid);
int entryno = TransactionIdToEntry(xid);
TransactionId *ptr;
Assert(TransactionIdIsValid(parent));
+ Assert(TransactionIdFollows(xid, parent));
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
ptr += entryno;
- /* Current state should be 0 */
- Assert(*ptr == InvalidTransactionId ||
- (*ptr == parent && overwriteOK));
-
- *ptr = parent;
-
- SubTransCtl->shared->page_dirty[slotno] = true;
+ /*
+ * It's possible we'll try to set the parent xid multiple times
+ * but we shouldn't ever be changing the xid from one valid xid
+ * to another valid xid, which would corrupt the data structure.
+ */
+ if (*ptr != parent)
+ {
+ Assert(*ptr == InvalidTransactionId);
+ *ptr = parent;
+ SubTransCtl->shared->page_dirty[slotno] = true;
+ }
LWLockRelease(SubtransControlLock);
}
if (TransactionIdPrecedes(parentXid, TransactionXmin))
break;
parentXid = SubTransGetParent(parentXid);
+
+ /*
+ * By convention the parent xid gets allocated first, so should
+ * always precede the child xid. Anything else points to a corrupted
+ * data structure that could lead to an infinite loop, so exit.
+ */
+ if (!TransactionIdPrecedes(parentXid, previousXid))
+ elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
+ previousXid, parentXid);
}
Assert(TransactionIdIsValid(previousXid));
static void XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len);
static char *ProcessTwoPhaseBuffer(TransactionId xid,
XLogRecPtr prepare_start_lsn,
- bool fromdisk, bool overwriteOK, bool setParent,
- bool setNextXid);
+ bool fromdisk, bool setParent, bool setNextXid);
static void MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid,
const char *gid, TimestampTz prepared_at, Oid owner,
Oid databaseid);
xid = (TransactionId) strtoul(clde->d_name, NULL, 16);
buf = ProcessTwoPhaseBuffer(xid, InvalidXLogRecPtr,
- true, false, false,
- false);
+ true, false, false);
if (buf == NULL)
continue;
buf = ProcessTwoPhaseBuffer(xid,
gxact->prepare_start_lsn,
- gxact->ondisk, false, false,
- true);
+ gxact->ondisk, false, true);
if (buf == NULL)
continue;
* This is never called at the end of recovery - we use
* RecoverPreparedTransactions() at that point.
*
- * Currently we simply call SubTransSetParent() for any subxids of prepared
- * transactions. If overwriteOK is true, it's OK if some XIDs have already
- * been marked in pg_subtrans.
+ * The lack of calls to SubTransSetParent() calls here is by design;
+ * those calls are made by RecoverPreparedTransactions() at the end of recovery
+ * for those xacts that need this.
*/
void
-StandbyRecoverPreparedTransactions(bool overwriteOK)
+StandbyRecoverPreparedTransactions(void)
{
int i;
buf = ProcessTwoPhaseBuffer(xid,
gxact->prepare_start_lsn,
- gxact->ondisk, overwriteOK, true,
- false);
+ gxact->ondisk, false, false);
if (buf != NULL)
pfree(buf);
}
* each prepared transaction (reacquire locks, etc).
*
* This is run during database startup.
+ *
+ * At the end of recovery the way we take snapshots will change. We now need
+ * to mark all running transactions with their full SubTransSetParent() info
+ * to allow normal snapshots to work correctly if snapshots overflow.
+ * We do this here because by definition prepared transactions are the only
+ * type of write transaction still running, so this is necessary and
+ * complete.
*/
void
RecoverPreparedTransactions(void)
TwoPhaseFileHeader *hdr;
TransactionId *subxids;
const char *gid;
- bool overwriteOK = false;
- int i;
xid = gxact->xid;
+ /*
+ * Reconstruct subtrans state for the transaction --- needed
+ * because pg_subtrans is not preserved over a restart. Note that
+ * we are linking all the subtransactions directly to the
+ * top-level XID; there may originally have been a more complex
+ * hierarchy, but there's no need to restore that exactly.
+ * It's possible that SubTransSetParent has been set before, if
+ * the prepared transaction generated xid assignment records.
+ */
buf = ProcessTwoPhaseBuffer(xid,
gxact->prepare_start_lsn,
- gxact->ondisk, false, false,
- false);
+ gxact->ondisk, true, false);
if (buf == NULL)
continue;
bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
- /*
- * It's possible that SubTransSetParent has been set before, if
- * the prepared transaction generated xid assignment records. Test
- * here must match one used in AssignTransactionId().
- */
- if (InHotStandby && (hdr->nsubxacts >= PGPROC_MAX_CACHED_SUBXIDS ||
- XLogLogicalInfoActive()))
- overwriteOK = true;
-
- /*
- * Reconstruct subtrans state for the transaction --- needed
- * because pg_subtrans is not preserved over a restart. Note that
- * we are linking all the subtransactions directly to the
- * top-level XID; there may originally have been a more complex
- * hierarchy, but there's no need to restore that exactly.
- */
- for (i = 0; i < hdr->nsubxacts; i++)
- SubTransSetParent(subxids[i], xid, true);
-
/*
* Recreate its GXACT and dummy PGPROC. But, check whether
* it was added in redo and already has a shmem entry for
* Given a transaction id, read it either from disk or read it directly
* via shmem xlog record pointer using the provided "prepare_start_lsn".
*
- * If setParent is true, then use the overwriteOK parameter to set up
- * subtransaction parent linkages.
+ * If setParent is true, set up subtransaction parent linkages.
*
* If setNextXid is true, set ShmemVariableCache->nextXid to the newest
* value scanned.
static char *
ProcessTwoPhaseBuffer(TransactionId xid,
XLogRecPtr prepare_start_lsn,
- bool fromdisk, bool overwriteOK,
+ bool fromdisk,
bool setParent, bool setNextXid)
{
TransactionId origNextXid = ShmemVariableCache->nextXid;
}
if (setParent)
- SubTransSetParent(subxid, xid, overwriteOK);
+ SubTransSetParent(subxid, xid);
}
return buf;