diff --git a/src/catalog.c b/src/catalog.c index 3ba17e9fd..0c4a0427f 100644 --- a/src/catalog.c +++ b/src/catalog.c @@ -2623,6 +2623,10 @@ readBackupControlFile(const char *path) backup->status = BACKUP_STATUS_DELETING; else if (strcmp(status, "DELETED") == 0) backup->status = BACKUP_STATUS_DELETED; + else if (strcmp(status, "DETACHING") == 0) + backup->status = BACKUP_STATUS_DETACHING; + else if (strcmp(status, "DETACHED") == 0) + backup->status = BACKUP_STATUS_DETACHED; else if (strcmp(status, "DONE") == 0) backup->status = BACKUP_STATUS_DONE; else if (strcmp(status, "ORPHAN") == 0) diff --git a/src/delete.c b/src/delete.c index d1afa2874..dbfd47b33 100644 --- a/src/delete.c +++ b/src/delete.c @@ -113,6 +113,165 @@ do_delete(time_t backup_id) parray_free(backup_list); } +void +do_detach(time_t backup_id) +{ + int i; + parray *backup_list, + *delete_list; + pgBackup *target_backup = NULL; + size_t size_to_delete = 0; + char size_to_delete_pretty[20]; + + /* Get complete list of backups */ + backup_list = catalog_get_backup_list(instance_name, INVALID_BACKUP_ID); + + delete_list = parray_new(); + + /* Find backup to be deleted and make increment backups array to be deleted */ + for (i = 0; i < parray_num(backup_list); i++) + { + pgBackup *backup = (pgBackup *) parray_get(backup_list, i); + + if (backup->start_time == backup_id) + { + target_backup = backup; + break; + } + } + + /* sanity */ + if (!target_backup) + elog(ERROR, "Failed to find backup %s, cannot detach", base36enc(backup_id)); + + /* form delete list */ + for (i = 0; i < parray_num(backup_list); i++) + { + pgBackup *backup = (pgBackup *) parray_get(backup_list, i); + + /* check if backup is descendant of delete target */ + if (is_parent(target_backup->start_time, backup, true)) + { + parray_append(delete_list, backup); + + elog(LOG, "Backup %s %s be detached", + base36enc(backup->start_time), dry_run? "can":"will"); + + size_to_delete += backup->data_bytes; + if (backup->stream) + size_to_delete += backup->wal_bytes; + } + } + + /* Report the resident size to delete */ + if (size_to_delete >= 0) + { + pretty_size(size_to_delete, size_to_delete_pretty, lengthof(size_to_delete_pretty)); + elog(INFO, "Resident data size to free by detach of backup %s : %s", + base36enc(target_backup->start_time), size_to_delete_pretty); + } + + if (!dry_run) + { + /* Lock marked for delete backups */ + catalog_lock_backup_list(delete_list, parray_num(delete_list) - 1, 0, false, true); + + /* Delete backups from the end of list */ + for (i = (int) parray_num(delete_list) - 1; i >= 0; i--) + { + pgBackup *backup = (pgBackup *) parray_get(delete_list, (size_t) i); + + if (interrupted) + elog(ERROR, "interrupted during detach backup"); + + detach_backup_files(backup); + } + } + + + /* cleanup */ + parray_free(delete_list); + parray_walk(backup_list, pgBackupFree); + parray_free(backup_list); +} + +/* +* Detach backup files of the backup and update the status of the backup to +* BACKUP_STATUS_DETACHED. +* TODO: delete files on multiple threads +*/ +void +detach_backup_files(pgBackup *backup) +{ + size_t i; + char timestamp[100]; + parray *files; + size_t num_files; + char full_path[MAXPGPATH]; + + /* + * If the backup was detached already, there is nothing to do. + */ + if (backup->status == BACKUP_STATUS_DETACHED) + { + elog(WARNING, "Backup %s already detached", + base36enc(backup->start_time)); + return; + } + + if (backup->recovery_time) + time2iso(timestamp, lengthof(timestamp), backup->recovery_time, false); + else + time2iso(timestamp, lengthof(timestamp), backup->start_time, false); + + elog(INFO, "Detach: %s %s", + base36enc(backup->start_time), timestamp); + elog(INFO, "Backup paths:\n root_dir = %s\n database_dir = %s\n", + backup->root_dir, backup->database_dir); + + + /* + * Update STATUS to BACKUP_STATUS_DETACHING in preparation for the case which + * the error occurs before deleting all backup files. + */ + write_backup_status(backup, BACKUP_STATUS_DETACHING, instance_name, false); + elog(INFO, "Set status in control file: DETACHING"); + + /* list files to be deleted */ + files = parray_new(); + dir_list_file(files, backup->database_dir, false, false, true, false, false, 0, FIO_BACKUP_HOST); + + /* delete leaf node first */ + parray_qsort(files, pgFileCompareRelPathWithExternalDesc); + num_files = parray_num(files); + for (i = 0; i < num_files; i++) + { + pgFile *file = (pgFile *) parray_get(files, i); + + join_path_components(full_path, backup->database_dir, file->rel_path); + + if (interrupted) + elog(ERROR, "interrupted during detach backup"); + + if (progress) + elog(INFO, "Progress: (%zd/%zd). Delete file \"%s\"", + i + 1, num_files, full_path); + + pgFileDelete(file->mode, full_path); + } + + parray_walk(files, pgFileFree); + parray_free(files); + backup->status = BACKUP_STATUS_DETACHED; + + /* Update STATUS to BACKUP_STATUS_DETACHED */ + write_backup_status(backup, BACKUP_STATUS_DETACHED, instance_name, true); + elog(INFO, "Set status in control file: DETACHED"); + + return; +} + + /* * Merge and purge backups by retention policy. Retention policy is configured by * retention_redundancy and retention_window variables. diff --git a/src/help.c b/src/help.c index f72dc90dc..d02a20108 100644 --- a/src/help.c +++ b/src/help.c @@ -15,6 +15,7 @@ static void help_restore(void); static void help_validate(void); static void help_show(void); static void help_delete(void); +static void help_detach(void); static void help_merge(void); static void help_set_backup(void); static void help_set_config(void); @@ -40,6 +41,8 @@ help_command(char *command) help_show(); else if (strcmp(command, "delete") == 0) help_delete(); + else if (strcmp(command, "detach") == 0) + help_detach(); else if (strcmp(command, "merge") == 0) help_merge(); else if (strcmp(command, "set-backup") == 0) @@ -200,6 +203,11 @@ help_pg_probackup(void) printf(_(" [--dry-run] [--no-validate] [--no-sync]\n")); printf(_(" [--help]\n")); + printf(_("\n %s detach -B backup-path --instance=instance_name\n"), PROGRAM_NAME); + printf(_(" [-j num-threads] [--progress]\n")); + printf(_(" [-i backup-id]\n")); + printf(_(" [--help]\n")); + printf(_("\n %s merge -B backup-path --instance=instance_name\n"), PROGRAM_NAME); printf(_(" -i backup-id [--progress] [-j num-threads]\n")); printf(_(" [--no-validate] [--no-sync]\n")); @@ -680,6 +688,20 @@ help_delete(void) printf(_(" available units: 'ms', 's', 'min', 'h', 'd' (default: min)\n\n")); } +static void +help_detach(void) +{ + printf(_("\n%s detach -B backup-path --instance=instance_name\n"), PROGRAM_NAME); + printf(_(" [-j num-threads] [--progress]\n")); + printf(_(" [-i backup-id ]\n\n")); + + printf(_(" -B, --backup-path=backup-path location of the backup storage area\n")); + printf(_(" --instance=instance_name name of the instance\n")); + printf(_(" -i, --backup-id=backup-id backup to detach\n")); + printf(_(" -j, --threads=NUM number of parallel threads\n")); + printf(_(" --progress show progress\n\n")); +} + static void help_merge(void) { diff --git a/src/pg_probackup.c b/src/pg_probackup.c index 854493bdc..689cd9c52 100644 --- a/src/pg_probackup.c +++ b/src/pg_probackup.c @@ -39,6 +39,7 @@ typedef enum ProbackupSubcmd RESTORE_CMD, VALIDATE_CMD, DELETE_CMD, + DETACH_CMD, MERGE_CMD, SHOW_CMD, SET_CONFIG_CMD, @@ -334,6 +335,8 @@ main(int argc, char *argv[]) backup_subcmd = VALIDATE_CMD; else if (strcmp(argv[1], "delete") == 0) backup_subcmd = DELETE_CMD; + else if (strcmp(argv[1], "detach") == 0) + backup_subcmd = DETACH_CMD; else if (strcmp(argv[1], "merge") == 0) backup_subcmd = MERGE_CMD; else if (strcmp(argv[1], "show") == 0) @@ -405,6 +408,7 @@ main(int argc, char *argv[]) backup_subcmd == RESTORE_CMD || backup_subcmd == VALIDATE_CMD || backup_subcmd == DELETE_CMD || + backup_subcmd == DETACH_CMD || backup_subcmd == MERGE_CMD || backup_subcmd == SET_CONFIG_CMD || backup_subcmd == SET_BACKUP_CMD) @@ -663,6 +667,7 @@ main(int argc, char *argv[]) if (backup_subcmd != RESTORE_CMD && backup_subcmd != VALIDATE_CMD && backup_subcmd != DELETE_CMD && + backup_subcmd != DETACH_CMD && backup_subcmd != MERGE_CMD && backup_subcmd != SET_BACKUP_CMD && backup_subcmd != SHOW_CMD) @@ -850,6 +855,9 @@ main(int argc, char *argv[]) else do_delete(current.backup_id); break; + case DETACH_CMD: + do_detach(current.backup_id); + break; case MERGE_CMD: do_merge(current.backup_id, no_validate, no_sync); break; diff --git a/src/pg_probackup.h b/src/pg_probackup.h index fca08bdac..6eb7d2997 100644 --- a/src/pg_probackup.h +++ b/src/pg_probackup.h @@ -284,7 +284,9 @@ typedef enum BackupStatus BACKUP_STATUS_DELETED, /* data files have been deleted */ BACKUP_STATUS_DONE, /* completed but not validated yet */ BACKUP_STATUS_ORPHAN, /* backup validity is unknown but at least one parent backup is corrupted */ - BACKUP_STATUS_CORRUPT /* files are corrupted, not available */ + BACKUP_STATUS_DETACHING, /* data files are being detached */ + BACKUP_STATUS_DETACHED, /* data files have been detached */ + BACKUP_STATUS_CORRUPT /* files are corrupted, not available */ } BackupStatus; typedef enum BackupMode @@ -869,6 +871,8 @@ extern int do_show(const char *instance_name, time_t requested_backup_id, bool s /* in delete.c */ extern void do_delete(time_t backup_id); extern void delete_backup_files(pgBackup *backup); +extern void do_detach(time_t backup_id); +extern void detach_backup_files(pgBackup *backup); extern void do_retention(bool no_validate, bool no_sync); extern int do_delete_instance(void); extern void do_delete_status(InstanceConfig *instance_config, const char *status); diff --git a/src/util.c b/src/util.c index 9fd0114bb..576d8c45f 100644 --- a/src/util.c +++ b/src/util.c @@ -30,6 +30,8 @@ static const char *statusName[] = "DELETED", "DONE", "ORPHAN", + "DETACHING", + "DETACHED", "CORRUPT" };
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: