Repository: arenadata/gpdb
Pull request #1497: Support new XLOG_SMGR_CREATE_PDL WAL record
Author: login: whitehawk
Created: 2025-04-22T02:10:39Z | Merged: 2025-04-25T07:57:18Z

Support new XLOG_SMGR_CREATE_PDL WAL record

Problem description:
XLOG_SMGR_CREATE WAL record doesn't contain information about relstorage for
the created relation. Orphaned files removal feature requires knowing the
relstorage info, otherwise it can't properly handle the removal of all orphaned
files for AO tables.

Fix:
In order to store relation's relstorage in xlog record and keep backward
compatibility with previous versions we introduce a new xlog record type 
XLOG_SMGR_CREATE_PDL. This new record type contains info about relstorage and
is used instead of XLOG_SMGR_CREATE.
Plus, this patch updates 'log_smgrcreate()' - now it creates
XLOG_SMGR_CREATE_PDL record and flushes it right after the creation (otherwise,
in case of a crash after file creation, the file may be orphaned).

No special tests are presented in this patch, as the added functionality will
be tested later together with other parts for the orphaned file removal feature.
At this point, it is enough to pass the current standard test set.


Diff:
diff --git a/.abi-check/6.27.1_arenadata61/postgres.symbols.ignore b/.abi-check/6.27.1_arenadata61/postgres.symbols.ignore
index aa4c33e2be25..2c3d6c6c1abe 100644
--- a/.abi-check/6.27.1_arenadata61/postgres.symbols.ignore
+++ b/.abi-check/6.27.1_arenadata61/postgres.symbols.ignore
@@ -1 +1,2 @@
 ConfigureNamesBool_gp
+log_smgrcreate
diff --git a/src/backend/access/rmgrdesc/smgrdesc.c b/src/backend/access/rmgrdesc/smgrdesc.c
index 65ff67428611..2a18785431b2 100644
--- a/src/backend/access/rmgrdesc/smgrdesc.c
+++ b/src/backend/access/rmgrdesc/smgrdesc.c
@@ -24,7 +24,7 @@ smgr_desc(StringInfo buf, XLogRecord *record)
 	uint8           info = record->xl_info & ~XLR_INFO_MASK;
 	char            *rec = XLogRecGetData(record);
 
-	if (info == XLOG_SMGR_CREATE)
+	if ((info == XLOG_SMGR_CREATE) || (info == XLOG_SMGR_CREATE_PDL))
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) rec;
 		char	   *path = relpathperm(xlrec->rnode, xlrec->forkNum);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 773d3bc32d9f..88a96cf04c5e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1895,7 +1895,9 @@ heap_create_init_fork(Relation rel)
 {
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
-	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
+				   INIT_FORKNUM,
+				   rel->rd_rel->relstorage);
 	smgrimmedsync(rel->rd_smgr, INIT_FORKNUM);
 }
 
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 4033fe403db3..1648329b45bb 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -105,7 +105,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage)
 	smgrcreate(srel, MAIN_FORKNUM, false);
 
 	if (needs_wal)
-		log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM);
+		log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM, relstorage);
 
 	/* Add the relation to the list of stuff to delete at abort */
 	pending = (PendingRelDelete *)
@@ -120,26 +120,29 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage)
 }
 
 /*
- * Perform XLogInsert of a XLOG_SMGR_CREATE record to WAL.
+ * Perform XLogInsert of a XLOG_SMGR_CREATE_PDL record to WAL.
  */
 void
