Skip to content

Commit 56b5e8a

Browse files
FEATURE (backups): Add retries in case of failed backups
1 parent aa6b495 commit 56b5e8a

File tree

16 files changed

+518
-50
lines changed

16 files changed

+518
-50
lines changed

backend/internal/features/backups/backups/background_service.go

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"log/slog"
55
"postgresus-backend/internal/config"
66
backups_config "postgresus-backend/internal/features/backups/config"
7-
"postgresus-backend/internal/features/databases"
87
"postgresus-backend/internal/features/storages"
98
"postgresus-backend/internal/util/period"
109
"time"
@@ -13,9 +12,8 @@ import (
1312
type BackupBackgroundService struct {
1413
backupService *BackupService
1514
backupRepository *BackupRepository
16-
databaseService *databases.DatabaseService
17-
storageService *storages.StorageService
1815
backupConfigService *backups_config.BackupConfigService
16+
storageService *storages.StorageService
1917

2018
lastBackupTime time.Time
2119
logger *slog.Logger
@@ -51,7 +49,7 @@ func (s *BackupBackgroundService) Run() {
5149
}
5250
}
5351

54-
func (s *BackupBackgroundService) IsBackupsRunning() bool {
52+
func (s *BackupBackgroundService) IsBackupsWorkerRunning() bool {
5553
// if last backup time is more than 5 minutes ago, return false
5654
return s.lastBackupTime.After(time.Now().UTC().Add(-5 * time.Minute))
5755
}
@@ -90,18 +88,12 @@ func (s *BackupBackgroundService) failBackupsInProgress() error {
9088
}
9189

9290
func (s *BackupBackgroundService) cleanOldBackups() error {
93-
allDatabases, err := s.databaseService.GetAllDatabases()
91+
enabledBackupConfigs, err := s.backupConfigService.GetBackupConfigsWithEnabledBackups()
9492
if err != nil {
9593
return err
9694
}
9795

98-
for _, database := range allDatabases {
99-
backupConfig, err := s.backupConfigService.GetBackupConfigByDbId(database.ID)
100-
if err != nil {
101-
s.logger.Error("Failed to get backup config by database ID", "error", err)
102-
continue
103-
}
104-
96+
for _, backupConfig := range enabledBackupConfigs {
10597
backupStorePeriod := backupConfig.StorePeriod
10698

10799
if backupStorePeriod == period.PeriodForever {
@@ -112,14 +104,14 @@ func (s *BackupBackgroundService) cleanOldBackups() error {
112104
dateBeforeBackupsShouldBeDeleted := time.Now().UTC().Add(-storeDuration)
113105

114106
oldBackups, err := s.backupRepository.FindBackupsBeforeDate(
115-
database.ID,
107+
backupConfig.DatabaseID,
116108
dateBeforeBackupsShouldBeDeleted,
117109
)
118110
if err != nil {
119111
s.logger.Error(
120112
"Failed to find old backups for database",
121113
"databaseId",
122-
database.ID,
114+
backupConfig.DatabaseID,
123115
"error",
124116
err,
125117
)
@@ -149,36 +141,36 @@ func (s *BackupBackgroundService) cleanOldBackups() error {
149141
continue
150142
}
151143

152-
s.logger.Info("Deleted old backup", "backupId", backup.ID, "databaseId", database.ID)
144+
s.logger.Info(
145+
"Deleted old backup",
146+
"backupId",
147+
backup.ID,
148+
"databaseId",
149+
backupConfig.DatabaseID,
150+
)
153151
}
154152
}
155153

156154
return nil
157155
}
158156

159157
func (s *BackupBackgroundService) runPendingBackups() error {
160-
allDatabases, err := s.databaseService.GetAllDatabases()
158+
enabledBackupConfigs, err := s.backupConfigService.GetBackupConfigsWithEnabledBackups()
161159
if err != nil {
162160
return err
163161
}
164162

165-
for _, database := range allDatabases {
166-
backupConfig, err := s.backupConfigService.GetBackupConfigByDbId(database.ID)
167-
if err != nil {
168-
s.logger.Error("Failed to get backup config by database ID", "error", err)
169-
continue
170-
}
171-
163+
for _, backupConfig := range enabledBackupConfigs {
172164
if backupConfig.BackupInterval == nil {
173165
continue
174166
}
175167

176-
lastBackup, err := s.backupRepository.FindLastByDatabaseID(database.ID)
168+
lastBackup, err := s.backupRepository.FindLastByDatabaseID(backupConfig.DatabaseID)
177169
if err != nil {
178170
s.logger.Error(
179171
"Failed to get last backup for database",
180172
"databaseId",
181-
database.ID,
173+
backupConfig.DatabaseID,
182174
"error",
183175
err,
184176
)
@@ -190,19 +182,71 @@ func (s *BackupBackgroundService) runPendingBackups() error {
190182
lastBackupTime = &lastBackup.CreatedAt
191183
}
192184

193-
if backupConfig.BackupInterval.ShouldTriggerBackup(time.Now().UTC(), lastBackupTime) {
185+
remainedBackupTryCount := s.GetRemainedBackupTryCount(lastBackup)
186+
187+
if backupConfig.BackupInterval.ShouldTriggerBackup(time.Now().UTC(), lastBackupTime) ||
188+
remainedBackupTryCount > 0 {
194189
s.logger.Info(
195190
"Triggering scheduled backup",
196191
"databaseId",
197-
database.ID,
192+
backupConfig.DatabaseID,
198193
"intervalType",
199194
backupConfig.BackupInterval.Interval,
200195
)
201196

202-
go s.backupService.MakeBackup(database.ID)
203-
s.logger.Info("Successfully triggered scheduled backup", "databaseId", database.ID)
197+
go s.backupService.MakeBackup(backupConfig.DatabaseID, remainedBackupTryCount == 1)
198+
s.logger.Info(
199+
"Successfully triggered scheduled backup",
200+
"databaseId",
201+
backupConfig.DatabaseID,
202+
)
204203
}
205204
}
206205

207206
return nil
208207
}
208+
209+
// GetRemainedBackupTryCount returns the number of remaining backup tries for a given backup.
210+
// If the backup is not failed or the backup config does not allow retries, it returns 0.
211+
// If the backup is failed and the backup config allows retries, it returns the number of remaining tries.
212+
// If the backup is failed and the backup config does not allow retries, it returns 0.
213+
func (s *BackupBackgroundService) GetRemainedBackupTryCount(lastBackup *Backup) int {
214+
if lastBackup == nil {
215+
return 0
216+
}
217+
218+
if lastBackup.Status != BackupStatusFailed {
219+
return 0
220+
}
221+
222+
backupConfig, err := s.backupConfigService.GetBackupConfigByDbId(lastBackup.DatabaseID)
223+
if err != nil {
224+
s.logger.Error("Failed to get backup config by database ID", "error", err)
225+
return 0
226+
}
227+
228+
if !backupConfig.IsRetryIfFailed {
229+
return 0
230+
}
231+
232+
maxFailedTriesCount := backupConfig.MaxFailedTriesCount
233+
234+
lastBackups, err := s.backupRepository.FindByDatabaseIDWithLimit(
235+
lastBackup.DatabaseID,
236+
maxFailedTriesCount,
237+
)
238+
if err != nil {
239+
s.logger.Error("Failed to find last backups by database ID", "error", err)
240+
return 0
241+
}
242+
243+
lastFailedBackups := make([]*Backup, 0)
244+
245+
for _, backup := range lastBackups {
246+
if backup.Status == BackupStatusFailed {
247+
lastFailedBackups = append(lastFailedBackups, backup)
248+
}
249+
}
250+
251+
return maxFailedTriesCount - len(lastFailedBackups)
252+
}

0 commit comments

Comments
 (0)