From b8513eb0b688d54e19ea54d3f40c3e19795078b7 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 10 Feb 2025 09:59:03 +0800 Subject: [PATCH] fix: different cronexpr rules between ui and pocketbase --- internal/repository/workflow.go | 2 +- internal/scheduler/certificate.go | 2 +- internal/scheduler/scheduler.go | 9 ++++++-- internal/scheduler/workflow.go | 2 +- internal/workflow/dispatcher/dispatcher.go | 6 +++++ internal/workflow/event.go | 1 - internal/workflow/service.go | 8 ++++++- .../i18n/locales/en/nls.workflow.nodes.json | 2 +- .../i18n/locales/zh/nls.workflow.nodes.json | 2 +- ui/src/utils/cron.ts | 22 +++++++++---------- 10 files changed, 35 insertions(+), 21 deletions(-) diff --git a/internal/repository/workflow.go b/internal/repository/workflow.go index 60d60899..baa5e21b 100644 --- a/internal/repository/workflow.go +++ b/internal/repository/workflow.go @@ -24,7 +24,7 @@ func (r *WorkflowRepository) ListEnabledAuto(ctx context.Context) ([]*domain.Wor "enabled={:enabled} && trigger={:trigger}", "-created", 0, 0, - dbx.Params{"enabled": true, "trigger": domain.WorkflowTriggerTypeAuto}, + dbx.Params{"enabled": true, "trigger": string(domain.WorkflowTriggerTypeAuto)}, ) if err != nil { return nil, err diff --git a/internal/scheduler/certificate.go b/internal/scheduler/certificate.go index 26c7311f..43887cb0 100644 --- a/internal/scheduler/certificate.go +++ b/internal/scheduler/certificate.go @@ -6,6 +6,6 @@ type certificateService interface { InitSchedule(ctx context.Context) error } -func NewCertificateScheduler(service certificateService) error { +func InitCertificateScheduler(service certificateService) error { return service.InitSchedule(context.Background()) } diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index f5029599..91dbf115 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -1,6 +1,7 @@ package scheduler import ( + "github.com/usual2970/certimate/internal/app" "github.com/usual2970/certimate/internal/certificate" "github.com/usual2970/certimate/internal/repository" "github.com/usual2970/certimate/internal/workflow" @@ -14,7 +15,11 @@ func Register() { certificateRepo := repository.NewCertificateRepository() certificateSvc := certificate.NewCertificateService(certificateRepo) - NewCertificateScheduler(certificateSvc) + if err := InitWorkflowScheduler(workflowSvc); err != nil { + app.GetLogger().Error("failed to init workflow scheduler", "err", err) + } - NewWorkflowScheduler(workflowSvc) + if err := InitCertificateScheduler(certificateSvc); err != nil { + app.GetLogger().Error("failed to init certificate scheduler", "err", err) + } } diff --git a/internal/scheduler/workflow.go b/internal/scheduler/workflow.go index 7cb4dfa8..cef4adce 100644 --- a/internal/scheduler/workflow.go +++ b/internal/scheduler/workflow.go @@ -6,6 +6,6 @@ type workflowService interface { InitSchedule(ctx context.Context) error } -func NewWorkflowScheduler(service workflowService) error { +func InitWorkflowScheduler(service workflowService) error { return service.InitSchedule(context.Background()) } diff --git a/internal/workflow/dispatcher/dispatcher.go b/internal/workflow/dispatcher/dispatcher.go index 0ecc0828..0b504c25 100644 --- a/internal/workflow/dispatcher/dispatcher.go +++ b/internal/workflow/dispatcher/dispatcher.go @@ -92,6 +92,7 @@ func (w *WorkflowDispatcher) Dispatch(data *WorkflowWorkerData) { } w.enqueueWorker(data) + select { case w.chWork <- data: default: @@ -138,6 +139,11 @@ func (w *WorkflowDispatcher) Shutdown() { w.queueMutex.Unlock() // 等待所有正在执行的 WorkflowRun 完成 + w.workerMutex.Lock() + for _, worker := range w.workers { + worker.Cancel() + } + w.workerMutex.Unlock() w.wg.Wait() w.workers = make(map[string]*workflowWorker) w.workerIdMap = make(map[string]string) diff --git a/internal/workflow/event.go b/internal/workflow/event.go index fa6b4b1a..0fedd67b 100644 --- a/internal/workflow/event.go +++ b/internal/workflow/event.go @@ -72,7 +72,6 @@ func onWorkflowRecordCreateOrUpdate(ctx context.Context, record *core.Record) er }) }) if err != nil { - app.GetLogger().Error("add cron job failed", "err", err) return fmt.Errorf("add cron job failed: %w", err) } diff --git a/internal/workflow/service.go b/internal/workflow/service.go index 2d0a224d..d2236a8d 100644 --- a/internal/workflow/service.go +++ b/internal/workflow/service.go @@ -48,6 +48,8 @@ func (s *WorkflowService) InitSchedule(ctx context.Context) error { scheduler := app.GetScheduler() for _, workflow := range workflows { + var errs []error + err := scheduler.Add(fmt.Sprintf("workflow#%s", workflow.Id), workflow.TriggerCron, func() { s.StartRun(ctx, &dtos.WorkflowStartRunReq{ WorkflowId: workflow.Id, @@ -55,7 +57,11 @@ func (s *WorkflowService) InitSchedule(ctx context.Context) error { }) }) if err != nil { - return err + errs = append(errs, err) + } + + if len(errs) > 0 { + return errors.Join(errs...) } } diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 964633e0..84f0f48e 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -18,7 +18,7 @@ "workflow_node.start.form.trigger_cron.label": "Cron expression", "workflow_node.start.form.trigger_cron.placeholder": "Please enter cron expression", "workflow_node.start.form.trigger_cron.errmsg.invalid": "Please enter a valid cron expression", - "workflow_node.start.form.trigger_cron.tooltip": "Time zone is based on the server.", + "workflow_node.start.form.trigger_cron.tooltip": "Exactly 5 space separated segments. Time zone is based on the server.", "workflow_node.start.form.trigger_cron.extra": "Expected execution time for the last 5 times:", "workflow_node.start.form.trigger_cron.guide": "Tips: If you have multiple workflows, it is recommended to set them to run at multiple times of the day instead of always running at specific times.

Reference links:
1. Let’s Encrypt rate limits
2. Why should my Let’s Encrypt (ACME) client run at a random time?", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index d132f935..40860e1e 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -18,7 +18,7 @@ "workflow_node.start.form.trigger_cron.label": "Cron 表达式", "workflow_node.start.form.trigger_cron.placeholder": "请输入 Cron 表达式", "workflow_node.start.form.trigger_cron.errmsg.invalid": "请输入正确的 Cron 表达式", - "workflow_node.start.form.trigger_cron.tooltip": "支持使用任意值(即 *)、值列表分隔符(即 ,)、值的范围(即 -)、步骤值(即 /)等四种表达式,时区以服务器设置为准。", + "workflow_node.start.form.trigger_cron.tooltip": "五段式表达式,支持使用任意值(即 *)、值列表分隔符(即 ,)、值的范围(即 -)、步骤值(即 /)等四种表达式。时区以服务器设置为准。", "workflow_node.start.form.trigger_cron.extra": "预计最近 5 次执行时间:", "workflow_node.start.form.trigger_cron.guide": "小贴士:如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。

参考链接:
1. Let’s Encrypt 速率限制
2. 为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?", diff --git a/ui/src/utils/cron.ts b/ui/src/utils/cron.ts index c2ca22e2..79a94ddc 100644 --- a/ui/src/utils/cron.ts +++ b/ui/src/utils/cron.ts @@ -3,6 +3,8 @@ export const validCronExpression = (expr: string): boolean => { try { parseExpression(expr); + + if (expr.trim().split(" ").length !== 5) return false; // pocketbase 后端仅支持五段式的表达式 return true; } catch { return false; @@ -10,19 +12,15 @@ export const validCronExpression = (expr: string): boolean => { }; export const getNextCronExecutions = (expr: string, times = 1): Date[] => { - if (!expr) return []; + if (!validCronExpression(expr)) return []; - try { - const now = new Date(); - const cron = parseExpression(expr, { currentDate: now, iterator: true }); + const now = new Date(); + const cron = parseExpression(expr, { currentDate: now, iterator: true }); - const result: Date[] = []; - for (let i = 0; i < times; i++) { - const next = cron.next(); - result.push(next.value.toDate()); - } - return result; - } catch { - return []; + const result: Date[] = []; + for (let i = 0; i < times; i++) { + const next = cron.next(); + result.push(next.value.toDate()); } + return result; };