-log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum)
+log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum, char relstorage)
 {
-	xl_smgr_create xlrec;
+	xl_smgr_create_pdl xlrec;
 	XLogRecData rdata;
 
 	/*
 	 * Make an XLOG entry reporting the file creation.
 	 */
-	xlrec.rnode = *rnode;
-	xlrec.forkNum = forkNum;
+	xlrec.createrec.rnode = *rnode;
+	xlrec.createrec.forkNum = forkNum;
+	xlrec.relstorage = relstorage;
 
 	rdata.data = (char *) &xlrec;
 	rdata.len = sizeof(xlrec);
 	rdata.buffer = InvalidBuffer;
 	rdata.next = NULL;
 
-	XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
+	XLogRecPtr recptr = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE_PDL, &rdata);
+
+	XLogFlush(recptr);
 }
 
 /*
@@ -514,29 +517,32 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		smgrcreate(reln, xlrec->forkNum, true);
+	}
+	else if (info == XLOG_SMGR_CREATE_PDL)
+	{
+		xl_smgr_create_pdl *xlrec =
+			(xl_smgr_create_pdl *) XLogRecGetData(record);
 		PendingRelXactDelete pd =
 		{
 			.relnode =
 			{
-				.node = xlrec->rnode,
+				.node = xlrec->createrec.rnode,
 				/*
 				 * Temp relations are not logged in WAL, so it is always false
 				 * here.
 				 */
 				.isTempRelation = false,
-				/*
-				 * TODO: We do not have info about relstorage from the record.
-				 * Need to handle it somehow - decision is yet pending.
-				 * Until then we can't handle AO tables properly...
-				 */
-				.relstorage = RELSTORAGE_HEAP
+				.relstorage = xlrec->relstorage
 			},
 			.xid = record->xl_xid
 		};
-		SMgrRelation reln;
 
-		reln = smgropen(xlrec->rnode, InvalidBackendId);
-		smgrcreate(reln, xlrec->forkNum, true);
+		SMgrRelation reln = smgropen(xlrec->createrec.rnode, InvalidBackendId);
+		smgrcreate(reln, xlrec->createrec.forkNum, true);
 
 		PdlRedoAdd(&pd);
 	}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f33ebdbf7511..947f90980889 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13196,7 +13196,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 			if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
 				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
 				 forkNum == INIT_FORKNUM))
-				log_smgrcreate(&newrnode, forkNum);
+				log_smgrcreate(&newrnode, forkNum, rel->rd_rel->relstorage);
 			copy_relation_data(rel->rd_smgr, dstrel, forkNum,
 							   rel->rd_rel->relpersistence);
 		}
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 13955b8a94cd..673f6d3bcea1 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -889,6 +889,7 @@ extractPageInfo(XLogRecord *record)
 			switch (info)
 			{
 				case XLOG_SMGR_CREATE:
+				case XLOG_SMGR_CREATE_PDL:
 					/*
 					 * We can safely ignore these. The local file will be
 					 * removed, if it doesn't exist in remote system. If a
diff --git a/src/include/catalog/storage_xlog.h b/src/include/catalog/storage_xlog.h
index f3916190ce29..2cf8a3ced7ca 100644
--- a/src/include/catalog/storage_xlog.h
+++ b/src/include/catalog/storage_xlog.h
@@ -26,22 +26,34 @@
  */
 
 /* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
+#define XLOG_SMGR_CREATE		0x10
+#define XLOG_SMGR_TRUNCATE		0x20
+#define XLOG_SMGR_CREATE_PDL	0x30
 
+/*
+ * We do not create `xl_smgr_create` records anymore. We use
+ * `xl_smgr_create_pdl` instead. But we still process `xl_smgr_create` records
+ * for backward compatibility.
+ */
 typedef struct xl_smgr_create
 {
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 } xl_smgr_create;
 
+typedef struct xl_smgr_create_pdl
+{
+	xl_smgr_create createrec;
+	char relstorage;
+} xl_smgr_create_pdl;
+
 typedef struct xl_smgr_truncate
 {
 	BlockNumber blkno;
 	RelFileNode rnode;
 } xl_smgr_truncate;
 
-extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum);
+extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum, char relstorage);
 
 extern void smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record);
 extern void smgr_desc(StringInfo buf, XLogRecord *record);


Reviews:
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T09:46:07Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T09:47:16Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T09:50:50Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T09:53:26Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T09:55:41Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T10:06:46Z
user: login: whitehawk
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T04:05:13Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T04:09:46Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T04:28:24Z
user: login: whitehawk
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T04:37:30Z
user: login: RekGRpth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T05:01:46Z
user: login: whitehawk
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T05:16:04Z
user: login: RekGRpth
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-23T05:49:13Z
user: login: andr-sokolov
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T07:55:39Z
user: login: whitehawk
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T08:02:15Z
user: login: andr-sokolov
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T08:27:27Z
user: login: AndrewSavitskiy
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T08:41:05Z
user: login: andr-sokolov
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T09:21:32Z
user: login: AndrewSavitskiy
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-24T09:41:44Z
user: login: andr-sokolov
state: APPROVED
author_association: NONE
submitted_at: 2025-04-25T07:34:39Z

