You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-11-29 22:28:02 +02:00
More flexible configuration for databases
Master and standby can both be configured on the backup server and pgBackRest will automatically determine which is the master. This means no configuration changes for backup are required after failing over from a master to standby when a separate backup server is used.
This commit is contained in:
@@ -16,7 +16,6 @@ use lib dirname($0) . '/../lib';
|
||||
use pgBackRest::Common::Exception;
|
||||
use pgBackRest::Common::Ini;
|
||||
use pgBackRest::Common::Log;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::Protocol::IO;
|
||||
use pgBackRest::Version;
|
||||
|
||||
@@ -28,6 +27,16 @@ use constant OP_NOOP => 'noop';
|
||||
use constant OP_EXIT => 'exit';
|
||||
push @EXPORT, qw(OP_EXIT);
|
||||
|
||||
####################################################################################################################################
|
||||
# DB/BACKUP Constants
|
||||
####################################################################################################################################
|
||||
use constant DB => 'db';
|
||||
push @EXPORT, qw(DB);
|
||||
use constant BACKUP => 'backup';
|
||||
push @EXPORT, qw(BACKUP);
|
||||
use constant NONE => 'none';
|
||||
push @EXPORT, qw(NONE);
|
||||
|
||||
####################################################################################################################################
|
||||
# CONSTRUCTOR
|
||||
####################################################################################################################################
|
||||
@@ -58,6 +67,9 @@ sub new
|
||||
{name => 'strName', required => false, trace => true}
|
||||
);
|
||||
|
||||
# By default remote type is NONE
|
||||
$self->{strRemoteType} = NONE;
|
||||
|
||||
# Create the greeting that will be used to check versions with the remote
|
||||
if (defined($self->{strName}))
|
||||
{
|
||||
@@ -603,4 +615,37 @@ sub binaryXfer
|
||||
return $strChecksum, $iFileSize;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# remoteType
|
||||
#
|
||||
# Return the remote type.
|
||||
####################################################################################################################################
|
||||
sub remoteType
|
||||
{
|
||||
return shift->{strRemoteType};
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# remoteTypeTest
|
||||
#
|
||||
# Determine if the remote matches the specified remote.
|
||||
####################################################################################################################################
|
||||
sub remoteTypeTest
|
||||
{
|
||||
my $self = shift;
|
||||
my $strRemoteType = shift;
|
||||
|
||||
return $self->{strRemoteType} eq $strRemoteType ? true : false;
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# isRemote
|
||||
#
|
||||
# Determine if the protocol object is communicating with a remote.
|
||||
####################################################################################################################################
|
||||
sub isRemote
|
||||
{
|
||||
return shift->{strRemoteType} ne NONE ? true : false;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -16,7 +16,6 @@ use lib dirname($0) . '/../lib';
|
||||
use pgBackRest::Common::Exception;
|
||||
use pgBackRest::Common::Ini;
|
||||
use pgBackRest::Common::Log;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::Protocol::Common;
|
||||
use pgBackRest::Protocol::IO;
|
||||
|
||||
@@ -31,6 +30,7 @@ sub new
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strRemoteType, # Type of remote (DB or BACKUP)
|
||||
$strName, # Name of the protocol
|
||||
$strId, # Id of this process for error messages
|
||||
$strCommand, # Command to execute on local/remote
|
||||
@@ -42,6 +42,7 @@ sub new
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
{name => 'strRemoteType'},
|
||||
{name => 'strName'},
|
||||
{name => 'strId'},
|
||||
{name => 'strCommand'},
|
||||
@@ -55,6 +56,9 @@ sub new
|
||||
my $self = $class->SUPER::new($iBufferMax, $iCompressLevel, $iCompressLevelNetwork, $iProtocolTimeout, $strName);
|
||||
bless $self, $class;
|
||||
|
||||
# Set remote to specificied value
|
||||
$self->{strRemoteType} = $strRemoteType;
|
||||
|
||||
# Set command
|
||||
if (!defined($strCommand))
|
||||
{
|
||||
@@ -92,8 +96,12 @@ sub close
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my ($strOperation) = logDebugParam(__PACKAGE__ . '->close');
|
||||
|
||||
# Exit status defaults to success
|
||||
my $iExitStatus = 0;
|
||||
my $bClosed = false;
|
||||
|
||||
# Only send the exit command if the process is running
|
||||
if (defined($self->{io}) && defined($self->{io}->pIdGet()))
|
||||
@@ -131,9 +139,15 @@ sub close
|
||||
}
|
||||
|
||||
undef($self->{io});
|
||||
$bClosed = true;
|
||||
}
|
||||
|
||||
return $iExitStatus;
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'iExitStatus', value => $iExitStatus, trace => !$bClosed}
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
@@ -183,13 +197,15 @@ sub outputRead
|
||||
(
|
||||
$strOperation,
|
||||
$bOutputRequired,
|
||||
$bSuppressLog
|
||||
$bSuppressLog,
|
||||
$bWarnOnError,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->outputRead', \@_,
|
||||
{name => 'bOutputRequired', default => false, trace => true},
|
||||
{name => 'bSuppressLog', required => false, trace => true}
|
||||
{name => 'bSuppressLog', required => false, trace => true},
|
||||
{name => 'bWarnOnError', default => false, trace => true},
|
||||
);
|
||||
|
||||
my $strLine;
|
||||
@@ -222,18 +238,26 @@ sub outputRead
|
||||
# Raise any errors
|
||||
if ($bError)
|
||||
{
|
||||
confess &log(ERROR, (defined($self->{strErrorPrefix}) ? "$self->{strErrorPrefix}: " : '') .
|
||||
(defined($strOutput) ? "${strOutput}" : ''), $iErrorCode, $bSuppressLog);
|
||||
my $strError = $self->{strErrorPrefix} . (defined($strOutput) ? ": ${strOutput}" : '');
|
||||
|
||||
# Raise the error if a warning is not requested
|
||||
if (!$bWarnOnError)
|
||||
{
|
||||
confess &log(ERROR, $strError, $iErrorCode, $bSuppressLog);
|
||||
}
|
||||
|
||||
&log(WARN, $strError, $iErrorCode);
|
||||
undef($strOutput);
|
||||
}
|
||||
|
||||
# Reset the keep alive time
|
||||
$self->{fKeepAliveTime} = gettimeofday();
|
||||
|
||||
# If output is required and there is no output, raise exception
|
||||
if (defined($bOutputRequired) && $bOutputRequired && !defined($strOutput))
|
||||
if ($bOutputRequired && !defined($strOutput))
|
||||
{
|
||||
$self->{io}->waitPid();
|
||||
confess &log(ERROR, (defined($self->{strErrorPrefix}) ? "$self->{strErrorPrefix}: " : '') . 'output is not defined');
|
||||
confess &log(ERROR, "$self->{strErrorPrefix}: output is not defined", ERROR_PROTOCOL_OUTPUT_REQUIRED);
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
@@ -327,6 +351,9 @@ sub cmdWrite
|
||||
# Write out the command
|
||||
$self->{io}->lineWrite($strCommand);
|
||||
|
||||
# Reset the keep alive time
|
||||
$self->{fKeepAliveTime} = gettimeofday();
|
||||
|
||||
# Return from function and log return values if any
|
||||
logDebugReturn($strOperation);
|
||||
}
|
||||
@@ -342,10 +369,11 @@ sub cmdExecute
|
||||
my $strCommand = shift;
|
||||
my $oParamRef = shift;
|
||||
my $bOutputRequired = shift;
|
||||
my $bWarnOnError = shift;
|
||||
|
||||
$self->cmdWrite($strCommand, $oParamRef);
|
||||
|
||||
return $self->outputRead($bOutputRequired);
|
||||
return $self->outputRead($bOutputRequired, undef, $bWarnOnError);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
|
||||
258
lib/pgBackRest/Protocol/Protocol.pm
Normal file
258
lib/pgBackRest/Protocol/Protocol.pm
Normal file
@@ -0,0 +1,258 @@
|
||||
####################################################################################################################################
|
||||
# PROTOCOL MODULE
|
||||
####################################################################################################################################
|
||||
package pgBackRest::Protocol::Protocol;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
|
||||
# use Cwd qw(abs_path);
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
# use File::Basename qw(dirname basename);
|
||||
# use Getopt::Long qw(GetOptions);
|
||||
# use Storable qw(dclone);
|
||||
|
||||
# use pgBackRest::Common::Exception;
|
||||
# use pgBackRest::Common::Ini;
|
||||
use pgBackRest::Common::Log;
|
||||
# use pgBackRest::Common::Wait;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::Protocol::Common;
|
||||
use pgBackRest::Protocol::RemoteMaster;
|
||||
# use pgBackRest::Version;
|
||||
|
||||
####################################################################################################################################
|
||||
# Module variables
|
||||
####################################################################################################################################
|
||||
my $hProtocol = {}; # Global remote hash that is created on first request (NOT THREADSAFE!)
|
||||
|
||||
####################################################################################################################################
|
||||
# isRepoLocal
|
||||
#
|
||||
# Is the backup/archive repository local? This does not take into account the spool path.
|
||||
####################################################################################################################################
|
||||
sub isRepoLocal
|
||||
{
|
||||
# Not valid for remote
|
||||
if (commandTest(CMD_REMOTE))
|
||||
{
|
||||
confess &log(ASSERT, 'isRepoLocal() not valid on remote');
|
||||
}
|
||||
|
||||
return optionTest(OPTION_BACKUP_HOST) ? false : true;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(isRepoLocal);
|
||||
|
||||
####################################################################################################################################
|
||||
# isDbLocal
|
||||
#
|
||||
# Is the database local?
|
||||
####################################################################################################################################
|
||||
sub isDbLocal
|
||||
{
|
||||
# Not valid for remote
|
||||
if (commandTest(CMD_REMOTE))
|
||||
{
|
||||
confess &log(ASSERT, 'isDbLocal() not valid on remote');
|
||||
}
|
||||
|
||||
return optionTest(OPTION_DB_HOST) ? false : true;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(isDbLocal);
|
||||
|
||||
####################################################################################################################################
|
||||
# protocolGet
|
||||
#
|
||||
# Get the protocol object or create it if does not exist. Shared protocol objects are used because they create an SSH connection
|
||||
# to the remote host and the number of these connections should be minimized. A protocol object can be shared within a single
|
||||
# thread - for new threads clone() should be called on the shared protocol object.
|
||||
####################################################################################################################################
|
||||
sub protocolGet
|
||||
{
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strRemoteType,
|
||||
$iRemoteIdx,
|
||||
$oParam,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '::protocolGet', \@_,
|
||||
{name => 'strRemoteType'},
|
||||
{name => 'iRemoteIdx', default => 1},
|
||||
{name => 'oParam', required => false},
|
||||
);
|
||||
|
||||
# Protocol object
|
||||
my $oProtocol;
|
||||
|
||||
# If no remote requested or if the requested remote type is local then return a local protocol object
|
||||
my $strRemoteHost = $strRemoteType eq NONE ? undef : optionIndex("${strRemoteType}-host", $iRemoteIdx);
|
||||
|
||||
if ($strRemoteType eq NONE || !optionTest($strRemoteHost))
|
||||
{
|
||||
logDebugMisc($strOperation, 'create local protocol');
|
||||
|
||||
$oProtocol = new pgBackRest::Protocol::Common
|
||||
(
|
||||
optionGet(OPTION_BUFFER_SIZE),
|
||||
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
|
||||
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
|
||||
optionGet(OPTION_PROTOCOL_TIMEOUT)
|
||||
);
|
||||
}
|
||||
# Else create the remote protocol
|
||||
else
|
||||
{
|
||||
my $bCache = defined($$oParam{bCache}) ? $$oParam{bCache} : true;
|
||||
|
||||
# Set protocol to cached value
|
||||
$oProtocol =
|
||||
$bCache && defined($$hProtocol{$strRemoteType}{$iRemoteIdx}) ? $$hProtocol{$strRemoteType}{$iRemoteIdx} : undef;
|
||||
|
||||
if ($bCache && $$hProtocol{$strRemoteType}{$iRemoteIdx})
|
||||
{
|
||||
$oProtocol = $$hProtocol{$strRemoteType}{$iRemoteIdx};
|
||||
logDebugMisc($strOperation, 'found cached protocol');
|
||||
}
|
||||
|
||||
# If protocol was not returned from cache then create it
|
||||
if (!defined($oProtocol))
|
||||
{
|
||||
logDebugMisc($strOperation, 'create (' . ($bCache ? '' : 'un') . 'cached) remote protocol');
|
||||
|
||||
# Return the remote when required
|
||||
my $strOptionCmd = OPTION_BACKUP_CMD;
|
||||
my $strOptionConfig = OPTION_BACKUP_CONFIG;
|
||||
my $strOptionHost = OPTION_BACKUP_HOST;
|
||||
my $strOptionUser = OPTION_BACKUP_USER;
|
||||
my $strOptionDbSocketPath = undef;
|
||||
|
||||
if ($strRemoteType eq DB)
|
||||
{
|
||||
$strOptionCmd = optionIndex(OPTION_DB_CMD, $iRemoteIdx);
|
||||
$strOptionConfig = optionIndex(OPTION_DB_CONFIG, $iRemoteIdx);
|
||||
$strOptionHost = optionIndex(OPTION_DB_HOST, $iRemoteIdx);
|
||||
$strOptionUser = optionIndex(OPTION_DB_USER, $iRemoteIdx);
|
||||
|
||||
}
|
||||
|
||||
# Db socket is not valid in all contexts (restore, for instance)
|
||||
if (optionValid(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx)))
|
||||
{
|
||||
$strOptionDbSocketPath =
|
||||
optionSource(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx)) eq SOURCE_DEFAULT ?
|
||||
undef : optionGet(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx));
|
||||
}
|
||||
|
||||
$oProtocol = new pgBackRest::Protocol::RemoteMaster
|
||||
(
|
||||
$strRemoteType,
|
||||
commandWrite(
|
||||
CMD_REMOTE, true, optionGet($strOptionCmd), undef,
|
||||
{
|
||||
&OPTION_COMMAND => {value => commandGet()},
|
||||
&OPTION_PROCESS => {value => $$oParam{iProcessIdx}},
|
||||
&OPTION_CONFIG => {
|
||||
value => optionSource($strOptionConfig) eq SOURCE_DEFAULT ? undef : optionGet($strOptionConfig)},
|
||||
&OPTION_TYPE => {value => $strRemoteType},
|
||||
&OPTION_LOG_PATH => {},
|
||||
&OPTION_LOCK_PATH => {},
|
||||
&OPTION_DB_SOCKET_PATH => {value => $strOptionDbSocketPath},
|
||||
}),
|
||||
optionGet(OPTION_BUFFER_SIZE),
|
||||
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
|
||||
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
|
||||
optionGet($strOptionHost),
|
||||
optionGet($strOptionUser),
|
||||
optionGet(OPTION_PROTOCOL_TIMEOUT)
|
||||
);
|
||||
|
||||
# Cache the protocol
|
||||
if ($bCache)
|
||||
{
|
||||
$$hProtocol{$strRemoteType}{$iRemoteIdx} = $oProtocol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'oProtocol', value => $oProtocol, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(protocolGet);
|
||||
|
||||
####################################################################################################################################
|
||||
# protocolDestroy
|
||||
#
|
||||
# Undefine the protocol if it is stored locally.
|
||||
####################################################################################################################################
|
||||
sub protocolDestroy
|
||||
{
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strRemoteType,
|
||||
$iRemoteIdx,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '::protocolDestroy', \@_,
|
||||
{name => 'strRemoteType', required => false},
|
||||
{name => 'iRemoteIdx', required => false},
|
||||
);
|
||||
|
||||
my $iExitStatus = 0;
|
||||
|
||||
if (defined($strRemoteType))
|
||||
{
|
||||
$iRemoteIdx = defined($iRemoteIdx) ? $iRemoteIdx : 1;
|
||||
|
||||
if (defined($$hProtocol{$strRemoteType}{$iRemoteIdx}))
|
||||
{
|
||||
$iExitStatus = ($$hProtocol{$strRemoteType}{$iRemoteIdx})->close();
|
||||
delete($$hProtocol{$strRemoteType}{$iRemoteIdx});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach my $strRemoteType (sort(keys(%{$hProtocol})))
|
||||
{
|
||||
foreach my $iRemoteIdx (sort(keys(%{$$hProtocol{$strRemoteType}})))
|
||||
{
|
||||
if (defined($$hProtocol{$strRemoteType}{$iRemoteIdx}))
|
||||
{
|
||||
logDebugMisc(
|
||||
$strOperation, 'found cached protocol',
|
||||
{name => 'strRemoteType', value => $strRemoteType},
|
||||
{name => 'iRemoteIdx', value => $iRemoteIdx});
|
||||
|
||||
$iExitStatus = ($$hProtocol{$strRemoteType}{$iRemoteIdx})->close();
|
||||
delete($$hProtocol{$strRemoteType}{$iRemoteIdx});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'iExitStatus', value => $iExitStatus}
|
||||
);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(protocolDestroy);
|
||||
|
||||
1;
|
||||
@@ -26,6 +26,7 @@ sub new
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strRemoteType, # Type of remote (DB or BACKUP)
|
||||
$strCommand, # Command to execute on local/remote
|
||||
$iBufferMax, # Maximum buffer size
|
||||
$iCompressLevel, # Set compression level
|
||||
@@ -37,6 +38,7 @@ sub new
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
{name => 'strRemoteType'},
|
||||
{name => 'strCommand'},
|
||||
{name => 'iBufferMax'},
|
||||
{name => 'iCompressLevel'},
|
||||
@@ -51,7 +53,7 @@ sub new
|
||||
|
||||
# Init object and store variables
|
||||
my $self = $class->SUPER::new(
|
||||
'remote', $strHost, $strCommand, $iBufferMax, $iCompressLevel, $iCompressLevelNetwork, $iProtocolTimeout);
|
||||
$strRemoteType, 'remote', $strHost, $strCommand, $iBufferMax, $iCompressLevel, $iCompressLevelNetwork, $iProtocolTimeout);
|
||||
bless $self, $class;
|
||||
|
||||
# Store the host
|
||||
|
||||
@@ -94,8 +94,7 @@ sub process
|
||||
(
|
||||
optionGet(OPTION_STANZA, false),
|
||||
optionGet(OPTION_REPO_PATH, false),
|
||||
undef,
|
||||
$self,
|
||||
$self
|
||||
);
|
||||
|
||||
# Create objects
|
||||
@@ -264,7 +263,7 @@ sub process
|
||||
elsif ($strCommand eq OP_DB_INFO)
|
||||
{
|
||||
my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) =
|
||||
$oDb->info($oFile, paramGet(\%oParamHash, 'db-path'));
|
||||
$oDb->info(paramGet(\%oParamHash, 'db-path'));
|
||||
|
||||
$self->outputWrite("${strDbVersion}\t${iControlVersion}\t${iCatalogVersion}\t${ullDbSysId}");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use pgBackRest::Common::Log;
|
||||
use pgBackRest::Common::Wait;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::BackupFile;
|
||||
use pgBackRest::Protocol::Protocol;
|
||||
use pgBackRest::RestoreFile;
|
||||
|
||||
####################################################################################################################################
|
||||
@@ -85,14 +86,14 @@ sub threadGroupThread
|
||||
eval
|
||||
{
|
||||
# Get the protocol object
|
||||
my $oProtocol = protocolGet(undef, false, $iThreadIdx + 1);
|
||||
my $oProtocol = protocolGet(
|
||||
$$oCommand{param}{remote_type}, $$oCommand{param}{remote_index}, {bCache => false, iProcessIdx => $iThreadIdx + 1});
|
||||
|
||||
# Create a file object
|
||||
$oFile = new pgBackRest::File
|
||||
(
|
||||
optionGet(OPTION_STANZA),
|
||||
optionGet(OPTION_REPO_PATH),
|
||||
optionRemoteType(),
|
||||
$oProtocol,
|
||||
undef, undef,
|
||||
$iThreadIdx + 1
|
||||
|
||||
Reference in New Issue
Block a user