Thread
-
[PATCH v35 07/21] Prevent orphan storage files after server crash
Kyotaro Horiguchi <horikyota.ntt@gmail.com> — 2024-10-03T10:30:23Z
When a server crashes during a transaction that creates tables, newly created but unused storage files are not removed. This patch prevents such orphan files by utilizing the UNDO log system for storage files. The behavior of this feature overlaps with the existing functionality that handles the removal of unnecessary files during rollback via pendingDeletes. As a result, that functionality has been removed in this commit. However, commit-time file deletions are not covered by the UNDO log system, so that part remains in use. Consequently, the isCommit flag for entries in the pendingDeletes list is now always true. To avoid unnecessary changes to the code, the flag has been retained. --- src/backend/access/heap/heapam_handler.c | 22 +-- src/backend/access/rmgrdesc/Makefile | 1 + src/backend/access/rmgrdesc/smgrundodesc.c | 49 ++++++ src/backend/access/rmgrdesc/undologdesc.c | 2 + src/backend/access/transam/undolog.c | 1 + src/backend/catalog/index.c | 4 +- src/backend/catalog/storage.c | 189 ++++++++++++++++++--- src/backend/commands/sequence.c | 4 +- src/backend/commands/tablecmds.c | 19 ++- src/backend/storage/buffer/bufmgr.c | 4 +- src/backend/storage/file/reinit.c | 92 ++++++++++ src/backend/storage/smgr/smgr.c | 9 + src/include/access/rmgrlist.h | 2 +- src/include/catalog/storage.h | 2 + src/include/catalog/storage_ulog.h | 41 +++++ src/include/storage/reinit.h | 4 + src/include/storage/smgr.h | 1 + src/test/recovery/t/013_crash_restart.pl | 19 +++ 18 files changed, 410 insertions(+), 55 deletions(-) create mode 100644 src/backend/access/rmgrdesc/smgrundodesc.c create mode 100644 src/include/catalog/storage_ulog.h diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index a8d95e0f1c1..00136b7e39b 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -611,8 +611,7 @@ heapam_relation_set_new_filelocator(Relation rel, { Assert(rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_TOASTVALUE); - smgrcreate(srel, INIT_FORKNUM, false); - log_smgrcreate(newrlocator, INIT_FORKNUM); + RelationCreateFork(srel, INIT_FORKNUM, true, true); } smgrclose(srel); @@ -656,16 +655,17 @@ heapam_relation_copy_data(Relation rel, const RelFileLocator *newrlocator) { if (smgrexists(RelationGetSmgr(rel), forkNum)) { - smgrcreate(dstrel, forkNum, false); - - /* - * WAL log creation if the relation is persistent, or this is the - * init fork of an unlogged relation. - */ - if (RelationIsPermanent(rel) || + bool wal_log = RelationIsPermanent(rel) | (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED && - forkNum == INIT_FORKNUM)) - log_smgrcreate(newrlocator, forkNum); + forkNum == INIT_FORKNUM); + + /* + * Usually, we don't use UNDO log for FSM or VM forks, as their + * creation is not transactional. However, we're currently copying + * the entire relation in a transactional manner, which requires + * after-crash cleanup. + */ + RelationCreateFork(dstrel, forkNum, wal_log, true); RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum, rel->rd_rel->relpersistence); } diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile index 542fd3d6a8e..fc4605bd30b 100644 --- a/src/backend/access/rmgrdesc/Makefile +++ b/src/backend/access/rmgrdesc/Makefile @@ -26,6 +26,7 @@ OBJS = \ rmgrdesc_utils.o \ seqdesc.o \ smgrdesc.o \ + smgrundodesc.o \ spgdesc.o \ standbydesc.o \ tblspcdesc.o \ diff --git a/src/backend/access/rmgrdesc/smgrundodesc.c b/src/backend/access/rmgrdesc/smgrundodesc.c new file mode 100644 index 00000000000..d9082ba079b --- /dev/null +++ b/src/backend/access/rmgrdesc/smgrundodesc.c @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * smgrundodesc.c + * rmgr undolog descriptor routines for catalog/storage.c + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/rmgrdesc/smgrundodesc.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "catalog/storage_ulog.h" +#include "lib/stringinfo.h" + +void +smgr_undodesc(StringInfo buf, UndoLogRecord *record) +{ + uint8 info = ULogRecGetInfo(record) & ~XLR_INFO_MASK; + + if (info == ULOG_SMGR_CREATE) + { + ul_smgr_create *urec = (ul_smgr_create *) ULogRecGetData(record); + + appendStringInfo(buf, ": %d/%d/%d, fork %d, backend %d", + urec->rlocator.spcOid, + urec->rlocator.dbOid, + urec->rlocator.relNumber, + urec->forknum, urec->backend); + } +} + +const char * +smgr_undoidentify(uint8 info) +{ + const char *id = NULL; + + switch (info & ~XLR_INFO_MASK) + { + case ULOG_SMGR_CREATE: + id = "SMGRCREATE"; + break; + } + + return id; +} diff --git a/src/backend/access/rmgrdesc/undologdesc.c b/src/backend/access/rmgrdesc/undologdesc.c index d717646d2e0..1810f2693ff 100644 --- a/src/backend/access/rmgrdesc/undologdesc.c +++ b/src/backend/access/rmgrdesc/undologdesc.c @@ -14,6 +14,8 @@ #include "postgres.h" #include "access/undolog.h" +#include "catalog/storage.h" +#include "catalog/storage_ulog.h" typedef struct UndoDescData { diff --git a/src/backend/access/transam/undolog.c b/src/backend/access/transam/undolog.c index d19caac5946..e7abca07e03 100644 --- a/src/backend/access/transam/undolog.c +++ b/src/backend/access/transam/undolog.c @@ -35,6 +35,7 @@ #include "access/xlog.h" #include "access/xloginsert.h" #include "lib/dshash.h" +#include "catalog/storage_ulog.h" #include "miscadmin.h" #include "storage/fd.h" #include "storage/procarray.h" diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 74d0f3097eb..7e283fdbe5b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -3042,8 +3042,8 @@ index_build(Relation heapRelation, if (indexRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED && !smgrexists(RelationGetSmgr(indexRelation), INIT_FORKNUM)) { - smgrcreate(RelationGetSmgr(indexRelation), INIT_FORKNUM, false); - log_smgrcreate(&indexRelation->rd_locator, INIT_FORKNUM); + RelationCreateFork(RelationGetSmgr(indexRelation), + INIT_FORKNUM, true, true); indexRelation->rd_indam->ambuildempty(indexRelation); } diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index bdbed9fba3c..0b6748d803f 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -19,19 +19,27 @@ #include "postgres.h" +#include "access/amapi.h" +#include "access/undolog.h" #include "access/visibilitymap.h" #include "access/xact.h" #include "access/xlog.h" #include "access/xloginsert.h" #include "access/xlogutils.h" #include "catalog/storage.h" +#include "catalog/storage_ulog.h" #include "catalog/storage_xlog.h" +#include "common/file_utils.h" #include "miscadmin.h" #include "storage/bulk_write.h" +#include "storage/copydir.h" +#include "storage/fd.h" #include "storage/freespace.h" #include "storage/proc.h" +#include "storage/reinit.h" #include "storage/smgr.h" #include "utils/hsearch.h" +#include "utils/inval.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -76,6 +84,8 @@ typedef struct PendingRelSync static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */ static HTAB *pendingSyncHash = NULL; +/* local functions */ +static void ulog_smgrcreate(SMgrRelation srel, ForkNumber forkNum); /* * AddPendingSync @@ -147,28 +157,8 @@ RelationCreateStorage(RelFileLocator rlocator, char relpersistence, } srel = smgropen(rlocator, procNumber); - smgrcreate(srel, MAIN_FORKNUM, false); - if (needs_wal) - log_smgrcreate(&srel->smgr_rlocator.locator, MAIN_FORKNUM); - - /* - * Add the relation to the list of stuff to delete at abort, if we are - * asked to do so. - */ - if (register_delete) - { - PendingRelDelete *pending; - - pending = (PendingRelDelete *) - MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); - pending->rlocator = rlocator; - pending->procNumber = procNumber; - pending->atCommit = false; /* delete if abort */ - pending->nestLevel = GetCurrentTransactionNestLevel(); - pending->next = pendingDeletes; - pendingDeletes = pending; - } + RelationCreateFork(srel, MAIN_FORKNUM, needs_wal, register_delete); if (relpersistence == RELPERSISTENCE_PERMANENT && !XLogIsNeeded()) { @@ -179,6 +169,31 @@ RelationCreateStorage(RelFileLocator rlocator, char relpersistence, return srel; } +/* + * RelationCreateFork + * Create physical storage for a fork of a relation. + * + * This function creates a relation fork in a transactional manner. When + * undo_log is true, the creation is UNDO-logged so that in case of transaction + * aborts or server crashes later on, the fork will be removed. If the caller + * plans to remove the fork in another way, it should pass false. Additionally, + * it is WAL-logged if wal_log is true. + */ +void +RelationCreateFork(SMgrRelation srel, ForkNumber forkNum, + bool wal_log, bool undo_log) +{ + /* Schedule the removal of this init fork at abort if requested. */ + if (undo_log) + ulog_smgrcreate(srel, forkNum); + + /* WAL-log this creation if requested. */ + if (wal_log) + log_smgrcreate(&srel->smgr_rlocator.locator, forkNum); + + smgrcreate(srel, forkNum, false); +} + /* * Perform XLogInsert of an XLOG_SMGR_CREATE record to WAL. */ @@ -198,6 +213,20 @@ log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum) XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLR_SPECIAL_REL_UPDATE); } +/* + * Perform UndoLogWrite of an XLOG_SMGR_CREATE record to UNDO log. + */ +void +ulog_smgrcreate(SMgrRelation srel, ForkNumber forkNum) +{ + ul_smgr_create ulrec; + + ulrec.rlocator = srel->smgr_rlocator.locator; + ulrec.backend = srel->smgr_rlocator.backend; + ulrec.forknum = forkNum; + UndoLogWrite(RM_SMGR_ID, ULOG_SMGR_CREATE, &ulrec, sizeof(ulrec)); +} + /* * RelationDropStorage * Schedule unlinking of physical storage at transaction commit. @@ -218,13 +247,12 @@ RelationDropStorage(Relation rel) pendingDeletes = pending; /* - * NOTE: if the relation was created in this transaction, it will now be - * present in the pending-delete list twice, once with atCommit true and - * once with atCommit false. Hence, it will be physically deleted at end - * of xact in either case (and the other entry will be ignored by - * smgrDoPendingDeletes, so no error will occur). We could instead remove - * the existing list entry and delete the physical file immediately, but - * for now I'll keep the logic simple. + * NOTE: If the relation was created in this transaction, it will now be + * present both in the pending-delete list for commit and in the UNDO log + * for abort. Thus, it will be physically deleted at the end of the + * transaction in either case. While we could remove existing UNDO log + * records, allowing this would add complexity and inefficiency to the UNDO + * log system. Therefore, keep the logic simple here. */ RelationCloseSmgr(rel); @@ -1060,3 +1088,108 @@ smgr_redo(XLogReaderState *record) else elog(PANIC, "smgr_redo: unknown op code %u", info); } + +void +smgr_undo(UndoLogRecord *record, ULogOp op, bool recovered, bool redo) +{ + uint8 info = record->ul_info & ~ULR_INFO_MASK; + + if (info == ULOG_SMGR_CREATE) + { + ul_smgr_create *ulrec = (ul_smgr_create *) ULogRecGetData(record); + + if (op == ULOGOP_ACTIVE) + { + /* + * This operation is specified only during recovery cleanups. If + * the transaction is prepared or still active, tell reinit not to + * reset this relation. + */ + ResetUnloggedRelationIgnore(ulrec->rlocator, ulrec->backend); + } + else if (op == ULOGOP_ABORT) + { + /* Otherwise, remove the file immediately. */ + SMgrRelation reln; + ForkNumber forks[3]; + BlockNumber firstblocks[3] = {0}; + int nforks = 0; + + forks[nforks++] = ulrec->forknum; + + /* + * If the MAIN fork was created in the transaction, the rollback + * should remove all forks of this relation. Although we could + * register an undo record individually for each fork, this may be + * more complex because VM and FSM can be created + * non-transactionally outside the transaction that created the + * MAIN fork. + */ + if (ulrec->forknum == MAIN_FORKNUM) + { + forks[nforks++] = VISIBILITYMAP_FORKNUM; + forks[nforks++] = FSM_FORKNUM; + } + + /* + * Drop buffers, then the files. This can be improved by using + * smgrdounlinkall(), but currently I take the simpler way. + */ + reln = smgropen(ulrec->rlocator, ulrec->backend); + DropRelationBuffers(reln, forks, nforks, firstblocks); + for (int i = 0 ; i < nforks ; i++) + smgrunlink(reln, forks[i], true); + + smgrclose(reln); + } + else if (op == ULOGOP_COMMIT) + { + /* + * If an init fork was created during recovery, the entire relation + * is set to be reset at recovery-end or the consistency point. + * Therefore, we need to drop the relation's buffers to prevent the + * end-of-recovery checkpoint from flushing storage files for these + * relations once they have been reset. + */ + if (redo && ulrec->forknum == INIT_FORKNUM) + { + SMgrRelation reln; + int nforks; + ForkNumber forks[MAX_FORKNUM + 1]; + BlockNumber firstblocks[MAX_FORKNUM + 1] = {0}; + + Assert(ulrec->backend == INVALID_PROC_NUMBER); + + reln = smgropen(ulrec->rlocator, ulrec->backend); + + nforks = 0; + for (int i = 0 ; i <= MAX_FORKNUM ; i++) + { + if (smgrexists(reln, i)) + forks[nforks++] = i; + } + + if (nforks > 0) + DropRelationBuffers(reln, forks, nforks, firstblocks); + + smgrclose(reln); + } + } + else + elog(PANIC, "smgr_undo: unknown ulogop code %d", op); + } + else + elog(PANIC, "smgr_undo: unknown op code %u", info); +} + +void +smgr_undocleanupinit(void) +{ + ResetUnloggedRelationIgnoreClear(); +} + +void +smgr_undoshutdown(void) +{ + ResetUnloggedRelationIgnoreClear(); +} diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0188e8bbd5b..be6afc7df52 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "catalog/objectaccess.h" #include "catalog/pg_sequence.h" #include "catalog/pg_type.h" +#include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/defrem.h" #include "commands/sequence.h" @@ -344,8 +345,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple) SMgrRelation srel; srel = smgropen(rel->rd_locator, INVALID_PROC_NUMBER); - smgrcreate(srel, INIT_FORKNUM, false); - log_smgrcreate(&rel->rd_locator, INIT_FORKNUM); + RelationCreateFork(srel, INIT_FORKNUM, true, true); fill_seq_fork_with_data(rel, tuple, INIT_FORKNUM); FlushRelationBuffers(rel); smgrclose(srel); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 4345b96de5e..56c9d61aa21 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -15655,16 +15655,17 @@ index_copy_data(Relation rel, RelFileLocator newrlocator) { if (smgrexists(RelationGetSmgr(rel), forkNum)) { - smgrcreate(dstrel, forkNum, false); - - /* - * WAL log creation if the relation is persistent, or this is the - * init fork of an unlogged relation. - */ - if (RelationIsPermanent(rel) || + bool wal_log = RelationIsPermanent(rel) | (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED && - forkNum == INIT_FORKNUM)) - log_smgrcreate(&newrlocator, forkNum); + forkNum == INIT_FORKNUM); + + /* + * Usually, we don't use UNDO log for FSM or VM forks, as their + * creation is not transactional. However, we're currently copying + * the entire relation in a transactional manner, which requires + * after-crash cleanup. + */ + RelationCreateFork(dstrel, forkNum, wal_log, true); RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum, rel->rd_rel->relpersistence); } diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 0f02bf62fa3..cb524cfa42c 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -4812,8 +4812,7 @@ CreateAndCopyRelationData(RelFileLocator src_rlocator, /* * Create and copy all forks of the relation. During create database we * have a separate cleanup mechanism which deletes complete database - * directory. Therefore, each individual relation doesn't need to be - * registered for cleanup. + * directory. Therefore, do not issue an UNDO log for this relation. */ RelationCreateStorage(dst_rlocator, relpersistence, false); @@ -4827,6 +4826,7 @@ CreateAndCopyRelationData(RelFileLocator src_rlocator, { if (smgrexists(src_rel, forkNum)) { + /* Use smgrcreate() directly as no UNDO log is required. */ smgrcreate(dst_rel, forkNum, false); /* diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c index 01e267abf9b..d3a42d3f566 100644 --- a/src/backend/storage/file/reinit.c +++ b/src/backend/storage/file/reinit.c @@ -34,6 +34,39 @@ typedef struct RelFileNumber relnumber; /* hash key */ } unlogged_relation_entry; +static char **ignore_files = NULL; +static int nignore_elems = 0; +static int nignore_files = 0; + +/* + * determine if the file should be ignored when resetting unlogged relations + */ +static bool +reinit_ignore_file(const char *dirname, const char *name) +{ + char fnamebuf[MAXPGPATH]; + int len; + + if (nignore_files == 0) + return false; + + strncpy(fnamebuf, dirname, MAXPGPATH - 1); + strncat(fnamebuf, "/", MAXPGPATH - 1); + strncat(fnamebuf, name, MAXPGPATH - 1); + fnamebuf[MAXPGPATH - 1] = 0; + + for (int i = 0 ; i < nignore_files ; i++) + { + /* match ignoring fork part */ + len = strlen(ignore_files[i]); + if (strncmp(fnamebuf, ignore_files[i], len) == 0 && + (fnamebuf[len] == 0 || fnamebuf[len] == '_')) + return true; + } + + return false; +} + /* * Reset unlogged relations from before the last restart. * @@ -204,6 +237,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) &forkNum, &segno)) continue; + /* Skip anything that undo log suggested to ignore */ + if (reinit_ignore_file(dbspacedirname, de->d_name)) + continue; + /* Also skip it unless this is the init fork. */ if (forkNum != INIT_FORKNUM) continue; @@ -243,6 +280,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) &forkNum, &segno)) continue; + /* Skip anything that undo log suggested to ignore */ + if (reinit_ignore_file(dbspacedirname, de->d_name)) + continue; + /* We never remove the init fork. */ if (forkNum == INIT_FORKNUM) continue; @@ -294,6 +335,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) &forkNum, &segno)) continue; + /* Skip anything that undo log suggested to ignore */ + if (reinit_ignore_file(dbspacedirname, de->d_name)) + continue; + /* Also skip it unless this is the init fork. */ if (forkNum != INIT_FORKNUM) continue; @@ -337,6 +382,10 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) &forkNum, &segno)) continue; + /* Skip anything that undo log suggested to ignore */ + if (reinit_ignore_file(dbspacedirname, de->d_name)) + continue; + /* Also skip it unless this is the init fork. */ if (forkNum != INIT_FORKNUM) continue; @@ -366,6 +415,49 @@ ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op) } } +/* + * Record relfilenodes that should be left alone during reinitializing unlogged + * relations. + */ +void +ResetUnloggedRelationIgnore(RelFileLocator rloc, ProcNumber backend) +{ + RelFileLocatorBackend rbloc; + + if (nignore_files >= nignore_elems) + { + if (ignore_files == NULL) + { + nignore_elems = 16; + ignore_files = palloc(sizeof(char *) * nignore_elems); + } + else + { + nignore_elems *= 2; + ignore_files = repalloc(ignore_files, + sizeof(char *) * nignore_elems); + } + } + + rbloc.backend = backend; + rbloc.locator = rloc; + ignore_files[nignore_files++] = relpath(rbloc, MAIN_FORKNUM); +} + +/* + * Clear the ignore list + */ +void +ResetUnloggedRelationIgnoreClear(void) +{ + if (nignore_elems == 0) + return; + + pfree(ignore_files); + ignore_files = NULL; + nignore_elems = 0; +} + /* * Basic parsing of putative relation filenames. * diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 925728eb6c1..5a403ffb04b 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -813,6 +813,15 @@ smgrimmedsync(SMgrRelation reln, ForkNumber forknum) smgrsw[reln->smgr_which].smgr_immedsync(reln, forknum); } +/* + * smgrunlink() -- unlink the storage file + */ +void +smgrunlink(SMgrRelation reln, ForkNumber forknum, bool isRedo) +{ + smgrsw[reln->smgr_which].smgr_unlink(reln->smgr_rlocator, forknum, isRedo); +} + /* * AtEOXact_SMgr * diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h index 02755b04bb9..66f564d9646 100644 --- a/src/include/access/rmgrlist.h +++ b/src/include/access/rmgrlist.h @@ -27,7 +27,7 @@ /* symbol name, textual name, redo, desc, identify, startup, cleanup, mask, decode, undo, undo_desc, undo_identify, undo_cleanup_init, undo_recoveryend */ PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL, NULL, xlog_decode, NULL, NULL, NULL, NULL, NULL) PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL, NULL, xact_decode, NULL, NULL, NULL, NULL, NULL) -PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) +PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL, NULL, NULL, smgr_undo, smgr_undodesc, smgr_undoidentify, smgr_undocleanupinit, smgr_undoshutdown) PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h index 72ef3ee92c0..3451d6ac80c 100644 --- a/src/include/catalog/storage.h +++ b/src/include/catalog/storage.h @@ -25,6 +25,8 @@ extern PGDLLIMPORT int wal_skip_threshold; extern SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete); +extern void RelationCreateFork(SMgrRelation srel, ForkNumber forkNum, + bool wal_log, bool undo_log); extern void RelationDropStorage(Relation rel); extern void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit); extern void RelationPreTruncate(Relation rel); diff --git a/src/include/catalog/storage_ulog.h b/src/include/catalog/storage_ulog.h new file mode 100644 index 00000000000..41c181ba2af --- /dev/null +++ b/src/include/catalog/storage_ulog.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * storage_ulog.h + * prototypes for Undo Log support for backend/catalog/storage.c + * + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/storage_ulog.h + * + *------------------------------------------------------------------------- + */ +#ifndef STORAGE_ULOG_H +#define STORAGE_ULOG_H + +#include "access/undolog.h" +#include "storage/smgr.h" + +/* ULOG gives us high 4 bits (just following xlog) */ +#define ULOG_SMGR_CREATE 0x10 + +/* undo log entry for storage file creation */ +typedef struct ul_smgr_create +{ + RelFileLocator rlocator; + ProcNumber backend; + ForkNumber forknum; +} ul_smgr_create; + +extern void smgr_undo(UndoLogRecord *record, ULogOp op, + bool recovered, bool redo); +extern void smgr_undodesc(StringInfo buf, UndoLogRecord *record); +extern const char *smgr_undoidentify(uint8 info); +extern void smgr_undocleanupinit(void); +extern void smgr_undoshutdown(void); + +#define ULogRecGetData(record) ((char *)record + sizeof(UndoLogRecord)) +#define ULogRecGetInfo(record) ((record)->ul_info) + +#endif /* STORAGE_XLOG_H */ diff --git a/src/include/storage/reinit.h b/src/include/storage/reinit.h index 1373d509df2..02bf55d3a6b 100644 --- a/src/include/storage/reinit.h +++ b/src/include/storage/reinit.h @@ -16,9 +16,13 @@ #define REINIT_H #include "common/relpath.h" +#include "storage/relfilelocator.h" extern void ResetUnloggedRelations(int op); +extern void ResetUnloggedRelationIgnore(RelFileLocator rloc, + ProcNumber backend); +extern void ResetUnloggedRelationIgnoreClear(void); extern bool parse_filename_for_nontemp_relation(const char *name, RelFileNumber *relnumber, ForkNumber *fork, diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h index 899d0d681c5..a05436a8a7c 100644 --- a/src/include/storage/smgr.h +++ b/src/include/storage/smgr.h @@ -109,6 +109,7 @@ extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nblocks); extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum); extern void smgrregistersync(SMgrRelation reln, ForkNumber forknum); +extern void smgrunlink(SMgrRelation reln, ForkNumber forknum, bool isRedo); extern void AtEOXact_SMgr(void); extern bool ProcessBarrierSmgrRelease(void); diff --git a/src/test/recovery/t/013_crash_restart.pl b/src/test/recovery/t/013_crash_restart.pl index d5d24e31d90..4df88efeb3d 100644 --- a/src/test/recovery/t/013_crash_restart.pl +++ b/src/test/recovery/t/013_crash_restart.pl @@ -86,6 +86,23 @@ ok( pump_until( $killme_stdout = ''; $killme_stderr = ''; +#also, create a table whose storage should *not* survive. +$killme_stdin .= q[ +CREATE TABLE should_not_survive (a int); +SELECT pg_relation_filepath('should_not_survive'); +]; +ok( pump_until( + $killme, $psql_timeout, \$killme_stdout, + qr/base\/[[:digit:]\/]+[\r\n]$/m), + 'created a table'); +my $relfilerelpath = $killme_stdout; +chomp($relfilerelpath); +$killme_stdout = ''; +$killme_stderr = ''; + +my $relfilepath = $node->data_dir . "/" . $relfilerelpath; +ok( -e $relfilepath, + "storage file is created in xact that is going to crash"); # Start longrunning query in second session; its failure will signal that # crash-restart has occurred. The initial wait for the trivial select is to @@ -144,6 +161,8 @@ $killme->run(); ($monitor_stdin, $monitor_stdout, $monitor_stderr) = ('', '', ''); $monitor->run(); +ok( ! -e $relfilepath, + "orphaned storage file is correctly removed"); # Acquire pid of new backend $killme_stdin .= q[ -- 2.43.5 ----Next_Part(Thu_Oct_31_17_01_30_2024_620)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v35-0008-Remove-isCommit-flag-from-PendingRelDelete.patch"