Review comments:
diff_hunk: @@ -1893,9 +1893,13 @@ heap_create_with_catalog(const char *relname,
 void
 heap_create_init_fork(Relation rel)
 {
+	XLogRecPtr recptr;
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
-	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	recptr = log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
+							INIT_FORKNUM,
+							rel->rd_rel->relstorage);
+	XLogFlush(recptr);
path: src/backend/catalog/heap.c
user: login: RekGRpth
body: Why do we need a local variable here?
created_at: 2025-04-22T09:46:07Z
author_association: MEMBER
diff_hunk: @@ -32,6 +32,14 @@ smgr_desc(StringInfo buf, XLogRecord *record)
 		appendStringInfo(buf, "file create: %s", path);
 		pfree(path);
 	}
+	else if (info == XLOG_SMGR_CREATE_PDL)
+	{
+		xl_smgr_create_pdl *xlrec = (xl_smgr_create_pdl *) rec;
+		char	   *path = relpathperm(xlrec->relnode.node, xlrec->forkNum);
+
+		appendStringInfo(buf, "file create: %s", path);
path: src/backend/access/rmgrdesc/smgrdesc.c
user: login: RekGRpth
body: Is it really good that the exact same string is added here as above?
created_at: 2025-04-22T09:47:16Z
author_association: MEMBER
diff_hunk: @@ -122,24 +127,27 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage)
 /*
  * Perform XLogInsert of a XLOG_SMGR_CREATE record to WAL.
  */
-void
-log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum)
+XLogRecPtr
+log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum, char relstorage)
 {
-	xl_smgr_create xlrec;
+	xl_smgr_create_pdl xlrec;
 	XLogRecData rdata;
 
 	/*
 	 * Make an XLOG entry reporting the file creation.
 	 */
-	xlrec.rnode = *rnode;
+	xlrec.relnode.node = *rnode;
+	/* temp relations are not logged in WAL, so it is always false here */
+	xlrec.relnode.isTempRelation = false;
+	xlrec.relnode.relstorage = relstorage;
 	xlrec.forkNum = forkNum;
 
 	rdata.data = (char *) &xlrec;
 	rdata.len = sizeof(xlrec);
 	rdata.buffer = InvalidBuffer;
 	rdata.next = NULL;
 
-	XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
+	return XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE_PDL, &rdata);
path: src/backend/catalog/storage.c
user: login: RekGRpth
body: Why not just do this:
```suggestion
	XLogFlush(XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE_PDL, &rdata));
```
created_at: 2025-04-22T09:50:50Z
author_association: MEMBER
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: RekGRpth
body: why not
```suggestion
		SMgrRelation reln = smgropen(xlrec->rnode, InvalidBackendId);
```
created_at: 2025-04-22T09:53:26Z
author_association: MEMBER
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		smgrcreate(reln, xlrec->forkNum, true);
+	}
+	if (info == XLOG_SMGR_CREATE_PDL)
path: src/backend/catalog/storage.c
user: login: RekGRpth
body: why not
```suggestion
	else if (info == XLOG_SMGR_CREATE_PDL)
```
created_at: 2025-04-22T09:55:41Z
author_association: MEMBER
diff_hunk: @@ -26,22 +26,34 @@
  */
 
 /* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
+#define XLOG_SMGR_CREATE		0x10
+#define XLOG_SMGR_TRUNCATE		0x20
+#define XLOG_SMGR_CREATE_PDL	0x30
 
+/*
+ * We do not create `xl_smgr_create` records anymore. We use
+ * `xl_smgr_create_pdl` instead. But we still process `xl_smgr_create` records
+ * for backward compatibility.
+ */
 typedef struct xl_smgr_create
 {
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 } xl_smgr_create;
 
