From 86188a643aebffc39a03b233362d4cd74506fd53 Mon Sep 17 00:00:00 2001 From: Alexandre Felipe Date: Thu, 17 Sep 2026 21:30:53 +0100 Subject: [PATCH] pgaio: file descriptor cache invalidation. It was reported that io_method=worker, I/O worker processes retain open file descriptors on relation that have been dropped [1] Regular backends do not accumulate them: they receive SMGR invalidation via the shared invalidation queue I/O workers were not handling invalidation messages. This patch implements cache invalidation processing for I/O workers. 1. https://www.postgresql.org/message-id/19622-639a4ba94c5a53d7%40postgresql.org --- src/backend/storage/aio/method_worker.c | 32 +++++++++++++++++ src/backend/storage/smgr/smgr.c | 46 +++++++++++++++++++------ src/include/storage/smgr.h | 1 + 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c index cf75b2816b7..fc9da145b8f 100644 --- a/src/backend/storage/aio/method_worker.c +++ b/src/backend/storage/aio/method_worker.c @@ -44,7 +44,9 @@ #include "storage/lwlock.h" #include "storage/pmsignal.h" #include "storage/proc.h" +#include "storage/sinvaladt.h" #include "storage/shmem.h" +#include "storage/smgr.h" #include "tcop/tcopprot.h" #include "utils/injection_point.h" #include "utils/memdebug.h" @@ -683,6 +685,25 @@ check_io_worker_gucs(void) "io_max_workers", io_max_workers))); } +/* + * Handle cache invalidation messages for IoWorker + * + * Same as LocalExecuteInvalidationMessage for backends, + * but in IoWorkers handle only file invalidation messages. + */ +static void +pgaio_cache_invalidation_callback(SharedInvalidationMessage *msg) +{ + if (msg->id == SHAREDINVALSMGR_ID) + { + RelFileLocatorBackend rlocator; + + rlocator.locator = msg->sm.rlocator; + rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo; + smgrdestroyrellocator(rlocator); + } +} + void IoWorkerMain(const void *startup_data, size_t startup_data_len) { @@ -698,6 +719,11 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len) AuxiliaryProcessMainCommon(); + /* + * IO workers cache file descriptors locally, subscribe to cluster-wide + * cache invalidation events. + */ + SharedInvalBackendInit(false); pqsignal(SIGHUP, SignalHandlerForConfigReload); pqsignal(SIGINT, die); /* to allow manually triggering worker restart */ @@ -1032,6 +1058,12 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len) CHECK_FOR_INTERRUPTS(); + /* + * Handle pending invalidation one by one. smgrdestroyall used when + * there are too many pending invalidations. + */ + ReceiveSharedInvalidMessages(pgaio_cache_invalidation_callback, smgrdestroyall); + if (ConfigReloadPending) { int io_max_workers_prev = io_max_workers; diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 5391640d861..8cf592281bc 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -164,7 +164,6 @@ static dlist_head unpinned_relns; /* local function prototypes */ static void smgrshutdown(int code, Datum arg); static void smgrdestroy(SMgrRelation reln); - static void smgr_aio_reopen(PgAioHandle *ioh); static char *smgr_aio_describe_identity(const PgAioTargetData *sd); @@ -317,7 +316,10 @@ smgrunpin(SMgrRelation reln) } /* - * smgrdestroy() -- Delete an SMgrRelation object. + * smgrdestroy() -- Destroy an SMgrRelation object. + * + * This closes associated files, and frees memory allocated for a relation + * by removing any in-memory reference. */ static void smgrdestroy(SMgrRelation reln) @@ -342,9 +344,10 @@ smgrdestroy(SMgrRelation reln) } /* - * smgrrelease() -- Release all resources used by this object. + * smgrrelease() -- Close relation files. * - * The object remains valid. + * In contrast to smgrdestroy(), smgrrelease() retains the relation entry, + * and will not free memory associated with it. */ void smgrrelease(SMgrRelation reln) @@ -377,7 +380,7 @@ smgrclose(SMgrRelation reln) } /* - * smgrdestroyall() -- Release resources used by all unpinned objects. + * smgrdestroyall() -- Destroy all unpinned objects. * * It must be known that there are no pointers to SMgrRelations, other than * those pinned with smgrpin(). @@ -406,7 +409,7 @@ smgrdestroyall(void) } /* - * smgrreleaseall() -- Release resources used by all objects. + * smgrreleaseall() -- Close all relations. */ void smgrreleaseall(void) @@ -432,12 +435,11 @@ smgrreleaseall(void) } /* - * smgrreleaserellocator() -- Release resources for given RelFileLocator, if - * it's open. + * smgrreleaserellocator() -- Locate a relation and close it. * * This has the same effects as smgrrelease(smgropen(rlocator)), but avoids - * uselessly creating a hashtable entry only to drop it again when no - * such entry exists already. + * uselessly creating a hashtable entry, if a hashtable entry exists it is + * retained. */ void smgrreleaserellocator(RelFileLocatorBackend rlocator) @@ -455,6 +457,30 @@ smgrreleaserellocator(RelFileLocatorBackend rlocator) smgrrelease(reln); } +/* + * smgrdestroyrellocator() -- Locate a relation and destroy it. + * + * This has the same effects as smgrdestroy(smgropen(rlocator)), but avoids + * uselessly creating a hashtable entry only to drop it again when no + * such entry exists already. + */ +void +smgrdestroyrellocator(RelFileLocatorBackend rlocator) +{ + SMgrRelation reln; + + /* Nothing to do if hashtable not set up */ + if (SMgrRelationHash == NULL) + return; + + reln = (SMgrRelation) hash_search(SMgrRelationHash, + &rlocator, + HASH_FIND, NULL); + if (reln != NULL) + smgrdestroy(reln); +} + + /* * smgrexists() -- Does the underlying file for a fork exist? */ diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h index 09bd42fcf4b..417cbaaba6d 100644 --- a/src/include/storage/smgr.h +++ b/src/include/storage/smgr.h @@ -86,6 +86,7 @@ extern void smgrdestroyall(void); extern void smgrrelease(SMgrRelation reln); extern void smgrreleaseall(void); extern void smgrreleaserellocator(RelFileLocatorBackend rlocator); +extern void smgrdestroyrellocator(RelFileLocatorBackend rlocator); extern void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo); extern void smgrdosyncall(SMgrRelation *rels, int nrels); extern void smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo); -- 2.53.0