1
0
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:
David Steele
2023-02-26 14:49:34 +07:00
parent 15d5dcdd3b
commit a9867cb0b8
15 changed files with 442 additions and 110 deletions

View File

@ -23,6 +23,7 @@
<commit subject="Remove parameter list from deltaMapNew()."/> <commit subject="Remove parameter list from deltaMapNew()."/>
<commit subject="Consistently declare block incremental size as size_t."/> <commit subject="Consistently declare block incremental size as size_t."/>
<commit subject="Rename DeltaMap to BlockHash."/> <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-list>
<release-item-contributor id="david.steele"/> <release-item-contributor id="david.steele"/>

View File

@ -1834,6 +1834,23 @@ option:
list: list:
- true - 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: repo-cipher-pass:
section: global section: global
type: string type: string

View File

@ -522,6 +522,26 @@
<example>y</example> <example>y</example>
</config-key> </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"> <config-key id="repo-bundle" name="Repository Bundles">
<summary>Bundle files in repository.</summary> <summary>Bundle files in repository.</summary>

View File

@ -26,7 +26,9 @@ Backup Command
#include "common/time.h" #include "common/time.h"
#include "common/type/convert.h" #include "common/type/convert.h"
#include "common/type/json.h" #include "common/type/json.h"
#include "config/common.h"
#include "config/config.h" #include "config/config.h"
#include "config/parse.h"
#include "db/helper.h" #include "db/helper.h"
#include "info/infoArchive.h" #include "info/infoArchive.h"
#include "info/infoBackup.h" #include "info/infoBackup.h"
@ -265,6 +267,144 @@ backupInit(const InfoBackup *infoBackup)
FUNCTION_LOG_RETURN(BACKUP_DATA, result); 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 Get time from the database or locally depending on online
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -2305,9 +2445,11 @@ cmdBackup(void)
BackupStartResult backupStartResult = backupStart(backupData); BackupStartResult backupStartResult = backupStart(backupData);
// Build the manifest // Build the manifest
const ManifestBlockIncrMap blockIncrMap = backupBlockIncrMap();
Manifest *manifest = manifestNewBuild( Manifest *manifest = manifestNewBuild(
backupData->storagePrimary, infoPg.version, infoPg.catalogVersion, timestampStart, cfgOptionBool(cfgOptOnline), 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); strLstNewVarLst(cfgOptionLst(cfgOptExclude)), backupStartResult.tablespaceList);
// Validate the manifest using the copy start time // Validate the manifest using the copy start time

View File

