Skip to content

Commit ee349b3

Browse files
authored
ADBM-2913: Implement repo-push command (#99)
Our users need the ability to quickly restore tables (or sets of tables) from a backup without the need to restore whole database. This accelerates recovery operations after incidents such as accidenatal deletion/corruption of data and saves disk space and time. To support it the repo-push command is designed to manage metadata files in the pgBackRest repository. This command allows saving table metadata associated with restore points. The metadata is stored in the same repository as the restore points and inherits the compression settings from the main pgBackRest configuration file. This commit implements repo-push command. ADBM-2913
1 parent 3113e80 commit ee349b3

18 files changed

Lines changed: 1193 additions & 178 deletions

File tree

src/Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ SRCS = \
8383
command/repo/common.c \
8484
command/repo/get.c \
8585
command/repo/ls.c \
86+
command/repo/push.c \
8687
command/repo/put.c \
8788
command/repo/rm.c \
8889
command/restore/blockChecksum.c \

src/build/config/config.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ command:
107107
log-level-default: DEBUG
108108
parameter-allowed: true
109109

110+
repo-push:
111+
command-role:
112+
async: {}
113+
local: {}
114+
remote: {}
115+
internal: true
116+
log-file: false
117+
log-level-default: DEBUG
118+
parameter-allowed: true
119+
110120
repo-put:
111121
command-role:
112122
remote: {}
@@ -328,6 +338,7 @@ option:
328338
backup: {}
329339
check: {}
330340
expire: {}
341+
repo-push: {}
331342
restore: {}
332343
stanza-create: {}
333344
stanza-upgrade: {}
@@ -392,6 +403,8 @@ option:
392403
manifest:
393404
default: latest
394405
required: true
406+
repo-push:
407+
required: true
395408
restore:
396409
default: latest
397410
required: true
@@ -425,6 +438,8 @@ option:
425438
required: false
426439
repo-ls:
427440
required: false
441+
repo-push:
442+
required: true
428443
repo-put:
429444
required: false
430445
repo-rm:
@@ -572,6 +587,7 @@ option:
572587
manifest: {}
573588
repo-get: {}
574589
repo-ls: {}
590+
repo-push: {}
575591
repo-put: {}
576592
repo-rm: {}
577593
restore: {}
@@ -614,6 +630,7 @@ option:
614630
manifest: {}
615631
repo-get: {}
616632
repo-ls: {}
633+
repo-push: {}
617634
repo-put: {}
618635
repo-rm: {}
619636
restore: {}
@@ -785,6 +802,7 @@ option:
785802
command:
786803
archive-push: {}
787804
backup: {}
805+
repo-push: {}
788806
command-role:
789807
main: {}
790808

@@ -812,6 +830,7 @@ option:
812830
manifest: {}
813831
repo-get: {}
814832
repo-ls: {}
833+
repo-push: {}
815834
repo-put: {}
816835
restore: {}
817836
stanza-create: {}
@@ -1731,6 +1750,9 @@ option:
17311750
command-role:
17321751
main: {}
17331752
remote: {}
1753+
repo-push:
1754+
command-role:
1755+
main: {}
17341756
repo-put:
17351757
command-role:
17361758
main: {}
@@ -1851,6 +1873,10 @@ option:
18511873
command-role:
18521874
main: {}
18531875
remote: {}
1876+
repo-push:
1877+
command-role:
1878+
main: {}
1879+
remote: {}
18541880
repo-put:
18551881
command-role:
18561882
main: {}

src/build/help/help.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,6 +2414,27 @@
24142414
</option-list>
24152415
</command>
24162416

2417+
<command id="repo-push" name="Repository Push File">
2418+
<summary>Upload a metadata file to the repository.</summary>
2419+
2420+
<text>
2421+
<p>Upload a metadata file to the repository. The metadata file is stored in the same storage as the restore point and inherits the compression settings defined in the pgBackRest configuration.</p>
2422+
</text>
2423+
2424+
<option-list>
2425+
<option id="set" name="Set">
2426+
<summary>Backup set.</summary>
2427+
2428+
<text>
2429+
<p>Identifier of the backup set (restore point). The metadata file will be placed in this set's directory.</p>
2430+
</text>
2431+
2432+
<example>20150131-153358F_20150131-153401I</example>
2433+
</option>
2434+
</option-list>
2435+
2436+
</command>
2437+
24172438
<command id="repo-put" name="Repository Put File">
24182439
<summary>Put a file in the repository.</summary>
24192440

src/command/repo/push.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/***********************************************************************************************************************************
2+
Repository Put Command
3+
***********************************************************************************************************************************/
4+
#include "build.auto.h"
5+
6+
#include <unistd.h>
7+
8+
#include "command/repo/common.h"
9+
#include "common/compress/helper.h"
10+
#include "common/debug.h"
11+
#include "common/io/fdRead.h"
12+
#include "common/io/filter/size.h"
13+
#include "common/io/io.h"
14+
#include "common/log.h"
15+
#include "common/memContext.h"
16+
#include "common/type/string.h"
17+
#include "config/config.h"
18+
#include "info/manifest.h"
19+
#include "storage/helper.h"
20+
21+
static String *
22+
composeDestinationPath(const String *backupLabel, const String *fileName)
23+
{
24+
FUNCTION_LOG_BEGIN(logLevelDebug);
25+
FUNCTION_LOG_PARAM(STRING, backupLabel);
26+
FUNCTION_LOG_PARAM(STRING, fileName);
27+
FUNCTION_LOG_END();
28+
29+
String *const result = strNewFmt(STORAGE_REPO_BACKUP "/%s/%s", strZ(backupLabel), strZ(fileName));
30+
31+
FUNCTION_LOG_RETURN(STRING, result);
32+
}
33+
34+
/***********************************************************************************************************************************
35+
Write source IO to destination file
36+
***********************************************************************************************************************************/
37+
static void
38+
storagePushProcess(const String *file, CompressType compressType, int compressLevel)
39+
{
40+
FUNCTION_LOG_BEGIN(logLevelDebug);
41+
FUNCTION_LOG_PARAM(STRING, file);
42+
FUNCTION_LOG_PARAM(ENUM, compressType);
43+
FUNCTION_LOG_PARAM(INT, compressLevel);
44+
FUNCTION_LOG_END();
45+
46+
// Ensure that the file exists and readable
47+
MEM_CONTEXT_TEMP_BEGIN()
48+
{
49+
// Normalize source file path
50+
// Get current working dir
51+
char currentWorkDir[1024];
52+
THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd");
53+
54+
String *sourcePath = strPathAbsolute(file, strNewZ(currentWorkDir));
55+
56+
// Repository Path Formation
57+
58+
const String *backupLabel = cfgOptionStr(cfgOptSet);
59+
60+
String *destFilename = strCat(strNew(), strFileName(file));
61+
compressExtCat(destFilename, compressType);
62+
63+
String *destPath = composeDestinationPath(backupLabel, destFilename);
64+
65+
// Is path valid for repo?
66+
destPath = repoPathIsValid(destPath);
67+
68+
bool repoChecksum = false;
69+
const Storage *storage = storageRepoWrite();
70+
const StorageWrite *const destination = storageNewWriteP(storage, destPath);
71+
72+
IoRead *const sourceIo = storageReadIo(storageNewReadP(storageLocal(), sourcePath));
73+
IoWrite *const destinationIo = storageWriteIo(destination);
74+
75+
IoFilterGroup *const readFilterGroup = ioReadFilterGroup(sourceIo);
76+
IoFilterGroup *const writeFilterGroup = ioWriteFilterGroup(destinationIo);
77+
78+
const String *manifestFileName = strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel));
79+
Manifest *manifest = manifestLoadFile(
80+
storage, manifestFileName,
81+
cipherTypeNone, NULL);
82+
83+
// Add SHA1 filter
84+
ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1));
85+
86+
// Add compression
87+
if (compressType != compressTypeNone)
88+
{
89+
ioFilterGroupAdd(
90+
writeFilterGroup,
91+
compressFilterP(compressType, compressLevel));
92+
93+
repoChecksum = true;
94+
}
95+
96+
// Capture checksum of file stored in the repo if filters that modify the output have been applied
97+
if (repoChecksum)
98+
ioFilterGroupAdd(writeFilterGroup, cryptoHashNew(hashTypeSha1));
99+
100+
// Add size filter last to calculate repo size
101+
ioFilterGroupAdd(writeFilterGroup, ioSizeNew());
102+
103+
// Add size filter last to calculate source file size
104+
ioFilterGroupAdd(readFilterGroup, ioSizeNew());
105+
106+
// Open source and destination
107+
ioReadOpen(sourceIo);
108+
ioWriteOpen(destinationIo);
109+
110+
// Copy data from source to destination
111+
ioCopyP(sourceIo, destinationIo);
112+
113+
// Close the source and destination
114+
ioReadClose(sourceIo);
115+
ioWriteClose(destinationIo);
116+
117+
// Use base path to set ownership and mode
118+
const ManifestPath *const basePath = manifestPathFind(manifest, MANIFEST_TARGET_PGDATA_STR);
119+
120+
// Add to manifest
121+
uint64_t rsize = pckReadU64P(ioFilterGroupResultP(readFilterGroup, SIZE_FILTER_TYPE));
122+
uint64_t wsize = pckReadU64P(ioFilterGroupResultP(writeFilterGroup, SIZE_FILTER_TYPE));
123+
ManifestFile customFile =
124+
{
125+
.name = destFilename,
126+
.mode = basePath->mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
127+
.size = wsize,
128+
.sizeOriginal = rsize,
129+
.sizeRepo = wsize,
130+
.timestamp = time(NULL),
131+
.checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))),
132+
};
133+
134+
if (repoChecksum)
135+
{
136+
PackRead *packRead = ioFilterGroupResultP(writeFilterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 1);
137+
ASSERT(packRead != NULL);
138+
customFile.checksumRepoSha1 = bufPtr(pckReadBinP(packRead));
139+
}
140+
141+
bool found = false;
142+
143+
for (unsigned int fileIdx = 0; fileIdx < manifestCustomFileTotal(manifest); fileIdx++)
144+
{
145+
ManifestFile manifestFile = manifestCustomFile(manifest, fileIdx);
146+
147+
if (strCmp(manifestFile.name, destFilename) == 0){
148+
manifestFile.mode = customFile.mode;
149+
manifestFile.size = customFile.size;
150+
manifestFile.sizeOriginal = customFile.sizeOriginal;
151+
manifestFile.sizeRepo = customFile.sizeRepo;
152+
manifestFile.timestamp = customFile.timestamp;
153+
manifestFile.checksumSha1 = customFile.checksumSha1;
154+
155+
manifestCustomFileUpdate(manifest, &manifestFile);
156+
found = true;
157+
break;
158+
}
159+
}
160+
161+
if (!found)
162+
manifestCustomFileAdd(manifest, &customFile);
163+
164+
// Save manifest
165+
IoWrite *const manifestWrite = storageWriteIo(
166+
storageNewWriteP(
167+
storageRepoWrite(),
168+
manifestFileName
169+
));
170+
171+
manifestSave(manifest, manifestWrite);
172+
}
173+
MEM_CONTEXT_TEMP_END();
174+
175+
FUNCTION_LOG_RETURN_VOID();
176+
}
177+
178+
/**********************************************************************************************************************************/
179+
FN_EXTERN void
180+
cmdStoragePush(void)
181+
{
182+
FUNCTION_LOG_VOID(logLevelDebug);
183+
184+
MEM_CONTEXT_TEMP_BEGIN()
185+
{
186+
const StringList *params = cfgCommandParam();
187+
188+
if (strLstSize(params) != 1)
189+
THROW(ParamInvalidError, "exactly one parameter is required");
190+
191+
String *filename = strLstGet(params, 0);
192+
193+
// Check that the filename does not end with a slash
194+
if (strEndsWith(filename, FSLASH_STR))
195+
THROW(ParamInvalidError, "file parameter should not end with a slash");
196+
197+
LOG_INFO_FMT(
198+
"push file %s to the archive.",
199+
strZ(filename));
200+
201+
storagePushProcess(filename,
202+
compressTypeEnum(cfgOptionStrId(cfgOptCompressType)),
203+
cfgOptionInt(cfgOptCompressLevel));
204+
}
205+
MEM_CONTEXT_TEMP_END();
206+
207+
FUNCTION_LOG_RETURN_VOID();
208+
}

