You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Add repo-block-age-map and repo-block-size-map options.
Make these options configurable. This is primarily for testing purposes so the new options will be kept internal.
This commit is contained in:
@ -23,6 +23,7 @@
|
||||
<commit subject="Remove parameter list from deltaMapNew()."/>
|
||||
<commit subject="Consistently declare block incremental size as size_t."/>
|
||||
<commit subject="Rename DeltaMap to BlockHash."/>
|
||||
<commit subject="Add repo-block-age-map and repo-block-size-map options."/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
|
@ -1834,6 +1834,23 @@ option:
|
||||
list:
|
||||
- true
|
||||
|
||||
repo-block-size-map:
|
||||
section: global
|
||||
group: repo
|
||||
type: hash
|
||||
required: false
|
||||
internal: true
|
||||
command: repo-block
|
||||
command-role:
|
||||
main: {}
|
||||
depend:
|
||||
option: repo-block
|
||||
list:
|
||||
- true
|
||||
|
||||
repo-block-age-map:
|
||||
inherit: repo-block-size-map
|
||||
|
||||
repo-cipher-pass:
|
||||
section: global
|
||||
type: string
|
||||
|
@ -522,6 +522,26 @@
|
||||
<example>y</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-block-age-map" name="Block Incremental Age Map">
|
||||
<summary>Block incremental age map.</summary>
|
||||
|
||||
<text>
|
||||
<p>Map file age (in days) to a block multiplier. Files that have not been modified recently are less likely to be modified in the future, so the block size is multiplied to reduce the map size. By default, if the file is old enough it will not be stored as a block incremental.</p>
|
||||
</text>
|
||||
|
||||
<example>7=2</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-block-size-map" name="Block Incremental Size Map">
|
||||
<summary>Block incremental size map.</summary>
|
||||
|
||||
<text>
|
||||
<p>Map file size to block size. Block size is the minimum unit that will be stored in the block incremental. Smaller sizes allow for more granular incremental backups but larger sizes mean smaller maps.</p>
|
||||
</text>
|
||||
|
||||
<example>16KiB=8KiB</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-bundle" name="Repository Bundles">
|
||||
<summary>Bundle files in repository.</summary>
|
||||
|
||||
|
@ -26,7 +26,9 @@ Backup Command
|
||||
#include "common/time.h"
|
||||
#include "common/type/convert.h"
|
||||
#include "common/type/json.h"
|
||||
#include "config/common.h"
|
||||
#include "config/config.h"
|
||||
#include "config/parse.h"
|
||||
#include "db/helper.h"
|
||||
#include "info/infoArchive.h"
|
||||
#include "info/infoBackup.h"
|
||||
@ -265,6 +267,144 @@ backupInit(const InfoBackup *infoBackup)
|
||||
FUNCTION_LOG_RETURN(BACKUP_DATA, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************
|
||||
Build block incremental maps
|
||||
***********************************************************************************************************************************/
|
||||
// Size map
|
||||
static const ManifestBlockIncrSizeMap manifestBlockIncrSizeMapDefault[] =
|
||||
{
|
||||
{.fileSize = 1024 * 1024 * 1024, .blockSize = 1024 * 1024},
|
||||
{.fileSize = 256 * 1024 * 1024, .blockSize = 768 * 1024},
|
||||
{.fileSize = 64 * 1024 * 1024, .blockSize = 512 * 1024},
|
||||
{.fileSize = 16 * 1024 * 1024, .blockSize = 384 * 1024},
|
||||
{.fileSize = 4 * 1024 * 1024, .blockSize = 256 * 1024},
|
||||
{.fileSize = 2 * 1024 * 1024, .blockSize = 192 * 1024},
|
||||
{.fileSize = 128 * 1024, .blockSize = 128 * 1024},
|
||||
};
|
||||
|
||||
// Age map
|
||||
static const ManifestBlockIncrAgeMap manifestBlockIncrAgeMapDefault[] =
|
||||
{
|
||||
{.fileAge = 4 * 7 * SEC_PER_DAY, .blockMultiplier = 0},
|
||||
{.fileAge = 2 * 7 * SEC_PER_DAY, .blockMultiplier = 4},
|
||||
{.fileAge = 7 * SEC_PER_DAY, .blockMultiplier = 2},
|
||||
};
|
||||
|
||||
// All maps
|
||||
static const ManifestBlockIncrMap manifestBlockIncrMap =
|
||||
{
|
||||
.sizeMap = manifestBlockIncrSizeMapDefault,
|
||||
.sizeMapSize = LENGTH_OF(manifestBlockIncrSizeMapDefault),
|
||||
.ageMap = manifestBlockIncrAgeMapDefault,
|
||||
.ageMapSize = LENGTH_OF(manifestBlockIncrAgeMapDefault),
|
||||
};
|
||||
|
||||
// Convert map size
|
||||
static unsigned int
|
||||
backupBlockIncrMapSize(ConfigOption optionId, unsigned int optionKeyIdx, const String *const value)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(ENUM, optionId);
|
||||
FUNCTION_TEST_PARAM(UINT, optionKeyIdx);
|
||||
FUNCTION_TEST_PARAM(STRING, value);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
unsigned int result = 0;
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
const int64_t valueI64 = cfgParseSize(value);
|
||||
|
||||
if (valueI64 <= UINT_MAX)
|
||||
result = (unsigned int)valueI64;
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
OptionInvalidValueError, "'%s' is not valid for '%s' option", strZ(value),
|
||||
cfgParseOptionKeyIdxName(optionId, optionKeyIdx));
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(UINT, result);
|
||||
}
|
||||
|
||||
static ManifestBlockIncrMap
|
||||
backupBlockIncrMap(void)
|
||||
{
|
||||
FUNCTION_TEST_VOID();
|
||||
|
||||
FUNCTION_AUDIT_HELPER();
|
||||
|
||||
ManifestBlockIncrMap result = manifestBlockIncrMap;
|
||||
|
||||
if (cfgOptionBool(cfgOptRepoBlock))
|
||||
{
|
||||
// Build size map
|
||||
const KeyValue *const manifestBlockIncrSizeKv = cfgOptionKvNull(cfgOptRepoBlockSizeMap);
|
||||
|
||||
if (manifestBlockIncrSizeKv != NULL)
|
||||
{
|
||||
List *const map = lstNewP(sizeof(ManifestBlockIncrSizeMap), .comparator = lstComparatorUInt);
|
||||
const VariantList *const mapKeyList = kvKeyList(manifestBlockIncrSizeKv);
|
||||
|
||||
for (unsigned int mapKeyIdx = 0; mapKeyIdx < varLstSize(mapKeyList); mapKeyIdx++)
|
||||
{
|
||||
const Variant *mapKey = varLstGet(mapKeyList, mapKeyIdx);
|
||||
|
||||
ManifestBlockIncrSizeMap manifestBuildBlockIncrSizeMap =
|
||||
{
|
||||
.fileSize = backupBlockIncrMapSize(
|
||||
cfgOptRepoBlockSizeMap, cfgOptionIdxDefault(cfgOptRepoBlockSizeMap), varStr(mapKey)),
|
||||
.blockSize = backupBlockIncrMapSize(
|
||||
cfgOptRepoBlockSizeMap, cfgOptionIdxDefault(cfgOptRepoBlockSizeMap),
|
||||
varStr(kvGet(manifestBlockIncrSizeKv, mapKey))),
|
||||
};
|
||||
|
||||
lstAdd(map, &manifestBuildBlockIncrSizeMap);
|
||||
}
|
||||
|
||||
lstSort(map, sortOrderDesc);
|
||||
|
||||
result.sizeMap = lstGet(map, 0);
|
||||
result.sizeMapSize = lstSize(map);
|
||||
}
|
||||
|
||||
// Build age map
|
||||
const KeyValue *const manifestBlockIncrAgeKv = cfgOptionKvNull(cfgOptRepoBlockAgeMap);
|
||||
|
||||
if (manifestBlockIncrAgeKv != NULL)
|
||||
{
|
||||
List *const map = lstNewP(sizeof(ManifestBlockIncrAgeMap), .comparator = lstComparatorUInt);
|
||||
const VariantList *const mapKeyList = kvKeyList(manifestBlockIncrAgeKv);
|
||||
|
||||
for (unsigned int mapKeyIdx = 0; mapKeyIdx < varLstSize(mapKeyList); mapKeyIdx++)
|
||||
{
|
||||
const Variant *mapKey = varLstGet(mapKeyList, mapKeyIdx);
|
||||
|
||||
ManifestBlockIncrAgeMap manifestBuildBlockIncrAgeMap =
|
||||
{
|
||||
.fileAge = varUIntForce(mapKey) * (unsigned int)SEC_PER_DAY,
|
||||
.blockMultiplier = varUIntForce(kvGet(manifestBlockIncrAgeKv, mapKey)),
|
||||
};
|
||||
|
||||
lstAdd(map, &manifestBuildBlockIncrAgeMap);
|
||||
}
|
||||
|
||||
lstSort(map, sortOrderDesc);
|
||||
|
||||
result.ageMap = lstGet(map, 0);
|
||||
result.ageMapSize = lstSize(map);
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_TYPE(ManifestBlockIncrMap, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************
|
||||
Get time from the database or locally depending on online
|
||||
***********************************************************************************************************************************/
|
||||
@ -2305,9 +2445,11 @@ cmdBackup(void)
|
||||
BackupStartResult backupStartResult = backupStart(backupData);
|
||||
|
||||
// Build the manifest
|
||||
const ManifestBlockIncrMap blockIncrMap = backupBlockIncrMap();
|
||||
|
||||
Manifest *manifest = manifestNewBuild(
|
||||
backupData->storagePrimary, infoPg.version, infoPg.catalogVersion, timestampStart, cfgOptionBool(cfgOptOnline),
|
||||
cfgOptionBool(cfgOptChecksumPage), cfgOptionBool(cfgOptRepoBundle), cfgOptionBool(cfgOptRepoBlock),
|
||||
cfgOptionBool(cfgOptChecksumPage), cfgOptionBool(cfgOptRepoBundle), cfgOptionBool(cfgOptRepoBlock), &blockIncrMap,
|
||||
strLstNewVarLst(cfgOptionLst(cfgOptExclude)), backupStartResult.tablespaceList);
|
||||
|
||||
// Validate the manifest using the copy start time
|
||||
|
@ -92,6 +92,27 @@ lstComparatorStr(const void *item1, const void *item2)
|
||||
FUNCTION_TEST_RETURN(INT, strCmp(*(String **)item1, *(String **)item2));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN int
|
||||
lstComparatorUInt(const void *const item1, const void *const item2)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM_P(VOID, item1);
|
||||
FUNCTION_TEST_PARAM_P(VOID, item2);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(item1 != NULL);
|
||||
ASSERT(item2 != NULL);
|
||||
|
||||
if (*(unsigned int *)item1 < *(unsigned int *)item2)
|
||||
FUNCTION_TEST_RETURN(INT, -1);
|
||||
|
||||
if (*(unsigned int *)item1 > *(unsigned int *)item2)
|
||||
FUNCTION_TEST_RETURN(INT, 1);
|
||||
|
||||
FUNCTION_TEST_RETURN(INT, 0);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN int
|
||||
lstComparatorZ(const void *item1, const void *item2)
|
||||
|
@ -46,6 +46,9 @@ typedef int ListComparator(const void *item1, const void *item2);
|
||||
// General purpose list comparator for Strings or structs with a String as the first member
|
||||
FN_EXTERN int lstComparatorStr(const void *item1, const void *item2);
|
||||
|
||||
// General purpose list comparator for unsigned int or structs with an unsigned int as the first member
|
||||
FN_EXTERN int lstComparatorUInt(const void *item1, const void *item2);
|
||||
|
||||
// General purpose list comparator for zero-terminated strings or structs with a zero-terminated string as the first member
|
||||
FN_EXTERN int lstComparatorZ(const void *item1, const void *item2);
|
||||
|
||||
|
@ -130,7 +130,7 @@ Option constants
|
||||
#define CFGOPT_TYPE "type"
|
||||
#define CFGOPT_VERBOSE "verbose"
|
||||
|
||||
#define CFG_OPTION_TOTAL 158
|
||||
#define CFG_OPTION_TOTAL 160
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option value constants
|
||||
@ -444,6 +444,8 @@ typedef enum
|
||||
cfgOptRepoAzureKeyType,
|
||||
cfgOptRepoAzureUriStyle,
|
||||
cfgOptRepoBlock,
|
||||
cfgOptRepoBlockAgeMap,
|
||||
cfgOptRepoBlockSizeMap,
|
||||
cfgOptRepoBundle,
|
||||
cfgOptRepoBundleLimit,
|
||||
cfgOptRepoBundleSize,
|
||||
|
@ -4888,6 +4888,64 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
), // opt/repo-block
|
||||
), // opt/repo-block
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-block-age-map
|
||||
( // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_NAME("repo-block-age-map"), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeHash), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_MULTI(true), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-block-age-map
|
||||
// opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-block-age-map
|
||||
( // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-block-age-map
|
||||
), // opt/repo-block-age-map
|
||||
// opt/repo-block-age-map
|
||||
PARSE_RULE_OPTIONAL // opt/repo-block-age-map
|
||||
( // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-block-age-map
|
||||
( // opt/repo-block-age-map
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-block-age-map
|
||||
( // opt/repo-block-age-map
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoBlock), // opt/repo-block-age-map
|
||||
PARSE_RULE_VAL_BOOL_TRUE, // opt/repo-block-age-map
|
||||
), // opt/repo-block-age-map
|
||||
), // opt/repo-block-age-map
|
||||
), // opt/repo-block-age-map
|
||||
), // opt/repo-block-age-map
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-block-size-map
|
||||
( // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_NAME("repo-block-size-map"), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeHash), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_MULTI(true), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-block-size-map
|
||||
// opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-block-size-map
|
||||
( // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-block-size-map
|
||||
), // opt/repo-block-size-map
|
||||
// opt/repo-block-size-map
|
||||
PARSE_RULE_OPTIONAL // opt/repo-block-size-map
|
||||
( // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-block-size-map
|
||||
( // opt/repo-block-size-map
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-block-size-map
|
||||
( // opt/repo-block-size-map
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoBlock), // opt/repo-block-size-map
|
||||
PARSE_RULE_VAL_BOOL_TRUE, // opt/repo-block-size-map
|
||||
), // opt/repo-block-size-map
|
||||
), // opt/repo-block-size-map
|
||||
), // opt/repo-block-size-map
|
||||
), // opt/repo-block-size-map
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-bundle
|
||||
( // opt/repo-bundle
|
||||
PARSE_RULE_OPTION_NAME("repo-bundle"), // opt/repo-bundle
|
||||
@ -9463,6 +9521,8 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptRepoAzureKeyType, // opt-resolve-order
|
||||
cfgOptRepoAzureUriStyle, // opt-resolve-order
|
||||
cfgOptRepoBlock, // opt-resolve-order
|
||||
cfgOptRepoBlockAgeMap, // opt-resolve-order
|
||||
cfgOptRepoBlockSizeMap, // opt-resolve-order
|
||||
cfgOptRepoCipherPass, // opt-resolve-order
|
||||
cfgOptRepoGcsKeyType, // opt-resolve-order
|
||||
cfgOptRepoHost, // opt-resolve-order
|
||||
|
@ -754,80 +754,6 @@ manifestLinkCheck(const Manifest *this)
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Calculate block incremental size for a file. The block size is based on the size and age of the file. Larger files get larger block
|
||||
sizes to reduce the cost of the map and individual block compression. Older files also get larger block sizes under the assumption
|
||||
that they are unlikely to be modified if they have not been modified in a while. Very old and very small files skip block
|
||||
incremental entirely.
|
||||
|
||||
The minimum practical block size is 128k. After that, the loss of compression efficiency becomes too expensive in terms of space.
|
||||
***********************************************************************************************************************************/
|
||||
// File size to block size map
|
||||
static struct ManifestBuildBlockIncrSizeMap
|
||||
{
|
||||
uint32_t fileSize;
|
||||
uint32_t blockSize;
|
||||
} manifestBuildBlockIncrSizeMap[] =
|
||||
{
|
||||
{.fileSize = 1024 * 1024 * 1024, .blockSize = 1024 * 1024},
|
||||
{.fileSize = 256 * 1024 * 1024, .blockSize = 768 * 1024},
|
||||
{.fileSize = 64 * 1024 * 1024, .blockSize = 512 * 1024},
|
||||
{.fileSize = 16 * 1024 * 1024, .blockSize = 384 * 1024},
|
||||
{.fileSize = 4 * 1024 * 1024, .blockSize = 256 * 1024},
|
||||
{.fileSize = 2 * 1024 * 1024, .blockSize = 192 * 1024},
|
||||
{.fileSize = 128 * 1024, .blockSize = 128 * 1024},
|
||||
};
|
||||
|
||||
// File age to block multiplier map
|
||||
static struct ManifestBuildBlockIncrTimeMap
|
||||
{
|
||||
uint32_t fileAge;
|
||||
uint32_t blockMultiplier;
|
||||
} manifestBuildBlockIncrTimeMap[] =
|
||||
{
|
||||
{.fileAge = 4 * 7 * 86400, .blockMultiplier = 0},
|
||||
{.fileAge = 2 * 7 * 86400, .blockMultiplier = 4},
|
||||
{.fileAge = 7 * 86400, .blockMultiplier = 2},
|
||||
};
|
||||
|
||||
static size_t
|
||||
manifestBuildBlockIncrSize(const time_t timeStart, const ManifestFile *const file)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(TIME, timeStart);
|
||||
FUNCTION_TEST_PARAM(MANIFEST_FILE, file);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
size_t result = 0;
|
||||
|
||||
// Search size map for the appropriate block size
|
||||
for (unsigned int sizeIdx = 0; sizeIdx < LENGTH_OF(manifestBuildBlockIncrSizeMap); sizeIdx++)
|
||||
{
|
||||
if (file->size >= manifestBuildBlockIncrSizeMap[sizeIdx].fileSize)
|
||||
{
|
||||
result = manifestBuildBlockIncrSizeMap[sizeIdx].blockSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If block size > 0 then search age map for a multiplier
|
||||
if (result != 0)
|
||||
{
|
||||
const time_t fileAge = timeStart - file->timestamp;
|
||||
|
||||
for (unsigned int timeIdx = 0; timeIdx < LENGTH_OF(manifestBuildBlockIncrTimeMap); timeIdx++)
|
||||
{
|
||||
if (fileAge >= (time_t)manifestBuildBlockIncrTimeMap[timeIdx].fileAge)
|
||||
{
|
||||
result *= manifestBuildBlockIncrTimeMap[timeIdx].blockMultiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(UINT64, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
typedef struct ManifestBuildData
|
||||
{
|
||||
@ -843,8 +769,53 @@ typedef struct ManifestBuildData
|
||||
ManifestLinkCheck *linkCheck; // List of links found during build (used for prefix check)
|
||||
StringList *excludeContent; // Exclude contents of directories
|
||||
StringList *excludeSingle; // Exclude a single file/link/path
|
||||
const ManifestBlockIncrMap *blockIncrMap; // Block incremental maps
|
||||
} ManifestBuildData;
|
||||
|
||||
// Calculate block incremental size for a file. The block size is based on the size and age of the file. Larger files get larger
|
||||
// block sizes to reduce the cost of the map and individual block compression. Older files also get larger block sizes under the
|
||||
// assumption that they are unlikely to be modified if they have not been modified in a while. Very old and very small files skip
|
||||
// block incremental entirely.
|
||||
//
|
||||
// The minimum practical block size is 128k. After that, the loss of compression efficiency becomes too expensive in terms of space.
|
||||
static size_t
|
||||
manifestBuildBlockIncrSize(const ManifestBuildData *const buildData, const ManifestFile *const file)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM_P(VOID, buildData);
|
||||
FUNCTION_TEST_PARAM(MANIFEST_FILE, file);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
size_t result = 0;
|
||||
|
||||
// Search size map for the appropriate block size
|
||||
for (unsigned int sizeIdx = 0; sizeIdx < buildData->blockIncrMap->sizeMapSize; sizeIdx++)
|
||||
{
|
||||
if (file->size >= buildData->blockIncrMap->sizeMap[sizeIdx].fileSize)
|
||||
{
|
||||
result = buildData->blockIncrMap->sizeMap[sizeIdx].blockSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If block size > 0 then search age map for a multiplier
|
||||
if (result != 0)
|
||||
{
|
||||
const time_t fileAge = buildData->manifest->pub.data.backupTimestampStart - file->timestamp;
|
||||
|
||||
for (unsigned int timeIdx = 0; timeIdx < buildData->blockIncrMap->ageMapSize; timeIdx++)
|
||||
{
|
||||
if (fileAge >= (time_t)buildData->blockIncrMap->ageMap[timeIdx].fileAge)
|
||||
{
|
||||
result *= buildData->blockIncrMap->ageMap[timeIdx].blockMultiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(UINT64, result);
|
||||
}
|
||||
|
||||
// Process files/links/paths and add them to the manifest
|
||||
static void
|
||||
manifestBuildInfo(
|
||||
@ -1066,7 +1037,7 @@ manifestBuildInfo(
|
||||
|
||||
// Get block incremental size
|
||||
if (info->size != 0 && buildData->manifest->pub.data.blockIncr)
|
||||
file.blockIncrSize = manifestBuildBlockIncrSize(buildData->manifest->pub.data.backupTimestampStart, &file);
|
||||
file.blockIncrSize = manifestBuildBlockIncrSize(buildData, &file);
|
||||
|
||||
// Determine if this file should be page checksummed
|
||||
if (dbPath && buildData->checksumPage)
|
||||
@ -1265,8 +1236,8 @@ manifestBuildInfo(
|
||||
FN_EXTERN Manifest *
|
||||
manifestNewBuild(
|
||||
const Storage *const storagePg, const unsigned int pgVersion, const unsigned int pgCatalogVersion, const time_t timestampStart,
|
||||
const bool online, const bool checksumPage, const bool bundle, const bool blockIncr, const StringList *const excludeList,
|
||||
const Pack *const tablespaceList)
|
||||
const bool online, const bool checksumPage, const bool bundle, const bool blockIncr, const ManifestBlockIncrMap *blockIncrMap,
|
||||
const StringList *const excludeList, const Pack *const tablespaceList)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STORAGE, storagePg);
|
||||
@ -1277,6 +1248,7 @@ manifestNewBuild(
|
||||
FUNCTION_LOG_PARAM(BOOL, checksumPage);
|
||||
FUNCTION_LOG_PARAM(BOOL, bundle);
|
||||
FUNCTION_LOG_PARAM(BOOL, blockIncr);
|
||||
FUNCTION_LOG_PARAM(VOID, blockIncrMap);
|
||||
FUNCTION_LOG_PARAM(STRING_LIST, excludeList);
|
||||
FUNCTION_LOG_PARAM(PACK, tablespaceList);
|
||||
FUNCTION_LOG_END();
|
||||
@ -1315,6 +1287,7 @@ manifestNewBuild(
|
||||
.tablespaceList = tablespaceList,
|
||||
.linkCheck = &linkCheck,
|
||||
.manifestWalName = strNewFmt(MANIFEST_TARGET_PGDATA "/%s", strZ(pgWalPath(pgVersion))),
|
||||
.blockIncrMap = blockIncrMap,
|
||||
};
|
||||
|
||||
// Build expressions to identify databases paths and temp relations
|
||||
|
@ -86,6 +86,31 @@ typedef struct ManifestData
|
||||
const Variant *backupOptionProcessMax; // How many processes will be used for backup?
|
||||
} ManifestData;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Block incremental size maps
|
||||
***********************************************************************************************************************************/
|
||||
// Map file size to block size
|
||||
typedef struct ManifestBlockIncrSizeMap
|
||||
{
|
||||
unsigned int fileSize; // File size
|
||||
unsigned int blockSize; // Block size for files >= file size
|
||||
} ManifestBlockIncrSizeMap;
|
||||
|
||||
// Map file age to block multiplier
|
||||
typedef struct ManifestBlockIncrAgeMap
|
||||
{
|
||||
uint32_t fileAge; // File age in seconds
|
||||
uint32_t blockMultiplier; // Block multiplier
|
||||
} ManifestBlockIncrAgeMap;
|
||||
|
||||
typedef struct ManifestBlockIncrMap
|
||||
{
|
||||
const ManifestBlockIncrSizeMap *sizeMap; // Block size map
|
||||
unsigned int sizeMapSize; // Block size map size
|
||||
const ManifestBlockIncrAgeMap *ageMap; // File age map
|
||||
unsigned int ageMapSize; // File age map size
|
||||
} ManifestBlockIncrMap;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Db type
|
||||
***********************************************************************************************************************************/
|
||||
@ -170,7 +195,8 @@ Constructors
|
||||
// Build a new manifest for a PostgreSQL data directory
|
||||
FN_EXTERN Manifest *manifestNewBuild(
|
||||
const Storage *storagePg, unsigned int pgVersion, unsigned int pgCatalogVersion, time_t timestampStart, bool online,
|
||||
bool checksumPage, bool bundle, bool blockIncr, const StringList *excludeList, const Pack *tablespaceList);
|
||||
bool checksumPage, bool bundle, bool blockIncr, const ManifestBlockIncrMap *blockIncrMap, const StringList *excludeList,
|
||||
const Pack *tablespaceList);
|
||||
|
||||
// Load a manifest from IO
|
||||
FN_EXTERN Manifest *manifestNewLoad(IoRead *read);
|
||||
|
@ -1083,6 +1083,16 @@ testRun(void)
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("BlockIncr"))
|
||||
{
|
||||
TEST_TITLE("block incremental config map");
|
||||
|
||||
TEST_ERROR(
|
||||
backupBlockIncrMapSize(cfgOptRepoBlockSizeMap, 0, STRDEF("Z")), OptionInvalidValueError,
|
||||
"'Z' is not valid for 'repo1-block-size-map' option");
|
||||
TEST_ERROR(
|
||||
backupBlockIncrMapSize(cfgOptRepoBlockSizeMap, 0, STRDEF("5GiB")), OptionInvalidValueError,
|
||||
"'5GiB' is not valid for 'repo1-block-size-map' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("full backup with zero block");
|
||||
|
||||
ioBufferSizeSet(2);
|
||||
@ -2454,7 +2464,7 @@ testRun(void)
|
||||
|
||||
// Create a backup manifest that looks like a halted backup manifest
|
||||
Manifest *manifestResume = manifestNewBuild(
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), true, false, false, false, 0, NULL, NULL);
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), 0, true, false, false, false, NULL, NULL, NULL);
|
||||
ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
|
||||
|
||||
manifestResumeData->backupType = backupTypeFull;
|
||||
@ -2547,7 +2557,7 @@ testRun(void)
|
||||
|
||||
// Create a backup manifest that looks like a halted backup manifest
|
||||
Manifest *manifestResume = manifestNewBuild(
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), true, false, false, false, 0, NULL, NULL);
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), 0, true, false, false, false, NULL, NULL, NULL);
|
||||
ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
|
||||
|
||||
manifestResumeData->backupType = backupTypeFull;
|
||||
@ -2734,7 +2744,7 @@ testRun(void)
|
||||
|
||||
// Create a backup manifest that looks like a halted backup manifest
|
||||
Manifest *manifestResume = manifestNewBuild(
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), true, false, false, false, 0, NULL, NULL);
|
||||
storagePg(), PG_VERSION_95, hrnPgCatalogVersion(PG_VERSION_95), 0, true, false, false, false, NULL, NULL, NULL);
|
||||
ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
|
||||
|
||||
manifestResumeData->backupType = backupTypeDiff;
|
||||
@ -3643,6 +3653,12 @@ testRun(void)
|
||||
|
||||
backupTimeStart = BACKUP_EPOCH + 2800000;
|
||||
|
||||
// Block sizes for testing
|
||||
#define BLOCK_MIN_SIZE 131072
|
||||
#define BLOCK_MIN_FILE_SIZE 131072
|
||||
#define BLOCK_MAX_SIZE 196608
|
||||
#define BLOCK_MAX_FILE_SIZE 2097152
|
||||
|
||||
{
|
||||
// Remove old pg data
|
||||
HRN_STORAGE_PATH_REMOVE(storageTest, "pg1", .recurse = true);
|
||||
@ -3664,24 +3680,26 @@ testRun(void)
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB");
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MAX_FILE_SIZE) "b=" STRINGIFY(BLOCK_MAX_SIZE) "b");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MIN_FILE_SIZE) "=" STRINGIFY(BLOCK_MIN_SIZE));
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
// File that uses block incr and will grow
|
||||
Buffer *file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize * 3);
|
||||
Buffer *file = bufNew(BLOCK_MIN_SIZE * 3);
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart);
|
||||
|
||||
// File that shrinks below the limit
|
||||
file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize + 1);
|
||||
file = bufNew(BLOCK_MIN_FILE_SIZE + 1);
|
||||
memset(bufPtr(file), 55, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart);
|
||||
|
||||
// File that grows above the limit
|
||||
file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize - 1);
|
||||
file = bufNew(BLOCK_MIN_FILE_SIZE - 1);
|
||||
memset(bufPtr(file), 77, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
@ -3762,32 +3780,34 @@ testRun(void)
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB");
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MAX_FILE_SIZE) "=" STRINGIFY(BLOCK_MAX_SIZE));
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MIN_FILE_SIZE) "=" STRINGIFY(BLOCK_MIN_SIZE));
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
// Grow file size to check block incr delta. This is also large enough that it would get a new block size if it were
|
||||
// and new file rather than a delta.
|
||||
Buffer *file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 2].fileSize);
|
||||
Buffer *file = bufNew(BLOCK_MAX_FILE_SIZE);
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart);
|
||||
|
||||
// File that gets a large block size
|
||||
file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 2].fileSize);
|
||||
file = bufNew(BLOCK_MAX_FILE_SIZE);
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-larger", file, .timeModified = backupTimeStart);
|
||||
|
||||
// Shrink file below the limit
|
||||
file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize - 1);
|
||||
file = bufNew(BLOCK_MIN_FILE_SIZE - 1);
|
||||
memset(bufPtr(file), 55, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart);
|
||||
|
||||
// Grow file above the limit
|
||||
file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize + 1);
|
||||
file = bufNew(BLOCK_MIN_FILE_SIZE + 1);
|
||||
memset(bufPtr(file), 77, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
@ -3892,13 +3912,15 @@ testRun(void)
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "8KiB");
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MAX_FILE_SIZE) "=" STRINGIFY(BLOCK_MAX_SIZE));
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockSizeMap, STRINGIFY(BLOCK_MIN_FILE_SIZE) "=" STRINGIFY(BLOCK_MIN_SIZE));
|
||||
hrnCfgArgRawZ(argList, cfgOptBufferSize, "16KiB");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc");
|
||||
hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS);
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
// File that uses block incr and will grow
|
||||
Buffer *file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize);
|
||||
Buffer *file = bufNew((size_t)(BLOCK_MIN_FILE_SIZE));
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
@ -3975,16 +3997,24 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "4MiB");
|
||||
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoBlockAgeMap, "1=2");
|
||||
hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS);
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
// File that uses block incr and grows
|
||||
Buffer *file = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize * 2);
|
||||
Buffer *file = bufNew(BLOCK_MIN_FILE_SIZE * 2);
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart);
|
||||
|
||||
// File with age multiplier
|
||||
file = bufNew(BLOCK_MIN_FILE_SIZE * 2);
|
||||
memset(bufPtr(file), 0, bufSize(file));
|
||||
bufUsedSet(file, bufSize(file));
|
||||
|
||||
HRN_STORAGE_PUT(storagePgWrite(), "block-age-multiplier", file, .timeModified = backupTimeStart - SEC_PER_DAY);
|
||||
|
||||
// Run backup
|
||||
testBackupPqScriptP(
|
||||
PG_VERSION_11, backupTimeStart, .walCompressType = compressTypeNone, .cipherType = cipherTypeAes256Cbc,
|
||||
@ -3996,8 +4026,9 @@ testRun(void)
|
||||
"P00 INFO: execute non-exclusive backup start: backup begins after the next regular checkpoint completes\n"
|
||||
"P00 INFO: backup start archive = 0000000105DC82D000000000, lsn = 5dc82d0/0\n"
|
||||
"P00 INFO: check archive for segment 0000000105DC82D000000000\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/0, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-grow (bundle 1/112, 256KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-age-multiplier (bundle 1/0, 256KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/355, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-grow (bundle 1/467, 256KB, [PCT]) checksum [SHA1]\n"
|
||||
"P00 DETAIL: reference pg_data/PG_VERSION to 20191108-080000F\n"
|
||||
"P00 INFO: execute non-exclusive backup stop and wait for all WAL segments to archive\n"
|
||||
"P00 INFO: backup stop archive = 0000000105DC82D000000001, lsn = 5dc82d0/300000\n"
|
||||
@ -4005,7 +4036,7 @@ testRun(void)
|
||||
"P00 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
|
||||
"P00 INFO: check archive for segment(s) 0000000105DC82D000000000:0000000105DC82D000000001\n"
|
||||
"P00 INFO: new backup label = 20191108-080000F_20191110-153320D\n"
|
||||
"P00 INFO: diff backup size = [SIZE], file total = 5");
|
||||
"P00 INFO: diff backup size = [SIZE], file total = 6");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
testBackupValidateP(
|
||||
@ -4013,6 +4044,7 @@ testRun(void)
|
||||
.cipherPass = TEST_CIPHER_PASS),
|
||||
". {link, d=20191108-080000F_20191110-153320D}\n"
|
||||
"bundle {path}\n"
|
||||
"bundle/1/pg_data/block-age-multiplier {file, m={1}, s=262144}\n"
|
||||
"bundle/1/pg_data/block-incr-grow {file, m={0,1}, s=262144}\n"
|
||||
"bundle/1/pg_data/global/pg_control {file, s=8192}\n"
|
||||
"pg_data {path}\n"
|
||||
@ -4027,6 +4059,8 @@ testRun(void)
|
||||
",\"size\":2,\"timestamp\":1572800000}\n"
|
||||
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
|
||||
",\"timestamp\":1573400002}\n"
|
||||
"pg_data/block-age-multiplier={\"bims\":40,\"bis\":32,\"checksum\":\"2e000fa7e85759c7f4c254d4d9c33ef481e459a7\""
|
||||
",\"size\":262144,\"timestamp\":1573313600}\n"
|
||||
"pg_data/block-incr-grow={\"bims\":72,\"bis\":16,\"checksum\":\"2e000fa7e85759c7f4c254d4d9c33ef481e459a7\""
|
||||
",\"size\":262144,\"timestamp\":1573400000}\n"
|
||||
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1573400000}\n"
|
||||
|
@ -3072,7 +3072,7 @@ testRun(void)
|
||||
TEST_TITLE("full backup with block incr");
|
||||
|
||||
// Zeroed file large enough to use block incr
|
||||
Buffer *relation = bufNew(manifestBuildBlockIncrSizeMap[LENGTH_OF(manifestBuildBlockIncrSizeMap) - 1].fileSize * 2);
|
||||
Buffer *relation = bufNew(256 * 1024);
|
||||
memset(bufPtr(relation), 0, bufSize(relation));
|
||||
bufUsedSet(relation, bufSize(relation));
|
||||
|
||||
|
@ -189,6 +189,16 @@ testRun(void)
|
||||
TEST_RESULT_INT(*(int *)lstGet(list, 2), 3, "sort value 2");
|
||||
TEST_RESULT_INT(*(int *)lstGet(list, 3), 2, "sort value 3");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("lstComparatorUInt()");
|
||||
|
||||
unsigned int uint1 = 1;
|
||||
unsigned int uint2 = 2;
|
||||
|
||||
TEST_RESULT_INT(lstComparatorUInt(&uint1, &uint1), 0, "uints are equal");
|
||||
TEST_RESULT_BOOL(lstComparatorUInt(&uint1, &uint2) < 0, true, "first uint is less");
|
||||
TEST_RESULT_BOOL(lstComparatorUInt(&uint2, &uint1) > 0, true, "first uint is greater");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("lstComparatorZ()");
|
||||
|
||||
|
@ -296,7 +296,7 @@ testRun(void)
|
||||
// Test tablespace error
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, false, false, false, false, exclusionList,
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, false, false, false, false, NULL, exclusionList,
|
||||
pckWriteResult(tablespaceList)),
|
||||
AssertError,
|
||||
"tablespace with oid 1 not found in tablespace map\n"
|
||||
@ -321,7 +321,7 @@ testRun(void)
|
||||
TEST_ASSIGN(
|
||||
manifest,
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, false, false, false, false, NULL,
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, false, false, false, false, NULL, NULL,
|
||||
pckWriteResult(tablespaceList)),
|
||||
"build manifest");
|
||||
TEST_RESULT_VOID(manifestBackupLabelSet(manifest, STRDEF("20190818-084502F")), "backup label set");
|
||||
@ -420,7 +420,7 @@ testRun(void)
|
||||
TEST_ASSIGN(
|
||||
manifest,
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, true, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_93, hrnPgCatalogVersion(PG_VERSION_93), 0, true, false, false, false, NULL, NULL, NULL),
|
||||
"build manifest");
|
||||
|
||||
contentSave = bufNew(0);
|
||||
@ -496,7 +496,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_96, hrnPgCatalogVersion(PG_VERSION_96), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_96, hrnPgCatalogVersion(PG_VERSION_96), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
LinkDestinationError,
|
||||
"link 'pg_xlog/wal' (" TEST_PATH "/wal) destination is the same directory as link 'pg_xlog' (" TEST_PATH "/wal)");
|
||||
|
||||
@ -552,7 +552,7 @@ testRun(void)
|
||||
TEST_ASSIGN(
|
||||
manifest,
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, true, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, true, false, false, NULL, NULL, NULL),
|
||||
"build manifest");
|
||||
|
||||
contentSave = bufNew(0);
|
||||
@ -647,7 +647,7 @@ testRun(void)
|
||||
// Tablespace link errors when correct verion not found
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_12, hrnPgCatalogVersion(PG_VERSION_12), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_12, hrnPgCatalogVersion(PG_VERSION_12), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
FileOpenError,
|
||||
"unable to get info for missing path/file '" TEST_PATH "/pg/pg_tblspc/1/PG_12_201909212': [2] No such file or"
|
||||
" directory");
|
||||
@ -668,7 +668,7 @@ testRun(void)
|
||||
TEST_ASSIGN(
|
||||
manifest,
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_12, hrnPgCatalogVersion(PG_VERSION_12), 0, true, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_12, hrnPgCatalogVersion(PG_VERSION_12), 0, true, false, false, false, NULL, NULL, NULL),
|
||||
"build manifest");
|
||||
|
||||
contentSave = bufNew(0);
|
||||
@ -752,11 +752,34 @@ testRun(void)
|
||||
// Create file that is large enough for block incr and old enough to not need block incr
|
||||
HRN_STORAGE_PUT(storagePgWrite, "128k-4week", buffer, .modeFile = 0600, .timeModified = 1570000000 - (28 * 86400));
|
||||
|
||||
// Block incremental maps
|
||||
static const ManifestBlockIncrSizeMap manifestBlockIncrSizeMap[] =
|
||||
{
|
||||
{.fileSize = 128 * 1024, .blockSize = 128 * 1024},
|
||||
{.fileSize = 8 * 1024, .blockSize = 8 * 1024},
|
||||
};
|
||||
|
||||
static const ManifestBlockIncrAgeMap manifestBlockIncrAgeMap[] =
|
||||
{
|
||||
{.fileAge = 4 * 7 * 86400, .blockMultiplier = 0},
|
||||
{.fileAge = 2 * 7 * 86400, .blockMultiplier = 4},
|
||||
{.fileAge = 7 * 86400, .blockMultiplier = 2},
|
||||
};
|
||||
|
||||
static const ManifestBlockIncrMap manifestBuildBlockIncrMap =
|
||||
{
|
||||
.sizeMap = manifestBlockIncrSizeMap,
|
||||
.sizeMapSize = LENGTH_OF(manifestBlockIncrSizeMap),
|
||||
.ageMap = manifestBlockIncrAgeMap,
|
||||
.ageMapSize = LENGTH_OF(manifestBlockIncrAgeMap),
|
||||
};
|
||||
|
||||
// pg_wal not ignored
|
||||
TEST_ASSIGN(
|
||||
manifest,
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_13, hrnPgCatalogVersion(PG_VERSION_13), 1570000000, false, false, true, true, NULL, NULL),
|
||||
storagePg, PG_VERSION_13, hrnPgCatalogVersion(PG_VERSION_13), 1570000000, false, false, true, true,
|
||||
&manifestBuildBlockIncrMap, NULL, NULL),
|
||||
"build manifest");
|
||||
|
||||
contentSave = bufNew(0);
|
||||
@ -831,7 +854,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
LinkDestinationError, "link 'link' destination '" TEST_PATH "/pg/base' is in PGDATA");
|
||||
|
||||
THROW_ON_SYS_ERROR(unlink(TEST_PATH "/pg/link") == -1, FileRemoveError, "unable to remove symlink");
|
||||
@ -843,7 +866,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
LinkExpectedError, "'pg_data/pg_tblspc/somedir' is not a symlink - pg_tblspc should contain only symlinks");
|
||||
|
||||
HRN_STORAGE_PATH_REMOVE(storagePgWrite, MANIFEST_TARGET_PGTBLSPC "/somedir");
|
||||
@ -855,7 +878,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
LinkExpectedError, "'pg_data/pg_tblspc/somefile' is not a symlink - pg_tblspc should contain only symlinks");
|
||||
|
||||
TEST_STORAGE_EXISTS(storagePgWrite, MANIFEST_TARGET_PGTBLSPC "/somefile", .remove = true);
|
||||
@ -867,7 +890,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, true, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, true, false, false, NULL, NULL, NULL),
|
||||
FileOpenError,
|
||||
"unable to get info for missing path/file '" TEST_PATH "/pg/link-to-link': [2] No such file or directory");
|
||||
|
||||
@ -884,7 +907,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
manifestNewBuild(
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL),
|
||||
storagePg, PG_VERSION_94, hrnPgCatalogVersion(PG_VERSION_94), 0, false, false, false, false, NULL, NULL, NULL),
|
||||
LinkDestinationError, "link '" TEST_PATH "/pg/linktolink' cannot reference another link '" TEST_PATH "/linktest'");
|
||||
|
||||
#undef TEST_MANIFEST_HEADER
|
||||
|
@ -272,7 +272,7 @@ testRun(void)
|
||||
MEM_CONTEXT_BEGIN(testContext)
|
||||
{
|
||||
TEST_ASSIGN(
|
||||
manifest, manifestNewBuild(storagePg, PG_VERSION_15, 999999999, false, false, false, false, 0, NULL, NULL),
|
||||
manifest, manifestNewBuild(storagePg, PG_VERSION_15, 999999999, 0, false, false, false, false, NULL, NULL, NULL),
|
||||
"build files");
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
Reference in New Issue
Block a user