|
1 | 1 | package internal |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
5 | 6 | "io" |
6 | 7 |
|
7 | 8 | "github.com/rs/zerolog/log" |
8 | 9 |
|
9 | 10 | "github.com/DeltaLaboratory/postgres-backup/internal/config" |
| 11 | + "github.com/DeltaLaboratory/postgres-backup/internal/storage/local" |
10 | 12 | "github.com/DeltaLaboratory/postgres-backup/internal/storage/s3" |
11 | 13 | ) |
12 | 14 |
|
13 | 15 | func Backup() { |
| 16 | + logger := log.Logger.With().Str("caller", "backup").Logger() |
| 17 | + |
| 18 | + // Get database name for logging context |
| 19 | + dbName := "unknown" |
| 20 | + if config.Loaded.Postgres.Database != nil { |
| 21 | + dbName = *config.Loaded.Postgres.Database |
| 22 | + } |
| 23 | + |
| 24 | + logger.Info().Str("database", dbName).Msg("starting database backup") |
| 25 | + |
14 | 26 | process, err := Dump() |
15 | 27 | if err != nil { |
16 | | - log.Error().Err(err).Msg("failed to dump database") |
| 28 | + logger.Error().Err(err).Str("database", dbName).Msg("failed to create database dump") |
17 | 29 | return |
18 | 30 | } |
19 | 31 |
|
20 | 32 | if err := process.Start(); err != nil { |
21 | | - log.Error().Err(err).Msg("failed to start backup process") |
| 33 | + logger.Error().Err(err).Str("database", dbName).Msg("failed to start pg_dump process") |
22 | 34 | return |
23 | 35 | } |
24 | 36 |
|
25 | 37 | var reader io.Reader = process |
26 | 38 |
|
27 | 39 | if config.Loaded.Compress != nil { |
28 | | - log.Info().Str("algorithm", config.Loaded.Compress.Algorithm).Int("compress_level", *config.Loaded.Compress.CompressLevel).Msg("start compress steam") |
| 40 | + logger.Info().Str("algorithm", config.Loaded.Compress.Algorithm).Int("compress_level", *config.Loaded.Compress.CompressLevel).Str("database", dbName).Msg("starting compression stream") |
29 | 41 | reader, err = Compress(reader) |
30 | 42 | if err != nil { |
31 | | - log.Error().Err(err).Msg("failed to compress dump") |
| 43 | + logger.Error().Err(err).Str("database", dbName).Str("algorithm", config.Loaded.Compress.Algorithm).Msg("failed to compress database dump") |
32 | 44 | return |
33 | 45 | } |
34 | 46 | } |
35 | 47 |
|
| 48 | + // Buffer the data if we need to upload to multiple storage backends |
| 49 | + var buffer *bytes.Buffer |
| 50 | + bothConfigured := config.Loaded.Storage.S3 != nil && config.Loaded.Storage.Local != nil |
| 51 | + |
| 52 | + if bothConfigured { |
| 53 | + // Read all data into buffer for multiple uploads |
| 54 | + logger.Info().Str("database", dbName).Msg("buffering backup data for multiple storage backends") |
| 55 | + buffer = &bytes.Buffer{} |
| 56 | + _, err = io.Copy(buffer, reader) |
| 57 | + if err != nil { |
| 58 | + logger.Error().Err(err).Str("database", dbName).Msg("failed to buffer backup data for storage") |
| 59 | + return |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + // Upload to S3 if configured |
36 | 64 | if config.Loaded.Storage.S3 != nil { |
37 | | - if err := s3.Upload(context.Background(), reader); err != nil { |
38 | | - log.Error().Err(err).Msg("failed to upload dump") |
| 65 | + var s3Reader io.Reader |
| 66 | + if bothConfigured { |
| 67 | + s3Reader = bytes.NewReader(buffer.Bytes()) |
| 68 | + } else { |
| 69 | + s3Reader = reader |
| 70 | + } |
| 71 | + |
| 72 | + if err := s3.Upload(context.Background(), s3Reader); err != nil { |
| 73 | + logger.Error().Err(err).Str("database", dbName).Str("bucket", config.Loaded.Storage.S3.Bucket).Msg("failed to upload backup to S3") |
| 74 | + } else { |
| 75 | + logger.Info().Str("database", dbName).Str("bucket", config.Loaded.Storage.S3.Bucket).Msg("successfully uploaded backup to S3") |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + // Upload to local storage if configured |
| 80 | + if config.Loaded.Storage.Local != nil { |
| 81 | + var localReader io.Reader |
| 82 | + if bothConfigured { |
| 83 | + localReader = bytes.NewReader(buffer.Bytes()) |
| 84 | + } else { |
| 85 | + localReader = reader |
| 86 | + } |
| 87 | + |
| 88 | + if err := local.Upload(context.Background(), localReader); err != nil { |
| 89 | + logger.Error().Err(err).Str("database", dbName).Str("directory", config.Loaded.Storage.Local.Directory).Msg("failed to upload backup to local storage") |
| 90 | + } else { |
| 91 | + logger.Info().Str("database", dbName).Str("directory", config.Loaded.Storage.Local.Directory).Msg("successfully uploaded backup to local storage") |
39 | 92 | } |
40 | 93 | } |
41 | 94 |
|
42 | 95 | if err := process.Wait(); err != nil { |
43 | | - log.Error().Err(err).Msg("failed to run backup process") |
| 96 | + logger.Error().Err(err).Str("database", dbName).Msg("pg_dump process finished with error") |
44 | 97 | } |
45 | 98 |
|
46 | | - log.Info().Msg("backup completed") |
| 99 | + logger.Info().Str("database", dbName).Msg("database backup completed successfully") |
47 | 100 | } |
0 commit comments