mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Allow archiving via loadable modules.
Running a shell command for each file to be archived has a lot of overhead and may not offer as much error checking as you want, or the exact semantics that you want. So, offer the option to call a loadable module for each file to be archived, rather than running a shell command. Also, add a 'basic_archive' contrib module as an example implementation that archives to a local directory. Nathan Bossart, with a little bit of kibitzing by me. Discussion: http://postgr.es/m/20220202224433.GA1036711@nathanxps13
This commit is contained in:
@ -8831,7 +8831,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);
|
||||
|
@ -89,6 +89,8 @@ typedef struct PgArchData
|
||||
slock_t arch_lck;
|
||||
} PgArchData;
|
||||
|
||||
char *XLogArchiveLibrary = "";
|
||||
|
||||
|
||||
/* ----------
|
||||
* Local data
|
||||
@ -96,6 +98,8 @@ typedef struct PgArchData
|
||||
*/
|
||||
static time_t last_sigterm_time = 0;
|
||||
static PgArchData *PgArch = NULL;
|
||||
static ArchiveModuleCallbacks ArchiveContext;
|
||||
|
||||
|
||||
/*
|
||||
* Stuff for tracking multiple files to archive from each scan of
|
||||
@ -140,6 +144,8 @@ 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);
|
||||
static void call_archive_module_shutdown_callback(int code, Datum arg);
|
||||
|
||||
/* Report shared memory space needed by PgArchShmemInit */
|
||||
Size
|
||||
@ -244,7 +250,16 @@ PgArchiverMain(void)
|
||||
arch_files->arch_heap = binaryheap_allocate(NUM_FILES_PER_DIRECTORY_SCAN,
|
||||
ready_file_comparator, NULL);
|
||||
|
||||
pgarch_MainLoop();
|
||||
/* Load the archive_library. */
|
||||
LoadArchiveLibrary();
|
||||
|
||||
PG_ENSURE_ERROR_CLEANUP(call_archive_module_shutdown_callback, 0);
|
||||
{
|
||||
pgarch_MainLoop();
|
||||
}
|
||||
PG_END_ENSURE_ERROR_CLEANUP(call_archive_module_shutdown_callback, 0);
|
||||
|
||||
call_archive_module_shutdown_callback(0, 0);
|
||||
|
||||
proc_exit(0);
|
||||
}
|
||||
@ -407,11 +422,12 @@ 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 != NULL &&
|
||||
!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;
|
||||
}
|
||||
|
||||
@ -492,7 +508,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
|
||||
*/
|
||||
@ -509,7 +525,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
|
||||
@ -759,13 +775,89 @@ HandlePgArchInterrupts(void)
|
||||
if (ProcSignalBarrierPending)
|
||||
ProcessProcSignalBarrier();
|
||||
|
||||
if (ConfigReloadPending)
|
||||
{
|
||||
ConfigReloadPending = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
}
|
||||
|
||||
/* Perform logging of memory contexts of this process */
|
||||
if (LogMemoryContextPending)
|
||||
ProcessLogMemoryContextInterrupt();
|
||||
|
||||
if (ConfigReloadPending)
|
||||
{
|
||||
char *archiveLib = pstrdup(XLogArchiveLibrary);
|
||||
bool archiveLibChanged;
|
||||
|
||||
ConfigReloadPending = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
|
||||
archiveLibChanged = strcmp(XLogArchiveLibrary, archiveLib) != 0;
|
||||
pfree(archiveLib);
|
||||
|
||||
if (archiveLibChanged)
|
||||
{
|
||||
/*
|
||||
* Call the currently loaded archive module's shutdown callback, if
|
||||
* one is defined.
|
||||
*/
|
||||
call_archive_module_shutdown_callback(0, 0);
|
||||
|
||||
/*
|
||||
* Ideally, we would simply unload the previous archive module and
|
||||
* load the new one, but there is presently no mechanism for
|
||||
* unloading a library (see the comment above
|
||||
* internal_unload_library()). To deal with this, we simply restart
|
||||
* the archiver. The new archive module will be loaded when the new
|
||||
* archiver process starts up.
|
||||
*/
|
||||
ereport(LOG,
|
||||
(errmsg("restarting archiver process because value of "
|
||||
"\"archive_library\" was changed")));
|
||||
|
||||
proc_exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* LoadArchiveLibrary
|
||||
*
|
||||
* Loads the archiving callbacks into our local ArchiveContext.
|
||||
*/
|
||||
static void
|
||||
LoadArchiveLibrary(void)
|
||||
{
|
||||
ArchiveModuleInit archive_init;
|
||||
|
||||
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 (XLogArchiveLibrary[0] == '\0')
|
||||
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.archive_file_cb == NULL)
|
||||
ereport(ERROR,
|
||||
(errmsg("archive modules must register an archive callback")));
|
||||
}
|
||||
|
||||
/*
|
||||
* call_archive_module_shutdown_callback
|
||||
*
|
||||
* Calls the loaded archive module's shutdown callback, if one is defined.
|
||||
*/
|
||||
static void
|
||||
call_archive_module_shutdown_callback(int code, Datum arg)
|
||||
{
|
||||
if (ArchiveContext.shutdown_cb != NULL)
|
||||
ArchiveContext.shutdown_cb();
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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"
|
||||
|
@ -3881,13 +3881,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 used only if \"archive_library\" is not set.")
|
||||
},
|
||||
&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("An empty string indicates that \"archive_command\" should be used.")
|
||||
},
|
||||
&XLogArchiveLibrary,
|
||||
"",
|
||||
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."),
|
||||
|
@ -245,6 +245,9 @@
|
||||
|
||||
#archive_mode = off # enables archiving; off, on, or always
|
||||
# (change requires restart)
|
||||
#archive_library = '' # library to use to archive a logfile segment
|
||||
# (empty string indicates archive_command should
|
||||
# be used)
|
||||
#archive_command = '' # command to use to archive a logfile segment
|
||||
# placeholders: %p = path of file to archive
|
||||
# %f = file name only
|
||||
|
@ -154,7 +154,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
|
||||
|
@ -33,7 +33,41 @@ 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;
|
||||
|
||||
/*
|
||||
* Archive module callbacks
|
||||
*
|
||||
* These callback functions should be defined by archive libraries and returned
|
||||
* via _PG_archive_module_init(). ArchiveFileCB is the only required callback.
|
||||
* For more information about the purpose of each callback, refer to the
|
||||
* archive modules documentation.
|
||||
*/
|
||||
typedef bool (*ArchiveCheckConfiguredCB) (void);
|
||||
typedef bool (*ArchiveFileCB) (const char *file, const char *path);
|
||||
typedef void (*ArchiveShutdownCB) (void);
|
||||
|
||||
typedef struct ArchiveModuleCallbacks
|
||||
{
|
||||
ArchiveCheckConfiguredCB check_configured_cb;
|
||||
ArchiveFileCB archive_file_cb;
|
||||
ArchiveShutdownCB shutdown_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);
|
||||
|
||||
#endif /* _PGARCH_H */
|
||||
|
Reference in New Issue
Block a user