Skip to content

Commit bc5fa17

Browse files
Add --uniform-snapshot-timestamp option (#146)
This option apply a single consistent timestamp to all filenames in a snapshot instead of using individual file creation times.
1 parent 04efb95 commit bc5fa17

3 files changed

Lines changed: 74 additions & 46 deletions

File tree

config.go

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,40 +47,41 @@ var defaultCfg string
4747

4848
// options struct holds command line and configuration file options
4949
type options struct {
50-
NoConfigFile bool
51-
BinDirectory string
52-
Directory string
53-
Host string
54-
Port int
55-
Username string
56-
ConnDb string
57-
ExcludeDbs []string
58-
Dbnames []string
59-
WithTemplates bool
60-
Format rune
61-
DirJobs int
62-
CompressLevel int
63-
Jobs int
64-
PauseTimeout int
65-
PurgeInterval time.Duration
66-
PurgeKeep int
67-
SumAlgo string
68-
PreHook string
69-
PostHook string
70-
PgDumpOpts []string
71-
PerDbOpts map[string]*dbOpts
72-
CfgFile string
73-
TimeFormat string
74-
Verbose bool
75-
Quiet bool
76-
Encrypt bool
77-
EncryptKeepSrc bool
78-
CipherPassphrase string
79-
CipherPublicKey string
80-
CipherPrivateKey string
81-
Decrypt bool
82-
WithRolePasswords bool
83-
DumpOnly bool
50+
NoConfigFile bool
51+
BinDirectory string
52+
Directory string
53+
Host string
54+
Port int
55+
Username string
56+
ConnDb string
57+
ExcludeDbs []string
58+
Dbnames []string
59+
WithTemplates bool
60+
Format rune
61+
DirJobs int
62+
CompressLevel int
63+
Jobs int
64+
PauseTimeout int
65+
PurgeInterval time.Duration
66+
PurgeKeep int
67+
SumAlgo string
68+
PreHook string
69+
PostHook string
70+
PgDumpOpts []string
71+
PerDbOpts map[string]*dbOpts
72+
CfgFile string
73+
TimeFormat string
74+
Verbose bool
75+
Quiet bool
76+
Encrypt bool
77+
EncryptKeepSrc bool
78+
CipherPassphrase string
79+
CipherPublicKey string
80+
CipherPrivateKey string
81+
Decrypt bool
82+
WithRolePasswords bool
83+
DumpOnly bool
84+
UniformSnapshotTimestamp bool
8485

8586
Upload string // values are none, b2, s3, sftp, gcs
8687
UploadPrefix string
@@ -285,6 +286,7 @@ func parseCli(args []string) (options, []string, error) {
285286
pflag.StringVarP(&purgeKeep, "purge-min-keep", "K", "0", "minimum number of dumps to keep when purging or 'all' to keep\neverything")
286287
pflag.StringVar(&opts.PreHook, "pre-backup-hook", "", "command to run before taking dumps")
287288
pflag.StringVar(&opts.PostHook, "post-backup-hook", "", "command to run after taking dumps\n")
289+
pflag.BoolVar(&opts.UniformSnapshotTimestamp, "uniform-snapshot-timestamp", false, "Apply a single consistent timestamp to all filenames in a snapshot instead of using individual file creation times")
288290

289291
pflag.BoolVar(&opts.Encrypt, "encrypt", false, "encrypt the dumps")
290292
NoEncrypt := pflag.Bool("no-encrypt", false, "do not encrypt the dumps")
@@ -532,7 +534,7 @@ func validateConfigurationFile(cfg *ini.File) error {
532534
"sftp_port", "sftp_user", "sftp_password", "sftp_directory", "sftp_identity",
533535
"sftp_ignore_hostkey", "gcs_bucket", "gcs_endpoint", "gcs_keyfile",
534536
"azure_container", "azure_account", "azure_key", "azure_endpoint", "pg_dump_options",
535-
"dump_role_passwords", "dump_only", "upload_prefix",
537+
"dump_role_passwords", "dump_only", "upload_prefix", "uniform_snapshot_timestamp",
536538
}
537539

538540
gkLoop:
@@ -625,6 +627,7 @@ func loadConfigurationFile(path string) (options, error) {
625627
opts.CipherPublicKey = s.Key("cipher_public_key").MustString("")
626628
opts.CipherPrivateKey = s.Key("cipher_private_key").MustString("")
627629
opts.EncryptKeepSrc = s.Key("encrypt_keep_source").MustBool(false)
630+
opts.UniformSnapshotTimestamp = s.Key("uniform_snapshot_timestamp").MustBool(false)
628631

629632
opts.Upload = s.Key("upload").MustString("none")
630633
opts.UploadPrefix = s.Key("upload_prefix").MustString("")
@@ -950,6 +953,8 @@ func mergeCliAndConfigOptions(cliOpts options, configOpts options, onCli []strin
950953
opts.Username = cliOpts.Username
951954
case "dbname":
952955
opts.ConnDb = cliOpts.ConnDb
956+
case "uniform-snapshot-timestamp":
957+
opts.UniformSnapshotTimestamp = cliOpts.UniformSnapshotTimestamp
953958
}
954959
}
955960

main.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ func run() (retVal error) {
302302
}
303303
defer db.Close()
304304

305+
// Generate a single datetime that will be used in all generated snapshot names
306+
var snapshotTime time.Time
307+
if opts.UniformSnapshotTimestamp {
308+
snapshotTime = time.Now()
309+
}
310+
305311
if !opts.DumpOnly {
306312
if !db.superuser {
307313
l.Infoln("connection user is not superuser, some information will not be dumped")
@@ -315,7 +321,7 @@ func run() (retVal error) {
315321
} else {
316322
l.Infoln("dumping globals without role passwords")
317323
}
318-
if err := dumpGlobals(opts.Directory, opts.TimeFormat, dumpRolePasswords, conninfo, producedFiles); err != nil {
324+
if err := dumpGlobals(opts.Directory, opts.TimeFormat, dumpRolePasswords, conninfo, producedFiles, snapshotTime); err != nil {
319325
return fmt.Errorf("pg_dumpall of globals failed: %w", err)
320326
}
321327

@@ -325,15 +331,15 @@ func run() (retVal error) {
325331
perr *pgPrivError
326332
)
327333

328-
if err := dumpSettings(opts.Directory, opts.TimeFormat, db, producedFiles); err != nil {
334+
if err := dumpSettings(opts.Directory, opts.TimeFormat, db, producedFiles, snapshotTime); err != nil {
329335
if errors.As(err, &verr) || errors.As(err, &perr) {
330336
l.Warnln(err)
331337
} else {
332338
return fmt.Errorf("could not dump configuration parameters: %w", err)
333339
}
334340
}
335341

336-
if err := dumpConfigFiles(opts.Directory, opts.TimeFormat, db, producedFiles); err != nil {
342+
if err := dumpConfigFiles(opts.Directory, opts.TimeFormat, db, producedFiles, snapshotTime); err != nil {
337343
return fmt.Errorf("could not dump configuration files: %w", err)
338344
}
339345
}
@@ -384,6 +390,7 @@ func run() (retVal error) {
384390
CipherPassphrase: passphrase,
385391
CipherPublicKey: publicKey,
386392
EncryptKeepSrc: opts.EncryptKeepSrc,
393+
When: snapshotTime,
387394
ExitCode: -1,
388395
PgDumpVersion: pgDumpVersion,
389396
}
@@ -612,8 +619,6 @@ func (d *dump) dump(fc chan<- sumFileJob) error {
612619
return fmt.Errorf("could not acquire lock for %s", dbname)
613620
}
614621

615-
d.When = time.Now()
616-
617622
var fileEnd string
618623
switch d.Options.Format {
619624
case 'p':
@@ -630,6 +635,10 @@ func (d *dump) dump(fc chan<- sumFileJob) error {
630635
fileEnd = "d"
631636
}
632637

638+
if d.When.IsZero() {
639+
d.When = time.Now()
640+
}
641+
633642
file := formatDumpPath(d.Directory, d.TimeFormat, fileEnd, dbname, d.When, d.Options.CompressLevel)
634643
formatOpt := fmt.Sprintf("-F%c", d.Options.Format)
635644

@@ -898,7 +907,7 @@ func pgToolVersion(tool string) int {
898907
return numver
899908
}
900909

901-
func dumpGlobals(dir string, timeFormat string, withRolePasswords bool, conninfo *ConnInfo, fc chan<- sumFileJob) error {
910+
func dumpGlobals(dir string, timeFormat string, withRolePasswords bool, conninfo *ConnInfo, fc chan<- sumFileJob, snapshotTime time.Time) error {
902911
command := execPath("pg_dumpall")
903912
args := []string{"-g", "-w"}
904913

@@ -930,7 +939,11 @@ func dumpGlobals(dir string, timeFormat string, withRolePasswords bool, conninfo
930939
args = append(args, "--no-role-passwords")
931940
}
932941

933-
file := formatDumpPath(dir, timeFormat, "sql", "pg_globals", time.Now(), 0)
942+
if snapshotTime.IsZero() {
943+
snapshotTime = time.Now()
944+
}
945+
946+
file := formatDumpPath(dir, timeFormat, "sql", "pg_globals", snapshotTime, 0)
934947
args = append(args, "-f", file)
935948

936949
if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil {
@@ -970,9 +983,12 @@ func dumpGlobals(dir string, timeFormat string, withRolePasswords bool, conninfo
970983
return nil
971984
}
972985

973-
func dumpSettings(dir string, timeFormat string, db *pg, fc chan<- sumFileJob) error {
986+
func dumpSettings(dir string, timeFormat string, db *pg, fc chan<- sumFileJob, snapshotTime time.Time) error {
987+
if snapshotTime.IsZero() {
988+
snapshotTime = time.Now()
989+
}
974990

975-
file := formatDumpPath(dir, timeFormat, "out", "pg_settings", time.Now(), 0)
991+
file := formatDumpPath(dir, timeFormat, "out", "pg_settings", snapshotTime, 0)
976992

977993
if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil {
978994
return err
@@ -1000,9 +1016,12 @@ func dumpSettings(dir string, timeFormat string, db *pg, fc chan<- sumFileJob) e
10001016
return nil
10011017
}
10021018

1003-
func dumpConfigFiles(dir string, timeFormat string, db *pg, fc chan<- sumFileJob) error {
1019+
func dumpConfigFiles(dir string, timeFormat string, db *pg, fc chan<- sumFileJob, snapshotTime time.Time) error {
10041020
for _, param := range []string{"hba_file", "ident_file"} {
1005-
file := formatDumpPath(dir, timeFormat, "out", param, time.Now(), 0)
1021+
if snapshotTime.IsZero() {
1022+
snapshotTime = time.Now()
1023+
}
1024+
file := formatDumpPath(dir, timeFormat, "out", param, snapshotTime, 0)
10061025

10071026
if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil {
10081027
return err

pg_back.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ with_templates = false
4343
# Dump only databases, excluding configuration and globals
4444
dump_only = false
4545

46+
# Apply a single consistent timestamp to all filenames in a snapshot
47+
# instead of using individual file creation times
48+
uniform_snapshot_timestamp = false
49+
4650
# Format of the dump, understood by pg_dump. Possible values are
4751
# plain, custom, tar or directory.
4852
format = custom

0 commit comments

Comments
 (0)