v3-0001-Fix-a-2PC-transaction-maybe-recovered-twice_master.patch
application/octet-stream
Filename: v3-0001-Fix-a-2PC-transaction-maybe-recovered-twice_master.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v3-0001
Subject: Fix the bug of a 2PC transaction maybe recovered twice
| File | + | − |
|---|---|---|
| src/backend/access/transam/twophase.c | 47 | 0 |
From a67bb9034800fcc2c7790a1547a2ddd8d4ec18ae Mon Sep 17 00:00:00 2001
From: "suyu.cmj" <mengjuan.cmj@alibaba-inc.com>
Date: Mon, 17 Jul 2023 05:52:59 +0000
Subject: [PATCH] Fix the bug of a 2PC transaction maybe recovered twice
During recovery, a two-phase transaction should be restored either from
the disk file or from the WAL. However, a two-phase transaction maybe
restored from both way if we crashed after writing a 2PC file to disk,
but before the redo LSN has been updated to pg_control during doing a
checkpoint. We check if the 2PC file of the transaction being recovery
is already in pg_twophase/ when adding an entry from a WAL record, if a
consistent point has not been reached by recovery and we find a file on
disk, then do nothing because we know restoreTwoPhaseData() has done that
at the beginning of recovery. If a consistent point has been reached in
recovery and we find a file on disk while replaying a WAL record for the
same 2PC file, then fail. If there is no file in pg_twophase/ for the
record replayed, then add it to the array.
---
src/backend/access/transam/twophase.c | 47 +++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 068e59bec0..0be105bbbc 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -86,6 +86,7 @@
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/xlogreader.h"
+#include "access/xlogrecovery.h"
#include "access/xlogutils.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
@@ -2477,6 +2478,52 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
* that it got added in the redo phase
*/
+ /*
+ * During recovery, the two-phase data can be restored in two ways:
+ * 1) restored from disk file when its xid < checkPoint.nextxid;
+ * 2) restored from the WAL record when its prepare_start_lsn > checkPoint.redo.
+ * A two-phase transaction can be restored from either disk file or WAL,
+ * but can't both. However, a 2PC may satisfy above two conditions if
+ * we crashed after writing a 2PC file to disk, but before the redo LSN
+ * has been updated to pg_control during doing a checkpoint.
+ *
+ * We check if the 2PC file of the transaction being recovery is already
+ * in pg_twophase/ when adding an entry from a WAL record, if a consistent
+ * point has not been reached by recovery and we find a file on disk, then
+ * do nothing because we know restoreTwoPhaseData() has done that at the
+ * beginning of recovery. If a consistent point has been reached in recovery
+ * and we find a file on disk while replaying a WAL record for the same 2PC
+ * file, then fail. If there is no file in pg_twophase/ for the record
+ * replayed, then add it to the array.
+ */
+ if (!XLogRecPtrIsInvalid(start_lsn))
+ {
+ char path[MAXPGPATH];
+ struct stat stat_buf;
+#ifdef USE_ASSERT_CHECKING
+ int i;
+#endif
+
+ TwoPhaseFilePath(path, hdr->xid);
+
+ if (stat(path, &stat_buf) == 0)
+ {
+ Assert(S_ISREG(stat_buf.st_mode));
+ ereport((reachedConsistency ? FATAL : WARNING),
+ (errmsg("could not recover two-phase state file for transaction %u", hdr->xid),
+ errdetail("Two-phase state file has been found in WAL record %X/%X,"
+ " but this transaction has already been restored from disk.",
+ LSN_FORMAT_ARGS(start_lsn))));
+ return;
+ }
+
+#ifdef USE_ASSERT_CHECKING
+ /* cross-check for duplicates in array */
+ for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
+ Assert(TwoPhaseState->prepXacts[i]->xid != hdr->xid);
+#endif
+ }
+
/* Get a free gxact from the freelist */
if (TwoPhaseState->freeGXacts == NULL)
ereport(ERROR,
--
2.19.1.6.gb485710b