feat: cron arithmetic and persisted workflow schedules

This commit is contained in:
2026-08-04 13:51:10 +01:00
parent b877024365
commit d0e1cc4ad6
5 changed files with 155 additions and 2 deletions
+48
View File
@@ -8,6 +8,7 @@ import (
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/workflowsched"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
@@ -60,6 +61,11 @@ func EnsureWorkflowIndexes() error {
}); err != nil {
return err
}
if _, err := db.Col("workflows").Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{Key: "next_run_at", Value: 1}},
}); err != nil {
return err
}
if err := EnsureLogIndexes(); err != nil {
return err
}
@@ -321,3 +327,45 @@ func DeleteWorkflow(instanceID, id string) error {
_, err := db.Col("workflows").DeleteOne(ctx, bson.M{"workflow_id": id, "instance_id": instanceID})
return err
}
// SetSchedule validates and stores a workflow's schedule, computing the first
// occurrence. next_run_at is persisted rather than held in memory: a leader
// handover between computing an occurrence and firing it would otherwise lose
// it or fire it twice.
//
// Passing s == nil, or a disabled schedule, clears next_run_at so the
// scheduler's query stops matching the document at all.
func SetSchedule(instanceID, workflowID string, s *models.Schedule) (*time.Time, error) {
ctx, cancel := wfCtx()
defer cancel()
set := bson.M{"schedule": s, "updated_at": time.Now()}
unset := bson.M{}
var next *time.Time
if s != nil && s.Enabled {
at, err := workflowsched.NextOccurrence(s.Cron, s.TZ, time.Now())
if err != nil {
return nil, err
}
next = &at
set["next_run_at"] = at
} else {
unset["next_run_at"] = ""
}
update := bson.M{"$set": set}
if len(unset) > 0 {
update["$unset"] = unset
}
res, err := db.Col("workflows").UpdateOne(ctx,
bson.M{"workflow_id": workflowID, "instance_id": instanceID}, update)
if err != nil {
return nil, err
}
if res.MatchedCount == 0 {
return nil, mongo.ErrNoDocuments
}
return next, nil
}