+typedef struct xl_smgr_create_pdl
+{
+	RelFileNodePendingDelete relnode;
path: src/include/catalog/storage_xlog.h
user: login: RekGRpth
body: I noticed that the `RelFileNodePendingDelete` type is easily cast to `RelFileNode` like this: `(RelFileNode)`, because the `RelFileNode` field is at the very beginning, I suggest using this to simplify functions.
created_at: 2025-04-22T10:06:46Z
author_association: MEMBER
diff_hunk: @@ -32,6 +32,14 @@ smgr_desc(StringInfo buf, XLogRecord *record)
 		appendStringInfo(buf, "file create: %s", path);
 		pfree(path);
 	}
+	else if (info == XLOG_SMGR_CREATE_PDL)
+	{
+		xl_smgr_create_pdl *xlrec = (xl_smgr_create_pdl *) rec;
+		char	   *path = relpathperm(xlrec->relnode.node, xlrec->forkNum);
+
+		appendStringInfo(buf, "file create: %s", path);
path: src/backend/access/rmgrdesc/smgrdesc.c
user: login: whitehawk
body: Semantics of `XLOG_SMGR_CREATE_PDL` are the same as `XLOG_SMGR_CREATE`, so it is Ok to have the same string here.
created_at: 2025-04-23T03:30:20Z
author_association: NONE
diff_hunk: @@ -1893,9 +1893,13 @@ heap_create_with_catalog(const char *relname,
 void
 heap_create_init_fork(Relation rel)
 {
+	XLogRecPtr recptr;
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
-	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	recptr = log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
+							INIT_FORKNUM,
+							rel->rd_rel->relstorage);
+	XLogFlush(recptr);
path: src/backend/catalog/heap.c
user: login: whitehawk
body: Because I think that 
```
XLogFlush(log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
							INIT_FORKNUM,
							rel->rd_rel->relstorage));
```
is much less readable. And I want to avoid such code. 
created_at: 2025-04-23T03:32:26Z
author_association: NONE
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: whitehawk
body: Because actually that is a revert to the [original 6x code](https://github.com/arenadata/gpdb/blob/84beaf4fd19ae4c30588c3c87342d10b16a1a5c0/src/backend/catalog/storage.c#L513) . 
created_at: 2025-04-23T03:41:56Z
author_association: NONE
diff_hunk: @@ -26,22 +26,34 @@
  */
 
 /* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
+#define XLOG_SMGR_CREATE		0x10
+#define XLOG_SMGR_TRUNCATE		0x20
+#define XLOG_SMGR_CREATE_PDL	0x30
 
+/*
+ * We do not create `xl_smgr_create` records anymore. We use
+ * `xl_smgr_create_pdl` instead. But we still process `xl_smgr_create` records
+ * for backward compatibility.
+ */
 typedef struct xl_smgr_create
 {
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 } xl_smgr_create;
 
+typedef struct xl_smgr_create_pdl
+{
+	RelFileNodePendingDelete relnode;
path: src/include/catalog/storage_xlog.h
user: login: whitehawk
body: Please give more details what exact part of code you mean to simplify.
created_at: 2025-04-23T03:48:36Z
author_association: NONE
diff_hunk: @@ -122,24 +127,27 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage)
 /*
  * Perform XLogInsert of a XLOG_SMGR_CREATE record to WAL.
  */
-void
-log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum)
+XLogRecPtr
+log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum, char relstorage)
 {
-	xl_smgr_create xlrec;
+	xl_smgr_create_pdl xlrec;
 	XLogRecData rdata;
 
 	/*
 	 * Make an XLOG entry reporting the file creation.
 	 */
-	xlrec.rnode = *rnode;
+	xlrec.relnode.node = *rnode;
+	/* temp relations are not logged in WAL, so it is always false here */
+	xlrec.relnode.isTempRelation = false;
+	xlrec.relnode.relstorage = relstorage;
 	xlrec.forkNum = forkNum;
 
 	rdata.data = (char *) &xlrec;
 	rdata.len = sizeof(xlrec);
 	rdata.buffer = InvalidBuffer;
 	rdata.next = NULL;
 
-	XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
+	return XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE_PDL, &rdata);
path: src/backend/catalog/storage.c
user: login: whitehawk
body: Because it is currently requested by the SRS, that `log_smgrcreate()` returns XLogRecPtr, and flush is done by the caller function.
created_at: 2025-04-23T04:01:08Z
author_association: NONE
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
+		smgrcreate(reln, xlrec->forkNum, true);
+	}
+	if (info == XLOG_SMGR_CREATE_PDL)
path: src/backend/catalog/storage.c
user: login: whitehawk
body: updated
created_at: 2025-04-23T04:05:00Z
author_association: NONE
diff_hunk: @@ -1893,9 +1893,13 @@ heap_create_with_catalog(const char *relname,
 void
 heap_create_init_fork(Relation rel)
 {
+	XLogRecPtr recptr;
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
-	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	recptr = log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
+							INIT_FORKNUM,
+							rel->rd_rel->relstorage);
+	XLogFlush(recptr);
path: src/backend/catalog/heap.c
user: login: RekGRpth
body: Then at least don't separate declaration from initialization.
created_at: 2025-04-23T04:09:45Z
author_association: MEMBER
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: RekGRpth
body: In the new code, declaration should not be separated from initialization.
created_at: 2025-04-23T04:28:24Z
author_association: MEMBER
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: whitehawk
body: Ok, updated the code for XLOG_SMGR_CREATE_PDL
created_at: 2025-04-23T04:37:09Z
author_association: NONE
diff_hunk: @@ -1893,9 +1893,13 @@ heap_create_with_catalog(const char *relname,
 void
 heap_create_init_fork(Relation rel)
 {
+	XLogRecPtr recptr;
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
-	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
+	recptr = log_smgrcreate(&rel->rd_smgr->smgr_rnode.node,
+							INIT_FORKNUM,
+							rel->rd_rel->relstorage);
+	XLogFlush(recptr);
path: src/backend/catalog/heap.c
user: login: whitehawk
body: ok
created_at: 2025-04-23T04:37:27Z
author_association: NONE
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: RekGRpth
body: How about moving the `XLOG_SMGR_CREATE` branch to the very end? Because now it will be called with minimal probability.
created_at: 2025-04-23T05:01:46Z
author_association: MEMBER
diff_hunk: @@ -514,28 +522,23 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record)
 	if (info == XLOG_SMGR_CREATE)
 	{
 		xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
+		SMgrRelation reln;
+
+		reln = smgropen(xlrec->rnode, InvalidBackendId);
path: src/backend/catalog/storage.c
user: login: whitehawk
body: That would introduce more delta comparing to Postgres upstream. I think we should avoid increasing such delta more than is needed.
created_at: 2025-04-23T05:16:03Z
author_association: NONE
diff_hunk: @@ -13196,7 +13196,12 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 			if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
 				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
 				 forkNum == INIT_FORKNUM))
-				log_smgrcreate(&newrnode, forkNum);
+			{
+				XLogRecPtr recptr = log_smgrcreate(&newrnode,
+												   forkNum,
+												   rel->rd_rel->relstorage);
+				XLogFlush(recptr);
path: src/backend/commands/tablecmds.c
user: login: andr-sokolov
body: XLogFlush is always called after log_smgrcreate. What about placing XLogFlush into log_smgrcreate?
created_at: 2025-04-24T07:55:39Z
author_association: NONE
diff_hunk: @@ -13196,7 +13196,12 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 			if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
 				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
 				 forkNum == INIT_FORKNUM))
