2018-01-17 15:52:00 -05:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Archive Push Command
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2018-04-29 10:16:59 -04:00
|
|
|
#include "command/archive/common.h"
|
2018-04-12 20:42:26 -04:00
|
|
|
#include "command/command.h"
|
|
|
|
#include "common/fork.h"
|
2018-01-17 15:52:00 -05:00
|
|
|
#include "common/log.h"
|
|
|
|
#include "common/memContext.h"
|
|
|
|
#include "common/wait.h"
|
|
|
|
#include "config/config.h"
|
2018-04-12 20:42:26 -04:00
|
|
|
#include "config/load.h"
|
2018-01-17 15:52:00 -05:00
|
|
|
#include "perl/exec.h"
|
|
|
|
#include "storage/helper.h"
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Push a WAL segment to the repository
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
void
|
|
|
|
cmdArchivePush()
|
|
|
|
{
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
|
|
|
{
|
|
|
|
// Make sure there is a parameter to retrieve the WAL segment from
|
|
|
|
const StringList *commandParam = cfgCommandParam();
|
|
|
|
|
|
|
|
if (strLstSize(commandParam) != 1)
|
|
|
|
THROW(ParamRequiredError, "WAL segment to push required");
|
|
|
|
|
|
|
|
// Get the segment name
|
|
|
|
String *walSegment = strBase(strLstGet(commandParam, 0));
|
|
|
|
|
|
|
|
if (cfgOptionBool(cfgOptArchiveAsync))
|
|
|
|
{
|
|
|
|
bool pushed = false; // Has the WAL segment been pushed yet?
|
2018-04-12 20:42:26 -04:00
|
|
|
bool forked = false; // Has the async process been forked yet?
|
2018-01-17 15:52:00 -05:00
|
|
|
bool confessOnError = false; // Should we confess errors?
|
2018-04-12 20:42:26 -04:00
|
|
|
bool server = false; // Is this the async server process?
|
2018-01-17 15:52:00 -05:00
|
|
|
|
|
|
|
// Loop and wait for the WAL segment to be pushed
|
|
|
|
Wait *wait = waitNew(cfgOptionDbl(cfgOptArchiveTimeout));
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Check if the WAL segment has been pushed. Errors will not be confessed on the first try to allow the async
|
|
|
|
// process a chance to fix them.
|
2018-04-30 17:27:39 -04:00
|
|
|
pushed = archiveAsyncStatus(archiveModePush, walSegment, confessOnError);
|
2018-01-17 15:52:00 -05:00
|
|
|
|
2018-04-12 20:42:26 -04:00
|
|
|
// If the WAL segment has not already been pushed then start the async process to push it. There's no point in
|
|
|
|
// forking the async process off more than once so track that as well. Use an archive lock to prevent more than
|
|
|
|
// one async process being launched.
|
|
|
|
if (!pushed && !forked &&
|
|
|
|
lockAcquire(cfgOptionStr(cfgOptLockPath), cfgOptionStr(cfgOptStanza), cfgLockType(), 0, false))
|
2018-01-17 15:52:00 -05:00
|
|
|
{
|
2018-04-12 20:42:26 -04:00
|
|
|
// Fork off the async process
|
|
|
|
if (fork() == 0)
|
2018-01-17 15:52:00 -05:00
|
|
|
{
|
2018-04-12 20:42:26 -04:00
|
|
|
// This is the server process
|
|
|
|
server = true;
|
|
|
|
|
|
|
|
// The async process should not output on the console at all
|
|
|
|
cfgOptionSet(cfgOptLogLevelConsole, cfgSourceParam, varNewStrZ("off"));
|
|
|
|
cfgOptionSet(cfgOptLogLevelStderr, cfgSourceParam, varNewStrZ("off"));
|
|
|
|
cfgLoadLogSetting();
|
|
|
|
|
|
|
|
// Open the log file
|
|
|
|
logFileSet(
|
|
|
|
strPtr(strNewFmt("%s/%s-%s-async.log", strPtr(cfgOptionStr(cfgOptLogPath)),
|
|
|
|
strPtr(cfgOptionStr(cfgOptStanza)), cfgCommandName(cfgCommand()))));
|
|
|
|
|
|
|
|
// Log command info since we are starting a new log
|
|
|
|
cmdBegin(true);
|
|
|
|
|
|
|
|
// Detach from parent process
|
|
|
|
forkDetach();
|
|
|
|
|
|
|
|
// Execute async process and catch exceptions
|
|
|
|
TRY_BEGIN()
|
|
|
|
{
|
|
|
|
perlExec();
|
|
|
|
}
|
|
|
|
CATCH_ANY()
|
|
|
|
{
|
|
|
|
RETHROW();
|
|
|
|
}
|
|
|
|
FINALLY()
|
|
|
|
{
|
|
|
|
// Release the lock (mostly here for testing since it would be freed in exitSafe() anyway)
|
|
|
|
lockRelease(true);
|
|
|
|
}
|
|
|
|
TRY_END();
|
2018-01-17 15:52:00 -05:00
|
|
|
}
|
2018-04-12 20:42:26 -04:00
|
|
|
// Else mark async process as forked
|
2018-01-17 15:52:00 -05:00
|
|
|
else
|
|
|
|
{
|
2018-04-12 20:42:26 -04:00
|
|
|
lockClear(true);
|
|
|
|
forked = true;
|
2018-01-17 15:52:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the async process has been launched, confess any errors that are found
|
|
|
|
confessOnError = true;
|
|
|
|
}
|
2018-04-12 20:42:26 -04:00
|
|
|
while (!server && !pushed && waitMore(wait));
|
2018-01-17 15:52:00 -05:00
|
|
|
|
2018-04-12 20:42:26 -04:00
|
|
|
// The aysnc server does not give notifications
|
|
|
|
if (!server)
|
2018-01-17 15:52:00 -05:00
|
|
|
{
|
2018-04-12 20:42:26 -04:00
|
|
|
// If the WAL segment was not pushed then error
|
|
|
|
if (!pushed)
|
|
|
|
{
|
2018-05-03 11:24:29 -04:00
|
|
|
THROW_FMT(
|
2018-04-12 20:42:26 -04:00
|
|
|
ArchiveTimeoutError, "unable to push WAL segment '%s' asynchronously after %lg second(s)",
|
|
|
|
strPtr(walSegment), cfgOptionDbl(cfgOptArchiveTimeout));
|
|
|
|
}
|
2018-01-17 15:52:00 -05:00
|
|
|
|
2018-04-12 20:42:26 -04:00
|
|
|
// Log success
|
|
|
|
LOG_INFO("pushed WAL segment %s asynchronously", strPtr(walSegment));
|
|
|
|
}
|
2018-01-17 15:52:00 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
THROW(AssertError, "archive-push in C does not support synchronous mode");
|
|
|
|
}
|
|
|
|
MEM_CONTEXT_TEMP_END();
|
|
|
|
}
|