1
0
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:
David Steele
2016-08-24 12:39:27 -04:00
parent cd6278e5af
commit d0b6f78b20
83 changed files with 7666 additions and 2542 deletions

View File

@@ -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;

View File

@@ -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);
}
####################################################################################################################################

View 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;

View File

@@ -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

View File

@@ -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}");
}

View File

@@ -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