-				log_smgrcreate(&newrnode, forkNum);
+			{
+				XLogRecPtr recptr = log_smgrcreate(&newrnode,
+												   forkNum,
+												   rel->rd_rel->relstorage);
+				XLogFlush(recptr);
path: src/backend/commands/tablecmds.c
user: login: whitehawk
body: https://github.com/arenadata/gpdb/pull/1497#discussion_r2055208778
created_at: 2025-04-24T08:02:15Z
author_association: NONE
diff_hunk: @@ -122,24 +127,27 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage)
 /*
  * Perform XLogInsert of a XLOG_SMGR_CREATE record to WAL.
path: src/backend/catalog/storage.c
user: login: andr-sokolov
body: The function creates XLOG_SMGR_CREATE_PDL, but not a XLOG_SMGR_CREATE record
created_at: 2025-04-24T08:27:27Z
author_association: NONE
diff_hunk: @@ -13196,7 +13196,12 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 			if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
 				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
 				 forkNum == INIT_FORKNUM))
-				log_smgrcreate(&newrnode, forkNum);
+			{
+				XLogRecPtr recptr = log_smgrcreate(&newrnode,
+												   forkNum,
+												   rel->rd_rel->relstorage);
+				XLogFlush(recptr);
path: src/backend/commands/tablecmds.c
user: login: AndrewSavitskiy
body: Initially the idea was to flush only in RelationCreateStorage, but then I've realized that in order not to miss potential orphaned files in heap_create_init_fork and ALTER TABLE SET TABLESPACE statement, xlog flush also needed there. 

One more point to move the XLogFlush to log_smgrcreate, that if some other (new) module will use log_smgrcreate, there will be XLogFlush be design.
created_at: 2025-04-24T08:41:04Z
author_association: NONE
diff_hunk: @@ -26,22 +26,34 @@
  */
 
 /* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
+#define XLOG_SMGR_CREATE		0x10
+#define XLOG_SMGR_TRUNCATE		0x20
+#define XLOG_SMGR_CREATE_PDL	0x30
 
+/*
+ * We do not create `xl_smgr_create` records anymore. We use
+ * `xl_smgr_create_pdl` instead. But we still process `xl_smgr_create` records
+ * for backward compatibility.
+ */
 typedef struct xl_smgr_create
 {
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 } xl_smgr_create;
 
