diff --git a/pkg/service/backup_routine_handler.go b/pkg/service/backup_routine_handler.go index 3144454a..9f192313 100644 --- a/pkg/service/backup_routine_handler.go +++ b/pkg/service/backup_routine_handler.go @@ -180,10 +180,15 @@ func (h *BackupRoutineHandler) runFullBackupInternal(ctx context.Context, now ti h.lastRun.SetFullBackupTime(&now) - err = h.retentionManager.deleteOldBackups(ctx) - if err != nil { - return fmt.Errorf("failed to clean up old backups: %w", err) - } + go func() { + // Cleanup old backups asynchronously. + // At this moment backup is already completed, but backupJob.isRunning flag is still set, + // we don't want to block other backup execution. + err = h.retentionManager.deleteOldBackups(ctx) + if err != nil { + h.logger.Error("failed to clean up old backups", slog.Any("error", err)) + } + }() return nil } diff --git a/pkg/service/retention_manager.go b/pkg/service/retention_manager.go index da927528..3a8b2a0a 100644 --- a/pkg/service/retention_manager.go +++ b/pkg/service/retention_manager.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "slices" + "sync" "time" "github.com/aerospike/aerospike-backup-service/v3/pkg/model" @@ -18,6 +19,7 @@ type RetentionManager interface { } type RetentionManagerImpl struct { + mu sync.Mutex backend BackupListReader storage model.Storage routineName string @@ -39,6 +41,11 @@ func NewBackupRetentionManager( } func (e *RetentionManagerImpl) deleteOldBackups(ctx context.Context) error { + if !e.mu.TryLock() { // If delete operation already in progress, skip this iteration. + return nil + } + defer e.mu.Unlock() + if e.policy == nil || (e.policy.FullBackups == nil && e.policy.IncrBackups == nil) { return nil // Retention policy is not enabled, do nothing. }