1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-02-07 14:18:17 +02:00

Remove unnecessary validate_backup_version

This commit is contained in:
Arthur Zakirov 2018-11-08 15:50:24 +03:00
parent 728e3d5bdc
commit 53c1a05b9b
2 changed files with 533 additions and 3 deletions

View File

@ -19,8 +19,6 @@ static void *pgBackupValidateFiles(void *arg);
static void do_validate_instance(void);
static bool corrupted_backup_found = false;
/* Program version of a current backup */
static uint32 validate_backup_version = 0;
typedef struct
{
@ -220,7 +218,7 @@ pgBackupValidateFiles(void *arg)
* To avoid this problem we need to use different algorithm, CRC-32 in
* this case.
*/
crc = pgFileGetCRC(file->path, validate_backup_version <= 20021);
crc = pgFileGetCRC(file->path, arguments->backup_version <= 20021);
if (crc != file->crc)
{
elog(WARNING, "Invalid CRC of backup file \"%s\" : %X. Expected %X",

532
src/validate.c.autosave Normal file
View File

@ -0,0 +1,532 @@
/*-------------------------------------------------------------------------
*
* validate.c: validate backup files.
*
* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Portions Copyright (c) 2015-2018, Postgres Professional
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
#include <sys/stat.h>
#include <dirent.h>
#include "utils/thread.h"
static void *pgBackupValidateFiles(void *arg);
static void do_validate_instance(void);
static bool corrupted_backup_found = false;
typedef struct
{
parray *files;
bool corrupted;
XLogRecPtr stop_lsn;
uint32 checksum_version;
uint32 backup_version;
/*
* Return value from the thread.
* 0 means there is no error, 1 - there is an error.
*/
int ret;
} validate_files_arg;
if (is_absolute_path(host))
if (hostaddr && *hostaddr)
printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
else
if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
/*
* Validate backup files.
*/
void
pgBackupValidate(pgBackup *backup)
{
char base_path[MAXPGPATH];
char path[MAXPGPATH];
parray *files;
bool corrupted = false;
bool validation_isok = true;
/* arrays with meta info for multi threaded validate */
pthread_t *threads;
validate_files_arg *threads_args;
int i;
/* Revalidation is attempted for DONE, ORPHAN and CORRUPT backups */
if (backup->status != BACKUP_STATUS_OK &&
backup->status != BACKUP_STATUS_DONE &&
backup->status != BACKUP_STATUS_ORPHAN &&
backup->status != BACKUP_STATUS_CORRUPT)
{
elog(WARNING, "Backup %s has status %s. Skip validation.",
base36enc(backup->start_time), status2str(backup->status));
corrupted_backup_found = true;
return;
}
if (backup->status == BACKUP_STATUS_OK || backup->status == BACKUP_STATUS_DONE)
elog(INFO, "Validating backup %s", base36enc(backup->start_time));
/* backups in MERGING status must have an option of revalidation without losing MERGING status
else if (backup->status == BACKUP_STATUS_MERGING)
{
some message here
}
*/
else
elog(INFO, "Revalidating backup %s", base36enc(backup->start_time));
if (backup->backup_mode != BACKUP_MODE_FULL &&
backup->backup_mode != BACKUP_MODE_DIFF_PAGE &&
backup->backup_mode != BACKUP_MODE_DIFF_PTRACK &&
backup->backup_mode != BACKUP_MODE_DIFF_DELTA)
elog(WARNING, "Invalid backup_mode of backup %s", base36enc(backup->start_time));
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path), DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
/* setup threads */
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
pg_atomic_clear_flag(&file->lock);
}
/* init thread args with own file lists */
threads = (pthread_t *) palloc(sizeof(pthread_t) * num_threads);
threads_args = (validate_files_arg *)
palloc(sizeof(validate_files_arg) * num_threads);
/* Validate files */
for (i = 0; i < num_threads; i++)
{
validate_files_arg *arg = &(threads_args[i]);
arg->files = files;
arg->corrupted = false;
arg->stop_lsn = backup->stop_lsn;
arg->checksum_version = backup->checksum_version;
arg->backup_version = parse_program_version(backup->program_version);
/* By default there are some error */
threads_args[i].ret = 1;
pthread_create(&threads[i], NULL, pgBackupValidateFiles, arg);
}
/* Wait theads */
for (i = 0; i < num_threads; i++)
{
validate_files_arg *arg = &(threads_args[i]);
pthread_join(threads[i], NULL);
if (arg->corrupted)
corrupted = true;
if (arg->ret == 1)
validation_isok = false;
}
if (!validation_isok)
elog(ERROR, "Data files validation failed");
pfree(threads);
pfree(threads_args);
/* cleanup */
parray_walk(files, pgFileFree);
parray_free(files);
/* Update backup status */
backup->status = corrupted ? BACKUP_STATUS_CORRUPT : BACKUP_STATUS_OK;
write_backup_status(backup);
if (corrupted)
elog(WARNING, "Backup %s data files are corrupted", base36enc(backup->start_time));
else
elog(INFO, "Backup %s data files are valid", base36enc(backup->start_time));
}
/*
* Validate files in the backup.
* NOTE: If file is not valid, do not use ERROR log message,
* rather throw a WARNING and set arguments->corrupted = true.
* This is necessary to update backup status.
*/
static void *
pgBackupValidateFiles(void *arg)
{
int i;
validate_files_arg *arguments = (validate_files_arg *)arg;
pg_crc32 crc;
for (i = 0; i < parray_num(arguments->files); i++)
{
struct stat st;
pgFile *file = (pgFile *) parray_get(arguments->files, i);
if (!pg_atomic_test_set_flag(&file->lock))
continue;
if (interrupted)
elog(ERROR, "Interrupted during validate");
/* Validate only regular files */
if (!S_ISREG(file->mode))
continue;
/*
* Skip files which has no data, because they
* haven't changed between backups.
*/
if (file->write_size == BYTES_INVALID)
continue;
/*
* Currently we don't compute checksums for
* cfs_compressed data files, so skip them.
*/
if (file->is_cfs)
continue;
/* print progress */
elog(VERBOSE, "Validate files: (%d/%lu) %s",
i + 1, (unsigned long) parray_num(arguments->files), file->path);
if (stat(file->path, &st) == -1)
{
if (errno == ENOENT)
elog(WARNING, "Backup file \"%s\" is not found", file->path);
else
elog(WARNING, "Cannot stat backup file \"%s\": %s",
file->path, strerror(errno));
arguments->corrupted = true;
break;
}
if (file->write_size != st.st_size)
{
elog(WARNING, "Invalid size of backup file \"%s\" : " INT64_FORMAT ". Expected %lu",
file->path, file->write_size, (unsigned long) st.st_size);
arguments->corrupted = true;
break;
}
/*
* Pre 2.0.22 we use CRC-32C, but in newer version of pg_probackup we
* use CRC-32.
*
* pg_control stores its content and checksum of the content, calculated
* using CRC-32C. If we calculate checksum of the whole pg_control using
* CRC-32C we get same checksum constantly. It might be because of the
* CRC-32C algorithm.
* To avoid this problem we need to use different algorithm, CRC-32 in
* this case.
*/
crc = pgFileGetCRC(file->path, arguments->backup_version <= 20021);
if (crc != file->crc)
{
elog(WARNING, "Invalid CRC of backup file \"%s\" : %X. Expected %X",
file->path, file->crc, crc);
arguments->corrupted = true;
/* validate relation blocks */
if (file->is_datafile)
{
if (!check_file_pages(file, arguments->stop_lsn,
arguments->checksum_version,
arguments->backup_version))
arguments->corrupted = true;
}
}
}
/* Data files validation is successful */
arguments->ret = 0;
return NULL;
}
/*
* Validate all backups in the backup catalog.
* If --instance option was provided, validate only backups of this instance.
*/
int
do_validate_all(void)
{
if (instance_name == NULL)
{
/* Show list of instances */
char path[MAXPGPATH];
DIR *dir;
struct dirent *dent;
/* open directory and list contents */
join_path_components(path, backup_path, BACKUPS_DIR);
dir = opendir(path);
if (dir == NULL)
elog(ERROR, "cannot open directory \"%s\": %s", path, strerror(errno));
errno = 0;
while ((dent = readdir(dir)))
{
char child[MAXPGPATH];
struct stat st;
/* skip entries point current dir or parent dir */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
join_path_components(child, path, dent->d_name);
if (lstat(child, &st) == -1)
elog(ERROR, "cannot stat file \"%s\": %s", child, strerror(errno));
if (!S_ISDIR(st.st_mode))
continue;
instance_name = dent->d_name;
sprintf(backup_instance_path, "%s/%s/%s", backup_path, BACKUPS_DIR, instance_name);
sprintf(arclog_path, "%s/%s/%s", backup_path, "wal", instance_name);
xlog_seg_size = get_config_xlog_seg_size();
do_validate_instance();
}
}
else
{
do_validate_instance();
}
if (corrupted_backup_found)
{
elog(WARNING, "Some backups are not valid");
return 1;
}
else
elog(INFO, "All backups are valid");
return 0;
}
/*
* Validate all backups in the given instance of the backup catalog.
*/
static void
do_validate_instance(void)
{
char *current_backup_id;
int i;
int j;
parray *backups;
pgBackup *current_backup = NULL;
elog(INFO, "Validate backups of the instance '%s'", instance_name);
/* Get exclusive lock of backup catalog */
catalog_lock();
/* Get list of all backups sorted in order of descending start time */
backups = catalog_get_backup_list(INVALID_BACKUP_ID);
/* Examine backups one by one and validate them */
for (i = 0; i < parray_num(backups); i++)
{
pgBackup *base_full_backup;
char *parent_backup_id;
current_backup = (pgBackup *) parray_get(backups, i);
/* Find ancestor for incremental backup */
if (current_backup->backup_mode != BACKUP_MODE_FULL)
{
pgBackup *tmp_backup = NULL;
int result;
result = scan_parent_chain(current_backup, &tmp_backup);
/* chain is broken */
if (result == 0)
{
/* determine missing backup ID */
parent_backup_id = base36enc_dup(tmp_backup->parent_backup);
corrupted_backup_found = true;
/* orphanize current_backup */
if (current_backup->status == BACKUP_STATUS_OK)
{
current_backup->status = BACKUP_STATUS_ORPHAN;
write_backup_status(current_backup);
elog(WARNING, "Backup %s is orphaned because his parent %s is missing",
base36enc(current_backup->start_time),
parent_backup_id);
}
else
{
elog(WARNING, "Backup %s has missing parent %s",
base36enc(current_backup->start_time), parent_backup_id);
}
continue;
}
/* chain is whole, but at least one parent is invalid */
else if (result == 1)
{
/* determine corrupt backup ID */
parent_backup_id = base36enc_dup(tmp_backup->start_time);
/* Oldest corrupt backup has a chance for revalidation */
if (current_backup->start_time != tmp_backup->start_time)
{
/* orphanize current_backup */
if (current_backup->status == BACKUP_STATUS_OK)
{
current_backup->status = BACKUP_STATUS_ORPHAN;
write_backup_status(current_backup);
elog(WARNING, "Backup %s is orphaned because his parent %s has status: %s",
base36enc(current_backup->start_time), parent_backup_id,
status2str(tmp_backup->status));
}
else
{
elog(WARNING, "Backup %s has parent %s with status: %s",
base36enc(current_backup->start_time),parent_backup_id,
status2str(tmp_backup->status));
}
continue;
}
base_full_backup = find_parent_full_backup(current_backup);
}
/* chain is whole, all parents are valid at first glance,
* current backup validation can proceed
*/
else
base_full_backup = tmp_backup;
}
else
base_full_backup = current_backup;
/* Valiate backup files*/
pgBackupValidate(current_backup);
/* Validate corresponding WAL files */
if (current_backup->status == BACKUP_STATUS_OK)
validate_wal(current_backup, arclog_path, 0,
0, 0, base_full_backup->tli, xlog_seg_size);
/*
* Mark every descendant of corrupted backup as orphan
*/
if (current_backup->status == BACKUP_STATUS_CORRUPT)
{
/* This is ridiculous but legal.
* PAGE1_2b <- OK
* PAGE1_2a <- OK
* PAGE1_1b <- ORPHAN
* PAGE1_1a <- CORRUPT
* FULL1 <- OK
*/
corrupted_backup_found = true;
current_backup_id = base36enc_dup(current_backup->start_time);
for (j = i - 1; j >= 0; j--)
{
pgBackup *backup = (pgBackup *) parray_get(backups, j);
if (is_parent(current_backup->start_time, backup, false))
{
if (backup->status == BACKUP_STATUS_OK)
{
backup->status = BACKUP_STATUS_ORPHAN;
write_backup_status(backup);
elog(WARNING, "Backup %s is orphaned because his parent %s has status: %s",
base36enc(backup->start_time),
current_backup_id,
status2str(current_backup->status));
}
}
}
free(current_backup_id);
}
/* For every OK backup we try to revalidate all his ORPHAN descendants. */
if (current_backup->status == BACKUP_STATUS_OK)
{
/* revalidate all ORPHAN descendats
* be very careful not to miss a missing backup
* for every backup we must check that he is descendant of current_backup
*/
for (j = i - 1; j >= 0; j--)
{
pgBackup *backup = (pgBackup *) parray_get(backups, j);
pgBackup *tmp_backup = NULL;
int result;
//PAGE3b ORPHAN
//PAGE2b ORPHAN -----
//PAGE6a ORPHAN |
//PAGE5a CORRUPT |
//PAGE4a missing |
//PAGE3a missing |
//PAGE2a ORPHAN |
//PAGE1a OK <- we are here <-|
//FULL OK
if (is_parent(current_backup->start_time, backup, false))
{
/* Revalidation make sense only if parent chain is whole.
* is_parent() do not guarantee that.
*/
result = scan_parent_chain(backup, &tmp_backup);
if (result == 1)
{
/* revalidation make sense only if oldest invalid backup is current_backup
*/
if (tmp_backup->start_time != backup->start_time)
continue;
if (backup->status == BACKUP_STATUS_ORPHAN)
{
/* Revaliate backup files*/
pgBackupValidate(backup);
if (backup->status == BACKUP_STATUS_OK)
{
//tmp_backup = find_parent_full_backup(dest_backup);
/* Revalidation successful, validate corresponding WAL files */
validate_wal(backup, arclog_path, 0,
0, 0, current_backup->tli,
xlog_seg_size);
}
}
if (backup->status != BACKUP_STATUS_OK)
{
corrupted_backup_found = true;
continue;
}
}
}
}
}
}
/* cleanup */
parray_walk(backups, pgBackupFree);
parray_free(backups);
}