Refactor code for restoring files via shell commands
Presently, restore_command uses a different code path than
archive_cleanup_command and recovery_end_command. These code paths
are similar and can be easily combined, as long as it is possible to
identify if a command should:
- Issue a FATAL on signal.
- Exit immediately on SIGTERM.
While on it, this removes src/common/archive.c and its associated
header. Since the introduction of c96de2c
, BuildRestoreCommand() has
become a simple wrapper of replace_percent_placeholders() able to call
make_native_path(). This simplifies shell_restore.c as long as
RestoreArchivedFile() includes a call to make_native_path().
Author: Nathan Bossart
Reviewed-by: Andres Freund, Michael Paquier
Discussion: https://postgr.es/m/20221227192449.GA3672473@nathanxps13
This commit is contained in:
parent
2f31f405e1
commit
14bdb3f13d
|
@ -20,16 +20,16 @@
|
||||||
|
|
||||||
#include "access/xlogarchive.h"
|
#include "access/xlogarchive.h"
|
||||||
#include "access/xlogrecovery.h"
|
#include "access/xlogrecovery.h"
|
||||||
#include "common/archive.h"
|
|
||||||
#include "common/percentrepl.h"
|
#include "common/percentrepl.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "utils/wait_event.h"
|
#include "utils/wait_event.h"
|
||||||
|
|
||||||
static void ExecuteRecoveryCommand(const char *command,
|
static bool ExecuteRecoveryCommand(const char *command,
|
||||||
const char *commandName,
|
const char *commandName,
|
||||||
bool failOnSignal,
|
bool failOnSignal,
|
||||||
|
bool exitOnSigterm,
|
||||||
uint32 wait_event_info,
|
uint32 wait_event_info,
|
||||||
const char *lastRestartPointFileName);
|
int fail_elevel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attempt to execute a shell-based restore command.
|
* Attempt to execute a shell-based restore command.
|
||||||
|
@ -40,25 +40,17 @@ bool
|
||||||
shell_restore(const char *file, const char *path,
|
shell_restore(const char *file, const char *path,
|
||||||
const char *lastRestartPointFileName)
|
const char *lastRestartPointFileName)
|
||||||
{
|
{
|
||||||
|
char *nativePath = pstrdup(path);
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int rc;
|
bool ret;
|
||||||
|
|
||||||
/* Build the restore command to execute */
|
/* Build the restore command to execute */
|
||||||
cmd = BuildRestoreCommand(recoveryRestoreCommand, path, file,
|
make_native_path(nativePath);
|
||||||
lastRestartPointFileName);
|
cmd = replace_percent_placeholders(recoveryRestoreCommand,
|
||||||
|
"restore_command", "frp", file,
|
||||||
ereport(DEBUG3,
|
lastRestartPointFileName,
|
||||||
(errmsg_internal("executing restore command \"%s\"", cmd)));
|
nativePath);
|
||||||
|
pfree(nativePath);
|
||||||
/*
|
|
||||||
* Copy xlog from archival storage to XLOGDIR
|
|
||||||
*/
|
|
||||||
fflush(NULL);
|
|
||||||
pgstat_report_wait_start(WAIT_EVENT_RESTORE_COMMAND);
|
|
||||||
rc = system(cmd);
|
|
||||||
pgstat_report_wait_end();
|
|
||||||
|
|
||||||
pfree(cmd);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember, we rollforward UNTIL the restore fails so failure here is
|
* Remember, we rollforward UNTIL the restore fails so failure here is
|
||||||
|
@ -84,17 +76,13 @@ shell_restore(const char *file, const char *path,
|
||||||
*
|
*
|
||||||
* We treat hard shell errors such as "command not found" as fatal, too.
|
* We treat hard shell errors such as "command not found" as fatal, too.
|
||||||
*/
|
*/
|
||||||
if (rc != 0)
|
ret = ExecuteRecoveryCommand(cmd, "restore_command",
|
||||||
{
|
true, /* failOnSignal */
|
||||||
if (wait_result_is_signal(rc, SIGTERM))
|
true, /* exitOnSigterm */
|
||||||
proc_exit(1);
|
WAIT_EVENT_RESTORE_COMMAND, DEBUG2);
|
||||||
|
pfree(cmd);
|
||||||
|
|
||||||
ereport(wait_result_is_any_signal(rc, true) ? FATAL : DEBUG2,
|
return ret;
|
||||||
(errmsg("could not restore file \"%s\" from archive: %s",
|
|
||||||
file, wait_result_to_str(rc))));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (rc == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -103,9 +91,14 @@ shell_restore(const char *file, const char *path,
|
||||||
void
|
void
|
||||||
shell_archive_cleanup(const char *lastRestartPointFileName)
|
shell_archive_cleanup(const char *lastRestartPointFileName)
|
||||||
{
|
{
|
||||||
ExecuteRecoveryCommand(archiveCleanupCommand, "archive_cleanup_command",
|
char *cmd;
|
||||||
false, WAIT_EVENT_ARCHIVE_CLEANUP_COMMAND,
|
|
||||||
lastRestartPointFileName);
|
cmd = replace_percent_placeholders(archiveCleanupCommand,
|
||||||
|
"archive_cleanup_command",
|
||||||
|
"r", lastRestartPointFileName);
|
||||||
|
(void) ExecuteRecoveryCommand(cmd, "archive_cleanup_command", false, false,
|
||||||
|
WAIT_EVENT_ARCHIVE_CLEANUP_COMMAND, WARNING);
|
||||||
|
pfree(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -114,9 +107,14 @@ shell_archive_cleanup(const char *lastRestartPointFileName)
|
||||||
void
|
void
|
||||||
shell_recovery_end(const char *lastRestartPointFileName)
|
shell_recovery_end(const char *lastRestartPointFileName)
|
||||||
{
|
{
|
||||||
ExecuteRecoveryCommand(recoveryEndCommand, "recovery_end_command", true,
|
char *cmd;
|
||||||
WAIT_EVENT_RECOVERY_END_COMMAND,
|
|
||||||
lastRestartPointFileName);
|
cmd = replace_percent_placeholders(recoveryEndCommand,
|
||||||
|
"recovery_end_command",
|
||||||
|
"r", lastRestartPointFileName);
|
||||||
|
(void) ExecuteRecoveryCommand(cmd, "recovery_end_command", true, false,
|
||||||
|
WAIT_EVENT_RECOVERY_END_COMMAND, WARNING);
|
||||||
|
pfree(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -125,26 +123,21 @@ shell_recovery_end(const char *lastRestartPointFileName)
|
||||||
* 'command' is the shell command to be executed, 'commandName' is a
|
* 'command' is the shell command to be executed, 'commandName' is a
|
||||||
* human-readable name describing the command emitted in the logs. If
|
* human-readable name describing the command emitted in the logs. If
|
||||||
* 'failOnSignal' is true and the command is killed by a signal, a FATAL
|
* 'failOnSignal' is true and the command is killed by a signal, a FATAL
|
||||||
* error is thrown. Otherwise a WARNING is emitted.
|
* error is thrown. Otherwise, 'fail_elevel' is used for the log message.
|
||||||
|
* If 'exitOnSigterm' is true and the command is killed by SIGTERM, we exit
|
||||||
|
* immediately.
|
||||||
*
|
*
|
||||||
* This is currently used for recovery_end_command and archive_cleanup_command.
|
* Returns whether the command succeeded.
|
||||||
*/
|
*/
|
||||||
static void
|
static bool
|
||||||
ExecuteRecoveryCommand(const char *command, const char *commandName,
|
ExecuteRecoveryCommand(const char *command, const char *commandName,
|
||||||
bool failOnSignal, uint32 wait_event_info,
|
bool failOnSignal, bool exitOnSigterm,
|
||||||
const char *lastRestartPointFileName)
|
uint32 wait_event_info, int fail_elevel)
|
||||||
{
|
{
|
||||||
char *xlogRecoveryCmd;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
Assert(command && commandName);
|
Assert(command && commandName);
|
||||||
|
|
||||||
/*
|
|
||||||
* construct the command to be executed
|
|
||||||
*/
|
|
||||||
xlogRecoveryCmd = replace_percent_placeholders(command, commandName, "r",
|
|
||||||
lastRestartPointFileName);
|
|
||||||
|
|
||||||
ereport(DEBUG3,
|
ereport(DEBUG3,
|
||||||
(errmsg_internal("executing %s \"%s\"", commandName, command)));
|
(errmsg_internal("executing %s \"%s\"", commandName, command)));
|
||||||
|
|
||||||
|
@ -153,18 +146,19 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
|
||||||
*/
|
*/
|
||||||
fflush(NULL);
|
fflush(NULL);
|
||||||
pgstat_report_wait_start(wait_event_info);
|
pgstat_report_wait_start(wait_event_info);
|
||||||
rc = system(xlogRecoveryCmd);
|
rc = system(command);
|
||||||
pgstat_report_wait_end();
|
pgstat_report_wait_end();
|
||||||
|
|
||||||
pfree(xlogRecoveryCmd);
|
|
||||||
|
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
|
if (exitOnSigterm && wait_result_is_signal(rc, SIGTERM))
|
||||||
|
proc_exit(1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the failure was due to any sort of signal, it's best to punt and
|
* If the failure was due to any sort of signal, it's best to punt and
|
||||||
* abort recovery. See comments in shell_restore().
|
* abort recovery. See comments in shell_restore().
|
||||||
*/
|
*/
|
||||||
ereport((failOnSignal && wait_result_is_any_signal(rc, true)) ? FATAL : WARNING,
|
ereport((failOnSignal && wait_result_is_any_signal(rc, true)) ? FATAL : fail_elevel,
|
||||||
/*------
|
/*------
|
||||||
translator: First %s represents a postgresql.conf parameter name like
|
translator: First %s represents a postgresql.conf parameter name like
|
||||||
"recovery_end_command", the 2nd is the value of that parameter, the
|
"recovery_end_command", the 2nd is the value of that parameter, the
|
||||||
|
@ -172,4 +166,6 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
|
||||||
(errmsg("%s \"%s\": %s", commandName,
|
(errmsg("%s \"%s\": %s", commandName,
|
||||||
command, wait_result_to_str(rc))));
|
command, wait_result_to_str(rc))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (rc == 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "access/xlog_internal.h"
|
#include "access/xlog_internal.h"
|
||||||
#include "access/xlogarchive.h"
|
#include "access/xlogarchive.h"
|
||||||
#include "common/archive.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "postmaster/startup.h"
|
#include "postmaster/startup.h"
|
||||||
|
|
|
@ -46,7 +46,6 @@ LIBS += $(PTHREAD_LIBS)
|
||||||
# If you add objects here, see also src/tools/msvc/Mkvcbuild.pm
|
# If you add objects here, see also src/tools/msvc/Mkvcbuild.pm
|
||||||
|
|
||||||
OBJS_COMMON = \
|
OBJS_COMMON = \
|
||||||
archive.o \
|
|
||||||
base64.o \
|
base64.o \
|
||||||
checksum_helper.o \
|
checksum_helper.o \
|
||||||
compression.o \
|
compression.o \
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* archive.c
|
|
||||||
* Common WAL archive routines
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* src/common/archive.c
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FRONTEND
|
|
||||||
#include "postgres.h"
|
|
||||||
#else
|
|
||||||
#include "postgres_fe.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/archive.h"
|
|
||||||
#include "common/percentrepl.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BuildRestoreCommand
|
|
||||||
*
|
|
||||||
* Builds a restore command to retrieve a file from WAL archives, replacing
|
|
||||||
* the supported aliases with values supplied by the caller as defined by
|
|
||||||
* the GUC parameter restore_command: xlogpath for %p, xlogfname for %f and
|
|
||||||
* lastRestartPointFname for %r.
|
|
||||||
*
|
|
||||||
* The result is a palloc'd string for the restore command built. The
|
|
||||||
* caller is responsible for freeing it. If any of the required arguments
|
|
||||||
* is NULL and that the corresponding alias is found in the command given
|
|
||||||
* by the caller, then an error is thrown.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
BuildRestoreCommand(const char *restoreCommand,
|
|
||||||
const char *xlogpath,
|
|
||||||
const char *xlogfname,
|
|
||||||
const char *lastRestartPointFname)
|
|
||||||
{
|
|
||||||
char *nativePath = NULL;
|
|
||||||
char *result;
|
|
||||||
|
|
||||||
if (xlogpath)
|
|
||||||
{
|
|
||||||
nativePath = pstrdup(xlogpath);
|
|
||||||
make_native_path(nativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = replace_percent_placeholders(restoreCommand, "restore_command", "frp",
|
|
||||||
xlogfname, lastRestartPointFname, nativePath);
|
|
||||||
|
|
||||||
if (nativePath)
|
|
||||||
pfree(nativePath);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
|
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
|
||||||
|
|
||||||
common_sources = files(
|
common_sources = files(
|
||||||
'archive.c',
|
|
||||||
'base64.c',
|
'base64.c',
|
||||||
'checksum_helper.c',
|
'checksum_helper.c',
|
||||||
'compression.c',
|
'compression.c',
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "access/xlog_internal.h"
|
#include "access/xlog_internal.h"
|
||||||
#include "common/archive.h"
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/percentrepl.h"
|
||||||
#include "fe_utils/archive.h"
|
#include "fe_utils/archive.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,13 +41,18 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
|
||||||
{
|
{
|
||||||
char xlogpath[MAXPGPATH];
|
char xlogpath[MAXPGPATH];
|
||||||
char *xlogRestoreCmd;
|
char *xlogRestoreCmd;
|
||||||
|
char *nativePath;
|
||||||
int rc;
|
int rc;
|
||||||
struct stat stat_buf;
|
struct stat stat_buf;
|
||||||
|
|
||||||
snprintf(xlogpath, MAXPGPATH, "%s/" XLOGDIR "/%s", path, xlogfname);
|
snprintf(xlogpath, MAXPGPATH, "%s/" XLOGDIR "/%s", path, xlogfname);
|
||||||
|
|
||||||
xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
|
nativePath = pstrdup(xlogpath);
|
||||||
xlogfname, NULL);
|
make_native_path(nativePath);
|
||||||
|
xlogRestoreCmd = replace_percent_placeholders(restoreCommand,
|
||||||
|
"restore_command", "fp",
|
||||||
|
xlogfname, nativePath);
|
||||||
|
pfree(nativePath);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute restore_command, which should copy the missing file from
|
* Execute restore_command, which should copy the missing file from
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* archive.h
|
|
||||||
* Common WAL archive routines
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
|
||||||
*
|
|
||||||
* src/include/common/archive.h
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
#ifndef ARCHIVE_H
|
|
||||||
#define ARCHIVE_H
|
|
||||||
|
|
||||||
extern char *BuildRestoreCommand(const char *restoreCommand,
|
|
||||||
const char *xlogpath, /* %p */
|
|
||||||
const char *xlogfname, /* %f */
|
|
||||||
const char *lastRestartPointFname); /* %r */
|
|
||||||
|
|
||||||
#endif /* ARCHIVE_H */
|
|
|
@ -133,7 +133,7 @@ sub mkvcbuild
|
||||||
}
|
}
|
||||||
|
|
||||||
our @pgcommonallfiles = qw(
|
our @pgcommonallfiles = qw(
|
||||||
archive.c base64.c checksum_helper.c compression.c
|
base64.c checksum_helper.c compression.c
|
||||||
config_info.c controldata_utils.c d2s.c encnames.c exec.c
|
config_info.c controldata_utils.c d2s.c encnames.c exec.c
|
||||||
f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
|
f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
|
||||||
keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c
|
keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c
|
||||||
|
|
Loading…
Reference in New Issue