// delayed-task 演示数据库计时的相对延迟、原键核对和实际 Worker 执行。 package main import ( "context" "encoding/json" "errors" "fmt" "os" "time" orbit "git.yuntuo.hk/k2safe/orbit/sdk/go" ) func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func run() error { token, queue := os.Getenv("ORBIT_TOKEN"), os.Getenv("ORBIT_QUEUE_ID") if path := os.Getenv("ORBIT_CREDENTIALS_FILE"); path != "" { raw, err := os.ReadFile(path) if err != nil { return fmt.Errorf("read private credentials: %w", err) } var credentials struct { Token string `json:"token"` QueueID string `json:"queue_id"` } if json.Unmarshal(raw, &credentials) != nil { return errors.New("invalid credentials file") } if token == "" { token = credentials.Token } if queue == "" { queue = credentials.QueueID } } client, err := orbit.NewClient(os.Getenv("ORBIT_URL"), orbit.Options{Token: token}) if err != nil { return err } op, err := orbit.NewWorkerMessageID() if err != nil { return err } ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() in := orbit.SubmitTaskRequest{QueueID: queue, Handler: "object.delayed." + op, HandlerVersion: "v1", PayloadVersion: "v1", Payload: json.RawMessage(`{"object_ref":"examples/input","sequence":9007199254740993}`), DelayMS: 1500, TaskTimeoutMS: 15000, Retry: orbit.RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 5000}} accepted, err := client.SubmitTask(ctx, op, in) if err != nil { return err } // 先输出任务身份,后续失败可据此核对;不输出凭据或任意业务正文。 fmt.Printf("task=%s delay_ms=%d\n", accepted.Task.ID, in.DelayMS) if !accepted.Task.AvailableAt.Equal(accepted.Task.CreatedAt.Add(1500 * time.Millisecond)) { return errors.New("delay was not based on database acceptance") } w, err := orbit.NewWorker(client, orbit.WorkerOptions{QueueID: queue, Capacity: 1, ShutdownTimeout: 5 * time.Second, Handlers: []orbit.WorkerHandler{{Capability: orbit.WorkerCapability{Handler: in.Handler, HandlerVersion: "v1", PayloadVersion: "v1"}, Handle: func(ctx context.Context, e orbit.WorkerExecution) (json.RawMessage, error) { if err := ctx.Err(); err != nil { return nil, err } // 无外部副作用的示例处理器,保持精确数字文本。 return e.Task.Payload, nil }}}}) if err != nil { return err } work, stop := context.WithCancel(ctx) done := make(chan error, 1) go func() { done <- w.Run(work) }() waited := false defer func() { stop() if !waited { <-done } }() for { current, err := client.GetTask(ctx, accepted.Task.ID) if err != nil { return err } if current.State == orbit.TaskSucceeded { history, err := client.ListTaskAttempts(ctx, current.ID, orbit.HistoryPageRequest{}) if err != nil { return err } if len(history.Items) != 1 || history.Items[0].AuthorizedAt == nil || history.Items[0].CreatedAt.Before(accepted.Task.AvailableAt) || history.Items[0].AuthorizedAt.Before(accepted.Task.AvailableAt) { return errors.New("execution preceded the saved release time") } break } if current.State == orbit.TaskFailed || current.State == orbit.TaskNeedsReview || current.State == orbit.TaskCancelled { return fmt.Errorf("task requires attention: %s", current.State) } select { case err := <-done: waited = true return fmt.Errorf("Worker stopped before completion: %v", err) case <-ctx.Done(): return ctx.Err() case <-time.After(100 * time.Millisecond): } } duplicate, err := client.SubmitTask(ctx, op, in) if err != nil { return err } if !duplicate.Deduplicated || duplicate.Task.ID != accepted.Task.ID || !duplicate.Task.AvailableAt.Equal(accepted.Task.AvailableAt) { return errors.New("replay changed the original delayed task") } stop() err = <-done waited = true if err != nil { return err } stats := w.Stats() if stats.Started != 1 || stats.ReportsConfirmed != 1 || stats.Running != 0 { return errors.New("Worker did not confirm and drain") } fmt.Println("state=SUCCEEDED attempts=1 no_early_start=true original_receipt=true worker_drained=true") return nil }