+typedef struct xl_smgr_create_pdl
+{
+	RelFileNodePendingDelete relnode;
+	ForkNumber	forkNum;
+} xl_smgr_create_pdl;
path: src/include/catalog/storage_xlog.h
user: login: andr-sokolov
body: relnode.isTempRelation is always false. I suggest to rewrite the structure to minimize the number of bytes written to WAL. We will be able to simplify `smgr_desc`, because it will be possible to get `xl_smgr_create` from `xl_smgr_create_pdl`
```
typedef struct xl_smgr_create_pdl
{
    xl_smgr_create c;
    char relstorage;
} xl_smgr_create_pdl;
```
created_at: 2025-04-24T09:21:32Z
author_association: NONE
diff_hunk: @@ -26,22 +26,34 @@
  */
 
 /* XLOG gives us high 4 bits */
-#define XLOG_SMGR_CREATE	0x10
-#define XLOG_SMGR_TRUNCATE	0x20
+#define XLOG_SMGR_CREATE		0x10
+#define XLOG_SMGR_TRUNCATE		0x20
+#define XLOG_SMGR_CREATE_PDL	0x30
 
+/*
+ * We do not create `xl_smgr_create` records anymore. We use
+ * `xl_smgr_create_pdl` instead. But we still process `xl_smgr_create` records
+ * for backward compatibility.
+ */
 typedef struct xl_smgr_create
 {
 	RelFileNode rnode;
 	ForkNumber	forkNum;
 } xl_smgr_create;
 
+typedef struct xl_smgr_create_pdl
+{
+	RelFileNodePendingDelete relnode;
+	ForkNumber	forkNum;
+} xl_smgr_create_pdl;
path: src/include/catalog/storage_xlog.h
user: login: AndrewSavitskiy
body: >  minimize the number of bytes written to WAL

Good point. No objections from me.

created_at: 2025-04-24T09:41:44Z
author_association: NONE

PR comments:
user: login: andr-sokolov
created_at: 2025-04-24T08:16:30Z
body: WAL is an abbreviation. Let's use uppercase in the PR description
author_association: NONE
user: login: whitehawk
created_at: 2025-04-24T08:24:21Z
body: > WAL is an abbreviation. Let's use uppercase in the PR description

updated
author_association: NONE
user: login: andr-sokolov
created_at: 2025-04-24T09:05:10Z
body: There are no tests in the patch. Add them please or  explain in the description why it is not possible
author_association: NONE