1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-07-12 06:40:11 +02:00

[Issue #231] make several attempts when creating backup directory, so two consecutive backups didn`t get the same backup ID

This commit is contained in:
Grigory Smolkin
2021-01-20 15:11:54 +03:00
parent 89931317e9
commit 286d30dbff
9 changed files with 90 additions and 50 deletions

View File

@ -95,7 +95,6 @@ static void
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs) do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
{ {
int i; int i;
char database_path[MAXPGPATH];
char external_prefix[MAXPGPATH]; /* Temp value. Used as template */ char external_prefix[MAXPGPATH]; /* Temp value. Used as template */
char dst_backup_path[MAXPGPATH]; char dst_backup_path[MAXPGPATH];
char label[1024]; char label[1024];
@ -265,10 +264,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
/* Update running backup meta with START LSN */ /* Update running backup meta with START LSN */
write_backup(&current, true); write_backup(&current, true);
pgBackupGetPath(&current, database_path, lengthof(database_path), join_path_components(external_prefix, current.database_dir, EXTERNAL_DIR);
DATABASE_DIR);
pgBackupGetPath(&current, external_prefix, lengthof(external_prefix),
EXTERNAL_DIR);
/* initialize backup's file list */ /* initialize backup's file list */
backup_files_list = parray_new(); backup_files_list = parray_new();
@ -276,7 +272,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
/* start stream replication */ /* start stream replication */
if (stream_wal) if (stream_wal)
{ {
join_path_components(dst_backup_path, database_path, PG_XLOG_DIR); join_path_components(dst_backup_path, current.database_dir, PG_XLOG_DIR);
fio_mkdir(dst_backup_path, DIR_PERMISSION, FIO_BACKUP_HOST); fio_mkdir(dst_backup_path, DIR_PERMISSION, FIO_BACKUP_HOST);
start_WAL_streaming(backup_conn, dst_backup_path, &instance_config.conn_opt, start_WAL_streaming(backup_conn, dst_backup_path, &instance_config.conn_opt,
@ -441,7 +437,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
join_path_components(dirpath, temp, file->rel_path); join_path_components(dirpath, temp, file->rel_path);
} }
else else
join_path_components(dirpath, database_path, file->rel_path); join_path_components(dirpath, current.database_dir, file->rel_path);
elog(VERBOSE, "Create directory '%s'", dirpath); elog(VERBOSE, "Create directory '%s'", dirpath);
fio_mkdir(dirpath, DIR_PERMISSION, FIO_BACKUP_HOST); fio_mkdir(dirpath, DIR_PERMISSION, FIO_BACKUP_HOST);
@ -475,7 +471,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
arg->nodeInfo = nodeInfo; arg->nodeInfo = nodeInfo;
arg->from_root = instance_config.pgdata; arg->from_root = instance_config.pgdata;
arg->to_root = database_path; arg->to_root = current.database_dir;
arg->external_prefix = external_prefix; arg->external_prefix = external_prefix;
arg->external_dirs = external_dirs; arg->external_dirs = external_dirs;
arg->files_list = backup_files_list; arg->files_list = backup_files_list;
@ -552,7 +548,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
elog(ERROR, "Failed to find file \"%s\" in backup filelist.", elog(ERROR, "Failed to find file \"%s\" in backup filelist.",
XLOG_CONTROL_FILE); XLOG_CONTROL_FILE);
set_min_recovery_point(pg_control, database_path, current.stop_lsn); set_min_recovery_point(pg_control, current.database_dir, current.stop_lsn);
} }
/* close and sync page header map */ /* close and sync page header map */
@ -609,7 +605,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
/* construct fullpath */ /* construct fullpath */
if (file->external_dir_num == 0) if (file->external_dir_num == 0)
join_path_components(to_fullpath, database_path, file->rel_path); join_path_components(to_fullpath, current.database_dir, file->rel_path);
else else
{ {
char external_dst[MAXPGPATH]; char external_dst[MAXPGPATH];
@ -726,8 +722,8 @@ pgdata_basic_setup(ConnectionOptions conn_opt, PGNodeInfo *nodeInfo)
* Entry point of pg_probackup BACKUP subcommand. * Entry point of pg_probackup BACKUP subcommand.
*/ */
int int
do_backup(time_t start_time, pgSetBackupParams *set_backup_params, do_backup(pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs) bool no_validate, bool no_sync, bool backup_logs)
{ {
PGconn *backup_conn = NULL; PGconn *backup_conn = NULL;
PGNodeInfo nodeInfo; PGNodeInfo nodeInfo;
@ -736,13 +732,16 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
/* Initialize PGInfonode */ /* Initialize PGInfonode */
pgNodeInit(&nodeInfo); pgNodeInit(&nodeInfo);
/* Create backup directory and BACKUP_CONTROL_FILE */
pgBackupCreateDir(&current, backup_instance_path);
if (!instance_config.pgdata) if (!instance_config.pgdata)
elog(ERROR, "required parameter not specified: PGDATA " elog(ERROR, "required parameter not specified: PGDATA "
"(-D, --pgdata)"); "(-D, --pgdata)");
/* Update backup status and other metainfo. */ /* Update backup status and other metainfo. */
current.status = BACKUP_STATUS_RUNNING; current.status = BACKUP_STATUS_RUNNING;
current.start_time = start_time; current.start_time = current.backup_id;
StrNCpy(current.program_version, PROGRAM_VERSION, StrNCpy(current.program_version, PROGRAM_VERSION,
sizeof(current.program_version)); sizeof(current.program_version));
@ -757,16 +756,13 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
elog(INFO, "Backup start, pg_probackup version: %s, instance: %s, backup ID: %s, backup mode: %s, " elog(INFO, "Backup start, pg_probackup version: %s, instance: %s, backup ID: %s, backup mode: %s, "
"wal mode: %s, remote: %s, compress-algorithm: %s, compress-level: %i", "wal mode: %s, remote: %s, compress-algorithm: %s, compress-level: %i",
PROGRAM_VERSION, instance_name, base36enc(start_time), pgBackupGetBackupMode(&current), PROGRAM_VERSION, instance_name, base36enc(current.backup_id), pgBackupGetBackupMode(&current),
current.stream ? "STREAM" : "ARCHIVE", IsSshProtocol() ? "true" : "false", current.stream ? "STREAM" : "ARCHIVE", IsSshProtocol() ? "true" : "false",
deparse_compress_alg(current.compress_alg), current.compress_level); deparse_compress_alg(current.compress_alg), current.compress_level);
/* Create backup directory and BACKUP_CONTROL_FILE */
if (pgBackupCreateDir(&current))
elog(ERROR, "Cannot create backup directory");
if (!lock_backup(&current, true, true)) if (!lock_backup(&current, true, true))
elog(ERROR, "Cannot lock backup %s directory", elog(ERROR, "Cannot lock backup %s directory",
base36enc(current.start_time)); base36enc(current.backup_id));
write_backup(&current, true); write_backup(&current, true);
/* set the error processing function for the backup process */ /* set the error processing function for the backup process */
@ -781,7 +777,7 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
backup_conn = pgdata_basic_setup(instance_config.conn_opt, &nodeInfo); backup_conn = pgdata_basic_setup(instance_config.conn_opt, &nodeInfo);
if (current.from_replica) if (current.from_replica)
elog(INFO, "Backup %s is going to be taken from standby", base36enc(start_time)); elog(INFO, "Backup %s is going to be taken from standby", base36enc(current.backup_id));
/* TODO, print PostgreSQL full version */ /* TODO, print PostgreSQL full version */
//elog(INFO, "PostgreSQL version: %s", nodeInfo.server_version_str); //elog(INFO, "PostgreSQL version: %s", nodeInfo.server_version_str);

View File

@ -23,6 +23,7 @@ static pgBackup* get_closest_backup(timelineInfo *tlinfo);
static pgBackup* get_oldest_backup(timelineInfo *tlinfo); static pgBackup* get_oldest_backup(timelineInfo *tlinfo);
static const char *backupModes[] = {"", "PAGE", "PTRACK", "DELTA", "FULL"}; static const char *backupModes[] = {"", "PAGE", "PTRACK", "DELTA", "FULL"};
static pgBackup *readBackupControlFile(const char *path); static pgBackup *readBackupControlFile(const char *path);
static time_t create_backup_dir(pgBackup *backup, const char *backup_instance_path);
static bool backup_lock_exit_hook_registered = false; static bool backup_lock_exit_hook_registered = false;
static parray *lock_files = NULL; static parray *lock_files = NULL;
@ -1136,12 +1137,18 @@ get_multi_timeline_parent(parray *backup_list, parray *tli_list,
return NULL; return NULL;
} }
/* create backup directory in $BACKUP_PATH */ /* Create backup directory in $BACKUP_PATH
int * Note, that backup_id attribute is updated,
pgBackupCreateDir(pgBackup *backup) * so it is possible to get diffrent values in
* pgBackup.start_time and pgBackup.backup_id.
* It may be ok or maybe not, so it's up to the caller
* to fix it or let it be.
*/
void
pgBackupCreateDir(pgBackup *backup, const char *backup_instance_path)
{ {
int i; int i;
char path[MAXPGPATH];
parray *subdirs = parray_new(); parray *subdirs = parray_new();
parray_append(subdirs, pg_strdup(DATABASE_DIR)); parray_append(subdirs, pg_strdup(DATABASE_DIR));
@ -1163,13 +1170,10 @@ pgBackupCreateDir(pgBackup *backup)
free_dir_list(external_list); free_dir_list(external_list);
} }
pgBackupGetPath(backup, path, lengthof(path), NULL); backup->backup_id = create_backup_dir(backup, backup_instance_path);
if (!dir_is_empty(path, FIO_BACKUP_HOST)) if (backup->backup_id == 0)
elog(ERROR, "backup destination is not empty \"%s\"", path); elog(ERROR, "Cannot create backup directory: %s", strerror(errno));
fio_mkdir(path, DIR_PERMISSION, FIO_BACKUP_HOST);
backup->root_dir = pgut_strdup(path);
backup->database_dir = pgut_malloc(MAXPGPATH); backup->database_dir = pgut_malloc(MAXPGPATH);
join_path_components(backup->database_dir, backup->root_dir, DATABASE_DIR); join_path_components(backup->database_dir, backup->root_dir, DATABASE_DIR);
@ -1180,11 +1184,47 @@ pgBackupCreateDir(pgBackup *backup)
/* create directories for actual backup files */ /* create directories for actual backup files */
for (i = 0; i < parray_num(subdirs); i++) for (i = 0; i < parray_num(subdirs); i++)
{ {
char path[MAXPGPATH];
join_path_components(path, backup->root_dir, parray_get(subdirs, i)); join_path_components(path, backup->root_dir, parray_get(subdirs, i));
fio_mkdir(path, DIR_PERMISSION, FIO_BACKUP_HOST); fio_mkdir(path, DIR_PERMISSION, FIO_BACKUP_HOST);
} }
free_dir_list(subdirs); free_dir_list(subdirs);
}
/*
* Create root directory for backup,
* update pgBackup.root_dir if directory creation was a success
*/
time_t
create_backup_dir(pgBackup *backup, const char *backup_instance_path)
{
int attempts = 10;
while (attempts--)
{
int rc;
char path[MAXPGPATH];
time_t backup_id = time(NULL);
join_path_components(path, backup_instance_path, base36enc(backup_id));
/* TODO: add wrapper for remote mode */
rc = dir_create_dir(path, DIR_PERMISSION, true);
if (rc == 0)
{
backup->root_dir = pgut_strdup(path);
return backup_id;
}
else
{
elog(WARNING, "Cannot create directory \"%s\": %s", path, strerror(errno));
sleep(1);
}
}
return 0; return 0;
} }

View File

@ -138,9 +138,13 @@ static TablespaceList external_remap_list = {NULL, NULL};
/* /*
* Create directory, also create parent directories if necessary. * Create directory, also create parent directories if necessary.
* In strict mode treat already existing directory as error.
* Return values:
* 0 - ok
* -1 - error (check errno)
*/ */
int int
dir_create_dir(const char *dir, mode_t mode) dir_create_dir(const char *dir, mode_t mode, bool strict)
{ {
char parent[MAXPGPATH]; char parent[MAXPGPATH];
@ -149,14 +153,14 @@ dir_create_dir(const char *dir, mode_t mode)
/* Create parent first */ /* Create parent first */
if (access(parent, F_OK) == -1) if (access(parent, F_OK) == -1)
dir_create_dir(parent, mode); dir_create_dir(parent, mode, false);
/* Create directory */ /* Create directory */
if (mkdir(dir, mode) == -1) if (mkdir(dir, mode) == -1)
{ {
if (errno == EEXIST) /* already exist */ if (errno == EEXIST && !strict) /* already exist */
return 0; return 0;
elog(ERROR, "cannot create directory \"%s\": %s", dir, strerror(errno)); return -1;
} }
return 0; return 0;

View File

@ -34,15 +34,15 @@ do_init(void)
} }
/* create backup catalog root directory */ /* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION); dir_create_dir(backup_path, DIR_PERMISSION, false);
/* create backup catalog data directory */ /* create backup catalog data directory */
join_path_components(path, backup_path, BACKUPS_DIR); join_path_components(path, backup_path, BACKUPS_DIR);
dir_create_dir(path, DIR_PERMISSION); dir_create_dir(path, DIR_PERMISSION, false);
/* create backup catalog wal directory */ /* create backup catalog wal directory */
join_path_components(arclog_path_dir, backup_path, "wal"); join_path_components(arclog_path_dir, backup_path, "wal");
dir_create_dir(arclog_path_dir, DIR_PERMISSION); dir_create_dir(arclog_path_dir, DIR_PERMISSION, false);
elog(INFO, "Backup catalog '%s' successfully inited", backup_path); elog(INFO, "Backup catalog '%s' successfully inited", backup_path);
return 0; return 0;
@ -91,8 +91,8 @@ do_add_instance(InstanceConfig *instance)
instance->name, instance->arclog_path); instance->name, instance->arclog_path);
/* Create directory for data files of this specific instance */ /* Create directory for data files of this specific instance */
dir_create_dir(instance->backup_instance_path, DIR_PERMISSION); dir_create_dir(instance->backup_instance_path, DIR_PERMISSION, false);
dir_create_dir(instance->arclog_path, DIR_PERMISSION); dir_create_dir(instance->arclog_path, DIR_PERMISSION, false);
/* /*
* Write initial configuration file. * Write initial configuration file.

View File

@ -639,7 +639,7 @@ merge_chain(parray *parent_chain, pgBackup *full_backup, pgBackup *dest_backup)
makeExternalDirPathByNum(new_container, full_external_prefix, makeExternalDirPathByNum(new_container, full_external_prefix,
file->external_dir_num); file->external_dir_num);
join_path_components(dirpath, new_container, file->rel_path); join_path_components(dirpath, new_container, file->rel_path);
dir_create_dir(dirpath, DIR_PERMISSION); dir_create_dir(dirpath, DIR_PERMISSION, false);
} }
pg_atomic_init_flag(&file->lock); pg_atomic_init_flag(&file->lock);

View File

@ -799,8 +799,6 @@ main(int argc, char *argv[])
return do_init(); return do_init();
case BACKUP_CMD: case BACKUP_CMD:
{ {
time_t start_time = time(NULL);
current.stream = stream_wal; current.stream = stream_wal;
/* sanity */ /* sanity */
@ -808,7 +806,7 @@ main(int argc, char *argv[])
elog(ERROR, "required parameter not specified: BACKUP_MODE " elog(ERROR, "required parameter not specified: BACKUP_MODE "
"(-b, --backup-mode)"); "(-b, --backup-mode)");
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs); return do_backup(set_backup_params, no_validate, no_sync, backup_logs);
} }
case RESTORE_CMD: case RESTORE_CMD:
return do_restore_or_validate(current.backup_id, return do_restore_or_validate(current.backup_id,

View File

@ -781,7 +781,7 @@ extern char** commands_args;
extern const char *pgdata_exclude_dir[]; extern const char *pgdata_exclude_dir[];
/* in backup.c */ /* in backup.c */
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params, extern int do_backup(pgSetBackupParams *set_backup_params,
bool no_validate, bool no_sync, bool backup_logs); bool no_validate, bool no_sync, bool backup_logs);
extern void do_checkdb(bool need_amcheck, ConnectionOptions conn_opt, extern void do_checkdb(bool need_amcheck, ConnectionOptions conn_opt,
char *pgdata); char *pgdata);
@ -916,7 +916,7 @@ extern void pgBackupGetPath2(const pgBackup *backup, char *path, size_t len,
extern void pgBackupGetPathInInstance(const char *instance_name, extern void pgBackupGetPathInInstance(const char *instance_name,
const pgBackup *backup, char *path, size_t len, const pgBackup *backup, char *path, size_t len,
const char *subdir1, const char *subdir2); const char *subdir1, const char *subdir2);
extern int pgBackupCreateDir(pgBackup *backup); extern void pgBackupCreateDir(pgBackup *backup, const char *backup_instance_path);
extern void pgNodeInit(PGNodeInfo *node); extern void pgNodeInit(PGNodeInfo *node);
extern void pgBackupInit(pgBackup *backup); extern void pgBackupInit(pgBackup *backup);
extern void pgBackupFree(void *backup); extern void pgBackupFree(void *backup);
@ -980,7 +980,7 @@ extern void makeExternalDirPathByNum(char *ret_path, const char *pattern_path,
const int dir_num); const int dir_num);
extern bool backup_contains_external(const char *dir, parray *dirs_list); extern bool backup_contains_external(const char *dir, parray *dirs_list);
extern int dir_create_dir(const char *path, mode_t mode); extern int dir_create_dir(const char *path, mode_t mode, bool strict);
extern bool dir_is_empty(const char *path, fio_location location); extern bool dir_is_empty(const char *path, fio_location location);
extern bool fileExists(const char *path, fio_location location); extern bool fileExists(const char *path, fio_location location);

View File

@ -1048,7 +1048,7 @@ int fio_mkdir(char const* path, int mode, fio_location location)
} }
else else
{ {
return dir_create_dir(path, mode); return dir_create_dir(path, mode, false);
} }
} }
@ -2648,7 +2648,7 @@ void fio_communicate(int in, int out)
break; break;
case FIO_MKDIR: /* Create directory */ case FIO_MKDIR: /* Create directory */
hdr.size = 0; hdr.size = 0;
hdr.arg = dir_create_dir(buf, hdr.arg); hdr.arg = dir_create_dir(buf, hdr.arg, false);
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr)); IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
break; break;
case FIO_CHMOD: /* Change file mode */ case FIO_CHMOD: /* Change file mode */

View File

@ -2921,12 +2921,14 @@ class BackupTest(ProbackupTest, unittest.TestCase):
try: try:
self.backup_node( self.backup_node(
backup_dir, 'node', node, backup_dir, 'node', node, data_dir='{0}'.format(datadir))
data_dir='{0}'.format(datadir), return_id=False)
except: except:
pass pass
self.backup_node(backup_dir, 'node', node, options=['--stream']) out = self.backup_node(backup_dir, 'node', node, options=['--stream'], return_id=False)
# it is a bit racy
self.assertIn("WARNING: Cannot create directory", out)
# Clean after yourself # Clean after yourself
self.del_test_dir(module_name, fname) self.del_test_dir(module_name, fname)