Skip to content

Commit f3c5543

Browse files
FIX (backups): Make backup after specified previous run if there are no backups yet
1 parent 6244965 commit f3c5543

File tree

2 files changed

+57
-30
lines changed

2 files changed

+57
-30
lines changed

backend/internal/features/intervals/model.go

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -64,41 +64,34 @@ func (i *Interval) ShouldTriggerBackup(now time.Time, lastBackupTime *time.Time)
6464
}
6565
}
6666

67-
// daily trigger: calendar-based if TimeOfDay set, otherwise next calendar day
67+
// daily trigger: honour the TimeOfDay slot and catch up the previous one
6868
func (i *Interval) shouldTriggerDaily(now, lastBackup time.Time) bool {
69-
if i.TimeOfDay != nil {
70-
target, err := time.Parse("15:04", *i.TimeOfDay)
71-
if err == nil {
72-
todayTarget := time.Date(
73-
now.Year(),
74-
now.Month(),
75-
now.Day(),
76-
target.Hour(),
77-
target.Minute(),
78-
0,
79-
0,
80-
now.Location(),
81-
)
82-
83-
// if it's past today's target time and we haven't backed up today
84-
if now.After(todayTarget) && !isSameDay(lastBackup, now) {
85-
return true
86-
}
69+
if i.TimeOfDay == nil {
70+
return !isSameDay(lastBackup, now)
71+
}
8772

88-
// if it's exactly the target time and we haven't backed up today
89-
if now.Equal(todayTarget) && !isSameDay(lastBackup, now) {
90-
return true
91-
}
73+
t, err := time.Parse("15:04", *i.TimeOfDay)
74+
if err != nil {
75+
return false // malformed ⇒ play safe
76+
}
9277

93-
// if it's before today's target time, don't trigger yet
94-
if now.Before(todayTarget) {
95-
return false
96-
}
97-
}
78+
// Today's scheduled slot (todayTgt)
79+
todayTgt := time.Date(
80+
now.Year(), now.Month(), now.Day(),
81+
t.Hour(), t.Minute(), 0, 0, now.Location(),
82+
)
83+
84+
// The last scheduled slot that should already have happened
85+
var lastScheduled time.Time
86+
if now.Before(todayTgt) {
87+
lastScheduled = todayTgt.AddDate(0, 0, -1)
88+
} else {
89+
lastScheduled = todayTgt
9890
}
9991

100-
// no TimeOfDay: if it's a new calendar day
101-
return !isSameDay(lastBackup, now)
92+
// Fire when we are past that slot AND no backup has been taken since it
93+
return (now.After(lastScheduled) || now.Equal(lastScheduled)) &&
94+
lastBackup.Before(lastScheduled)
10295
}
10396

10497
// weekly trigger: on specified weekday/calendar week, otherwise ≥7 days

backend/internal/features/intervals/model_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,40 @@ func TestInterval_ShouldTriggerBackup_Daily(t *testing.T) {
104104
assert.True(t, should)
105105
},
106106
)
107+
108+
t.Run(
109+
"Manual backup before scheduled time should not prevent scheduled backup",
110+
func(t *testing.T) {
111+
timeOfDay := "21:00"
112+
interval := &Interval{
113+
ID: uuid.New(),
114+
Interval: IntervalDaily,
115+
TimeOfDay: &timeOfDay,
116+
}
117+
118+
manual := time.Date(2025, 6, 6, 16, 17, 0, 0, time.UTC) // manual earlier
119+
scheduled := time.Date(2025, 6, 6, 21, 0, 0, 0, time.UTC) // scheduled time
120+
121+
should := interval.ShouldTriggerBackup(scheduled, &manual)
122+
assert.True(t, should, "scheduled run should trigger even after earlier manual backup")
123+
},
124+
)
125+
126+
t.Run("Catch up previous time slot", func(t *testing.T) {
127+
timeOfDay := "21:00"
128+
interval := &Interval{
129+
ID: uuid.New(),
130+
Interval: IntervalDaily,
131+
TimeOfDay: &timeOfDay,
132+
}
133+
134+
// It's June-07 15:00 UTC, yesterday's scheduled backup was missed
135+
now := time.Date(2025, 6, 7, 15, 0, 0, 0, time.UTC)
136+
lastBackup := time.Date(2025, 6, 6, 16, 0, 0, 0, time.UTC) // before yesterday's 21:00
137+
138+
should := interval.ShouldTriggerBackup(now, &lastBackup)
139+
assert.True(t, should, "should catch up missed 21:00 backup the next day at 15:00")
140+
})
107141
}
108142

109143
func TestInterval_ShouldTriggerBackup_Weekly(t *testing.T) {

0 commit comments

Comments
 (0)