From 9375fcb07b8e048e62c65e6f33b67e6d9c69dd59 Mon Sep 17 00:00:00 2001 From: Simon Riggs Date: Thu, 12 Feb 2009 07:45:47 +0000 Subject: [PATCH] further changes for prepared transactions --- src/backend/access/transam/twophase.c | 108 +++++++++++++++----------- src/backend/access/transam/xact.c | 79 +++++++++++++------ src/backend/access/transam/xlog.c | 18 ++--- src/include/access/twophase.h | 4 +- 4 files changed, 131 insertions(+), 78 deletions(-) diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 99b8276bed..5fe2062292 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -1603,7 +1603,7 @@ PrescanPreparedTransactions(void) * to recover the full state for each gxact. */ void -RecoverPreparedTransactions(void) +RecoverPreparedTransactions(bool init_procs) { char dir[MAXPGPATH]; DIR *cldir; @@ -1648,51 +1648,7 @@ RecoverPreparedTransactions(void) continue; } - ereport(LOG, - (errmsg("recovering prepared transaction %u", xid))); - - /* Deconstruct header */ - hdr = (TwoPhaseFileHeader *) buf; - Assert(TransactionIdEquals(hdr->xid, xid)); - bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); - subxids = (TransactionId *) bufptr; - bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); - bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); - bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); - - if (InRecovery) - { - /* - * 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); - - /* - * Recreate its GXACT and dummy PGPROC - * - * Note: since we don't have the PREPARE record's WAL location at - * hand, we leave prepare_lsn zeroes. This means the GXACT will - * be fsync'd on every future checkpoint. We assume this - * situation is infrequent enough that the performance cost is - * negligible (especially since we know the state file has already - * been fsynced). - */ - gxact = MarkAsPreparing(xid, hdr->gid, - hdr->prepared_at, - hdr->owner, hdr->database); - GXactLoadSubxactData(gxact, hdr->nsubxacts, subxids); - MarkAsPrepared(gxact); - } - - /* - * Recover other state (notably locks) using resource managers - */ - ProcessRecords(bufptr, xid, twophase_recover_callbacks); + RecoverOnePreparedTransaction(xid, buf, init_procs, init_procs); pfree(buf); } @@ -1700,6 +1656,66 @@ RecoverPreparedTransactions(void) FreeDir(cldir); } +void +RecoverOnePreparedTransaction(TransactionId xid, char *buf, + bool create_gxacts, bool mark_subtrans) +{ + char *bufptr; + TwoPhaseFileHeader *hdr; + TransactionId *subxids; + GlobalTransaction gxact; + int i; + + ereport(LOG, + (errmsg("recovering prepared transaction %u", xid))); + + /* Deconstruct header */ + hdr = (TwoPhaseFileHeader *) buf; + Assert(TransactionIdEquals(hdr->xid, xid)); + bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); + subxids = (TransactionId *) bufptr; + bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); + bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); + bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); + + if (mark_subtrans) + { + /* + * 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); + } + + if (create_gxacts) + { + /* + * Recreate its GXACT and dummy PGPROC + * + * Note: since we don't have the PREPARE record's WAL location at + * hand, we leave prepare_lsn zeroes. This means the GXACT will + * be fsync'd on every future checkpoint. We assume this + * situation is infrequent enough that the performance cost is + * negligible (especially since we know the state file has already + * been fsynced). + */ + gxact = MarkAsPreparing(xid, hdr->gid, + hdr->prepared_at, + hdr->owner, hdr->database); + GXactLoadSubxactData(gxact, hdr->nsubxacts, subxids); + MarkAsPrepared(gxact); + } + + /* + * Recover other state (notably locks) using resource managers + */ + ProcessRecords(bufptr, xid, twophase_recover_callbacks); +} + /* * RecordTransactionCommitPrepared * diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 6e41958316..3d7bd5b835 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -4785,16 +4785,21 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, bool preparedXact) * happened, but there are cases where they might sneak through. * Leave these for the periodic cleanup by XACT_RUNNING_XACT records. */ - if (IsRunningXactDataValid() && !preparedXact) + if (IsRunningXactDataValid()) { - Xid_to_Proc *hentry = (Xid_to_Proc *) - hash_search(local_recovery_xid_to_proc_hash, - (void *) &xid, - HASH_REMOVE, NULL); - Assert(hentry->proc != NULL); + if (!preparedXact) + { + Xid_to_Proc *hentry = (Xid_to_Proc *) + hash_search(local_recovery_xid_to_proc_hash, + (void *) &xid, + HASH_REMOVE, NULL); + Assert(hentry->proc != NULL); - ProcArrayRemove(hentry->proc, max_xid, xlrec->nsubxacts, sub_xids); - FreeRecoveryProcess(hentry->proc); + ProcArrayRemove(hentry->proc, max_xid, xlrec->nsubxacts, sub_xids); + FreeRecoveryProcess(hentry->proc); + } + else + twophase_remove_xid(); } /* @@ -4894,24 +4899,29 @@ xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid, TransactionId topxid, b * happened, but there are cases where they might sneak through. * Leave these for the periodic cleanup by XACT_RUNNING_XACT records. */ - if (IsRunningXactDataValid() && !preparedXact) + if (IsRunningXactDataValid()) { - Xid_to_Proc *hentry = (Xid_to_Proc *) - hash_search(local_recovery_xid_to_proc_hash, - (void *) &xid, - HASH_REMOVE, NULL); - Assert(hentry->proc != NULL); - - /* - * Do we have a top-level transaction abort, or not? - */ - if (topxid == xid) + if (!!preparedXact) { - ProcArrayRemove(hentry->proc, max_xid, xlrec->nsubxacts, sub_xids); - FreeRecoveryProcess(hentry->proc); + Xid_to_Proc *hentry = (Xid_to_Proc *) + hash_search(local_recovery_xid_to_proc_hash, + (void *) &xid, + HASH_REMOVE, NULL); + Assert(hentry->proc != NULL); + + /* + * Do we have a top-level transaction abort, or not? + */ + if (topxid == xid) + { + ProcArrayRemove(hentry->proc, max_xid, xlrec->nsubxacts, sub_xids); + FreeRecoveryProcess(hentry->proc); + } + else + XidCacheRemoveRunningXids(hentry->proc, xid, xlrec->nsubxacts, sub_xids, max_xid); } else - XidCacheRemoveRunningXids(hentry->proc, xid, xlrec->nsubxacts, sub_xids, max_xid); + twophase_remove_xid(); } /* @@ -5029,6 +5039,31 @@ xact_redo(XLogRecPtr lsn, XLogRecord *record) /* the record contents are exactly the 2PC file */ RecreateTwoPhaseFile(record->xl_xid, XLogRecGetData(record), record->xl_len); + if (InArchiveRecovery) + { + Xid_to_Proc *hentry; + char *bufptr; + TwoPhaseFileHeader *hdr; + TransactionId *subxids; + + RecoverOnePreparedTransaction(record->xl_xid, + XLogRecGetData(record), true, false); + + Assert(local_recovery_xid_to_proc_hash); + + hentry = (Xid_to_Proc *) hash_search(local_recovery_xid_to_proc_hash, + (void *) &record->xl_xid, + HASH_REMOVE, NULL); + Assert(hentry->proc != NULL); + + hdr = (TwoPhaseFileHeader *) XLogRecGetData(record); + Assert(TransactionIdEquals(hdr->xid, record->xl_xid)); + bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); + subxids = (TransactionId *) bufptr; + + ProcArrayRemove(hentry->proc, max_xid, hdr->nsubxacts, sub_xids); + FreeRecoveryProcess(hentry->proc); + } } else if (info == XLOG_XACT_COMMIT_PREPARED) { diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 8bdf87c59a..377e58a2c5 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5787,14 +5787,9 @@ StartupXLOG(void) RmgrTable[rmid].rm_startup(); } - /* Pre-scan prepared transactions to find out the range of XIDs present */ - oldestActiveXID = PrescanPreparedTransactions(); - - /* Start up the commit log and related stuff, too */ -// InitSUBTRANS(oldestActiveXID); - /* Reload shared-memory state for prepared transactions */ - RecoverPreparedTransactions(); + if (InHotStandby) + RecoverPreparedTransactions(true); /* * Find the first record that logically follows the checkpoint --- it @@ -6105,8 +6100,13 @@ StartupXLOG(void) StartupSUBTRANS(oldestActiveXID); StartupMultiXact(); - /* Reacquire full resources for prepared transactions */ - RecoverPreparedTransactions(); + /* + * Reacquire full resources for prepared transactions. This *must* + * run after we have set InRecovery to false. InHotStandby we will + * have already created gxact procs, so there is no need to recreate + * them, whereas in crash recovery this call does everything we need. + */ + RecoverPreparedTransactions(!InHotStandby); /* Shut down readFile facility, free space */ if (readFile >= 0) diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h index 64dba0cd3f..6d66f49bf9 100644 --- a/src/include/access/twophase.h +++ b/src/include/access/twophase.h @@ -40,7 +40,9 @@ extern void StartPrepare(GlobalTransaction gxact); extern void EndPrepare(GlobalTransaction gxact); extern TransactionId PrescanPreparedTransactions(void); -extern void RecoverPreparedTransactions(void); +extern void RecoverPreparedTransactions(bool init_procs); +extern void RecoverOnePreparedTransaction(TransactionId xid, char *buf, + bool create_gxacts, bool mark_subtrans); extern void RecreateTwoPhaseFile(TransactionId xid, void *content, int len); extern void RemoveTwoPhaseFile(TransactionId xid, bool giveWarning); -- 2.39.5