From 97bb113b5bf5427449b748c3ee25b647a2c5fef5 Mon Sep 17 00:00:00 2001 From: David Steele Date: Sun, 19 Nov 2023 16:54:36 +0000 Subject: Add label and start/stop time to backup manifest. Add label passed by the user to pg_basebackup and backup start/stop time to the backup_manifest file. Currently these fields are purely for informational purposes. --- doc/src/sgml/backup-manifest.sgml | 51 +++++++++++++++++++++ src/backend/backup/backup_manifest.c | 56 +++++++++++++++++++++++- src/backend/backup/basebackup.c | 9 +++- src/bin/pg_verifybackup/parse_manifest.c | 40 +++++++++++++++++ src/include/backup/backup_manifest.h | 5 ++- 5 files changed, 157 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/backup-manifest.sgml b/doc/src/sgml/backup-manifest.sgml index 771be1310a..a80b79e587 100644 --- a/doc/src/sgml/backup-manifest.sgml +++ b/doc/src/sgml/backup-manifest.sgml @@ -42,6 +42,16 @@ + + Backup-Label + + + Backup label specified by the user. This will be set to a default value + if no backup label was specified. + + + + Files @@ -67,6 +77,17 @@ + + Time-Range + + + The associated value is always an object describing the start/stop time + of the backup. The structure of this object is further described in + . + + + + Manifest-Checksum @@ -213,4 +234,34 @@ for the same timeline. + + + Backup Manifest Time Range Object + + + The object which describes the time range always has three keys: + + + + + Start-Time + + + The start time for the backup. + + + + + + End-Time + + + The end time for the backup. This is when the backup was stopped in + PostgreSQL and represents the earliest time + that can be used for time-based Point-In-Time Recovery. + + + + + diff --git a/src/backend/backup/backup_manifest.c b/src/backend/backup/backup_manifest.c index aeed362a9a..9540f05964 100644 --- a/src/backend/backup/backup_manifest.c +++ b/src/backend/backup/backup_manifest.c @@ -56,7 +56,8 @@ IsManifestEnabled(backup_manifest_info *manifest) void InitializeBackupManifest(backup_manifest_info *manifest, backup_manifest_option want_manifest, - pg_checksum_type manifest_checksum_type) + pg_checksum_type manifest_checksum_type, + const char *label) { memset(manifest, 0, sizeof(backup_manifest_info)); manifest->checksum_type = manifest_checksum_type; @@ -78,9 +79,21 @@ InitializeBackupManifest(backup_manifest_info *manifest, manifest->still_checksumming = true; if (want_manifest != MANIFEST_OPTION_NO) + { + StringInfoData buf; + AppendToManifest(manifest, "{ \"PostgreSQL-Backup-Manifest-Version\": 1,\n" - "\"Files\": ["); + "\"Backup-Label\": "); + + /* JSON encode label and add it to manifest */ + initStringInfo(&buf); + escape_json(&buf, label); + AppendStringToManifest(manifest, buf.data); + pfree(buf.data); + + AppendToManifest(manifest, ",\n\"Files\": ["); + } } /* @@ -308,6 +321,45 @@ AddWALInfoToBackupManifest(backup_manifest_info *manifest, XLogRecPtr startptr, AppendStringToManifest(manifest, "\n],\n"); } +/* + * Add backup start/end time information to the manifest. + */ +void +AddTimeInfoToBackupManifest(backup_manifest_info *manifest, pg_time_t starttime, + pg_time_t endtime) +{ + StringInfoData buf; + + if (!IsManifestEnabled(manifest)) + return; + + /* Start the time range. */ + AppendStringToManifest(manifest, "\"Time-Range\": { "); + + /* + * Convert start/end time to strings and append them to the manifest. Since + * it's not clear what time zone to use and since time zone definitions can + * change, possibly causing confusion, use GMT always. + */ + initStringInfo(&buf); + + appendStringInfoString(&buf, "\"Start-Time\": \""); + enlargeStringInfo(&buf, 128); + buf.len += pg_strftime(&buf.data[buf.len], 128, "%Y-%m-%d %H:%M:%S %Z", + pg_gmtime(&starttime)); + appendStringInfoString(&buf, "\", \"End-Time\": \""); + enlargeStringInfo(&buf, 128); + buf.len += pg_strftime(&buf.data[buf.len], 128, "%Y-%m-%d %H:%M:%S %Z", + pg_gmtime(&endtime)); + appendStringInfoString(&buf, "\" },\n"); + + /* Add to the manifest. */ + AppendStringToManifest(manifest, buf.data); + + /* Avoid leaking memory. */ + pfree(buf.data); +} + /* * Finalize the backup manifest, and send it to the client. */ diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c index 35dd79babc..c7b3ba3e6e 100644 --- a/src/backend/backup/basebackup.c +++ b/src/backend/backup/basebackup.c @@ -225,6 +225,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) bbsink_state state; XLogRecPtr endptr; TimeLineID endtli; + pg_time_t starttime; + pg_time_t stoptime; backup_manifest_info manifest; BackupState *backup_state; StringInfo tablespace_map; @@ -243,7 +245,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) backup_started_in_recovery = RecoveryInProgress(); InitializeBackupManifest(&manifest, opt->manifest, - opt->manifest_checksum_type); + opt->manifest_checksum_type, opt->label); total_checksum_failures = 0; @@ -380,6 +382,10 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) endptr = backup_state->stoppoint; endtli = backup_state->stoptli; + /* Record start/stop time for manifest */ + starttime = backup_state->starttime; + stoptime = backup_state->stoptime; + /* Deallocate backup-related variables. */ pfree(tablespace_map->data); pfree(tablespace_map); @@ -629,6 +635,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) AddWALInfoToBackupManifest(&manifest, state.startptr, state.starttli, endptr, endtli); + AddTimeInfoToBackupManifest(&manifest, starttime, stoptime); SendBackupManifest(&manifest, sink); diff --git a/src/bin/pg_verifybackup/parse_manifest.c b/src/bin/pg_verifybackup/parse_manifest.c index bf0227c668..408af88e58 100644 --- a/src/bin/pg_verifybackup/parse_manifest.c +++ b/src/bin/pg_verifybackup/parse_manifest.c @@ -25,6 +25,7 @@ typedef enum JM_EXPECT_TOPLEVEL_END, JM_EXPECT_TOPLEVEL_FIELD, JM_EXPECT_VERSION_VALUE, + JM_EXPECT_BACKUP_LABEL_VALUE, JM_EXPECT_FILES_START, JM_EXPECT_FILES_NEXT, JM_EXPECT_THIS_FILE_FIELD, @@ -33,6 +34,9 @@ typedef enum JM_EXPECT_WAL_RANGES_NEXT, JM_EXPECT_THIS_WAL_RANGE_FIELD, JM_EXPECT_THIS_WAL_RANGE_VALUE, + JM_EXPECT_TIME_RANGE_START, + JM_EXPECT_THIS_TIME_RANGE_FIELD, + JM_EXPECT_THIS_TIME_RANGE_VALUE, JM_EXPECT_MANIFEST_CHECKSUM_VALUE, JM_EXPECT_EOF, } JsonManifestSemanticState; @@ -188,6 +192,9 @@ json_manifest_object_start(void *state) parse->start_lsn = NULL; parse->end_lsn = NULL; break; + case JM_EXPECT_TIME_RANGE_START: + parse->state = JM_EXPECT_THIS_TIME_RANGE_FIELD; + break; default: json_manifest_parse_failure(parse->context, "unexpected object start"); @@ -223,6 +230,9 @@ json_manifest_object_end(void *state) json_manifest_finalize_wal_range(parse); parse->state = JM_EXPECT_WAL_RANGES_NEXT; break; + case JM_EXPECT_THIS_TIME_RANGE_FIELD: + parse->state = JM_EXPECT_TOPLEVEL_FIELD; + break; default: json_manifest_parse_failure(parse->context, "unexpected object end"); @@ -312,6 +322,13 @@ json_manifest_object_field_start(void *state, char *fname, bool isnull) break; } + /* Is this the backup label? */ + if (strcmp(fname, "Backup-Label") == 0) + { + parse->state = JM_EXPECT_BACKUP_LABEL_VALUE; + break; + } + /* Is this the list of files? */ if (strcmp(fname, "Files") == 0) { @@ -326,6 +343,13 @@ json_manifest_object_field_start(void *state, char *fname, bool isnull) break; } + /* Is this the time range? */ + if (strcmp(fname, "Time-Range") == 0) + { + parse->state = JM_EXPECT_TIME_RANGE_START; + break; + } + /* Is this the manifest checksum? */ if (strcmp(fname, "Manifest-Checksum") == 0) { @@ -372,6 +396,14 @@ json_manifest_object_field_start(void *state, char *fname, bool isnull) parse->state = JM_EXPECT_THIS_WAL_RANGE_VALUE; break; + case JM_EXPECT_THIS_TIME_RANGE_FIELD: + if (strcmp(fname, "Start-Time") != 0 && + strcmp(fname, "End-Time") != 0) + json_manifest_parse_failure(parse->context, + "unexpected time range field"); + parse->state = JM_EXPECT_THIS_TIME_RANGE_VALUE; + break; + default: json_manifest_parse_failure(parse->context, "unexpected object field"); @@ -410,6 +442,10 @@ json_manifest_scalar(void *state, char *token, JsonTokenType tokentype) parse->state = JM_EXPECT_TOPLEVEL_FIELD; break; + case JM_EXPECT_BACKUP_LABEL_VALUE: + parse->state = JM_EXPECT_TOPLEVEL_FIELD; + break; + case JM_EXPECT_THIS_FILE_VALUE: switch (parse->file_field) { @@ -451,6 +487,10 @@ json_manifest_scalar(void *state, char *token, JsonTokenType tokentype) parse->state = JM_EXPECT_THIS_WAL_RANGE_FIELD; break; + case JM_EXPECT_THIS_TIME_RANGE_VALUE: + parse->state = JM_EXPECT_THIS_TIME_RANGE_FIELD; + break; + case JM_EXPECT_MANIFEST_CHECKSUM_VALUE: parse->state = JM_EXPECT_TOPLEVEL_END; parse->manifest_checksum = token; diff --git a/src/include/backup/backup_manifest.h b/src/include/backup/backup_manifest.h index bd7067ae42..4ab1291bba 100644 --- a/src/include/backup/backup_manifest.h +++ b/src/include/backup/backup_manifest.h @@ -37,7 +37,8 @@ typedef struct backup_manifest_info extern void InitializeBackupManifest(backup_manifest_info *manifest, backup_manifest_option want_manifest, - pg_checksum_type manifest_checksum_type); + pg_checksum_type manifest_checksum_type, + const char *label); extern void AddFileToBackupManifest(backup_manifest_info *manifest, Oid spcoid, const char *pathname, size_t size, @@ -47,6 +48,8 @@ extern void AddWALInfoToBackupManifest(backup_manifest_info *manifest, XLogRecPtr startptr, TimeLineID starttli, XLogRecPtr endptr, TimeLineID endtli); +extern void AddTimeInfoToBackupManifest(backup_manifest_info *manifest, + pg_time_t starttime, pg_time_t endtime); extern void SendBackupManifest(backup_manifest_info *manifest, bbsink *sink); extern void FreeBackupManifest(backup_manifest_info *manifest); -- 2.34.1