src/command/repo/push.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/***********************************************************************************************************************************
2+
Repository Push Command
3+
***********************************************************************************************************************************/
4+
#ifndef COMMAND_STORAGE_PUSH_H
5+
#define COMMAND_STORAGE_PUSH_H
6+
7+
/***********************************************************************************************************************************
8+
Functions
9+
***********************************************************************************************************************************/
10+
// Push a metadata file into the repository
11+
FN_EXTERN void cmdStoragePush(void);
12+
13+
#endif

src/common/type/string.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,27 @@ strPath(const String *const this)
796796
end - this->pub.buffer <= 1 ? (size_t)(end - this->pub.buffer) : (size_t)(end - this->pub.buffer - 1)));
797797
}
798798

799+
/**********************************************************************************************************************************/
800+
FN_EXTERN String *
801+
strFileName(const String *const this)
802+
{
803+
FUNCTION_TEST_BEGIN();
804+
FUNCTION_TEST_PARAM(STRING, this);
805+
FUNCTION_TEST_END();
806+
807+
ASSERT(this != NULL);
808+
809+
const char *end = this->pub.buffer + strSize(this);
810+
const char *ptr = end;
811+
812+
while (ptr > this->pub.buffer && *(ptr - 1) != '/')
813+
ptr--;
814+
815+
FUNCTION_TEST_RETURN(
816+
STRING,
817+
strNewZN(ptr, (size_t)(end - ptr)));
818+
}
819+
799820
/**********************************************************************************************************************************/
800821
FN_EXTERN String *
801822
strPathAbsolute(const String *const this, const String *const base)

src/common/type/string.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ FN_EXTERN String *strLower(String *this);
197197
// Return the path part of a string (i.e. everything before the last / or "" if there is no /)
198198
FN_EXTERN String *strPath(const String *this);
199199

200+
// Return the file name part of a string (i.e. everything after the last / or "" if there is no /)
201+
FN_EXTERN String *strFileName(const String *const this);
202+
200203
// Combine with a base path to get an absolute path
201204
FN_EXTERN String *strPathAbsolute(const String *this, const String *base);
202205

0 commit comments

Comments
 (0)