pg_waldump and PREPARE

Started by Fujii Masaoover 6 years ago33 messages
#1Fujii Masao
masao.fujii@gmail.com
1 attachment(s)

Hi,

pg_waldump report no detail information about PREPARE TRANSACTION record,
as follows.

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE

I'd like to modify pg_waldump, i.e., xact_desc(), so that it reports
detail information like GID, as follows. Attached patch does that.
This would be helpful, for example, when diagnosing 2PC-related
trouble by checking the status of 2PC transaction with the specified GID.
Thought?

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE gid abc: 2004-06-17
05:26:27.500240 JST

I will add this to next CommitFest page.

Regards,

--
Fujii Masao

Attachments:

prepare-xact-desc.patchapplication/octet-stream; name=prepare-xact-desc.patchDownload
*** a/src/backend/access/rmgrdesc/xactdesc.c
--- b/src/backend/access/rmgrdesc/xactdesc.c
***************
*** 209,214 **** ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
--- 209,249 ----
  	}
  }
  
+ /*
+  * ParsePrepareRecord
+  */
+ void
+ ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
+ {
+ 	char	   *bufptr;
+ 
+ 	bufptr = (char *) xlrec + MAXALIGN(sizeof(xl_xact_prepare));
+ 
+ 	parsed->origin_lsn = xlrec->origin_lsn;
+ 	parsed->origin_timestamp = xlrec->origin_timestamp;
+ 	parsed->twophase_xid = xlrec->xid;
+ 	parsed->dbId = xlrec->database;
+ 	parsed->nsubxacts = xlrec->nsubxacts;
+ 	parsed->nrels = xlrec->ncommitrels;
+ 	parsed->nabortrels = xlrec->nabortrels;
+ 	parsed->nmsgs = xlrec->ninvalmsgs;
+ 
+ 	strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
+ 	bufptr += MAXALIGN(xlrec->gidlen);
+ 
+ 	parsed->subxacts = (TransactionId *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
+ 
+ 	parsed->xnodes = (RelFileNode *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
+ 
+ 	parsed->abortnodes = (RelFileNode *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
+ 
+ 	parsed->msgs = (SharedInvalidationMessage *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
+ }
+ 
  static void
  xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
  {
***************
*** 293,298 **** xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
--- 328,344 ----
  	}
  }
  
+ static void
+ xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
+ {
+ 	xl_xact_parsed_prepare parsed;
+ 
+ 	ParsePrepareRecord(info, xlrec, &parsed);
+ 
+ 	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
+ 	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
+ }
+ 
  static void
  xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
  {
***************
*** 323,328 **** xact_desc(StringInfo buf, XLogReaderState *record)
--- 369,380 ----
  
  		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
  	}
+ 	else if (info == XLOG_XACT_PREPARE)
+ 	{
+ 		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
+ 
+ 		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
+ 	}
  	else if (info == XLOG_XACT_ASSIGNMENT)
  	{
  		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
*** a/src/backend/access/transam/twophase.c
--- b/src/backend/access/transam/twophase.c
***************
*** 911,933 **** TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
   */
  #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
  
! typedef struct TwoPhaseFileHeader
! {
! 	uint32		magic;			/* format identifier */
! 	uint32		total_len;		/* actual file length */
! 	TransactionId xid;			/* original transaction XID */
! 	Oid			database;		/* OID of database it was in */
! 	TimestampTz prepared_at;	/* time of preparation */
! 	Oid			owner;			/* user running the transaction */
! 	int32		nsubxacts;		/* number of following subxact XIDs */
! 	int32		ncommitrels;	/* number of delete-on-commit rels */
! 	int32		nabortrels;		/* number of delete-on-abort rels */
! 	int32		ninvalmsgs;		/* number of cache invalidation messages */
! 	bool		initfileinval;	/* does relcache init file need invalidation? */
! 	uint16		gidlen;			/* length of the GID - GID follows the header */
! 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
! 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
! } TwoPhaseFileHeader;
  
  /*
   * Header for each record in a state file
--- 911,917 ----
   */
  #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
  
! typedef xl_xact_prepare TwoPhaseFileHeader;
  
  /*
   * Header for each record in a state file
***************
*** 1332,1375 **** ReadTwoPhaseFile(TransactionId xid, bool missing_ok)
  	return buf;
  }
  
- /*
-  * ParsePrepareRecord
-  */
- void
- ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
- {
- 	TwoPhaseFileHeader *hdr;
- 	char	   *bufptr;
- 
- 	hdr = (TwoPhaseFileHeader *) xlrec;
- 	bufptr = xlrec + MAXALIGN(sizeof(TwoPhaseFileHeader));
- 
- 	parsed->origin_lsn = hdr->origin_lsn;
- 	parsed->origin_timestamp = hdr->origin_timestamp;
- 	parsed->twophase_xid = hdr->xid;
- 	parsed->dbId = hdr->database;
- 	parsed->nsubxacts = hdr->nsubxacts;
- 	parsed->nrels = hdr->ncommitrels;
- 	parsed->nabortrels = hdr->nabortrels;
- 	parsed->nmsgs = hdr->ninvalmsgs;
- 
- 	strncpy(parsed->twophase_gid, bufptr, hdr->gidlen);
- 	bufptr += MAXALIGN(hdr->gidlen);
- 
- 	parsed->subxacts = (TransactionId *) bufptr;
- 	bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId));
- 
- 	parsed->xnodes = (RelFileNode *) bufptr;
- 	bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
- 
- 	parsed->abortnodes = (RelFileNode *) bufptr;
- 	bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
- 
- 	parsed->msgs = (SharedInvalidationMessage *) bufptr;
- 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
- }
- 
- 
  
  /*
   * Reads 2PC data from xlog. During checkpoint this data will be moved to
--- 1316,1321 ----
*** a/src/include/access/twophase.h
--- b/src/include/access/twophase.h
***************
*** 47,54 **** extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
  
  extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p,
  							int *nxids_p);
- extern void ParsePrepareRecord(uint8 info, char *xlrec,
- 				   xl_xact_parsed_prepare *parsed);
  extern void StandbyRecoverPreparedTransactions(void);
  extern void RecoverPreparedTransactions(void);
  
--- 47,52 ----
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
***************
*** 292,297 **** typedef struct xl_xact_abort
--- 292,315 ----
  } xl_xact_abort;
  #define MinSizeOfXactAbort sizeof(xl_xact_abort)
  
+ typedef struct xl_xact_prepare
+ {
+ 	uint32		magic;			/* format identifier */
+ 	uint32		total_len;		/* actual file length */
+ 	TransactionId xid;			/* original transaction XID */
+ 	Oid			database;		/* OID of database it was in */
+ 	TimestampTz prepared_at;	/* time of preparation */
+ 	Oid			owner;			/* user running the transaction */
+ 	int32		nsubxacts;		/* number of following subxact XIDs */
+ 	int32		ncommitrels;	/* number of delete-on-commit rels */
+ 	int32		nabortrels;		/* number of delete-on-abort rels */
+ 	int32		ninvalmsgs;		/* number of cache invalidation messages */
+ 	bool		initfileinval;	/* does relcache init file need invalidation? */
+ 	uint16		gidlen;			/* length of the GID - GID follows the header */
+ 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
+ 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+ } xl_xact_prepare;
+ 
  /*
   * Commit/Abort records in the above form are a bit verbose to parse, so
   * there's a deconstructed versions generated by ParseCommit/AbortRecord() for
***************
*** 435,440 **** extern const char *xact_identify(uint8 info);
--- 453,459 ----
  /* also in xactdesc.c, so they can be shared between front/backend code */
  extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
  extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
+ extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed);
  
  extern void EnterParallelMode(void);
  extern void ExitParallelMode(void);
#2Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fujii Masao (#1)
Re: pg_waldump and PREPARE

On 2019-Apr-26, Fujii Masao wrote:

Hi,

pg_waldump report no detail information about PREPARE TRANSACTION record,
as follows.

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE

I'd like to modify pg_waldump, i.e., xact_desc(), so that it reports
detail information like GID, as follows. Attached patch does that.
This would be helpful, for example, when diagnosing 2PC-related
trouble by checking the status of 2PC transaction with the specified GID.
Thought?

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE gid abc: 2004-06-17
05:26:27.500240 JST

