Skip to content

Commit c4b49c6

Browse files
committed
[Issue #68] the backup pinning
1 parent 0cd9e35 commit c4b49c6

13 files changed

+548
-15
lines changed

Documentation.md

+66-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Current version - 2.1.5
3838
* [Managing the Backup Catalog](#managing-the-backup-catalog)
3939
* [Viewing WAL Archive Information](#viewing-wal-archive-information)
4040
* [Configuring Backup Retention Policy](#configuring-backup-retention-policy)
41+
* [Pinning a Backup]
4142
* [Merging Backups](#merging-backups)
4243
* [Deleting Backups](#deleting-backups)
4344

@@ -49,6 +50,7 @@ Current version - 2.1.5
4950
* [add-instance](#add-instance)
5051
* [del-instance](#del-instance)
5152
* [set-config](#set-config)
53+
* [set-backup](#set-backup)
5254
* [show-config](#show-config)
5355
* [show](#show)
5456
* [backup](#backup)
@@ -63,6 +65,7 @@ Current version - 2.1.5
6365
* [Common Options](#common-options)
6466
* [Recovery Target Options](#recovery-target-options)
6567
* [Retention Options](#retention-options)
68+
* [Pinning Options](#pinning-options)
6669
* [Logging Options](#logging-options)
6770
* [Connection Options](#connection-options)
6871
* [Compression Options](#compression-options)
@@ -89,6 +92,8 @@ Current version - 2.1.5
8992

9093
`pg_probackup set-config -B backup_dir --instance instance_name [option...]`
9194

95+
`pg_probackup set-backup -B backup_dir --instance instance_name -i backup_id [option...]`
96+
9297
`pg_probackup show-config -B backup_dir --instance instance_name [--format=format]`
9398

9499
`pg_probackup show -B backup_dir [option...]`
@@ -775,6 +780,7 @@ start-time = '2017-05-16 12:57:29'
775780
end-time = '2017-05-16 12:57:31'
776781
recovery-xid = 597
777782
recovery-time = '2017-05-16 12:57:31'
783+
expire-time = '2020-05-16 12:57:31'
778784
data-bytes = 22288792
779785
wal-bytes = 16777216
780786
uncompressed-bytes = 39961833
@@ -794,6 +800,7 @@ Detailed output has additional attributes:
794800
- program-version — full version of pg_probackup binary used to create backup.
795801
- start-time — the backup starting time.
796802
- end-time — the backup ending time.
803+
- expire-time — if the backup was pinned, then until this point in time the backup cannot be removed by retention purge.
797804
- uncompressed-bytes — size of the data files before adding page headers and applying compression. You can evaluate the effectiveness of compression by comparing 'uncompressed-bytes' to 'data-bytes' if compression if used.
798805
- pgdata-bytes — size of the PostgreSQL cluster data files at the time of backup. You can evaluate the effectiveness of incremental backup by comparing 'pgdata-bytes' to 'uncompressed-bytes'.
799806
- recovery-xid — current transaction id at the moment of backup ending.
@@ -1171,6 +1178,42 @@ BACKUP INSTANCE 'node'
11711178

11721179
>NOTE: The Time field for the merged backup displays the time required for the merge.
11731180
1181+
#### Pinning a Backup
1182+
1183+
If you have the necessity to exclude certain backups from established retention policy then it is possible to pin a backup for an arbitrary amount of time. Example:
1184+
1185+
pg_probackup set-backup -B backup_dir --instance instance_name -i backup_id --ttl=30d
1186+
1187+
This command will set `expire-time` of specified backup to 30 days starting from backup `recovery-time` attribute. Basically 'expire-time = recovery-time + ttl'.
1188+
1189+
You can set `expire-time` explicitly using `--expire-time` option. Example:
1190+
1191+
pg_probackup set-backup -B backup_dir --instance instance_name -i backup_id --expire-time='2020-01-01 00:00:00+03'
1192+
1193+
Alternatively you can use the `--ttl` and `--expire-time` options with the [backup](#backup) command to pin newly created backup:
1194+
1195+
pg_probackup backup -B backup_dir --instance instance_name -b FULL --ttl=30d
1196+
pg_probackup backup -B backup_dir --instance instance_name -b FULL --expire-time='2020-01-01 00:00:00+03'
1197+
1198+
You can determine the fact that backup is pinned and check due expire time by looking up 'expire-time' attribute in backup metadata via (show)[#show] command:
1199+
1200+
pg_probackup show --instance instance_name -i backup_id
1201+
1202+
Pinned backup has `expire-time` attribute:
1203+
```
1204+
...
1205+
recovery-time = '2017-05-16 12:57:31'
1206+
expire-time = '2020-01-01 00:00:00+03'
1207+
data-bytes = 22288792
1208+
...
1209+
```
1210+
1211+
You can unpin a backup by setting `--ttl` option to zero using `set-backup` command. Example:
1212+
1213+
pg_probackup set-backup -B backup_dir --instance instance_name -i backup_id --ttl=0
1214+
1215+
Only pinned backups have `expire-time` attribute in backup metadata.
1216+
11741217
### Merging Backups
11751218

11761219
As you take more and more incremental backups, the total size of the backup catalog can substantially grow. To save disk space, you can merge incremental backups to their parent full backup by running the merge command, specifying the backup ID of the most recent incremental backup you would like to merge:
@@ -1267,6 +1310,15 @@ For all available settings, see the [Options](#options) section.
12671310

12681311
It is **not recommended** to edit pg_probackup.conf manually.
12691312

1313+
#### set-backup
1314+
1315+
pg_probackup set-backup -B backup_dir --instance instance_name -i backup_id
1316+
{--ttl=ttl | --expire-time=time} [--help]
1317+
1318+
Sets the provided backup-specific settings into the backup.control configuration file, or modifies previously defined values.
1319+
1320+
For all available settings, see the section [Pinning Options](#pinning-options).
1321+
12701322
#### show-config
12711323

12721324
pg_probackup show-config -B backup_dir --instance instance_name [--format=plain|json]
@@ -1296,7 +1348,7 @@ For details on usage, see the sections [Managing the Backup Catalog](#managing-t
12961348
[-w --no-password] [-W --password]
12971349
[--archive-timeout=timeout] [--external-dirs=external_directory_path]
12981350
[connection_options] [compression_options] [remote_options]
1299-
[retention_options] [logging_options]
1351+
[retention_options] [pinning_options] [logging_options]
13001352

13011353
Creates a backup copy of the PostgreSQL instance. The *backup_mode* option specifies the backup mode to use.
13021354

@@ -1342,7 +1394,7 @@ Disables block-level checksum verification to speed up backup.
13421394
--no-validate
13431395
Skips automatic validation after successfull backup. You can use this flag if you validate backups regularly and would like to save time when running backup operations.
13441396

1345-
Additionally [Connection Options](#connection-options), [Retention Options](#retention-options), [Remote Mode Options](#remote-mode-options), [Compression Options](#compression-options), [Logging Options](#logging-options) and [Common Options](#common-options) can be used.
1397+
Additionally [Connection Options](#connection-options), [Retention Options](#retention-options), [Pinning Options](#pinning-options), [Remote Mode Options](#remote-mode-options), [Compression Options](#compression-options), [Logging Options](#logging-options) and [Common Options](#common-options) can be used.
13461398

13471399
For details on usage, see the section [Creating a Backup](#creating-a-backup).
13481400

@@ -1565,6 +1617,18 @@ Merges the oldest incremental backup that satisfies the requirements of retentio
15651617
--dry-run
15661618
Displays the current status of all the available backups, without deleting or merging expired backups, if any.
15671619

1620+
##### Pinning Options
1621+
1622+
You can use these options together with [backup](#backup) and [set-delete](#set-backup) commands.
1623+
1624+
For details on backup pinning, see the section [Pinning a Backup](#pinning-a-backup).
1625+
1626+
--ttl=ttl
1627+
Specifies the amount of time the backup should be pinned. Must be a positive integer. The zero value unpin already pinned backup. Supported units: ms, s, min, h, d (s by default). Example: `--ttl=30d`.
1628+
1629+
--expire-time=time
1630+
Specifies the timestamp up to which the backup will stay pinned. Must be a ISO-8601 complaint timestamp. Example: `--expire-time='2020-01-01 00:00:00+03'`
1631+
15681632
#### Logging Options
15691633

15701634
You can use these options with any command.

src/backup.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,8 @@ pgdata_basic_setup(ConnectionOptions conn_opt, PGNodeInfo *nodeInfo)
695695
* Entry point of pg_probackup BACKUP subcommand.
696696
*/
697697
int
698-
do_backup(time_t start_time, bool no_validate)
698+
do_backup(time_t start_time, bool no_validate,
699+
pgSetBackupParams *set_backup_params)
699700
{
700701
PGconn *backup_conn = NULL;
701702
PGNodeInfo nodeInfo;
@@ -801,6 +802,15 @@ do_backup(time_t start_time, bool no_validate)
801802
current.status = BACKUP_STATUS_DONE;
802803
write_backup(&current);
803804

805+
/* Pin backup if requested */
806+
if (set_backup_params &&
807+
(set_backup_params->ttl > 0 ||
808+
set_backup_params->expire_time > 0))
809+
{
810+
if (!pin_backup(&current, set_backup_params))
811+
elog(ERROR, "Failed to pin the backup %s", base36enc(current.backup_id));
812+
}
813+
804814
if (!no_validate)
805815
pgBackupValidate(&current, NULL);
806816

src/catalog.c

+82
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,81 @@ get_oldest_backup(timelineInfo *tlinfo)
987987
return oldest_backup;
988988
}
989989

990+
/*
991+
* Overwrite backup metadata.
992+
*/
993+
void
994+
do_set_backup(const char *instance_name, time_t backup_id,
995+
pgSetBackupParams *set_backup_params)
996+
{
997+
pgBackup *target_backup = NULL;
998+
parray *backup_list = NULL;
999+
1000+
if (!set_backup_params)
1001+
elog(ERROR, "Nothing to set by 'set-backup' command");
1002+
1003+
backup_list = catalog_get_backup_list(instance_name, backup_id);
1004+
if (parray_num(backup_list) != 1)
1005+
elog(ERROR, "Failed to find backup %s", base36enc(backup_id));
1006+
1007+
target_backup = (pgBackup *) parray_get(backup_list, 0);
1008+
1009+
if (!pin_backup(target_backup, set_backup_params))
1010+
elog(ERROR, "Failed to pin the backup %s", base36enc(backup_id));
1011+
}
1012+
1013+
/*
1014+
* Set 'expire-time' attribute based on set_backup_params, or unpin backup
1015+
* if ttl is equal to zero.
1016+
*/
1017+
bool
1018+
pin_backup(pgBackup *target_backup, pgSetBackupParams *set_backup_params)
1019+
{
1020+
1021+
if (target_backup->recovery_time <= 0)
1022+
elog(ERROR, "Failed to set 'expire-time' for backup %s: invalid 'recovery-time'",
1023+
base36enc(target_backup->backup_id));
1024+
1025+
/* Pin comes from ttl */
1026+
if (set_backup_params->ttl > 0)
1027+
target_backup->expire_time = target_backup->recovery_time + set_backup_params->ttl;
1028+
/* Unpin backup */
1029+
else if (set_backup_params->ttl == 0)
1030+
{
1031+
/* If backup was not pinned in the first place,
1032+
* then there is nothing to unpin.
1033+
*/
1034+
if (target_backup->expire_time == 0)
1035+
{
1036+
elog(WARNING, "Backup %s is not pinned, nothing to unpin",
1037+
base36enc(target_backup->start_time));
1038+
return false;
1039+
}
1040+
target_backup->expire_time = 0;
1041+
}
1042+
/* Pin comes from expire-time */
1043+
else if (set_backup_params->expire_time > 0)
1044+
target_backup->expire_time = set_backup_params->expire_time;
1045+
else
1046+
return false;
1047+
1048+
/* Update backup.control */
1049+
write_backup(target_backup);
1050+
1051+
if (set_backup_params->ttl > 0 || set_backup_params->expire_time > 0)
1052+
{
1053+
char expire_timestamp[100];
1054+
1055+
time2iso(expire_timestamp, lengthof(expire_timestamp), target_backup->expire_time);
1056+
elog(INFO, "Backup %s is pinned until '%s'", base36enc(target_backup->start_time),
1057+
expire_timestamp);
1058+
}
1059+
else
1060+
elog(INFO, "Backup %s is unpinned", base36enc(target_backup->start_time));
1061+
1062+
return true;
1063+
}
1064+
9901065
/*
9911066
* Write information about backup.in to stream "out".
9921067
*/
@@ -1041,6 +1116,11 @@ pgBackupWriteControl(FILE *out, pgBackup *backup)
10411116
time2iso(timestamp, lengthof(timestamp), backup->recovery_time);
10421117
fio_fprintf(out, "recovery-time = '%s'\n", timestamp);
10431118
}
1119+
if (backup->expire_time > 0)
1120+
{
1121+
time2iso(timestamp, lengthof(timestamp), backup->expire_time);
1122+
fio_fprintf(out, "expire-time = '%s'\n", timestamp);
1123+
}
10441124

10451125
/*
10461126
* Size of PGDATA directory. The size does not include size of related
@@ -1284,6 +1364,7 @@ readBackupControlFile(const char *path)
12841364
{'t', 0, "end-time", &backup->end_time, SOURCE_FILE_STRICT},
12851365
{'U', 0, "recovery-xid", &backup->recovery_xid, SOURCE_FILE_STRICT},
12861366
{'t', 0, "recovery-time", &backup->recovery_time, SOURCE_FILE_STRICT},
1367+
{'t', 0, "expire-time", &backup->expire_time, SOURCE_FILE_STRICT},
12871368
{'I', 0, "data-bytes", &backup->data_bytes, SOURCE_FILE_STRICT},
12881369
{'I', 0, "wal-bytes", &backup->wal_bytes, SOURCE_FILE_STRICT},
12891370
{'I', 0, "uncompressed-bytes", &backup->uncompressed_bytes, SOURCE_FILE_STRICT},
@@ -1529,6 +1610,7 @@ pgBackupInit(pgBackup *backup)
15291610
backup->end_time = (time_t) 0;
15301611
backup->recovery_xid = 0;
15311612
backup->recovery_time = (time_t) 0;
1613+
backup->expire_time = (time_t) 0;
15321614

15331615
backup->data_bytes = BYTES_INVALID;
15341616
backup->wal_bytes = BYTES_INVALID;

src/delete.c

+23-3
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,20 @@ do_retention_internal(parray *backup_list, parray *to_keep_list, parray *to_purg
292292
* FULL CORRUPT out of retention
293293
*/
294294

295+
/* Save backup from purge if backup is pinned and
296+
* expire date is not yet due.
297+
*/
298+
if ((backup->expire_time > 0) &&
299+
(backup->expire_time > current_time))
300+
{
301+
char expire_timestamp[100];
302+
time2iso(expire_timestamp, lengthof(expire_timestamp), backup->expire_time);
303+
304+
elog(LOG, "Backup %s is pinned until '%s', retain",
305+
base36enc(backup->start_time), expire_timestamp);
306+
continue;
307+
}
308+
295309
/* Add backup to purge_list */
296310
elog(VERBOSE, "Mark backup %s for purge.", base36enc(backup->start_time));
297311
parray_append(to_purge_list, backup);
@@ -355,6 +369,7 @@ do_retention_internal(parray *backup_list, parray *to_keep_list, parray *to_purg
355369
for (i = 0; i < parray_num(backup_list); i++)
356370
{
357371
char *action = "Active";
372+
uint32 pinning_window = 0;
358373

359374
pgBackup *backup = (pgBackup *) parray_get(backup_list, i);
360375

@@ -364,7 +379,11 @@ do_retention_internal(parray *backup_list, parray *to_keep_list, parray *to_purg
364379
if (backup->recovery_time == 0)
365380
actual_window = 0;
366381
else
367-
actual_window = (current_time - backup->recovery_time)/(60 * 60 * 24);
382+
actual_window = (current_time - backup->recovery_time)/(3600 * 24);
383+
384+
/* For pinned backups show expire date */
385+
if (backup->expire_time > 0 && backup->expire_time > backup->recovery_time)
386+
pinning_window = (backup->expire_time - backup->recovery_time)/(3600 * 24);
368387

369388
/* TODO: add ancestor(chain full backup) ID */
370389
elog(INFO, "Backup %s, mode: %s, status: %s. Redundancy: %i/%i, Time Window: %ud/%ud. %s",
@@ -373,7 +392,8 @@ do_retention_internal(parray *backup_list, parray *to_keep_list, parray *to_purg
373392
status2str(backup->status),
374393
cur_full_backup_num,
375394
instance_config.retention_redundancy,
376-
actual_window, instance_config.retention_window,
395+
actual_window,
396+
pinning_window ? pinning_window : instance_config.retention_window,
377397
action);
378398

379399
if (backup->backup_mode == BACKUP_MODE_FULL)
@@ -576,7 +596,7 @@ do_retention_purge(parray *to_keep_list, parray *to_purge_list)
576596
{
577597

578598
/* We must not delete this backup, evict it from purge list */
579-
elog(LOG, "Retain backup %s from purge because his "
599+
elog(LOG, "Retain backup %s because his "
580600
"descendant %s is guarded by retention",
581601
base36enc(delete_backup->start_time), keeped_backup_id);
582602

0 commit comments

Comments
 (0)