From 76e0d1b1c32cba4d1b4cfe9f3d71c9d43a3e9b6c Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 19 Nov 2021 01:04:41 +0000 Subject: [PATCH v12 2/4] Introduce archive modules infrastructure. --- src/backend/access/transam/xlog.c | 2 +- src/backend/postmaster/pgarch.c | 71 +++++++++++++++++++++++++-- src/backend/postmaster/shell_archive.c | 24 ++++++++- src/backend/utils/init/miscinit.c | 1 + src/backend/utils/misc/guc.c | 12 ++++- src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/access/xlog.h | 1 - src/include/postmaster/pgarch.h | 46 ++++++++++++++++- 8 files changed, 147 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 87cd05c945..fa13bf4d21 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8832,7 +8832,7 @@ ShutdownXLOG(int code, Datum arg) * process one more time at the end of shutdown). The checkpoint * record will go to the next XLOG file and won't be archived (yet). */ - if (XLogArchivingActive() && XLogArchiveCommandSet()) + if (XLogArchivingActive()) RequestXLogSwitch(false); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c index dc68dd570e..61e88ff133 100644 --- a/src/backend/postmaster/pgarch.c +++ b/src/backend/postmaster/pgarch.c @@ -88,6 +88,8 @@ typedef struct PgArchData slock_t arch_lck; } PgArchData; +char *XLogArchiveLibrary = ""; + /* ---------- * Local data @@ -95,6 +97,8 @@ typedef struct PgArchData */ static time_t last_sigterm_time = 0; static PgArchData *PgArch = NULL; +static ArchiveModuleCallbacks *ArchiveContext = NULL; + /* * Stuff for tracking multiple files to archive from each scan of @@ -139,6 +143,7 @@ static void pgarch_archiveDone(char *xlog); static void pgarch_die(int code, Datum arg); static void HandlePgArchInterrupts(void); static int ready_file_comparator(Datum a, Datum b, void *arg); +static void LoadArchiveLibrary(void); /* Report shared memory space needed by PgArchShmemInit */ Size @@ -235,6 +240,11 @@ PgArchiverMain(void) */ PgArch->pgprocno = MyProc->pgprocno; + /* + * Load the archive_library. + */ + LoadArchiveLibrary(); + /* Create workspace for pgarch_readyXlog() */ arch_files = palloc(sizeof(struct arch_files_state)); arch_files->arch_files_size = 0; @@ -406,11 +416,11 @@ pgarch_ArchiverCopyLoop(void) */ HandlePgArchInterrupts(); - /* can't do anything if no command ... */ - if (!XLogArchiveCommandSet()) + /* can't do anything if not configured ... */ + if (!ArchiveContext->check_configured_cb()) { ereport(WARNING, - (errmsg("archive_mode enabled, yet archive_command is not set"))); + (errmsg("archive_mode enabled, yet archiving is not configured"))); return; } @@ -491,7 +501,7 @@ pgarch_ArchiverCopyLoop(void) /* * pgarch_archiveXlog * - * Invokes system(3) to copy one archive file to wherever it should go + * Invokes archive_file_cb to copy one archive file to wherever it should go * * Returns true if successful */ @@ -508,7 +518,7 @@ pgarch_archiveXlog(char *xlog) snprintf(activitymsg, sizeof(activitymsg), "archiving %s", xlog); set_ps_display(activitymsg); - ret = shell_archive_file(xlog, pathname); + ret = ArchiveContext->archive_file_cb(xlog, pathname); if (ret) snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog); else @@ -761,5 +771,56 @@ HandlePgArchInterrupts(void) { ConfigReloadPending = false; ProcessConfigFile(PGC_SIGHUP); + + /* + * Load the archive_library in case it changed. Ideally, this would + * first unload any pre-existing loaded archive library to release + * custom GUCs, decommission background workers, etc., but there is + * presently no mechanism for unloading a library. For more + * information, see the comment above internal_unload_library(). + */ + LoadArchiveLibrary(); } } + +/* + * LoadArchiveLibrary + * + * Loads the archiving callbacks into our local ArchiveContext. + */ +static void +LoadArchiveLibrary(void) +{ + ArchiveModuleInit archive_init; + + if (ArchiveContext == NULL) + ArchiveContext = palloc(sizeof(ArchiveModuleCallbacks)); + + memset(ArchiveContext, 0, sizeof(ArchiveModuleCallbacks)); + + /* + * If shell archiving is enabled, use our special initialization + * function. Otherwise, load the library and call its + * _PG_archive_module_init(). + */ + if (ShellArchivingEnabled()) + archive_init = shell_archive_init; + else + archive_init = (ArchiveModuleInit) + load_external_function(XLogArchiveLibrary, + "_PG_archive_module_init", false, NULL); + + if (archive_init == NULL) + ereport(ERROR, + (errmsg("archive modules have to declare the " + "_PG_archive_module_init symbol"))); + + archive_init(ArchiveContext); + + if (ArchiveContext->check_configured_cb == NULL) + ereport(ERROR, + (errmsg("archive modules must register a check callback"))); + if (ArchiveContext->archive_file_cb == NULL) + ereport(ERROR, + (errmsg("archive modules must register an archive callback"))); +} diff --git a/src/backend/postmaster/shell_archive.c b/src/backend/postmaster/shell_archive.c index b54e701da4..19e240c205 100644 --- a/src/backend/postmaster/shell_archive.c +++ b/src/backend/postmaster/shell_archive.c @@ -2,6 +2,10 @@ * * shell_archive.c * + * This archiving function uses a user-specified shell command (the + * archive_command GUC) to copy write-ahead log files. It is used as the + * default, but other modules may define their own custom archiving logic. + * * Copyright (c) 2022, PostgreSQL Global Development Group * * IDENTIFICATION @@ -17,7 +21,25 @@ #include "pgstat.h" #include "postmaster/pgarch.h" -bool +static bool shell_archive_configured(void); +static bool shell_archive_file(const char *file, const char *path); + +void +shell_archive_init(ArchiveModuleCallbacks *cb) +{ + AssertVariableIsOfType(&shell_archive_init, ArchiveModuleInit); + + cb->check_configured_cb = shell_archive_configured; + cb->archive_file_cb = shell_archive_file; +} + +static bool +shell_archive_configured(void) +{ + return XLogArchiveCommand[0] != '\0'; +} + +static bool shell_archive_file(const char *file, const char *path) { char xlogarchcmd[MAXPGPATH]; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 88801374b5..358d9ed029 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -38,6 +38,7 @@ #include "pgstat.h" #include "postmaster/autovacuum.h" #include "postmaster/interrupt.h" +#include "postmaster/pgarch.h" #include "postmaster/postmaster.h" #include "storage/fd.h" #include "storage/ipc.h" diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f9504d3aec..2554923e1d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3869,13 +3869,23 @@ static struct config_string ConfigureNamesString[] = { {"archive_command", PGC_SIGHUP, WAL_ARCHIVING, gettext_noop("Sets the shell command that will be called to archive a WAL file."), - NULL + gettext_noop("This is unused if \"archive_library\" does not indicate archiving via shell is enabled.") }, &XLogArchiveCommand, "", NULL, NULL, show_archive_command }, + { + {"archive_library", PGC_SIGHUP, WAL_ARCHIVING, + gettext_noop("Sets the library that will be called to archive a WAL file."), + gettext_noop("A value of \"shell\" or an empty string indicates that \"archive_command\" should be used.") + }, + &XLogArchiveLibrary, + "shell", + NULL, NULL, NULL + }, + { {"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY, gettext_noop("Sets the shell command that will be called to retrieve an archived WAL file."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index a1acd46b61..e8bdd1fe13 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -245,6 +245,7 @@ #archive_mode = off # enables archiving; off, on, or always # (change requires restart) +#archive_library = 'shell' # library to use to archive a logfile segment #archive_command = '' # command to use to archive a logfile segment # placeholders: %p = path of file to archive # %f = file name only diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 34f6c89f06..b7dfb580ad 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -155,7 +155,6 @@ extern PGDLLIMPORT int wal_level; /* Is WAL archiving enabled always (even during recovery)? */ #define XLogArchivingAlways() \ (AssertMacro(XLogArchiveMode == ARCHIVE_MODE_OFF || wal_level >= WAL_LEVEL_REPLICA), XLogArchiveMode == ARCHIVE_MODE_ALWAYS) -#define XLogArchiveCommandSet() (XLogArchiveCommand[0] != '\0') /* * Is WAL-logging necessary for archival or log-shipping, or can we skip diff --git a/src/include/postmaster/pgarch.h b/src/include/postmaster/pgarch.h index 9c4bd69b56..03b5ab7c22 100644 --- a/src/include/postmaster/pgarch.h +++ b/src/include/postmaster/pgarch.h @@ -33,7 +33,49 @@ extern void PgArchiverMain(void) pg_attribute_noreturn(); extern void PgArchWakeup(void); extern void PgArchForceDirScan(void); -/* in shell_archive.c */ -extern bool shell_archive_file(const char *file, const char *path); +/* + * The value of the archive_library GUC. + */ +extern char *XLogArchiveLibrary; + +/* + * Callback that gets called to determine if the archive module is + * configured. + */ +typedef bool (*ArchiveCheckConfiguredCB) (void); + +/* + * Callback called to archive a single WAL file. + */ +typedef bool (*ArchiveFileCB) (const char *file, const char *path); + +/* + * Archive module callbacks + */ +typedef struct ArchiveModuleCallbacks +{ + ArchiveCheckConfiguredCB check_configured_cb; + ArchiveFileCB archive_file_cb; +} ArchiveModuleCallbacks; + +/* + * Type of the shared library symbol _PG_archive_module_init that is looked + * up when loading an archive library. + */ +typedef void (*ArchiveModuleInit) (ArchiveModuleCallbacks *cb); + +/* + * Since the logic for archiving via a shell command is in the core server + * and does not need to be loaded via a shared library, it has a special + * initialization function. + */ +extern void shell_archive_init(ArchiveModuleCallbacks *cb); + +/* + * We consider archiving via shell to be enabled if archive_library is + * empty or if archive_library is set to "shell". + */ +#define ShellArchivingEnabled() \ + (XLogArchiveLibrary[0] == '\0' || strcmp(XLogArchiveLibrary, "shell") == 0) #endif /* _PGARCH_H */ -- 2.16.6