I think this is a great change to make.

Strangely, before your patch, ParsePrepareRecord seems completely
unused.

I'm not sure I like the moving of that routine to xactdesc.c ...
on one hand, it would be side-by-side with ParseCommitRecord, but OTOH
it seems weird to have twophase.c call xactdesc.c. I also wonder if
defining the structs in the way you do is the most sensible arrangement.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#3Fujii Masao
masao.fujii@gmail.com
In reply to: Alvaro Herrera (#2)
Re: pg_waldump and PREPARE

On Fri, Apr 26, 2019 at 1:04 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

On 2019-Apr-26, Fujii Masao wrote:

Hi,

pg_waldump report no detail information about PREPARE TRANSACTION record,
as follows.

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE

I'd like to modify pg_waldump, i.e., xact_desc(), so that it reports
detail information like GID, as follows. Attached patch does that.
This would be helpful, for example, when diagnosing 2PC-related
trouble by checking the status of 2PC transaction with the specified GID.
Thought?

rmgr: Transaction len (rec/tot): 250/ 250, tx: 485,
lsn: 0/020000A8, prev 0/02000060, desc: PREPARE gid abc: 2004-06-17
05:26:27.500240 JST

I think this is a great change to make.

Thanks!

Strangely, before your patch, ParsePrepareRecord seems completely
unused.

Yes, this seems to be the leftover of commit 1eb6d6527a.

I'm not sure I like the moving of that routine to xactdesc.c ...
on one hand, it would be side-by-side with ParseCommitRecord, but OTOH
it seems weird to have twophase.c call xactdesc.c.

I moved ParsePrepareRecord() to xactdesc.c because it should be
accessed in backend (when replaying WAL) and frontend (pg_waldump) code
and xactdesc.c looked like proper place for that purpose
ParseCommitRecord() is also in xactdesc.c because of the same reason..

I also wonder if
defining the structs in the way you do is the most sensible arrangement.

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

Regards,

--
Fujii Masao

#4Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fujii Masao (#3)
Re: pg_waldump and PREPARE

On 2019-Apr-26, Fujii Masao wrote:

On Fri, Apr 26, 2019 at 1:04 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

I also wonder if
defining the structs in the way you do is the most sensible arrangement.

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

I don't know. I spent some time staring at the code involved, and it
seems it'd be possible to improve just a little bit on cleanliness
grounds, with a lot of effort, but not enough practical value.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#5Michael Paquier
michael@paquier.xyz
In reply to: Alvaro Herrera (#4)
Re: pg_waldump and PREPARE

On Thu, Apr 25, 2019 at 03:08:36PM -0400, Alvaro Herrera wrote:

On 2019-Apr-26, Fujii Masao wrote:

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

I don't know. I spent some time staring at the code involved, and it
seems it'd be possible to improve just a little bit on cleanliness
grounds, with a lot of effort, but not enough practical value.

Describing those records is something we should do. There are other
parsing routines in xactdesc.c for commit and abort records, so having
that extra routine for prepare at the same place does not sound
strange to me.

+typedef xl_xact_prepare TwoPhaseFileHeader;
I find this mapping implementation a bit lazy, and your
newly-introduced xl_xact_prepare does not count for all the contents
of the actual WAL record for PREPARE TRANSACTION. Wouldn't it be
better to put all the contents of the record in the same structure,
and not only the 2PC header information?

This is not v12 material of course.
--
Michael

#6Julien Rouhaud
rjuju123@gmail.com
In reply to: Michael Paquier (#5)
Re: pg_waldump and PREPARE

On Fri, Apr 26, 2019 at 5:38 AM Michael Paquier <michael@paquier.xyz> wrote:

On Thu, Apr 25, 2019 at 03:08:36PM -0400, Alvaro Herrera wrote:

On 2019-Apr-26, Fujii Masao wrote:

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

I don't know. I spent some time staring at the code involved, and it
seems it'd be possible to improve just a little bit on cleanliness
grounds, with a lot of effort, but not enough practical value.

Describing those records is something we should do. There are other
parsing routines in xactdesc.c for commit and abort records, so having
that extra routine for prepare at the same place does not sound
strange to me.

+typedef xl_xact_prepare TwoPhaseFileHeader;
I find this mapping implementation a bit lazy, and your
newly-introduced xl_xact_prepare does not count for all the contents
of the actual WAL record for PREPARE TRANSACTION. Wouldn't it be
better to put all the contents of the record in the same structure,
and not only the 2PC header information?

This patch doesn't apply anymore, could you send a rebase?

#7Fujii Masao
masao.fujii@gmail.com
In reply to: Julien Rouhaud (#6)
1 attachment(s)
Re: pg_waldump and PREPARE

On Tue, Jul 2, 2019 at 7:16 PM Julien Rouhaud <rjuju123@gmail.com> wrote:

On Fri, Apr 26, 2019 at 5:38 AM Michael Paquier <michael@paquier.xyz> wrote:

On Thu, Apr 25, 2019 at 03:08:36PM -0400, Alvaro Herrera wrote:

On 2019-Apr-26, Fujii Masao wrote:

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

I don't know. I spent some time staring at the code involved, and it
seems it'd be possible to improve just a little bit on cleanliness
grounds, with a lot of effort, but not enough practical value.

Describing those records is something we should do. There are other
parsing routines in xactdesc.c for commit and abort records, so having
that extra routine for prepare at the same place does not sound
strange to me.

+typedef xl_xact_prepare TwoPhaseFileHeader;
I find this mapping implementation a bit lazy, and your
newly-introduced xl_xact_prepare does not count for all the contents
of the actual WAL record for PREPARE TRANSACTION. Wouldn't it be
better to put all the contents of the record in the same structure,
and not only the 2PC header information?

This patch doesn't apply anymore, could you send a rebase?

Yes, attached is the updated version of the patch.

Regards,

--
Fujii Masao

Attachments:

prepare-xact-desc_v2.patchapplication/octet-stream; name=prepare-xact-desc_v2.patchDownload
*** a/src/backend/access/rmgrdesc/xactdesc.c
--- b/src/backend/access/rmgrdesc/xactdesc.c
***************
*** 209,214 **** ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
--- 209,249 ----
  	}
  }
  
+ /*
+  * ParsePrepareRecord
+  */
+ void
+ ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
+ {
+ 	char	   *bufptr;
+ 
+ 	bufptr = (char *) xlrec + MAXALIGN(sizeof(xl_xact_prepare));
+ 
+ 	parsed->origin_lsn = xlrec->origin_lsn;
+ 	parsed->origin_timestamp = xlrec->origin_timestamp;
+ 	parsed->twophase_xid = xlrec->xid;
+ 	parsed->dbId = xlrec->database;
+ 	parsed->nsubxacts = xlrec->nsubxacts;
+ 	parsed->nrels = xlrec->ncommitrels;
+ 	parsed->nabortrels = xlrec->nabortrels;
+ 	parsed->nmsgs = xlrec->ninvalmsgs;
+ 
+ 	strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
+ 	bufptr += MAXALIGN(xlrec->gidlen);
+ 
+ 	parsed->subxacts = (TransactionId *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
+ 
+ 	parsed->xnodes = (RelFileNode *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
+ 
+ 	parsed->abortnodes = (RelFileNode *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
+ 
+ 	parsed->msgs = (SharedInvalidationMessage *) bufptr;
+ 	bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
+ }
+ 
  static void
  xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
  {
***************
*** 293,298 **** xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
--- 328,344 ----
  	}
  }
  
+ static void
+ xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
+ {
+ 	xl_xact_parsed_prepare parsed;
+ 
+ 	ParsePrepareRecord(info, xlrec, &parsed);
+ 
+ 	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
+ 	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
+ }
+ 
  static void
  xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
  {
***************
*** 323,328 **** xact_desc(StringInfo buf, XLogReaderState *record)
--- 369,380 ----
  
  		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
  	}
+ 	else if (info == XLOG_XACT_PREPARE)
+ 	{
+ 		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
+ 
+ 		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
+ 	}
  	else if (info == XLOG_XACT_ASSIGNMENT)
  	{
  		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
*** a/src/backend/access/transam/twophase.c
--- b/src/backend/access/transam/twophase.c
***************
*** 911,933 **** TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
   */
  #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
  
! typedef struct TwoPhaseFileHeader
! {
! 	uint32		magic;			/* format identifier */
! 	uint32		total_len;		/* actual file length */
! 	TransactionId xid;			/* original transaction XID */
! 	Oid			database;		/* OID of database it was in */
! 	TimestampTz prepared_at;	/* time of preparation */
! 	Oid			owner;			/* user running the transaction */
! 	int32		nsubxacts;		/* number of following subxact XIDs */
! 	int32		ncommitrels;	/* number of delete-on-commit rels */
! 	int32		nabortrels;		/* number of delete-on-abort rels */
! 	int32		ninvalmsgs;		/* number of cache invalidation messages */
! 	bool		initfileinval;	/* does relcache init file need invalidation? */
! 	uint16		gidlen;			/* length of the GID - GID follows the header */
! 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
! 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
! } TwoPhaseFileHeader;
  
  /*
   * Header for each record in a state file
--- 911,917 ----
   */
  #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
  
! typedef xl_xact_prepare TwoPhaseFileHeader;
  
  /*
   * Header for each record in a state file
***************
*** 1332,1375 **** ReadTwoPhaseFile(TransactionId xid, bool missing_ok)
  	return buf;
  }
  
- /*
-  * ParsePrepareRecord
-  */
- void
- ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
- {
- 	TwoPhaseFileHeader *hdr;
- 	char	   *bufptr;
- 
- 	hdr = (TwoPhaseFileHeader *) xlrec;
- 	bufptr = xlrec + MAXALIGN(sizeof(TwoPhaseFileHeader));
- 
- 	parsed->origin_lsn = hdr->origin_lsn;
- 	parsed->origin_timestamp = hdr->origin_timestamp;
- 	parsed->twophase_xid = hdr->xid;
- 	parsed->dbId = hdr->database;
- 	parsed->nsubxacts = hdr->nsubxacts;
- 	parsed->nrels = hdr->ncommitrels;
- 	parsed->nabortrels = hdr->nabortrels;
- 	parsed->nmsgs = hdr->ninvalmsgs;
- 
- 	strncpy(parsed->twophase_gid, bufptr, hdr->gidlen);
- 	bufptr += MAXALIGN(hdr->gidlen);
- 
- 	parsed->subxacts = (TransactionId *) bufptr;
- 	bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId));
- 
- 	parsed->xnodes = (RelFileNode *) bufptr;
- 	bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
- 
- 	parsed->abortnodes = (RelFileNode *) bufptr;
- 	bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
- 
- 	parsed->msgs = (SharedInvalidationMessage *) bufptr;
- 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
- }
- 
- 
  
  /*
   * Reads 2PC data from xlog. During checkpoint this data will be moved to
--- 1316,1321 ----
*** a/src/include/access/twophase.h
--- b/src/include/access/twophase.h
***************
*** 47,54 **** extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
  
  extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p,
  												 int *nxids_p);
- extern void ParsePrepareRecord(uint8 info, char *xlrec,
- 							   xl_xact_parsed_prepare *parsed);
  extern void StandbyRecoverPreparedTransactions(void);
  extern void RecoverPreparedTransactions(void);
  
--- 47,52 ----
*** a/src/include/access/xact.h
--- b/src/include/access/xact.h
***************
*** 292,297 **** typedef struct xl_xact_abort
--- 292,315 ----
  } xl_xact_abort;
  #define MinSizeOfXactAbort sizeof(xl_xact_abort)
  
+ typedef struct xl_xact_prepare
+ {
+ 	uint32		magic;			/* format identifier */
+ 	uint32		total_len;		/* actual file length */
+ 	TransactionId xid;			/* original transaction XID */
+ 	Oid			database;		/* OID of database it was in */
+ 	TimestampTz prepared_at;	/* time of preparation */
+ 	Oid			owner;			/* user running the transaction */
+ 	int32		nsubxacts;		/* number of following subxact XIDs */
+ 	int32		ncommitrels;	/* number of delete-on-commit rels */
+ 	int32		nabortrels;		/* number of delete-on-abort rels */
+ 	int32		ninvalmsgs;		/* number of cache invalidation messages */
+ 	bool		initfileinval;	/* does relcache init file need invalidation? */
+ 	uint16		gidlen;			/* length of the GID - GID follows the header */
+ 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
+ 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+ } xl_xact_prepare;
+ 
  /*
   * Commit/Abort records in the above form are a bit verbose to parse, so
   * there's a deconstructed versions generated by ParseCommit/AbortRecord() for
***************
*** 435,440 **** extern const char *xact_identify(uint8 info);
--- 453,459 ----
  /* also in xactdesc.c, so they can be shared between front/backend code */
  extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
  extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
+ extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed);
  
  extern void EnterParallelMode(void);
  extern void ExitParallelMode(void);
#8Julien Rouhaud
rjuju123@gmail.com
In reply to: Fujii Masao (#7)
Re: pg_waldump and PREPARE

On Wed, Jul 3, 2019 at 5:21 PM Fujii Masao <masao.fujii@gmail.com> wrote:

On Tue, Jul 2, 2019 at 7:16 PM Julien Rouhaud <rjuju123@gmail.com> wrote:

On Fri, Apr 26, 2019 at 5:38 AM Michael Paquier <michael@paquier.xyz> wrote:

On Thu, Apr 25, 2019 at 03:08:36PM -0400, Alvaro Herrera wrote:

On 2019-Apr-26, Fujii Masao wrote:

I did that arrangement because the format of PREPARE TRANSACTION record,
i.e., that struct, also needs to be accessed in backend and frontend.
But, of course, if there is smarter way, I'm happy to adopt that!

I don't know. I spent some time staring at the code involved, and it
seems it'd be possible to improve just a little bit on cleanliness
grounds, with a lot of effort, but not enough practical value.

Describing those records is something we should do. There are other
parsing routines in xactdesc.c for commit and abort records, so having
that extra routine for prepare at the same place does not sound
strange to me.

+typedef xl_xact_prepare TwoPhaseFileHeader;
I find this mapping implementation a bit lazy, and your
newly-introduced xl_xact_prepare does not count for all the contents
of the actual WAL record for PREPARE TRANSACTION. Wouldn't it be
better to put all the contents of the record in the same structure,
and not only the 2PC header information?

This patch doesn't apply anymore, could you send a rebase?

Yes, attached is the updated version of the patch.

Thanks!

So the patch compiles and works as intended. I don't have much to say
about it, it all looks good to me, since the concerns about xactdesc.c
aren't worth the trouble.

I'm not sure that I understand Michael's objection though, as
xl_xact_prepare is not a new definition and AFAICS it couldn't contain
the records anyway. So I'll let him say if he has further objections
or if it's ready for committer!

#9Michael Paquier
michael@paquier.xyz
In reply to: Julien Rouhaud (#8)
Re: pg_waldump and PREPARE

On Wed, Jul 03, 2019 at 08:23:44PM +0200, Julien Rouhaud wrote:

So the patch compiles and works as intended. I don't have much to say
about it, it all looks good to me, since the concerns about xactdesc.c
aren't worth the trouble.

I'm not sure that I understand Michael's objection though, as
xl_xact_prepare is not a new definition and AFAICS it couldn't contain
the records anyway. So I'll let him say if he has further objections
or if it's ready for committer!

This patch provides parsing information only for the header of the 2PC
record. Wouldn't it be interesting to get more information from the
various TwoPhaseRecordOnDisk's callbacks? We could also print much
more information in xact_desc_prepare(). Like the subxacts, the XID,
the invalidation messages and the delete-on-abort/commit rels.
--
Michael

#10Julien Rouhaud
rjuju123@gmail.com
In reply to: Michael Paquier (#9)
Re: pg_waldump and PREPARE

On Thu, Jul 4, 2019 at 9:45 AM Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Jul 03, 2019 at 08:23:44PM +0200, Julien Rouhaud wrote:

So the patch compiles and works as intended. I don't have much to say
about it, it all looks good to me, since the concerns about xactdesc.c
aren't worth the trouble.

I'm not sure that I understand Michael's objection though, as
xl_xact_prepare is not a new definition and AFAICS it couldn't contain
the records anyway. So I'll let him say if he has further objections
or if it's ready for committer!

This patch provides parsing information only for the header of the 2PC
record. Wouldn't it be interesting to get more information from the
various TwoPhaseRecordOnDisk's callbacks? We could also print much
more information in xact_desc_prepare(). Like the subxacts, the XID,
the invalidation messages and the delete-on-abort/commit rels.

Most of those are already described in the COMMIT PREPARE message,
wouldn't that be redundant? abortrels aren't displayed anywhere
though, so +1 for adding them.

I also see that the dbid isn't displayed in any of the 2PC message,
that'd be useful to have it directly instead of looking for it in
other messages for the same transaction.

#11Thomas Munro
thomas.munro@gmail.com
In reply to: Julien Rouhaud (#10)
Re: pg_waldump and PREPARE

On Thu, Jul 4, 2019 at 8:25 PM Julien Rouhaud <rjuju123@gmail.com> wrote:

On Thu, Jul 4, 2019 at 9:45 AM Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Jul 03, 2019 at 08:23:44PM +0200, Julien Rouhaud wrote:

So the patch compiles and works as intended. I don't have much to say
about it, it all looks good to me, since the concerns about xactdesc.c
aren't worth the trouble.

I'm not sure that I understand Michael's objection though, as
xl_xact_prepare is not a new definition and AFAICS it couldn't contain
the records anyway. So I'll let him say if he has further objections
or if it's ready for committer!

This patch provides parsing information only for the header of the 2PC
record. Wouldn't it be interesting to get more information from the
various TwoPhaseRecordOnDisk's callbacks? We could also print much
more information in xact_desc_prepare(). Like the subxacts, the XID,
the invalidation messages and the delete-on-abort/commit rels.

Most of those are already described in the COMMIT PREPARE message,
wouldn't that be redundant? abortrels aren't displayed anywhere
though, so +1 for adding them.

I also see that the dbid isn't displayed in any of the 2PC message,
that'd be useful to have it directly instead of looking for it in
other messages for the same transaction.

Hello all,

I've moved this to the next CF, and set it to "Needs review" since a
rebase was provided.

--
Thomas Munro
https://enterprisedb.com

#12Michael Paquier
michael@paquier.xyz
In reply to: Thomas Munro (#11)
Re: pg_waldump and PREPARE

On Thu, Aug 01, 2019 at 11:05:34PM +1200, Thomas Munro wrote:

I've moved this to the next CF, and set it to "Needs review" since a
rebase was provided.

I may be missing something of course, but in this case we argued about
adding a couple of more fields. In consequence, the patch should be
waiting on its author, no?
--
Michael

#13Julien Rouhaud
rjuju123@gmail.com
In reply to: Michael Paquier (#12)
Re: pg_waldump and PREPARE

Hi,

Le jeu. 1 août 2019 à 13:51, Michael Paquier <michael@paquier.xyz> a écrit :

On Thu, Aug 01, 2019 at 11:05:34PM +1200, Thomas Munro wrote:

I've moved this to the next CF, and set it to "Needs review" since a
rebase was provided.

I may be missing something of course, but in this case we argued about
adding a couple of more fields. In consequence, the patch should be
waiting on its author, no?

That's also my understanding.

Show quoted text
#14Thomas Munro
thomas.munro@gmail.com
In reply to: Michael Paquier (#12)
Re: pg_waldump and PREPARE

On Thu, Aug 1, 2019 at 11:51 PM Michael Paquier <michael@paquier.xyz> wrote:

On Thu, Aug 01, 2019 at 11:05:34PM +1200, Thomas Munro wrote:

I've moved this to the next CF, and set it to "Needs review" since a
rebase was provided.

I may be missing something of course, but in this case we argued about
adding a couple of more fields. In consequence, the patch should be
waiting on its author, no?

Oh, OK. Changed. So we're waiting for Fujii-san to respond to the
suggestions about new fields.

--
Thomas Munro
https://enterprisedb.com

#15Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#12)
Re: pg_waldump and PREPARE

On 2019-Aug-01, Michael Paquier wrote:

I may be missing something of course, but in this case we argued about
adding a couple of more fields. In consequence, the patch should be
waiting on its author, no?

Fujii,

Are you in a position to submit an updated version of this patch?

Maybe Vignesh is in a position to help to complete this, since he has
been eyeing this code lately. Vignesh?

Thanks,

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#16Fujii Masao
masao.fujii@gmail.com
In reply to: Julien Rouhaud (#10)
Re: pg_waldump and PREPARE

Sorry for the long delay...

On Thu, Jul 4, 2019 at 5:25 PM Julien Rouhaud <rjuju123@gmail.com> wrote:

On Thu, Jul 4, 2019 at 9:45 AM Michael Paquier <michael@paquier.xyz> wrote:

On Wed, Jul 03, 2019 at 08:23:44PM +0200, Julien Rouhaud wrote:

So the patch compiles and works as intended. I don't have much to say
about it, it all looks good to me, since the concerns about xactdesc.c
aren't worth the trouble.

I'm not sure that I understand Michael's objection though, as
xl_xact_prepare is not a new definition and AFAICS it couldn't contain
the records anyway. So I'll let him say if he has further objections
or if it's ready for committer!

This patch provides parsing information only for the header of the 2PC
record. Wouldn't it be interesting to get more information from the
various TwoPhaseRecordOnDisk's callbacks? We could also print much
more information in xact_desc_prepare(). Like the subxacts, the XID,
the invalidation messages and the delete-on-abort/commit rels.

Most of those are already described in the COMMIT PREPARE message,
wouldn't that be redundant? abortrels aren't displayed anywhere
though, so +1 for adding them.

xact_desc_abort() for ROLLBACK PREPARED describes abortrels. No?

I also see that the dbid isn't displayed in any of the 2PC message,
that'd be useful to have it directly instead of looking for it in
other messages for the same transaction.

dbid is not reported even in COMMIT message. So I don't like adding
dbid into only the PREPARE message.

Regards,

--
Fujii Masao

#17Fujii Masao
masao.fujii@gmail.com
In reply to: Alvaro Herrera (#15)
Re: pg_waldump and PREPARE

On Tue, Sep 3, 2019 at 3:04 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

On 2019-Aug-01, Michael Paquier wrote:

I may be missing something of course, but in this case we argued about
adding a couple of more fields. In consequence, the patch should be
waiting on its author, no?

Fujii,

Are you in a position to submit an updated version of this patch?

Sorry for the long delay... Yes, I will update the patch if necessary.

Regards,

--
Fujii Masao

#18Michael Paquier
michael@paquier.xyz
In reply to: Fujii Masao (#17)
Re: pg_waldump and PREPARE

On Tue, Sep 03, 2019 at 10:00:08AM +0900, Fujii Masao wrote:

Sorry for the long delay... Yes, I will update the patch if necessary.

Fujii-san, are you planning to update this patch then? I have
switched it as waiting on author.
--
Michael

#19Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#18)
Re: pg_waldump and PREPARE

On Fri, Nov 8, 2019 at 9:41 AM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Sep 03, 2019 at 10:00:08AM +0900, Fujii Masao wrote:

Sorry for the long delay... Yes, I will update the patch if necessary.

Fujii-san, are you planning to update this patch then? I have
switched it as waiting on author.

No because there has been nothing to update in the latest patch for now
unless I'm missing something. So I'm just waiting for some new review
comments against the latest patch to come :)
Can I switch the status back to "Needs review"?

Regards,

--
Fujii Masao

#20Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Fujii Masao (#19)
Re: pg_waldump and PREPARE

On 08/11/2019 05:53, Fujii Masao wrote:

On Fri, Nov 8, 2019 at 9:41 AM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Sep 03, 2019 at 10:00:08AM +0900, Fujii Masao wrote:

Sorry for the long delay... Yes, I will update the patch if necessary.

Fujii-san, are you planning to update this patch then? I have
switched it as waiting on author.

No because there has been nothing to update in the latest patch for now
unless I'm missing something. So I'm just waiting for some new review
comments against the latest patch to come :)
Can I switch the status back to "Needs review"?

Regards,

One issue is that your patch provides small information. WAL errors
Investigation often requires information on xid, subxacts,
delete-on-abort/commit rels; rarely - invalidation messages etc.

--
Andrey Lepikhov
Postgres Professional
https://postgrespro.com
The Russian Postgres Company

#21Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Fujii Masao (#19)
Re: pg_waldump and PREPARE

At Fri, 8 Nov 2019 09:53:07 +0900, Fujii Masao <masao.fujii@gmail.com> wrote in

On Fri, Nov 8, 2019 at 9:41 AM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Sep 03, 2019 at 10:00:08AM +0900, Fujii Masao wrote:

Sorry for the long delay... Yes, I will update the patch if necessary.

Fujii-san, are you planning to update this patch then? I have
switched it as waiting on author.

No because there has been nothing to update in the latest patch for now
unless I'm missing something. So I'm just waiting for some new review
comments against the latest patch to come :)
Can I switch the status back to "Needs review"?

On Fri, Apr 26, 2019 at 5:38 AM Michael Paquier <michael(at)paquier(dot)xyz> wrote:

+typedef xl_xact_prepare TwoPhaseFileHeader
I find this mapping implementation a bit lazy, and your
newly-introduced xl_xact_prepare does not count for all the contents
of the actual WAL record for PREPARE TRANSACTION. Wouldn't it be
better to put all the contents of the record in the same structure,
and not only the 2PC header information?

I agree to this in principle, but I'm afraid that we cannot do that
actually. Doing it straight way would result in something like this.

  typedef struct xl_xact_prepare
  {
    uint32      magic;
  ...
    TimestampTz origin_timestamp;
    /* correct alignment here */
+   char        gid[FLEXIBLE_ARRAY_MEMBER]; /* the GID of the prepred xact */
+   /* subxacts, xnodes, msgs and sentinel follow the gid[] array */
} xl_xact_prepare;

I don't think this is meaningful..

After all, the new xlog record struct is used only at one place.
xlog_redo is the correspondent of xact_desc, but it is not aware of
the stuct and PrepareRedoAdd decodes it using TwoPhaseFileHeader. In
that sense, the details of the record is a secret of twophase.c. What
is worse, apparently TwoPhaseFileHeader is a *subset* of
xl_xact_prepare but what we want to expose is the super set. Thus I
porpose to add a comment instead of exposing the full structure in
xl_xact_prepare definition.

  typedef struct xl_xact_prepare
  {
    uint32      magic;
  ...
    TimestampTz origin_timestamp;
+   /*
+    * This record has multiple trailing data sections with variable
+    * length. See twophase.c for the details.
+    */
  } xl_xact_prepare;

Then, teach xlog_redo to resolve the record pointer to this type
before passing it to PrepareRedoAdd.

Does it make sense?

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#22Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Andrey Lepikhov (#20)
Re: pg_waldump and PREPARE

Hello.

At Fri, 8 Nov 2019 08:23:41 +0500, Andrey Lepikhov <a.lepikhov@postgrespro.ru> wrote in

Can I switch the status back to "Needs review"?
Regards,

One issue is that your patch provides small information. WAL errors
Investigation often requires information on xid, subxacts,
delete-on-abort/commit rels; rarely - invalidation messages etc.

Basically agrred, but it can be very large in certain cases, even if
it is rare.

By the way, in the patch xact_desc_prepare seems printing
parseed.xact_time, which is not actually set by ParsePrepareRecord.

# I missed the funtion. xl_xact_prepare is used in *two* places in
# front end.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#23Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Kyotaro Horiguchi (#22)
Re: pg_waldump and PREPARE

On 08/11/2019 09:26, Kyotaro Horiguchi wrote:

Hello.

At Fri, 8 Nov 2019 08:23:41 +0500, Andrey Lepikhov <a.lepikhov@postgrespro.ru> wrote in

Can I switch the status back to "Needs review"?
Regards,

One issue is that your patch provides small information. WAL errors
Investigation often requires information on xid, subxacts,
delete-on-abort/commit rels; rarely - invalidation messages etc.

Basically agrred, but it can be very large in certain cases, even if
it is rare.

Maybe this is the reason for introducing a “verbose” option?

--
Andrey Lepikhov
Postgres Professional
https://postgrespro.com
The Russian Postgres Company

#24Fujii Masao
masao.fujii@gmail.com
In reply to: Andrey Lepikhov (#23)
1 attachment(s)
Re: pg_waldump and PREPARE

On Fri, Nov 8, 2019 at 1:33 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

On 08/11/2019 09:26, Kyotaro Horiguchi wrote:

Hello.

At Fri, 8 Nov 2019 08:23:41 +0500, Andrey Lepikhov <a.lepikhov@postgrespro.ru> wrote in

Can I switch the status back to "Needs review"?
Regards,

One issue is that your patch provides small information. WAL errors
Investigation often requires information on xid, subxacts,
delete-on-abort/commit rels; rarely - invalidation messages etc.

Thanks for the review!

xid is already included in the pg_waldump output for
PREPARE TRANSACTION record. Regarding subxacts, rels and invals,
I agree that they might be useful when diagnosing 2PC-related trouble.
I attached the updated version of the patch that also changes
pg_waldump so that it outputs delete-on-commit rels, delete-on-aborts,
subxacts and invals.

Here is the example of output for PREPARE TRASACTION record, with the pach.

rmgr: Transaction len (rec/tot): 837/ 837, tx: 503, lsn:
0/030055C8, prev 0/03005588, desc: PREPARE gid xxx: 2019-11-11
13:00:18.616056 JST; rels(commit): base/12923/16408 base/12923/16407
base/12923/16406 base/12923/16405; rels(abort): base/12923/16412
base/12923/16411 base/12923/16408 base/12923/16407; subxacts: 505;
inval msgs: catcache 50 catcache 49 catcache 50 catcache 49 catcache
50 catcache 49 catcache 50 catcache 49 catcache 50 catcache 49
catcache 50 catcache 49 relcache 16386 relcache 16390 relcache 16390
relcache 16386 relcache 16386 relcache 16390 relcache 16390 relcache
16386

By the way, in the patch xact_desc_prepare seems printing
parseed.xact_time, which is not actually set by ParsePrepareRecord.

Thanks for the review! You are right.
I fixed this issue in the attached patch.

Regards,

--
Fujii Masao

Attachments:

prepare-xact-desc_v3.patchapplication/octet-stream; name=prepare-xact-desc_v3.patchDownload
diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
index a61f38dd19..96e96667b7 100644
--- a/src/backend/access/rmgrdesc/xactdesc.c
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -209,37 +209,92 @@ ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
 	}
 }
 
-static void
-xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
+/*
+ * ParsePrepareRecord
+ */
+void
+ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
 {
-	xl_xact_parsed_commit parsed;
-	int			i;
+	char	   *bufptr;
 
-	ParseCommitRecord(info, xlrec, &parsed);
+	bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
 
-	/* If this is a prepared xact, show the xid of the original xact */
-	if (TransactionIdIsValid(parsed.twophase_xid))
-		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+	memset(parsed, 0, sizeof(*parsed));
 
-	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+	parsed->xact_time = xlrec->prepared_at;
+	parsed->origin_lsn = xlrec->origin_lsn;
+	parsed->origin_timestamp = xlrec->origin_timestamp;
+	parsed->twophase_xid = xlrec->xid;
+	parsed->dbId = xlrec->database;
+	parsed->nsubxacts = xlrec->nsubxacts;
+	parsed->nrels = xlrec->ncommitrels;
+	parsed->nabortrels = xlrec->nabortrels;
+	parsed->nmsgs = xlrec->ninvalmsgs;
+
+	strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
+	bufptr += MAXALIGN(xlrec->gidlen);
+
+	parsed->subxacts = (TransactionId *) bufptr;
+	bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
+
+	parsed->xnodes = (RelFileNode *) bufptr;
+	bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
 
-	if (parsed.nrels > 0)
+	parsed->abortnodes = (RelFileNode *) bufptr;
+	bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
+
+	parsed->msgs = (SharedInvalidationMessage *) bufptr;
+	bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
+}
+
+static void
+xact_desc_relations(StringInfo buf, char *label, int nrels,
+					RelFileNode *xnodes)
+{
+	int		i;
+
+	if (nrels > 0)
 	{
-		appendStringInfoString(buf, "; rels:");
-		for (i = 0; i < parsed.nrels; i++)
+		appendStringInfo(buf, "; %s:", label);
+		for (i = 0; i < nrels; i++)
 		{
-			char	   *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
+			char	   *path = relpathperm(xnodes[i], MAIN_FORKNUM);
 
 			appendStringInfo(buf, " %s", path);
 			pfree(path);
 		}
 	}
-	if (parsed.nsubxacts > 0)
+}
+
+static void
+xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
+{
+	int		i;
+
+	if (nsubxacts > 0)
 	{
 		appendStringInfoString(buf, "; subxacts:");
-		for (i = 0; i < parsed.nsubxacts; i++)
-			appendStringInfo(buf, " %u", parsed.subxacts[i]);
+		for (i = 0; i < nsubxacts; i++)
+			appendStringInfo(buf, " %u", subxacts[i]);
 	}
+}
+
+static void
+xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
+{
+	xl_xact_parsed_commit parsed;
+
+	ParseCommitRecord(info, xlrec, &parsed);
+
+	/* If this is a prepared xact, show the xid of the original xact */
+	if (TransactionIdIsValid(parsed.twophase_xid))
+		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+
+	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+
+	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+
 	if (parsed.nmsgs > 0)
 	{
 		standby_desc_invalidations(
@@ -264,7 +319,6 @@ static void
 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
 {
 	xl_xact_parsed_abort parsed;
-	int			i;
 
 	ParseAbortRecord(info, xlrec, &parsed);
 
@@ -273,23 +327,31 @@ xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
 		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
 
 	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
-	if (parsed.nrels > 0)
-	{
-		appendStringInfoString(buf, "; rels:");
-		for (i = 0; i < parsed.nrels; i++)
-		{
-			char	   *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
 
-			appendStringInfo(buf, " %s", path);
-			pfree(path);
-		}
-	}
+	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+}
+
+static void
+xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
+{
+	xl_xact_parsed_prepare parsed;
+
+	ParsePrepareRecord(info, xlrec, &parsed);
+
+	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
+	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
 
-	if (parsed.nsubxacts > 0)
+	xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
+	xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
+						parsed.abortnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+
+	if (parsed.nmsgs > 0)
 	{
-		appendStringInfoString(buf, "; subxacts:");
-		for (i = 0; i < parsed.nsubxacts; i++)
-			appendStringInfo(buf, " %u", parsed.subxacts[i]);
+		standby_desc_invalidations(
+								   buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
+								   xlrec->initfileinval);
 	}
 }
 
@@ -323,6 +385,12 @@ xact_desc(StringInfo buf, XLogReaderState *record)
 
 		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
 	}
+	else if (info == XLOG_XACT_PREPARE)
+	{
+		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
+
+		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
+	}
 	else if (info == XLOG_XACT_ASSIGNMENT)
 	{
 		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 546bd43ce8..4cab5961e7 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -911,23 +911,7 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
  */
 #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
 
-typedef struct TwoPhaseFileHeader
-{
-	uint32		magic;			/* format identifier */
-	uint32		total_len;		/* actual file length */
-	TransactionId xid;			/* original transaction XID */
-	Oid			database;		/* OID of database it was in */
-	TimestampTz prepared_at;	/* time of preparation */
-	Oid			owner;			/* user running the transaction */
-	int32		nsubxacts;		/* number of following subxact XIDs */
-	int32		ncommitrels;	/* number of delete-on-commit rels */
-	int32		nabortrels;		/* number of delete-on-abort rels */
-	int32		ninvalmsgs;		/* number of cache invalidation messages */
-	bool		initfileinval;	/* does relcache init file need invalidation? */
-	uint16		gidlen;			/* length of the GID - GID follows the header */
-	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
-	TimestampTz origin_timestamp;	/* time of prepare at origin node */
-} TwoPhaseFileHeader;
+typedef xl_xact_prepare TwoPhaseFileHeader;
 
 /*
  * Header for each record in a state file
@@ -1332,44 +1316,6 @@ ReadTwoPhaseFile(TransactionId xid, bool missing_ok)
 	return buf;
 }
 
-/*
- * ParsePrepareRecord
- */
-void
-ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
-{
-	TwoPhaseFileHeader *hdr;
-	char	   *bufptr;
-
-	hdr = (TwoPhaseFileHeader *) xlrec;
-	bufptr = xlrec + MAXALIGN(sizeof(TwoPhaseFileHeader));
-
-	parsed->origin_lsn = hdr->origin_lsn;
-	parsed->origin_timestamp = hdr->origin_timestamp;
-	parsed->twophase_xid = hdr->xid;
-	parsed->dbId = hdr->database;
-	parsed->nsubxacts = hdr->nsubxacts;
-	parsed->nrels = hdr->ncommitrels;
-	parsed->nabortrels = hdr->nabortrels;
-	parsed->nmsgs = hdr->ninvalmsgs;
-
-	strncpy(parsed->twophase_gid, bufptr, hdr->gidlen);
-	bufptr += MAXALIGN(hdr->gidlen);
-
-	parsed->subxacts = (TransactionId *) bufptr;
-	bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId));
-
-	parsed->xnodes = (RelFileNode *) bufptr;
-	bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
-
-	parsed->abortnodes = (RelFileNode *) bufptr;
-	bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
-
-	parsed->msgs = (SharedInvalidationMessage *) bufptr;
-	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
-}
-
-
 
 /*
  * Reads 2PC data from xlog. During checkpoint this data will be moved to
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c96e..1093085a24 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -47,8 +47,6 @@ extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
 extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p,
 												 int *nxids_p);
-extern void ParsePrepareRecord(uint8 info, char *xlrec,
-							   xl_xact_parsed_prepare *parsed);
 extern void StandbyRecoverPreparedTransactions(void);
 extern void RecoverPreparedTransactions(void);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index d714551704..42b76cb4dd 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -292,6 +292,24 @@ typedef struct xl_xact_abort
 } xl_xact_abort;
 #define MinSizeOfXactAbort sizeof(xl_xact_abort)
 
+typedef struct xl_xact_prepare
+{
+	uint32		magic;			/* format identifier */
+	uint32		total_len;		/* actual file length */
+	TransactionId xid;			/* original transaction XID */
+	Oid			database;		/* OID of database it was in */
+	TimestampTz prepared_at;	/* time of preparation */
+	Oid			owner;			/* user running the transaction */
+	int32		nsubxacts;		/* number of following subxact XIDs */
+	int32		ncommitrels;	/* number of delete-on-commit rels */
+	int32		nabortrels;		/* number of delete-on-abort rels */
+	int32		ninvalmsgs;		/* number of cache invalidation messages */
+	bool		initfileinval;	/* does relcache init file need invalidation? */
+	uint16		gidlen;			/* length of the GID - GID follows the header */
+	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
+	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+} xl_xact_prepare;
+
 /*
  * Commit/Abort records in the above form are a bit verbose to parse, so
  * there's a deconstructed versions generated by ParseCommit/AbortRecord() for
@@ -435,6 +453,7 @@ extern const char *xact_identify(uint8 info);
 /* also in xactdesc.c, so they can be shared between front/backend code */
 extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
 extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
+extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed);
 
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
#25Michael Paquier
michael@paquier.xyz
In reply to: Fujii Masao (#24)
Re: pg_waldump and PREPARE

On Mon, Nov 11, 2019 at 01:21:28PM +0900, Fujii Masao wrote:

Thanks for the review! You are right.
I fixed this issue in the attached patch.

The proposed format looks fine to me. I have just one comment. All
three callers of standby_desc_invalidations() don't actually need to
print any data if there are zero messages, so you can simplify a bit
xact_desc_commit() and xact_desc_prepare() regarding the check on
parsed.nmsgs, no?
--
Michael

#26Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#25)
Re: pg_waldump and PREPARE

On Mon, Nov 11, 2019 at 4:16 PM Michael Paquier <michael@paquier.xyz> wrote:

On Mon, Nov 11, 2019 at 01:21:28PM +0900, Fujii Masao wrote:

Thanks for the review! You are right.
I fixed this issue in the attached patch.

The proposed format looks fine to me. I have just one comment. All
three callers of standby_desc_invalidations() don't actually need to
print any data if there are zero messages, so you can simplify a bit
xact_desc_commit() and xact_desc_prepare() regarding the check on
parsed.nmsgs, no?

Thanks for the review! But probably I failed to understand your point...
Could you clarify what actual change is necessary? You are thinking that
the check of "parsed.nmsgs >= 0" should be moved to the inside of
standby_desc_invalidations()?

Regards,

--
Fujii Masao

#27Michael Paquier
michael@paquier.xyz
In reply to: Fujii Masao (#26)
Re: pg_waldump and PREPARE

On Tue, Nov 12, 2019 at 05:53:02PM +0900, Fujii Masao wrote:

Thanks for the review! But probably I failed to understand your point...
Could you clarify what actual change is necessary? You are thinking that
the check of "parsed.nmsgs >= 0" should be moved to the inside of
standby_desc_invalidations()?

Yes that's what I meant here.
--
Michael

#28Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#27)
1 attachment(s)
Re: pg_waldump and PREPARE

On Tue, Nov 12, 2019 at 6:03 PM Michael Paquier <michael@paquier.xyz> wrote:

On Tue, Nov 12, 2019 at 05:53:02PM +0900, Fujii Masao wrote:

Thanks for the review! But probably I failed to understand your point...
Could you clarify what actual change is necessary? You are thinking that
the check of "parsed.nmsgs >= 0" should be moved to the inside of
standby_desc_invalidations()?

Yes that's what I meant here.

Ok, I changed the patch that way.
Attached is the latest version of the patch.

Regards,

--
Fujii Masao

Attachments:

prepare-xact-desc_v4.patchapplication/octet-stream; name=prepare-xact-desc_v4.patchDownload
diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c
index c295358f36..aaff652aeb 100644
--- a/src/backend/access/rmgrdesc/standbydesc.c
+++ b/src/backend/access/rmgrdesc/standbydesc.c
@@ -102,6 +102,10 @@ standby_desc_invalidations(StringInfo buf,
 {
 	int			i;
 
+	/* Do nothing if there are no invalidation messages */
+	if (nmsgs <= 0)
+		return;
+
 	if (relcacheInitFileInval)
 		appendStringInfo(buf, "; relcache init file inval dbid %u tsid %u",
 						 dbId, tsId);
diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
index a61f38dd19..4c411c5322 100644
--- a/src/backend/access/rmgrdesc/xactdesc.c
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -209,43 +209,95 @@ ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
 	}
 }
 
-static void
-xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
+/*
+ * ParsePrepareRecord
+ */
+void
+ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed)
 {
-	xl_xact_parsed_commit parsed;
-	int			i;
+	char	   *bufptr;
 
-	ParseCommitRecord(info, xlrec, &parsed);
+	bufptr = ((char *) xlrec) + MAXALIGN(sizeof(xl_xact_prepare));
 
-	/* If this is a prepared xact, show the xid of the original xact */
-	if (TransactionIdIsValid(parsed.twophase_xid))
-		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+	memset(parsed, 0, sizeof(*parsed));
 
-	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+	parsed->xact_time = xlrec->prepared_at;
+	parsed->origin_lsn = xlrec->origin_lsn;
+	parsed->origin_timestamp = xlrec->origin_timestamp;
+	parsed->twophase_xid = xlrec->xid;
+	parsed->dbId = xlrec->database;
+	parsed->nsubxacts = xlrec->nsubxacts;
+	parsed->nrels = xlrec->ncommitrels;
+	parsed->nabortrels = xlrec->nabortrels;
+	parsed->nmsgs = xlrec->ninvalmsgs;
+
+	strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen);
+	bufptr += MAXALIGN(xlrec->gidlen);
+
+	parsed->subxacts = (TransactionId *) bufptr;
+	bufptr += MAXALIGN(xlrec->nsubxacts * sizeof(TransactionId));
+
+	parsed->xnodes = (RelFileNode *) bufptr;
+	bufptr += MAXALIGN(xlrec->ncommitrels * sizeof(RelFileNode));
+
+	parsed->abortnodes = (RelFileNode *) bufptr;
+	bufptr += MAXALIGN(xlrec->nabortrels * sizeof(RelFileNode));
 
-	if (parsed.nrels > 0)
+	parsed->msgs = (SharedInvalidationMessage *) bufptr;
+	bufptr += MAXALIGN(xlrec->ninvalmsgs * sizeof(SharedInvalidationMessage));
+}
+
+static void
+xact_desc_relations(StringInfo buf, char *label, int nrels,
+					RelFileNode *xnodes)
+{
+	int		i;
+
+	if (nrels > 0)
 	{
-		appendStringInfoString(buf, "; rels:");
-		for (i = 0; i < parsed.nrels; i++)
+		appendStringInfo(buf, "; %s:", label);
+		for (i = 0; i < nrels; i++)
 		{
-			char	   *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
+			char	   *path = relpathperm(xnodes[i], MAIN_FORKNUM);
 
 			appendStringInfo(buf, " %s", path);
 			pfree(path);
 		}
 	}
-	if (parsed.nsubxacts > 0)
+}
+
+static void
+xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
+{
+	int		i;
+
+	if (nsubxacts > 0)
 	{
 		appendStringInfoString(buf, "; subxacts:");
-		for (i = 0; i < parsed.nsubxacts; i++)
-			appendStringInfo(buf, " %u", parsed.subxacts[i]);
-	}
-	if (parsed.nmsgs > 0)
-	{
-		standby_desc_invalidations(
-								   buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
-								   XactCompletionRelcacheInitFileInval(parsed.xinfo));
+		for (i = 0; i < nsubxacts; i++)
+			appendStringInfo(buf, " %u", subxacts[i]);
 	}
+}
+
+static void
+xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId origin_id)
+{
+	xl_xact_parsed_commit parsed;
+
+	ParseCommitRecord(info, xlrec, &parsed);
+
+	/* If this is a prepared xact, show the xid of the original xact */
+	if (TransactionIdIsValid(parsed.twophase_xid))
+		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+
+	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
+
+	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+
+	standby_desc_invalidations(
+						buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
+						XactCompletionRelcacheInitFileInval(parsed.xinfo));
 
 	if (XactCompletionForceSyncCommit(parsed.xinfo))
 		appendStringInfoString(buf, "; sync");
@@ -264,7 +316,6 @@ static void
 xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
 {
 	xl_xact_parsed_abort parsed;
-	int			i;
 
 	ParseAbortRecord(info, xlrec, &parsed);
 
@@ -273,24 +324,29 @@ xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec)
 		appendStringInfo(buf, "%u: ", parsed.twophase_xid);
 
 	appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
-	if (parsed.nrels > 0)
-	{
-		appendStringInfoString(buf, "; rels:");
-		for (i = 0; i < parsed.nrels; i++)
-		{
-			char	   *path = relpathperm(parsed.xnodes[i], MAIN_FORKNUM);
 
-			appendStringInfo(buf, " %s", path);
-			pfree(path);
-		}
-	}
+	xact_desc_relations(buf, "rels", parsed.nrels, parsed.xnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+}
 
-	if (parsed.nsubxacts > 0)
-	{
-		appendStringInfoString(buf, "; subxacts:");
-		for (i = 0; i < parsed.nsubxacts; i++)
-			appendStringInfo(buf, " %u", parsed.subxacts[i]);
-	}
+static void
+xact_desc_prepare(StringInfo buf, uint8 info, xl_xact_prepare *xlrec)
+{
+	xl_xact_parsed_prepare parsed;
+
+	ParsePrepareRecord(info, xlrec, &parsed);
+
+	appendStringInfo(buf, "gid %s: ", parsed.twophase_gid);
+	appendStringInfoString(buf, timestamptz_to_str(parsed.xact_time));
+
+	xact_desc_relations(buf, "rels(commit)", parsed.nrels, parsed.xnodes);
+	xact_desc_relations(buf, "rels(abort)", parsed.nabortrels,
+						parsed.abortnodes);
+	xact_desc_subxacts(buf, parsed.nsubxacts, parsed.subxacts);
+
+	standby_desc_invalidations(
+						buf, parsed.nmsgs, parsed.msgs, parsed.dbId, parsed.tsId,
+						xlrec->initfileinval);
 }
 
 static void
@@ -323,6 +379,12 @@ xact_desc(StringInfo buf, XLogReaderState *record)
 
 		xact_desc_abort(buf, XLogRecGetInfo(record), xlrec);
 	}
+	else if (info == XLOG_XACT_PREPARE)
+	{
+		xl_xact_prepare *xlrec = (xl_xact_prepare *) rec;
+
+		xact_desc_prepare(buf, XLogRecGetInfo(record), xlrec);
+	}
 	else if (info == XLOG_XACT_ASSIGNMENT)
 	{
 		xl_xact_assignment *xlrec = (xl_xact_assignment *) rec;
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 712e842b90..b3ad0d08e3 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -910,23 +910,7 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
  */
 #define TWOPHASE_MAGIC	0x57F94534	/* format identifier */
 
-typedef struct TwoPhaseFileHeader
-{
-	uint32		magic;			/* format identifier */
-	uint32		total_len;		/* actual file length */
-	TransactionId xid;			/* original transaction XID */
-	Oid			database;		/* OID of database it was in */
-	TimestampTz prepared_at;	/* time of preparation */
-	Oid			owner;			/* user running the transaction */
-	int32		nsubxacts;		/* number of following subxact XIDs */
-	int32		ncommitrels;	/* number of delete-on-commit rels */
-	int32		nabortrels;		/* number of delete-on-abort rels */
-	int32		ninvalmsgs;		/* number of cache invalidation messages */
-	bool		initfileinval;	/* does relcache init file need invalidation? */
-	uint16		gidlen;			/* length of the GID - GID follows the header */
-	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
-	TimestampTz origin_timestamp;	/* time of prepare at origin node */
-} TwoPhaseFileHeader;
+typedef xl_xact_prepare TwoPhaseFileHeader;
 
 /*
  * Header for each record in a state file
@@ -1331,44 +1315,6 @@ ReadTwoPhaseFile(TransactionId xid, bool missing_ok)
 	return buf;
 }
 
-/*
- * ParsePrepareRecord
- */
-void
-ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
-{
-	TwoPhaseFileHeader *hdr;
-	char	   *bufptr;
-
-	hdr = (TwoPhaseFileHeader *) xlrec;
-	bufptr = xlrec + MAXALIGN(sizeof(TwoPhaseFileHeader));
-
-	parsed->origin_lsn = hdr->origin_lsn;
-	parsed->origin_timestamp = hdr->origin_timestamp;
-	parsed->twophase_xid = hdr->xid;
-	parsed->dbId = hdr->database;
-	parsed->nsubxacts = hdr->nsubxacts;
-	parsed->nrels = hdr->ncommitrels;
-	parsed->nabortrels = hdr->nabortrels;
-	parsed->nmsgs = hdr->ninvalmsgs;
-
-	strncpy(parsed->twophase_gid, bufptr, hdr->gidlen);
-	bufptr += MAXALIGN(hdr->gidlen);
-
-	parsed->subxacts = (TransactionId *) bufptr;
-	bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId));
-
-	parsed->xnodes = (RelFileNode *) bufptr;
-	bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode));
-
-	parsed->abortnodes = (RelFileNode *) bufptr;
-	bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
-
-	parsed->msgs = (SharedInvalidationMessage *) bufptr;
-	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
-}
-
-
 
 /*
  * Reads 2PC data from xlog. During checkpoint this data will be moved to
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c96e..1093085a24 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -47,8 +47,6 @@ extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
 extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p,
 												 int *nxids_p);
-extern void ParsePrepareRecord(uint8 info, char *xlrec,
-							   xl_xact_parsed_prepare *parsed);
 extern void StandbyRecoverPreparedTransactions(void);
 extern void RecoverPreparedTransactions(void);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index d714551704..42b76cb4dd 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -292,6 +292,24 @@ typedef struct xl_xact_abort
 } xl_xact_abort;
 #define MinSizeOfXactAbort sizeof(xl_xact_abort)
 
+typedef struct xl_xact_prepare
+{
+	uint32		magic;			/* format identifier */
+	uint32		total_len;		/* actual file length */
+	TransactionId xid;			/* original transaction XID */
+	Oid			database;		/* OID of database it was in */
+	TimestampTz prepared_at;	/* time of preparation */
+	Oid			owner;			/* user running the transaction */
+	int32		nsubxacts;		/* number of following subxact XIDs */
+	int32		ncommitrels;	/* number of delete-on-commit rels */
+	int32		nabortrels;		/* number of delete-on-abort rels */
+	int32		ninvalmsgs;		/* number of cache invalidation messages */
+	bool		initfileinval;	/* does relcache init file need invalidation? */
+	uint16		gidlen;			/* length of the GID - GID follows the header */
+	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
+	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+} xl_xact_prepare;
+
 /*
  * Commit/Abort records in the above form are a bit verbose to parse, so
  * there's a deconstructed versions generated by ParseCommit/AbortRecord() for
@@ -435,6 +453,7 @@ extern const char *xact_identify(uint8 info);
 /* also in xactdesc.c, so they can be shared between front/backend code */
 extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
 extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
+extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed);
 
 extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
