Skip to content

Commit 161a32e

Browse files
committed
pgmoneta#1069 fix: S3 restore and upload atomicity with metadata safeguards
Signed-off-by: Amr-Shams <amr.shams2015.as@gmail.com>
1 parent 49e6028 commit 161a32e

2 files changed

Lines changed: 107 additions & 34 deletions

File tree

doc/manual/en/17-s3.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,13 @@ pgmoneta-cli s3 delete primary <prefix>
9999
```
100100

101101
`pgmoneta-cli s3 delete` removes all objects under `<s3_base_dir>/<server>/backup/<prefix>`.
102+
103+
Restore a backup from S3:
104+
105+
``` sh
106+
pgmoneta-cli s3 restore primary <label>
107+
```
108+
109+
`pgmoneta-cli s3 restore` downloads all files from S3 for the given backup label into the local backup directory.
110+
111+
Metadata files (`backup.info`, `backup.sha512`, `backup.manifest`) are downloaded as temporary files first, then renamed in order after all data files are restored. `backup.info` is renamed last and its presence marks a complete restore.

src/libpgmoneta/se_s3.c

Lines changed: 97 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,14 @@ s3_storage_restore(char* name __attribute__((unused)), struct art* nodes)
513513
int server = -1;
514514
char* label = NULL;
515515
char* s3_root = NULL;
516-
char* temp_label = NULL;
517516
char* local_root = NULL;
518-
char* final_root = NULL;
519517
char* base_dir = NULL;
518+
char* manifest_tmp = NULL;
519+
char* manifest_final = NULL;
520+
char* sha512_tmp = NULL;
521+
char* sha512_final = NULL;
522+
char* info_tmp = NULL;
523+
char* info_final = NULL;
520524
struct backup* backup = NULL;
521525
struct main_configuration* config;
522526

@@ -540,10 +544,7 @@ s3_storage_restore(char* name __attribute__((unused)), struct art* nodes)
540544
s3_get_effective_endpoint(server));
541545

542546
s3_root = s3_get_basepath(server, label);
543-
544-
temp_label = pgmoneta_append(temp_label, ".pgmoneta_temp_");
545-
temp_label = pgmoneta_append(temp_label, label);
546-
local_root = pgmoneta_get_server_backup_identifier(server, temp_label);
547+
local_root = pgmoneta_get_server_backup_identifier(server, label);
547548

548549
if (s3_bootstrap(s3_root, server, local_root))
549550
{
@@ -552,7 +553,7 @@ s3_storage_restore(char* name __attribute__((unused)), struct art* nodes)
552553

553554
base_dir = pgmoneta_get_server_backup(server);
554555

555-
if (pgmoneta_load_info(base_dir, temp_label, &backup))
556+
if (pgmoneta_load_info(base_dir, label, &backup))
556557
{
557558
pgmoneta_log_error("S3 restore: failed to load backup.info from %s", local_root);
558559
goto error;
@@ -565,38 +566,84 @@ s3_storage_restore(char* name __attribute__((unused)), struct art* nodes)
565566
goto error;
566567
}
567568

568-
final_root = pgmoneta_get_server_backup_identifier(server, label);
569+
manifest_tmp = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.manifest.tmp");
570+
manifest_final = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.manifest");
571+
sha512_tmp = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.sha512.tmp");
572+
sha512_final = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.sha512");
573+
info_tmp = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.info.tmp");
574+
info_final = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.info");
575+
576+
if (rename(manifest_tmp, manifest_final))
577+
{
578+
pgmoneta_log_error("S3 restore: could not rename %s to %s", manifest_tmp, manifest_final);
579+
goto error;
580+
}
581+
582+
if (rename(sha512_tmp, sha512_final))
583+
{
584+
pgmoneta_log_error("S3 restore: could not rename %s to %s", sha512_tmp, sha512_final);
585+
goto error;
586+
}
569587

570-
if (rename(local_root, final_root))
588+
if (rename(info_tmp, info_final))
571589
{
572-
pgmoneta_log_error("S3 restore: could not rename %s to %s", local_root, final_root);
590+
pgmoneta_log_error("S3 restore: could not rename %s to %s", info_tmp, info_final);
573591
goto error;
574592
}
575593

576594
pgmoneta_log_info("S3 restore: %s/%s completed", config->common.servers[server].name, label);
577595

578596
free(s3_root);
579-
free(temp_label);
580597
free(local_root);
581-
free(final_root);
582598
free(base_dir);
583599
free(backup);
600+
free(manifest_tmp);
601+
free(manifest_final);
602+
free(sha512_tmp);
603+
free(sha512_final);
604+
free(info_tmp);
605+
free(info_final);
584606

585607
return 0;
586608

587609
error:
588610

589611
if (local_root != NULL)
590612
{
591-
pgmoneta_delete_directory(local_root);
613+
char* cleanup = NULL;
614+
615+
cleanup = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.manifest.tmp");
616+
if (pgmoneta_exists(cleanup))
617+
{
618+
pgmoneta_delete_file(cleanup, NULL);
619+
}
620+
free(cleanup);
621+
622+
cleanup = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.sha512.tmp");
623+
if (pgmoneta_exists(cleanup))
624+
{
625+
pgmoneta_delete_file(cleanup, NULL);
626+
}
627+
free(cleanup);
628+
629+
cleanup = pgmoneta_append(pgmoneta_append(NULL, local_root), "backup.info.tmp");
630+
if (pgmoneta_exists(cleanup))
631+
{
632+
pgmoneta_delete_file(cleanup, NULL);
633+
}
634+
free(cleanup);
592635
}
593636

594637
free(s3_root);
595-
free(temp_label);
596638
free(local_root);
597-
free(final_root);
598639
free(base_dir);
599640
free(backup);
641+
free(manifest_tmp);
642+
free(manifest_final);
643+
free(sha512_tmp);
644+
free(sha512_final);
645+
free(info_tmp);
646+
free(info_final);
600647
return 1;
601648
}
602649

@@ -629,7 +676,12 @@ s3_bootstrap(char* s3_root, int server, char* local_root)
629676
}
630677

631678
sha512_path = pgmoneta_append(sha512_path, local_root);
632-
sha512_path = pgmoneta_append(sha512_path, "backup.sha512");
679+
sha512_path = pgmoneta_append(sha512_path, "backup.sha512.tmp");
680+
681+
if (pgmoneta_exists(sha512_path))
682+
{
683+
pgmoneta_delete_file(sha512_path, NULL);
684+
}
633685

634686
if (pgmoneta_append_file_chunk(sha512_path, response->payload.data, response->payload.data_size, 0))
635687
{
@@ -654,7 +706,12 @@ s3_bootstrap(char* s3_root, int server, char* local_root)
654706
}
655707

656708
info_path = pgmoneta_append(info_path, local_root);
657-
info_path = pgmoneta_append(info_path, "backup.info");
709+
info_path = pgmoneta_append(info_path, "backup.info.tmp");
710+
711+
if (pgmoneta_exists(info_path))
712+
{
713+
pgmoneta_delete_file(info_path, NULL);
714+
}
658715

659716
if (pgmoneta_append_file_chunk(info_path, response->payload.data, response->payload.data_size, 0))
660717
{
@@ -720,7 +777,12 @@ s3_bootstrap(char* s3_root, int server, char* local_root)
720777
}
721778

722779
manifest_path = pgmoneta_append(manifest_path, local_root);
723-
manifest_path = pgmoneta_append(manifest_path, "backup.manifest");
780+
manifest_path = pgmoneta_append(manifest_path, "backup.manifest.tmp");
781+
782+
if (pgmoneta_exists(manifest_path))
783+
{
784+
pgmoneta_delete_file(manifest_path, NULL);
785+
}
724786

725787
if (pgmoneta_append_file_chunk(manifest_path, response->payload.data, response->payload.data_size, 0))
726788
{
@@ -771,7 +833,7 @@ s3_download_files(char* s3_root, char* local_root, int server, int compression,
771833
struct worker_input* payload = NULL;
772834

773835
manifest_path = pgmoneta_append(manifest_path, local_root);
774-
manifest_path = pgmoneta_append(manifest_path, "backup.manifest");
836+
manifest_path = pgmoneta_append(manifest_path, "backup.manifest.tmp");
775837

776838
if (pgmoneta_extraction_get_suffix(compression, encryption, &suffix))
777839
{
@@ -891,6 +953,11 @@ do_download_file(struct worker_common* wc)
891953
goto error;
892954
}
893955

956+
if (pgmoneta_exists(local_path))
957+
{
958+
pgmoneta_delete_file(local_path, NULL);
959+
}
960+
894961
if (pgmoneta_append_file_chunk(local_path, response->payload.data, response->payload.data_size, 0))
895962
{
896963
pgmoneta_log_error("S3 download: failed to write %s", local_path);
@@ -954,19 +1021,6 @@ s3_upload_files(char* local_root, char* s3_root, int server, int compression, in
9541021
pgmoneta_workers_initialize(number_of_workers, &workers);
9551022
}
9561023

957-
/* upload backup.info and backup.sha512 first */
958-
if (s3_send_upload_request(local_root, s3_root, "backup.info", server))
959-
{
960-
pgmoneta_log_error("S3 upload: failed to upload backup.info");
961-
goto error;
962-
}
963-
964-
if (s3_send_upload_request(local_root, s3_root, "backup.sha512", server))
965-
{
966-
pgmoneta_log_error("S3 upload: failed to upload backup.sha512");
967-
goto error;
968-
}
969-
9701024
if (pgmoneta_manifest_get_paths(manifest_path, &paths))
9711025
{
9721026
pgmoneta_log_error("S3 upload: failed to read manifest %s", manifest_path);
@@ -1022,13 +1076,22 @@ s3_upload_files(char* local_root, char* s3_root, int server, int compression, in
10221076
iter = NULL;
10231077
paths = NULL;
10241078

1025-
/* upload backup.manifest last (commit marker) */
1079+
/* upload metadata file last (commit marker) */
10261080
if (s3_send_upload_request(local_root, s3_root, "backup.manifest", server))
10271081
{
10281082
pgmoneta_log_error("S3 upload: failed to upload backup.manifest");
10291083
goto error;
10301084
}
1031-
1085+
if (s3_send_upload_request(local_root, s3_root, "backup.sha512", server))
1086+
{
1087+
pgmoneta_log_error("S3 upload: failed to upload backup.sha512");
1088+
goto error;
1089+
}
1090+
if (s3_send_upload_request(local_root, s3_root, "backup.info", server))
1091+
{
1092+
pgmoneta_log_error("S3 upload: failed to upload backup.info");
1093+
goto error;
1094+
}
10321095
free(manifest_path);
10331096
free(suffix);
10341097

0 commit comments

Comments
 (0)