@ -92,6 +92,27 @@ lstComparatorStr(const void *item1, const void *item2)
FUNCTION_TEST_RETURN(INT, strCmp(*(String **)item1, *(String **)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 FN_EXTERN int
lstComparatorZ(const void *item1, const void *item2) lstComparatorZ(const void *item1, const void *item2)

View File

@ -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 // 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); 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 // 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); FN_EXTERN int lstComparatorZ(const void *item1, const void *item2);

View File

@ -130,7 +130,7 @@ Option constants
#define CFGOPT_TYPE "type" #define CFGOPT_TYPE "type"
#define CFGOPT_VERBOSE "verbose" #define CFGOPT_VERBOSE "verbose"
#define CFG_OPTION_TOTAL 158 #define CFG_OPTION_TOTAL 160
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Option value constants Option value constants
@ -444,6 +444,8 @@ typedef enum
cfgOptRepoAzureKeyType, cfgOptRepoAzureKeyType,
cfgOptRepoAzureUriStyle, cfgOptRepoAzureUriStyle,
cfgOptRepoBlock, cfgOptRepoBlock,
cfgOptRepoBlockAgeMap,
cfgOptRepoBlockSizeMap,
cfgOptRepoBundle, cfgOptRepoBundle,
cfgOptRepoBundleLimit, cfgOptRepoBundleLimit,
cfgOptRepoBundleSize, cfgOptRepoBundleSize,

View File

@ -4888,6 +4888,64 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
), // opt/repo-block ), // opt/repo-block
), // 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 PARSE_RULE_OPTION // opt/repo-bundle
( // opt/repo-bundle ( // opt/repo-bundle
PARSE_RULE_OPTION_NAME("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 cfgOptRepoAzureKeyType, // opt-resolve-order
cfgOptRepoAzureUriStyle, // opt-resolve-order cfgOptRepoAzureUriStyle, // opt-resolve-order
cfgOptRepoBlock, // opt-resolve-order cfgOptRepoBlock, // opt-resolve-order
cfgOptRepoBlockAgeMap, // opt-resolve-order
cfgOptRepoBlockSizeMap, // opt-resolve-order
cfgOptRepoCipherPass, // opt-resolve-order cfgOptRepoCipherPass, // opt-resolve-order
cfgOptRepoGcsKeyType, // opt-resolve-order cfgOptRepoGcsKeyType, // opt-resolve-order
cfgOptRepoHost, // opt-resolve-order cfgOptRepoHost, // opt-resolve-order

View File

@ -754,80 +754,6 @@ manifestLinkCheck(const Manifest *this)
FUNCTION_LOG_RETURN_VOID(); 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 typedef struct ManifestBuildData
{ {
@ -843,8 +769,53 @@ typedef struct ManifestBuildData
ManifestLinkCheck *linkCheck; // List of links found during build (used for prefix check) ManifestLinkCheck *linkCheck; // List of links found during build (used for prefix check)
StringList *excludeContent; // Exclude contents of directories StringList *excludeContent; // Exclude contents of directories
StringList *excludeSingle; // Exclude a single file/link/path StringList *excludeSingle; // Exclude a single file/link/path
const ManifestBlockIncrMap *blockIncrMap; // Block incremental maps
} ManifestBuildData; } 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 // Process files/links/paths and add them to the manifest
static void static void
manifestBuildInfo( manifestBuildInfo(
@ -1066,7 +1037,7 @@ manifestBuildInfo(
// Get block incremental size // Get block incremental size
if (info->size != 0 && buildData->manifest->pub.data.blockIncr) 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 // Determine if this file should be page checksummed
if (dbPath && buildData->checksumPage) if (dbPath && buildData->checksumPage)
@ -1265,8 +1236,8 @@ manifestBuildInfo(
FN_EXTERN Manifest * FN_EXTERN Manifest *
manifestNewBuild( manifestNewBuild(
const Storage *const storagePg, const unsigned int pgVersion, const unsigned int pgCatalogVersion, const time_t timestampStart, 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 bool online, const bool checksumPage, const bool bundle, const bool blockIncr, const ManifestBlockIncrMap *blockIncrMap,
const Pack *const tablespaceList) const StringList *const excludeList, const Pack *const tablespaceList)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug); FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STORAGE, storagePg); FUNCTION_LOG_PARAM(STORAGE, storagePg);
@ -1277,6 +1248,7 @@ manifestNewBuild(
FUNCTION_LOG_PARAM(BOOL, checksumPage); FUNCTION_LOG_PARAM(BOOL, checksumPage);
FUNCTION_LOG_PARAM(BOOL, bundle); FUNCTION_LOG_PARAM(BOOL, bundle);
FUNCTION_LOG_PARAM(BOOL, blockIncr); FUNCTION_LOG_PARAM(BOOL, blockIncr);
FUNCTION_LOG_PARAM(VOID, blockIncrMap);
FUNCTION_LOG_PARAM(STRING_LIST, excludeList); FUNCTION_LOG_PARAM(STRING_LIST, excludeList);
FUNCTION_LOG_PARAM(PACK, tablespaceList); FUNCTION_LOG_PARAM(PACK, tablespaceList);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
@ -1315,6 +1287,7 @@ manifestNewBuild(
.tablespaceList = tablespaceList, .tablespaceList = tablespaceList,
.linkCheck = &linkCheck, .linkCheck = &linkCheck,
.manifestWalName = strNewFmt(MANIFEST_TARGET_PGDATA "/%s", strZ(pgWalPath(pgVersion))), .manifestWalName = strNewFmt(MANIFEST_TARGET_PGDATA "/%s", strZ(pgWalPath(pgVersion))),
.blockIncrMap = blockIncrMap,
}; };
// Build expressions to identify databases paths and temp relations // Build expressions to identify databases paths and temp relations

View File

@ -86,6 +86,31 @@ typedef struct ManifestData
const Variant *backupOptionProcessMax; // How many processes will be used for backup? const Variant *backupOptionProcessMax; // How many processes will be used for backup?
} ManifestData; } 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 Db type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -170,7 +195,8 @@ Constructors
// Build a new manifest for a PostgreSQL data directory // Build a new manifest for a PostgreSQL data directory
FN_EXTERN Manifest *manifestNewBuild( FN_EXTERN Manifest *manifestNewBuild(
const Storage *storagePg, unsigned int pgVersion, unsigned int pgCatalogVersion, time_t timestampStart, bool online, 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 // Load a manifest from IO
FN_EXTERN Manifest *manifestNewLoad(IoRead *read); FN_EXTERN Manifest *manifestNewLoad(IoRead *read);

View File

@ -1083,6 +1083,16 @@ testRun(void)
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("BlockIncr")) 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"); TEST_TITLE("full backup with zero block");
ioBufferSizeSet(2); ioBufferSizeSet(2);
@ -2454,7 +2464,7 @@ testRun(void)
// Create a backup manifest that looks like a halted backup manifest // Create a backup manifest that looks like a halted backup manifest
Manifest *manifestResume = manifestNewBuild( 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); ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
manifestResumeData->backupType = backupTypeFull; manifestResumeData->backupType = backupTypeFull;
@ -2547,7 +2557,7 @@ testRun(void)
// Create a backup manifest that looks like a halted backup manifest // Create a backup manifest that looks like a halted backup manifest
Manifest *manifestResume = manifestNewBuild( 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); ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
manifestResumeData->backupType = backupTypeFull; manifestResumeData->backupType = backupTypeFull;
@ -2734,7 +2744,7 @@ testRun(void)
// Create a backup manifest that looks like a halted backup manifest // Create a backup manifest that looks like a halted backup manifest
Manifest *manifestResume = manifestNewBuild( 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); ManifestData *manifestResumeData = (ManifestData *)manifestData(manifestResume);
manifestResumeData->backupType = backupTypeDiff; manifestResumeData->backupType = backupTypeDiff;
@ -3643,6 +3653,12 @@ testRun(void)
backupTimeStart = BACKUP_EPOCH + 2800000; 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 // Remove old pg data
HRN_STORAGE_PATH_REMOVE(storageTest, "pg1", .recurse = true); HRN_STORAGE_PATH_REMOVE(storageTest, "pg1", .recurse = true);
@ -3664,24 +3680,26 @@ testRun(void)
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true); hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB"); hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB");
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true); 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); HRN_CFG_LOAD(cfgCmdBackup, argList);
// File that uses block incr and will grow // 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)); memset(bufPtr(file), 0, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart); HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart);
// File that shrinks below the limit // 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)); memset(bufPtr(file), 55, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart); HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart);
// File that grows above the limit // 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)); memset(bufPtr(file), 77, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
@ -3762,32 +3780,34 @@ testRun(void)
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true); hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB"); hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "256kB");
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true); 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); 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 // 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. // 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)); memset(bufPtr(file), 0, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart); HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart);
// File that gets a large block size // 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)); memset(bufPtr(file), 0, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-larger", file, .timeModified = backupTimeStart); HRN_STORAGE_PUT(storagePgWrite(), "block-incr-larger", file, .timeModified = backupTimeStart);
// Shrink file below the limit // 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)); memset(bufPtr(file), 55, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart); HRN_STORAGE_PUT(storagePgWrite(), "block-incr-shrink", file, .timeModified = backupTimeStart);
// Grow file above the limit // 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)); memset(bufPtr(file), 77, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
@ -3892,13 +3912,15 @@ testRun(void)
hrnCfgArgRawBool(argList, cfgOptRepoBundle, true); hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "8KiB"); hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "8KiB");
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true); 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, cfgOptBufferSize, "16KiB");
hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc"); hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc");
hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS); hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS);
HRN_CFG_LOAD(cfgCmdBackup, argList); HRN_CFG_LOAD(cfgCmdBackup, argList);
// File that uses block incr and will grow // 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)); memset(bufPtr(file), 0, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
@ -3975,16 +3997,24 @@ testRun(void)
hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "4MiB"); hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "4MiB");
hrnCfgArgRawBool(argList, cfgOptRepoBlock, true); hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc"); hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc");
hrnCfgArgRawZ(argList, cfgOptRepoBlockAgeMap, "1=2");
hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS); hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS);
HRN_CFG_LOAD(cfgCmdBackup, argList); HRN_CFG_LOAD(cfgCmdBackup, argList);
// File that uses block incr and grows // 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)); memset(bufPtr(file), 0, bufSize(file));
bufUsedSet(file, bufSize(file)); bufUsedSet(file, bufSize(file));
HRN_STORAGE_PUT(storagePgWrite(), "block-incr-grow", file, .timeModified = backupTimeStart); 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 // Run backup
testBackupPqScriptP( testBackupPqScriptP(
PG_VERSION_11, backupTimeStart, .walCompressType = compressTypeNone, .cipherType = cipherTypeAes256Cbc, 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: 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: backup start archive = 0000000105DC82D000000000, lsn = 5dc82d0/0\n"
"P00 INFO: check archive for segment 0000000105DC82D000000000\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-age-multiplier (bundle 1/0, 256KB, [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/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 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: execute non-exclusive backup stop and wait for all WAL segments to archive\n"
"P00 INFO: backup stop archive = 0000000105DC82D000000001, lsn = 5dc82d0/300000\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 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
"P00 INFO: check archive for segment(s) 0000000105DC82D000000000:0000000105DC82D000000001\n" "P00 INFO: check archive for segment(s) 0000000105DC82D000000000:0000000105DC82D000000001\n"
"P00 INFO: new backup label = 20191108-080000F_20191110-153320D\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( TEST_RESULT_STR_Z(
testBackupValidateP( testBackupValidateP(
@ -4013,6 +4044,7 @@ testRun(void)
.cipherPass = TEST_CIPHER_PASS), .cipherPass = TEST_CIPHER_PASS),
". {link, d=20191108-080000F_20191110-153320D}\n" ". {link, d=20191108-080000F_20191110-153320D}\n"
"bundle {path}\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/block-incr-grow {file, m={0,1}, s=262144}\n"
"bundle/1/pg_data/global/pg_control {file, s=8192}\n" "bundle/1/pg_data/global/pg_control {file, s=8192}\n"
"pg_data {path}\n" "pg_data {path}\n"
@ -4027,6 +4059,8 @@ testRun(void)
",\"size\":2,\"timestamp\":1572800000}\n" ",\"size\":2,\"timestamp\":1572800000}\n"
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17" "pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
",\"timestamp\":1573400002}\n" ",\"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\"" "pg_data/block-incr-grow={\"bims\":72,\"bis\":16,\"checksum\":\"2e000fa7e85759c7f4c254d4d9c33ef481e459a7\""
",\"size\":262144,\"timestamp\":1573400000}\n" ",\"size\":262144,\"timestamp\":1573400000}\n"
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1573400000}\n" "pg_data/global/pg_control={\"size\":8192,\"timestamp\":1573400000}\n"

View File

@ -3072,7 +3072,7 @@ testRun(void)
TEST_TITLE("full backup with block incr"); TEST_TITLE("full backup with block incr");
// Zeroed file large enough to use 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)); memset(bufPtr(relation), 0, bufSize(relation));
bufUsedSet(relation, bufSize(relation)); bufUsedSet(relation, bufSize(relation));

View File

@ -189,6 +189,16 @@ testRun(void)
TEST_RESULT_INT(*(int *)lstGet(list, 2), 3, "sort value 2"); TEST_RESULT_INT(*(int *)lstGet(list, 2), 3, "sort value 2");
TEST_RESULT_INT(*(int *)lstGet(list, 3), 2, "sort value 3"); 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()"); TEST_TITLE("lstComparatorZ()");

View File

@ -296,7 +296,7 @@ testRun(void)
// Test tablespace error // Test tablespace error
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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)), pckWriteResult(tablespaceList)),
AssertError, AssertError,
"tablespace with oid 1 not found in tablespace map\n" "tablespace with oid 1 not found in tablespace map\n"
@ -321,7 +321,7 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
manifest, manifest,
manifestNewBuild( 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)), pckWriteResult(tablespaceList)),
"build manifest"); "build manifest");
TEST_RESULT_VOID(manifestBackupLabelSet(manifest, STRDEF("20190818-084502F")), "backup label set"); TEST_RESULT_VOID(manifestBackupLabelSet(manifest, STRDEF("20190818-084502F")), "backup label set");
@ -420,7 +420,7 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
manifest, manifest,
manifestNewBuild( 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"); "build manifest");
contentSave = bufNew(0); contentSave = bufNew(0);
@ -496,7 +496,7 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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, LinkDestinationError,
"link 'pg_xlog/wal' (" TEST_PATH "/wal) destination is the same directory as link 'pg_xlog' (" TEST_PATH "/wal)"); "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( TEST_ASSIGN(
manifest, manifest,
manifestNewBuild( 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"); "build manifest");
contentSave = bufNew(0); contentSave = bufNew(0);
@ -647,7 +647,7 @@ testRun(void)
// Tablespace link errors when correct verion not found // Tablespace link errors when correct verion not found
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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, FileOpenError,
"unable to get info for missing path/file '" TEST_PATH "/pg/pg_tblspc/1/PG_12_201909212': [2] No such file or" "unable to get info for missing path/file '" TEST_PATH "/pg/pg_tblspc/1/PG_12_201909212': [2] No such file or"
" directory"); " directory");
@ -668,7 +668,7 @@ testRun(void)
TEST_ASSIGN( TEST_ASSIGN(
manifest, manifest,
manifestNewBuild( 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"); "build manifest");
contentSave = bufNew(0); 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 // 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)); 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 // pg_wal not ignored
TEST_ASSIGN( TEST_ASSIGN(
manifest, manifest,
manifestNewBuild( 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"); "build manifest");
contentSave = bufNew(0); contentSave = bufNew(0);
@ -831,7 +854,7 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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"); 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"); THROW_ON_SYS_ERROR(unlink(TEST_PATH "/pg/link") == -1, FileRemoveError, "unable to remove symlink");
@ -843,7 +866,7 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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"); 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"); HRN_STORAGE_PATH_REMOVE(storagePgWrite, MANIFEST_TARGET_PGTBLSPC "/somedir");
@ -855,7 +878,7 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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"); 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); TEST_STORAGE_EXISTS(storagePgWrite, MANIFEST_TARGET_PGTBLSPC "/somefile", .remove = true);
@ -867,7 +890,7 @@ testRun(void)
TEST_ERROR( TEST_ERROR(
manifestNewBuild( 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, FileOpenError,
"unable to get info for missing path/file '" TEST_PATH "/pg/link-to-link': [2] No such file or directory"); "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( TEST_ERROR(
manifestNewBuild( 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'"); LinkDestinationError, "link '" TEST_PATH "/pg/linktolink' cannot reference another link '" TEST_PATH "/linktest'");
#undef TEST_MANIFEST_HEADER #undef TEST_MANIFEST_HEADER

View File

@ -272,7 +272,7 @@ testRun(void)
MEM_CONTEXT_BEGIN(testContext) MEM_CONTEXT_BEGIN(testContext)
{ {
TEST_ASSIGN( 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"); "build files");
} }
MEM_CONTEXT_END(); MEM_CONTEXT_END();