#29Michael Paquier
michael@paquier.xyz
In reply to: Fujii Masao (#28)
Re: pg_waldump and PREPARE

On Tue, Nov 12, 2019 at 06:41:12PM +0900, Fujii Masao wrote:

Ok, I changed the patch that way.
Attached is the latest version of the patch.

Thanks for the new patch. Looks fine to me.
--
Michael

#30Andrey Lepikhov
a.lepikhov@postgrespro.ru
In reply to: Fujii Masao (#28)
Re: pg_waldump and PREPARE

12.11.2019 12:41, Fujii Masao пишет:

Ok, I changed the patch that way.
Attached is the latest version of the patch.

Regards,

I did not see any problems in this version of the patch. The information
displayed by pg_waldump for the PREPARE record is sufficient for use.

--
Andrey Lepikhov
Postgres Professional
https://postgrespro.com
The Russian Postgres Company

#31Fujii Masao
masao.fujii@gmail.com
In reply to: Andrey Lepikhov (#30)
Re: pg_waldump and PREPARE

On Wed, Nov 13, 2019 at 3:53 PM Andrey Lepikhov
<a.lepikhov@postgrespro.ru> wrote:

12.11.2019 12:41, Fujii Masao пишет:

Ok, I changed the patch that way.
Attached is the latest version of the patch.

Regards,

I did not see any problems in this version of the patch. The information
displayed by pg_waldump for the PREPARE record is sufficient for use.

Thanks Andrey and Michael for the review! I committed the patch.

Regards,

--
Fujii Masao

#32btkimurayuzk
btkimurayuzk@oss.nttdata.com
In reply to: Fujii Masao (#31)
1 attachment(s)
Re: pg_waldump and PREPARE

I did not see any problems in this version of the patch. The
information
displayed by pg_waldump for the PREPARE record is sufficient for use.

Thanks Andrey and Michael for the review! I committed the patch.

Regards,

Hi,
There is a mistake in the comment in the definition of
xl_xact_relfilenodes.
This is a small patch to correct it.

Regards,

Yu Kimura

Attachments:

modify_xl_xact_relfilenodes_attr_comment.patchtext/x-diff; name=modify_xl_xact_relfilenodes_attr_comment.patchDownload
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 42b76cb4dd..9d2899dea1 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -239,7 +239,7 @@ typedef struct xl_xact_subxacts
 
 typedef struct xl_xact_relfilenodes
 {
-	int			nrels;			/* number of subtransaction XIDs */
+	int			nrels;			/* number of relations */
 	RelFileNode xnodes[FLEXIBLE_ARRAY_MEMBER];
 } xl_xact_relfilenodes;
 #define MinSizeOfXactRelfilenodes offsetof(xl_xact_relfilenodes, xnodes)
#33Michael Paquier
michael@paquier.xyz
In reply to: btkimurayuzk (#32)
Re: pg_waldump and PREPARE

On Wed, Nov 20, 2019 at 04:16:45PM +0900, btkimurayuzk wrote:

typedef struct xl_xact_relfilenodes
{
-	int			nrels;			/* number of subtransaction XIDs */
+	int			nrels;			/* number of relations */
RelFileNode xnodes[FLEXIBLE_ARRAY_MEMBER];
} xl_xact_relfilenodes;
#define MinSizeOfXactRelfilenodes offsetof(xl_xact_relfilenodes, xnodes)

These are relations, and it smells like a copy-pasto coming from
xl_xact_subxacts. Thanks Kimura-san, committed.
--
Michael