From 56bfeae90d1d5c464b49f46974e4cb25bcf53d76 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Mon, 8 Apr 2024 11:31:29 +1000 Subject: [PATCH] add simple cron implementation # Conflicts: # buildengine/testdata/projects/another/go.sum # go-runtime/compile/testdata/one/go.sum # go-runtime/compile/testdata/two/go.sum # Conflicts: # internal/cron/cron.go # internal/cron/cron_test.go # internal/cron/pattern.go --- backend/controller/cronjobs/cronjobs.go | 14 +++++++++----- .../controller/scheduledtask/scheduledtask_test.go | 11 ----------- backend/schema/metadatacronjob.go | 8 ++++++-- backend/schema/validate.go | 10 ++++++---- buildengine/testdata/projects/another/go.mod | 1 - buildengine/testdata/projects/another/go.sum | 2 -- go-runtime/compile/testdata/one/go.mod | 1 - go-runtime/compile/testdata/one/go.sum | 2 -- go-runtime/compile/testdata/two/go.mod | 1 - go-runtime/compile/testdata/two/go.sum | 2 -- 10 files changed, 21 insertions(+), 31 deletions(-) diff --git a/backend/controller/cronjobs/cronjobs.go b/backend/controller/cronjobs/cronjobs.go index 6727aad341..e49bc6f97b 100644 --- a/backend/controller/cronjobs/cronjobs.go +++ b/backend/controller/cronjobs/cronjobs.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "strings" "time" "connectrpc.com/connect" @@ -13,6 +12,7 @@ import ( ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" schemapb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1/schema" "github.com/TBD54566975/ftl/backend/schema" + "github.com/TBD54566975/ftl/internal/cron" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/model" "github.com/TBD54566975/ftl/internal/slices" @@ -99,17 +99,21 @@ func (s *Service) CreatedDeployment(ctx context.Context, deploymentKey model.Dep newJobs := []dal.CronJob{} for _, decl := range module.Decls { if verb, ok := decl.(*schema.Verb); ok { - if cron, ok := verb.GetMetadataCronJob().Get(); ok { + if cronJob, ok := verb.GetMetadataCronJob().Get(); ok { //TODO: remove prefix trimming when swapping out cron lib - schedule := strings.TrimPrefix(cron.String(), "+cron ") + schedule, err := cron.Parse(cronJob.CronString()) + if err != nil { + logger.Errorf(err, "failed to parse cron schedule %q", cronJob.CronString()) + continue + } start := time.Now().UTC() - next, err := gronx.NextTickAfter(schedule, start, false) + next, err := cron.NextAfter(schedule, start, false) if err != nil { logger.Errorf(err, "failed to calculate next execution for cron job %v:%v with schedule %q", deploymentKey, verb.Name, schedule) continue } - created, err := s.dal.CreateCronJob(ctx, deploymentKey, module.Name, verb.Name, schedule, start, next) + created, err := s.dal.CreateCronJob(ctx, deploymentKey, module.Name, verb.Name, schedule.String(), start, next) if err != nil { logger.Errorf(err, "failed to create cron job %v:%v", deploymentKey, verb.Name) } else { diff --git a/backend/controller/scheduledtask/scheduledtask_test.go b/backend/controller/scheduledtask/scheduledtask_test.go index a4d27507f3..6f759a68fb 100644 --- a/backend/controller/scheduledtask/scheduledtask_test.go +++ b/backend/controller/scheduledtask/scheduledtask_test.go @@ -2,13 +2,10 @@ package scheduledtask import ( "context" - "fmt" "sync/atomic" "testing" "time" - "github.com/adhocore/gronx" - "github.com/alecthomas/assert/v2" "github.com/benbjohnson/clock" "github.com/jpillora/backoff" @@ -61,11 +58,3 @@ func TestCron(t *testing.T) { assert.True(t, singletonCount.Load() >= 5 && singletonCount.Load() < 10, "expected singletonCount to be >= 5 but was %d", singletonCount.Load()) assert.True(t, multiCount.Load() >= 20 && multiCount.Load() < 30, "expected multiCount to be >= 20 but was %d", multiCount.Load()) } - -func TestCronLib(t *testing.T) { - expr := "0 0 1 1 *" - allowCurrent := true // includes current time as well - prevTime, err := gronx.PrevTickBefore(expr, time.Now().UTC(), allowCurrent) // gives time.Time, error - assert.NoError(t, err) - fmt.Printf("prev time: %s", prevTime.String()) -} diff --git a/backend/schema/metadatacronjob.go b/backend/schema/metadatacronjob.go index fcc6fa5739..d61b1161d1 100644 --- a/backend/schema/metadatacronjob.go +++ b/backend/schema/metadatacronjob.go @@ -21,10 +21,14 @@ var _ Metadata = (*MetadataCronJob)(nil) func (m *MetadataCronJob) Position() Position { return m.Pos } func (m *MetadataCronJob) String() string { - pattern := strings.Join(slices.Map(m.Components, func(component *CronJobComponent) string { + + return fmt.Sprintf("+cron %s", m.CronString()) +} + +func (m *MetadataCronJob) CronString() string { + return strings.Join(slices.Map(m.Components, func(component *CronJobComponent) string { return component.String() }), " ") - return fmt.Sprintf("+cron %s", pattern) } func (m *MetadataCronJob) schemaChildren() []Node { diff --git a/backend/schema/validate.go b/backend/schema/validate.go index 11ff1dba21..3034ae8cef 100644 --- a/backend/schema/validate.go +++ b/backend/schema/validate.go @@ -9,11 +9,11 @@ import ( "sort" "strings" - "github.com/adhocore/gronx" "github.com/alecthomas/participle/v2" "github.com/alecthomas/types/optional" "golang.org/x/exp/maps" + "github.com/TBD54566975/ftl/internal/cron" "github.com/TBD54566975/ftl/internal/errors" dc "github.com/TBD54566975/ftl/internal/reflect" ) @@ -158,9 +158,11 @@ func Validate(schema *Schema) (*Schema, error) { hasCron = true //TODO: remove prefix trimming when swapping out cron lib - schedule := strings.TrimPrefix(md.String(), "+cron ") - if valid := gronx.New().IsValid(schedule); !valid { - merr = append(merr, errorf(md, "invalid cron schedule %q", schedule)) + schedule, err := cron.Parse(md.CronString()) + if err != nil { + merr = append(merr, err) + } else if err := cron.Validate(schedule); err != nil { + merr = append(merr, err) } case *MetadataCalls, *MetadataDatabases, *MetadataAlias: diff --git a/buildengine/testdata/projects/another/go.mod b/buildengine/testdata/projects/another/go.mod index c46aa5cf4d..cea2d677f1 100644 --- a/buildengine/testdata/projects/another/go.mod +++ b/buildengine/testdata/projects/another/go.mod @@ -10,7 +10,6 @@ require ( connectrpc.com/otelconnect v0.7.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/TBD54566975/scaffolder v0.8.0 // indirect - github.com/adhocore/gronx v1.8.1 // indirect github.com/alecthomas/concurrency v0.0.2 // indirect github.com/alecthomas/kong v0.9.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect diff --git a/buildengine/testdata/projects/another/go.sum b/buildengine/testdata/projects/another/go.sum index 55e2cc04db..fdea3963d3 100644 --- a/buildengine/testdata/projects/another/go.sum +++ b/buildengine/testdata/projects/another/go.sum @@ -8,8 +8,6 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/TBD54566975/scaffolder v0.8.0 h1:DWl1K3dWcLsOPAYGQGPQXtffrml6XCB0tF05JdpMqZU= github.com/TBD54566975/scaffolder v0.8.0/go.mod h1:Ab/jbQ4q8EloYL0nbkdh2DVvkGc4nxr1OcIbdMpTxxg= -github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A= -github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/alecthomas/assert/v2 v2.8.0 h1:8b0foWfS2dH6MltxQMPvWdSGN1wE4K1vyab9PGW4qgE= github.com/alecthomas/assert/v2 v2.8.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= diff --git a/go-runtime/compile/testdata/one/go.mod b/go-runtime/compile/testdata/one/go.mod index 94ce4cbdaf..02838ec1ed 100644 --- a/go-runtime/compile/testdata/one/go.mod +++ b/go-runtime/compile/testdata/one/go.mod @@ -12,7 +12,6 @@ require ( connectrpc.com/otelconnect v0.7.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/TBD54566975/scaffolder v0.8.0 // indirect - github.com/adhocore/gronx v1.8.1 // indirect github.com/alecthomas/concurrency v0.0.2 // indirect github.com/alecthomas/kong v0.9.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect diff --git a/go-runtime/compile/testdata/one/go.sum b/go-runtime/compile/testdata/one/go.sum index 55e2cc04db..fdea3963d3 100644 --- a/go-runtime/compile/testdata/one/go.sum +++ b/go-runtime/compile/testdata/one/go.sum @@ -8,8 +8,6 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/TBD54566975/scaffolder v0.8.0 h1:DWl1K3dWcLsOPAYGQGPQXtffrml6XCB0tF05JdpMqZU= github.com/TBD54566975/scaffolder v0.8.0/go.mod h1:Ab/jbQ4q8EloYL0nbkdh2DVvkGc4nxr1OcIbdMpTxxg= -github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A= -github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/alecthomas/assert/v2 v2.8.0 h1:8b0foWfS2dH6MltxQMPvWdSGN1wE4K1vyab9PGW4qgE= github.com/alecthomas/assert/v2 v2.8.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= diff --git a/go-runtime/compile/testdata/two/go.mod b/go-runtime/compile/testdata/two/go.mod index a9d1b33fcd..e78011106d 100644 --- a/go-runtime/compile/testdata/two/go.mod +++ b/go-runtime/compile/testdata/two/go.mod @@ -12,7 +12,6 @@ require ( connectrpc.com/otelconnect v0.7.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/TBD54566975/scaffolder v0.8.0 // indirect - github.com/adhocore/gronx v1.8.1 // indirect github.com/alecthomas/concurrency v0.0.2 // indirect github.com/alecthomas/kong v0.9.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect diff --git a/go-runtime/compile/testdata/two/go.sum b/go-runtime/compile/testdata/two/go.sum index 55e2cc04db..fdea3963d3 100644 --- a/go-runtime/compile/testdata/two/go.sum +++ b/go-runtime/compile/testdata/two/go.sum @@ -8,8 +8,6 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/TBD54566975/scaffolder v0.8.0 h1:DWl1K3dWcLsOPAYGQGPQXtffrml6XCB0tF05JdpMqZU= github.com/TBD54566975/scaffolder v0.8.0/go.mod h1:Ab/jbQ4q8EloYL0nbkdh2DVvkGc4nxr1OcIbdMpTxxg= -github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A= -github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/alecthomas/assert/v2 v2.8.0 h1:8b0foWfS2dH6MltxQMPvWdSGN1wE4K1vyab9PGW4qgE= github.com/alecthomas/assert/v2 v2.8.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo=