Skip to content

Commit 49163b3

Browse files
committed
[Issue #83] data corruption as a result of failed merge
1 parent e495a9a commit 49163b3

File tree

1 file changed

+40
-21
lines changed

1 file changed

+40
-21
lines changed

src/merge.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ do_merge(time_t backup_id)
160160

161161
/*
162162
* Merge two backups data files using threads.
163+
* - to_backup - FULL, from_backup - incremental.
163164
* - move instance files from from_backup to to_backup
164165
* - remove unnecessary directories and files from to_backup
165166
* - update metadata of from_backup, it becames FULL backup
@@ -355,9 +356,12 @@ merge_backups(pgBackup *to_backup, pgBackup *from_backup)
355356
pgFile *file = (pgFile *) parray_get(files, i);
356357

357358
if (S_ISDIR(file->mode))
359+
{
358360
to_backup->data_bytes += 4096;
361+
continue;
362+
}
359363
/* Count the amount of the data actually copied */
360-
else if (S_ISREG(file->mode))
364+
if (file->write_size > 0)
361365
to_backup->data_bytes += file->write_size;
362366
}
363367
/* compute size of wal files of this backup stored in the archive */
@@ -381,7 +385,7 @@ merge_backups(pgBackup *to_backup, pgBackup *from_backup)
381385
/*
382386
* Delete files which are not in from_backup file list.
383387
*/
384-
parray_qsort(files, pgFileComparePathDesc);
388+
parray_qsort(files, pgFileComparePathWithExternalDesc);
385389
for (i = 0; i < parray_num(to_files); i++)
386390
{
387391
pgFile *file = (pgFile *) parray_get(to_files, i);
@@ -394,7 +398,7 @@ merge_backups(pgBackup *to_backup, pgBackup *from_backup)
394398
continue;
395399
}
396400

397-
if (parray_bsearch(files, file, pgFileComparePathDesc) == NULL)
401+
if (parray_bsearch(files, file, pgFileComparePathWithExternalDesc) == NULL)
398402
{
399403
char to_file_path[MAXPGPATH];
400404
char *prev_path;
@@ -507,7 +511,7 @@ merge_files(void *arg)
507511
(!file->is_datafile || file->is_cfs))
508512
{
509513
elog(VERBOSE, "Skip merging file \"%s\", the file didn't change",
510-
file->path);
514+
file->rel_path);
511515

512516
/*
513517
* If the file wasn't changed, retreive its
@@ -529,6 +533,10 @@ merge_files(void *arg)
529533
}
530534
}
531535

536+
/* TODO optimization: file from incremental backup has size 0, then
537+
* just truncate the file from FULL backup
538+
*/
539+
532540
/* We need to make full path, file object has relative path */
533541
if (file->external_dir_num)
534542
{
@@ -560,46 +568,51 @@ merge_files(void *arg)
560568
if (to_backup->compress_alg == PGLZ_COMPRESS ||
561569
to_backup->compress_alg == ZLIB_COMPRESS)
562570
{
571+
char merge_to_file_path[MAXPGPATH];
563572
char tmp_file_path[MAXPGPATH];
564573
char *prev_path;
565574

575+
snprintf(merge_to_file_path, MAXPGPATH, "%s_merge", to_file_path);
566576
snprintf(tmp_file_path, MAXPGPATH, "%s_tmp", to_file_path);
567577

568578
/* Start the magic */
569579

570580
/*
571581
* Merge files:
572-
* - if target file exists restore and decompress it to the temp
573-
* path
582+
* - if to_file in FULL backup exists, restore and decompress it to to_file_merge
574583
* - decompress source file if necessary and merge it with the
575-
* target decompressed file
576-
* - compress result file
584+
* target decompressed file in to_file_merge.
585+
* - compress result file to to_file_tmp
586+
* - rename to_file_tmp to to_file
577587
*/
578588

579589
/*
580-
* We need to decompress target file if it exists.
590+
* We need to decompress target file in FULL backup if it exists.
581591
*/
582592
if (to_file)
583593
{
584594
elog(VERBOSE, "Merge target and source files into the temporary path \"%s\"",
585-
tmp_file_path);
595+
merge_to_file_path);
586596

587597
/*
588-
* file->path points to the file in from_root directory. But we
589-
* need the file in directory to_root.
598+
* file->path is relative, to_file_path - is absolute.
599+
* Substitute them.
590600
*/
591601
prev_path = to_file->path;
592602
to_file->path = to_file_path;
593603
/* Decompress target file into temporary one */
594-
restore_data_file(tmp_file_path, to_file, false, false,
604+
restore_data_file(merge_to_file_path, to_file, false, false,
595605
parse_program_version(to_backup->program_version));
596606
to_file->path = prev_path;
597607
}
598608
else
599609
elog(VERBOSE, "Restore source file into the temporary path \"%s\"",
600-
tmp_file_path);
610+
merge_to_file_path);
611+
612+
/* TODO: Optimize merge of new files */
613+
601614
/* Merge source file with target file */
602-
restore_data_file(tmp_file_path, file,
615+
restore_data_file(merge_to_file_path, file,
603616
from_backup->backup_mode == BACKUP_MODE_DIFF_DELTA,
604617
false,
605618
parse_program_version(from_backup->program_version));
@@ -609,12 +622,12 @@ merge_files(void *arg)
609622

610623
/* Again we need to change path */
611624
prev_path = file->path;
612-
file->path = tmp_file_path;
625+
file->path = merge_to_file_path;
613626
/* backup_data_file() requires file size to calculate nblocks */
614627
file->size = pgFileSize(file->path);
615628
/* Now we can compress the file */
616629
backup_data_file(NULL, /* We shouldn't need 'arguments' here */
617-
to_file_path, file,
630+
tmp_file_path, file,
618631
to_backup->start_lsn,
619632
to_backup->backup_mode,
620633
to_backup->compress_alg,
@@ -623,10 +636,15 @@ merge_files(void *arg)
623636

624637
file->path = prev_path;
625638

626-
/* We can remove temporary file now */
627-
if (unlink(tmp_file_path))
639+
/* rename temp file */
640+
if (rename(tmp_file_path, to_file_path) == -1)
641+
elog(ERROR, "Could not rename file \"%s\" to \"%s\": %s",
642+
file->path, tmp_file_path, strerror(errno));
643+
644+
/* We can remove temporary file */
645+
if (unlink(merge_to_file_path))
628646
elog(ERROR, "Could not remove temporary file \"%s\": %s",
629-
tmp_file_path, strerror(errno));
647+
merge_to_file_path, strerror(errno));
630648
}
631649
/*
632650
* Otherwise merging algorithm is simpler.
@@ -676,7 +694,8 @@ merge_files(void *arg)
676694
elog(VERBOSE, "Merged file \"%s\": " INT64_FORMAT " bytes",
677695
file->path, file->write_size);
678696
else
679-
elog(ERROR, "Merge of file \"%s\" failed. Invalid size: %i", BYTES_INVALID);
697+
elog(ERROR, "Merge of file \"%s\" failed. Invalid size: %i",
698+
file->path, BYTES_INVALID);
680699

681700
/* Restore relative path */
682701
file->path = prev_file_path;

0 commit comments

Comments
 (0)