mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-12 10:04:14 +02:00
Optimize WAL segment check after successful backup.
The prior code did one list command against the storage for each WAL segment. This led to a lot of lists and was especially inefficient when the WAL (or the majority of it) was already present. Optimize to keep the contents of a WAL directory and use them on a subsequent search. Leave the optimizations for a single WAL segment since other places still use that mode.
This commit is contained in:
parent
edbd520c81
commit
9d3a605900
@ -38,6 +38,17 @@
|
||||
<p>Retry reads of <file>pg_control</file> until checksum is valid.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-pull-request id="2169"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="stephen.frost"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Optimize WAL segment check after successful backup.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-pull-request id="2162"/>
|
||||
|
||||
|
@ -52,6 +52,7 @@ SRCS_COMMON = \
|
||||
SRCS = \
|
||||
command/annotate/annotate.c \
|
||||
command/archive/common.c \
|
||||
command/archive/find.c \
|
||||
command/archive/get/file.c \
|
||||
command/archive/get/get.c \
|
||||
command/archive/get/protocol.c \
|
||||
|
@ -20,7 +20,6 @@ Archive Common
|
||||
#include "postgres/interface.h"
|
||||
#include "postgres/version.h"
|
||||
#include "storage/helper.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
WAL segment constants
|
||||
@ -405,77 +404,6 @@ walIsSegment(const String *walSegment)
|
||||
FUNCTION_LOG_RETURN(BOOL, regExpMatch(regExpSegment, walSegment));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
walSegmentFind(const Storage *storage, const String *archiveId, const String *walSegment, TimeMSec timeout)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STORAGE, storage);
|
||||
FUNCTION_LOG_PARAM(STRING, archiveId);
|
||||
FUNCTION_LOG_PARAM(STRING, walSegment);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(storage != NULL);
|
||||
ASSERT(archiveId != NULL);
|
||||
ASSERT(walSegment != NULL);
|
||||
ASSERT(walIsSegment(walSegment));
|
||||
|
||||
String *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
Wait *wait = waitNew(timeout);
|
||||
|
||||
do
|
||||
{
|
||||
// Get a list of all WAL segments that match
|
||||
StringList *list = storageListP(
|
||||
storage, strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strZ(archiveId), strZ(strSubN(walSegment, 0, 16))),
|
||||
.expression = strNewFmt(
|
||||
"^%s%s-[0-f]{40}" COMPRESS_TYPE_REGEXP "{0,1}$", strZ(strSubN(walSegment, 0, 24)),
|
||||
walIsPartial(walSegment) ? WAL_SEGMENT_PARTIAL_EXT : ""),
|
||||
.nullOnMissing = true);
|
||||
|
||||
// If there are results
|
||||
if (list != NULL && !strLstEmpty(list))
|
||||
{
|
||||
// Error if there is more than one match
|
||||
if (strLstSize(list) > 1)
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveDuplicateError,
|
||||
"duplicates found in archive for WAL segment %s: %s\n"
|
||||
"HINT: are multiple primaries archiving to this stanza?",
|
||||
strZ(walSegment), strZ(strLstJoin(strLstSort(list, sortOrderAsc), ", ")));
|
||||
}
|
||||
|
||||
// Copy file name of WAL segment found into the prior context
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = strDup(strLstGet(list, 0));
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
}
|
||||
}
|
||||
while (result == NULL && waitMore(wait));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
if (result == NULL && timeout != 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveTimeoutError,
|
||||
"WAL segment %s was not archived before the %" PRIu64 "ms timeout\n"
|
||||
"HINT: check the archive_command to ensure that all options are correct (especially --stanza).\n"
|
||||
"HINT: check the PostgreSQL server log for errors.\n"
|
||||
"HINT: run the 'start' command if the stanza was previously stopped.",
|
||||
strZ(walSegment), timeout);
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
walSegmentNext(const String *walSegment, size_t walSegmentSize, unsigned int pgVersion)
|
||||
|
@ -94,11 +94,6 @@ FN_EXTERN bool walIsSegment(const String *walSegment);
|
||||
// Generates the location of the wal directory using a relative wal path and the supplied pg path
|
||||
FN_EXTERN String *walPath(const String *walFile, const String *pgPath, const String *command);
|
||||
|
||||
// Find a WAL segment in the repository. The file name can have several things appended such as a hash, compression extension, and
|
||||
// partial extension so it is possible to have multiple files that match the segment, though more than one match is not a good
|
||||
// thing.
|
||||
FN_EXTERN String *walSegmentFind(const Storage *storage, const String *archiveId, const String *walSegment, TimeMSec timeout);
|
||||
|
||||
// Get the next WAL segment given a WAL segment and WAL segment size
|
||||
FN_EXTERN String *walSegmentNext(const String *walSegment, size_t walSegmentSize, unsigned int pgVersion);
|
||||
|
||||
|
230
src/command/archive/find.c
Normal file
230
src/command/archive/find.c
Normal file
@ -0,0 +1,230 @@
|
||||
/***********************************************************************************************************************************
|
||||
Archive Segment Find
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "command/archive/common.h"
|
||||
#include "command/archive/find.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/regExp.h"
|
||||
#include "common/wait.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
struct WalSegmentFind
|
||||
{
|
||||
const Storage *storage; // Storage to find WAL in
|
||||
const String *archiveId; // Archive id to find segments in
|
||||
bool single; // Optimize for a single segment?
|
||||
TimeMSec timeout; // Timeout for each segment
|
||||
String *prefix; // Current list prefix
|
||||
StringList *list; // List of found segments
|
||||
};
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
#define FUNCTION_LOG_WAL_SEGMENT_FIND_TYPE \
|
||||
WalSegmentFind *
|
||||
#define FUNCTION_LOG_WAL_SEGMENT_FIND_FORMAT(value, buffer, bufferSize) \
|
||||
objNameToLog(value, "WalSegmentFind", buffer, bufferSize)
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN WalSegmentFind *
|
||||
walSegmentFindNew(const Storage *const storage, const String *const archiveId, const bool single, const TimeMSec timeout)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STORAGE, storage);
|
||||
FUNCTION_LOG_PARAM(STRING, archiveId);
|
||||
FUNCTION_LOG_PARAM(BOOL, single);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(storage != NULL);
|
||||
ASSERT(archiveId != NULL);
|
||||
|
||||
OBJ_NEW_BEGIN(WalSegmentFind, .childQty = MEM_CONTEXT_QTY_MAX)
|
||||
{
|
||||
*this = (WalSegmentFind)
|
||||
{
|
||||
.storage = storage,
|
||||
.archiveId = strDup(archiveId),
|
||||
.single = single,
|
||||
.timeout = timeout,
|
||||
};
|
||||
}
|
||||
OBJ_NEW_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(WAL_SEGMENT_FIND, this);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
walSegmentFind(WalSegmentFind *const this, const String *const walSegment)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(WAL_SEGMENT_FIND, this);
|
||||
FUNCTION_LOG_PARAM(STRING, walSegment);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(walSegment != NULL);
|
||||
ASSERT(walIsSegment(walSegment));
|
||||
|
||||
String *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
const String *const prefix = strSubN(walSegment, 0, 16);
|
||||
const String *const path = strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strZ(this->archiveId), strZ(prefix));
|
||||
const String *const expression = strNewFmt(
|
||||
"^%s%s-[0-f]{40}" COMPRESS_TYPE_REGEXP "{0,1}$", strZ(strSubN(walSegment, 0, 24)),
|
||||
walIsPartial(walSegment) ? WAL_SEGMENT_PARTIAL_EXT : "");
|
||||
RegExp *regExp = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
// Get a list of all WAL segments that match the directory (and prefix when finding a single WAL)
|
||||
if (this->list == NULL || !strEq(prefix, this->prefix))
|
||||
{
|
||||
MEM_CONTEXT_OBJ_BEGIN(this)
|
||||
{
|
||||
// Free and store prefix
|
||||
if (!strEq(prefix, this->prefix))
|
||||
{
|
||||
strFree(this->prefix);
|
||||
this->prefix = strDup(prefix);
|
||||
}
|
||||
|
||||
// Free list
|
||||
strLstFree(this->list);
|
||||
|
||||
// Get list
|
||||
this->list = strLstSort(
|
||||
storageListP(this->storage, path, .expression = this->single ? expression : NULL), sortOrderAsc);
|
||||
}
|
||||
MEM_CONTEXT_OBJ_END();
|
||||
}
|
||||
|
||||
// If there are results
|
||||
if (!strLstEmpty(this->list))
|
||||
{
|
||||
// By default the match size is the list size since filtering happened above. When not finding a single WAL then
|
||||
// non-matching entries before the matching WAL will need to be removed and then the matching WAL counted.
|
||||
unsigned int match = strLstSize(this->list);
|
||||
|
||||
if (!this->single)
|
||||
{
|
||||
// Build regexp if not yet built
|
||||
if (regExp == NULL)
|
||||
regExp = regExpNew(expression);
|
||||
|
||||
// Remove list items that do not match. This prevents us from having check them again on the next find.
|
||||
while (!strLstEmpty(this->list) && !regExpMatch(regExp, strLstGet(this->list, 0)))
|
||||
strLstRemoveIdx(this->list, 0);
|
||||
|
||||
// Find matches at the beginning of the remaining list
|
||||
match = 0;
|
||||
|
||||
while (match < strLstSize(this->list) && regExpMatch(regExp, strLstGet(this->list, match)))
|
||||
match++;
|
||||
}
|
||||
|
||||
// Error if there is more than one match
|
||||
if (match > 1)
|
||||
{
|
||||
// Build list of duplicate WAL
|
||||
StringList *const matchList = strLstNew();
|
||||
|
||||
for (unsigned int matchIdx = 0; matchIdx < match; matchIdx++)
|
||||
strLstAdd(matchList, strLstGet(this->list, matchIdx));
|
||||
|
||||
// Clear list for next find
|
||||
strLstFree(this->list);
|
||||
this->list = NULL;
|
||||
|
||||
THROW_FMT(
|
||||
ArchiveDuplicateError,
|
||||
"duplicates found in archive for WAL segment %s: %s\n"
|
||||
"HINT: are multiple primaries archiving to this stanza?",
|
||||
strZ(walSegment), strZ(strLstJoin(matchList, ", ")));
|
||||
}
|
||||
|
||||
// On match copy file name of WAL segment found into the prior context
|
||||
if (match == 1)
|
||||
{
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = strDup(strLstGet(this->list, 0));
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
}
|
||||
|
||||
// Remove matching entries so list will be reloaded when empty
|
||||
if (!this->single)
|
||||
{
|
||||
ASSERT(regExp != NULL);
|
||||
|
||||
while (!strLstEmpty(this->list) && regExpMatch(regExp, strLstGet(this->list, 0)))
|
||||
strLstRemoveIdx(this->list, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear list for next find
|
||||
if (this->single || strLstEmpty(this->list))
|
||||
{
|
||||
strLstFree(this->list);
|
||||
this->list = NULL;
|
||||
}
|
||||
}
|
||||
while (result == NULL && waitMore(wait));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Error if segment not found before timeout
|
||||
if (result == NULL && this->timeout != 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
ArchiveTimeoutError,
|
||||
"WAL segment %s was not archived before the %" PRIu64 "ms timeout\n"
|
||||
"HINT: check the archive_command to ensure that all options are correct (especially --stanza).\n"
|
||||
"HINT: check the PostgreSQL server log for errors.\n"
|
||||
"HINT: run the 'start' command if the stanza was previously stopped.",
|
||||
strZ(walSegment), this->timeout);
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
walSegmentFindOne(
|
||||
const Storage *const storage, const String *const archiveId, const String *const walSegment, const TimeMSec timeout)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STORAGE, storage);
|
||||
FUNCTION_LOG_PARAM(STRING, archiveId);
|
||||
FUNCTION_LOG_PARAM(STRING, walSegment);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
String *result = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
WalSegmentFind *const find = walSegmentFindNew(storage, archiveId, true, timeout);
|
||||
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = walSegmentFind(find, walSegment);
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
}
|
36
src/command/archive/find.h
Normal file
36
src/command/archive/find.h
Normal file
@ -0,0 +1,36 @@
|
||||
/***********************************************************************************************************************************
|
||||
Archive Segment Find
|
||||
|
||||
Find a WAL segment (or segments) in a repository. The code paths for finding single or multiple WAL segments are both optimized.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_ARCHIVE_FIND_H
|
||||
#define COMMAND_ARCHIVE_FIND_H
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct WalSegmentFind WalSegmentFind;
|
||||
|
||||
#include "common/type/string.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN WalSegmentFind *walSegmentFindNew(const Storage *storage, const String *archiveId, bool single, TimeMSec timeout);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Find a WAL segment in the repository. The file name can have several things appended such as a hash, compression extension, and
|
||||
// partial extension so it is possible to have multiple files that match the segment, though more than one match is not a good
|
||||
// thing.
|
||||
FN_EXTERN String *walSegmentFind(WalSegmentFind *this, const String *walSegment);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Helper functions
|
||||
***********************************************************************************************************************************/
|
||||
// Find a single WAL segment (see walSegmentFind() for details)
|
||||
FN_EXTERN String *walSegmentFindOne(const Storage *storage, const String *archiveId, const String *walSegment, TimeMSec timeout);
|
||||
|
||||
#endif
|
@ -4,6 +4,7 @@ Archive Push File
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "command/archive/common.h"
|
||||
#include "command/archive/find.h"
|
||||
#include "command/archive/push/file.h"
|
||||
#include "command/control/common.h"
|
||||
#include "common/crypto/cipherBlock.h"
|
||||
@ -175,7 +176,7 @@ archivePushFile(
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
walSegmentFile = walSegmentFind(storageRepoIdx(repoData->repoIdx), repoData->archiveId, archiveFile, 0);
|
||||
walSegmentFile = walSegmentFindOne(storageRepoIdx(repoData->repoIdx), repoData->archiveId, archiveFile, 0);
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ Backup Command
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "command/archive/common.h"
|
||||
#include "command/archive/find.h"
|
||||
#include "command/backup/backup.h"
|
||||
#include "command/backup/common.h"
|
||||
#include "command/backup/file.h"
|
||||
@ -1118,7 +1118,7 @@ backupStart(BackupData *const backupData)
|
||||
strEq(result.walSegmentName, dbBackupStartResult.walSegmentCheck) ? "" : "prior ",
|
||||
strZ(dbBackupStartResult.walSegmentCheck));
|
||||
|
||||
walSegmentFind(
|
||||
walSegmentFindOne(
|
||||
storageRepo(), backupData->archiveId, dbBackupStartResult.walSegmentCheck,
|
||||
cfgOptionUInt64(cfgOptArchiveTimeout));
|
||||
}
|
||||
@ -2338,16 +2338,16 @@ backupArchiveCheckCopy(const BackupData *const backupData, Manifest *const manif
|
||||
// Loop through all the segments in the lsn range
|
||||
const StringList *const walSegmentList = pgLsnRangeToWalSegmentList(
|
||||
backupData->timeline, lsnStart, lsnStop, backupData->walSegmentSize);
|
||||
WalSegmentFind *const find = walSegmentFindNew(
|
||||
storageRepo(), backupData->archiveId, strLstSize(walSegmentList) == 1, cfgOptionUInt64(cfgOptArchiveTimeout));
|
||||
|
||||
for (unsigned int walSegmentIdx = 0; walSegmentIdx < strLstSize(walSegmentList); walSegmentIdx++)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Find the actual wal segment file (including checksum compression extension) in the archive
|
||||
const String *const walSegment = strLstGet(walSegmentList, walSegmentIdx);
|
||||
|
||||
// Find the actual wal segment file in the archive
|
||||
const String *const archiveFile = walSegmentFind(
|
||||
storageRepo(), backupData->archiveId, walSegment, cfgOptionUInt64(cfgOptArchiveTimeout));
|
||||
const String *const archiveFile = walSegmentFind(find, walSegment);
|
||||
|
||||
if (cfgOptionBool(cfgOptArchiveCopy))
|
||||
{
|
||||
|
@ -3,7 +3,7 @@ Check Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "command/archive/common.h"
|
||||
#include "command/archive/find.h"
|
||||
#include "command/check/check.h"
|
||||
#include "command/check/common.h"
|
||||
#include "common/debug.h"
|
||||
@ -151,7 +151,7 @@ checkPrimary(const DbGetResult dbGroup)
|
||||
{
|
||||
LOG_INFO_FMT(CFGCMD_CHECK " %s archive for WAL (primary)", cfgOptionGroupName(cfgOptGrpRepo, repoIdx));
|
||||
|
||||
const String *const walSegmentFile = walSegmentFind(
|
||||
const String *const walSegmentFile = walSegmentFindOne(
|
||||
storageRepoIdx(repoIdx), repoArchiveId[repoIdx], walSegment, cfgOptionUInt64(cfgOptArchiveTimeout));
|
||||
|
||||
LOG_INFO_FMT(
|
||||
|
@ -116,6 +116,7 @@ subdir('postgres')
|
||||
src_pgbackrest = [
|
||||
'command/annotate/annotate.c',
|
||||
'command/archive/common.c',
|
||||
'command/archive/find.c',
|
||||
'command/archive/get/file.c',
|
||||
'command/archive/get/get.c',
|
||||
'command/archive/get/protocol.c',
|
||||
|
@ -795,6 +795,7 @@ unit:
|
||||
|
||||
coverage:
|
||||
- command/archive/common
|
||||
- command/archive/find
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: archive-get
|
||||
|
@ -236,13 +236,13 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("no path or segment");
|
||||
|
||||
TEST_RESULT_STR(walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0), NULL, "no path");
|
||||
TEST_RESULT_STR(walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0), NULL, "no path");
|
||||
|
||||
HRN_STORAGE_PATH_CREATE(storageTest, "archive/db/9.6-2/1234567812345678");
|
||||
TEST_RESULT_STR(
|
||||
walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0), NULL, "no segment");
|
||||
walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0), NULL, "no segment");
|
||||
TEST_ERROR(
|
||||
walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 100), ArchiveTimeoutError,
|
||||
walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 100), ArchiveTimeoutError,
|
||||
"WAL segment 123456781234567812345678 was not archived before the 100ms timeout\n"
|
||||
"HINT: check the archive_command to ensure that all options are correct (especially --stanza).\n"
|
||||
"HINT: check the PostgreSQL server log for errors.\n"
|
||||
@ -267,7 +267,7 @@ testRun(void)
|
||||
HRN_FORK_PARENT_BEGIN()
|
||||
{
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 1000),
|
||||
walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 1000),
|
||||
"123456781234567812345678-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "found segment");
|
||||
}
|
||||
HRN_FORK_PARENT_END();
|
||||
@ -281,19 +281,99 @@ testRun(void)
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/123456781234567812345678-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz");
|
||||
|
||||
TEST_ERROR(
|
||||
walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0),
|
||||
walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678"), 0),
|
||||
ArchiveDuplicateError,
|
||||
"duplicates found in archive for WAL segment 123456781234567812345678:"
|
||||
" 123456781234567812345678-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
", 123456781234567812345678-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz\n"
|
||||
"HINT: are multiple primaries archiving to this stanza?");
|
||||
|
||||
HRN_STORAGE_REMOVE(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/123456781234567812345678-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("partial not found");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
walSegmentFind(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678.partial"), 0), NULL,
|
||||
walSegmentFindOne(storageRepo(), STRDEF("9.6-2"), STRDEF("123456781234567812345678.partial"), 0), NULL,
|
||||
"did not find partial segment");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("find more than one segment with caching");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/123456781234567812345677-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.gz");
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/123456781234567812345679-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz");
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/12345678123456781234567A-cccccccccccccccccccccccccccccccccccccccc.gz");
|
||||
|
||||
WalSegmentFind *find;
|
||||
TEST_ASSIGN(find, walSegmentFindNew(storageRepo(), STRDEF("9.6-2"), false, 500), "new find");
|
||||
|
||||
// First find will build the list
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(find, STRDEF("123456781234567812345678")),
|
||||
"123456781234567812345678-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "find");
|
||||
TEST_RESULT_STRLST_Z(
|
||||
find->list == NULL ? strLstNew() : find->list,
|
||||
"123456781234567812345679-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz\n"
|
||||
"12345678123456781234567A-cccccccccccccccccccccccccccccccccccccccc.gz\n",
|
||||
"list contents");
|
||||
|
||||
// Write this now to verify that it is not visible immediately since the list should be reused in the next test
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/12345678123456781234567B-dddddddddddddddddddddddddddddddddddddddd.lz4");
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345678/12345678123456781234567C-dddddddddddddddddddddddddddddddddddddddd.lz4");
|
||||
|
||||
// List is reused from prior call -- we know this because 12345678123456781234567B has not appeared
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(find, STRDEF("123456781234567812345679")),
|
||||
"123456781234567812345679-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gz", "find");
|
||||
TEST_RESULT_STRLST_Z(
|
||||
find->list == NULL ? strLstNew() : find->list,
|
||||
"12345678123456781234567A-cccccccccccccccccccccccccccccccccccccccc.gz\n",
|
||||
"list contents");
|
||||
|
||||
// Now this is a reload and 12345678123456781234567B appears
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(find, STRDEF("12345678123456781234567B")),
|
||||
"12345678123456781234567B-dddddddddddddddddddddddddddddddddddddddd.lz4", "find");
|
||||
TEST_RESULT_STRLST_Z(
|
||||
find->list == NULL ? strLstNew() : find->list,
|
||||
"12345678123456781234567C-dddddddddddddddddddddddddddddddddddddddd.lz4\n",
|
||||
"list contents");
|
||||
|
||||
// Search for 12345678123456781234567B again
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(find, STRDEF("12345678123456781234567B")),
|
||||
"12345678123456781234567B-dddddddddddddddddddddddddddddddddddddddd.lz4", "find");
|
||||
TEST_RESULT_STRLST_Z(
|
||||
find->list == NULL ? strLstNew() : find->list,
|
||||
"12345678123456781234567C-dddddddddddddddddddddddddddddddddddddddd.lz4\n",
|
||||
"list contents");
|
||||
|
||||
// Force load by finding WAL with a difference prefix
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345679/123456781234567912345679-dddddddddddddddddddddddddddddddddddddddd.zst");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
walSegmentFind(find, STRDEF("123456781234567912345679")),
|
||||
"123456781234567912345679-dddddddddddddddddddddddddddddddddddddddd.zst", "find");
|
||||
TEST_RESULT_STRLST_Z(find->list == NULL ? strLstNew() : find->list, NULL, "list contents");
|
||||
|
||||
// Error on duplicate WAL
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageTest, "archive/db/9.6-2/1234567812345679/123456781234567912345679-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.gz");
|
||||
|
||||
TEST_ERROR(
|
||||
walSegmentFind(find, STRDEF("123456781234567912345679")),
|
||||
ArchiveDuplicateError,
|
||||
"duplicates found in archive for WAL segment 123456781234567912345679:"
|
||||
" 123456781234567912345679-dddddddddddddddddddddddddddddddddddddddd.zst"
|
||||
", 123456781234567912345679-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.gz\n"
|
||||
"HINT: are multiple primaries archiving to this stanza?");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
Loading…
Reference in New Issue
Block a user