// cancellation 演示运行中取消、处理器确认停止、结果查询和有界排空。 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 c struct { Token string `json:"token"` QueueID string `json:"queue_id"` } if json.Unmarshal(raw, &c) != nil { return errors.New("invalid credentials file") } if token == "" { token = c.Token } if queue == "" { queue = c.QueueID } } client, err := orbit.NewClient(os.Getenv("ORBIT_URL"), orbit.Options{Token: token}) if err != nil { return err } operation, err := orbit.NewWorkerMessageID() if err != nil { return err } name := "object.cancel." + operation ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() entered := make(chan struct{}) w, err := orbit.NewWorker(client, orbit.WorkerOptions{QueueID: queue, Capacity: 1, ShutdownTimeout: 5 * time.Second, Handlers: []orbit.WorkerHandler{{Capability: orbit.WorkerCapability{Handler: name, HandlerVersion: "v1", PayloadVersion: "v1"}, Handle: func(work context.Context, e orbit.WorkerExecution) (json.RawMessage, error) { close(entered) <-work.Done() if !errors.Is(context.Cause(work), orbit.ErrTaskCancellationRequested) { return nil, work.Err() } // 此示例没有外部副作用,因此可以确认安全停止;真实接入需完成自己的核实。 return nil, &orbit.WorkerCancellationConfirmed{Details: json.RawMessage(`{"evidence_ref":"objects/no-external-work"}`)} }}}}) if err != nil { return err } task, err := client.SubmitTask(ctx, "submit-"+operation, orbit.SubmitTaskRequest{QueueID: queue, Handler: name, HandlerVersion: "v1", PayloadVersion: "v1", Payload: json.RawMessage(`{"object_ref":"objects/example"}`), TaskTimeoutMS: 20000, Retry: orbit.RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 15000}}) if err != nil { return err } run, stop := context.WithCancel(ctx) done := make(chan error, 1) go func() { done <- w.Run(run) }() waited := false defer func() { stop() if !waited { <-done } }() select { case <-entered: case err := <-done: waited = true return fmt.Errorf("Worker stopped before handler: %v", err) case <-ctx.Done(): return ctx.Err() } for { current, err := client.GetTask(ctx, task.Task.ID) if err != nil { return err } key, err := orbit.NewWorkerMessageID() if err != nil { return err } out, err := client.CancelTask(ctx, current.ID, key, orbit.CancelTaskRequest{ExpectedRevision: current.Revision, Reason: "finish cancellation example"}) if err == nil { fmt.Printf("task=%s cancel_requested=%t accepted_state=%s\n", out.Task.ID, out.Task.CancelRequestedAt != nil, out.Task.State) break } var api *orbit.APIError if !errors.As(err, &api) || api.Code != "STATE_CONFLICT" { return err } } for { current, err := client.GetTask(ctx, task.Task.ID) if err != nil { return err } if current.State == orbit.TaskCancelled { fmt.Printf("final_state=%s attempts=%d\n", current.State, current.AttemptCount) break } if current.State == orbit.TaskNeedsReview || current.State == orbit.TaskFailed || current.State == orbit.TaskSucceeded { return fmt.Errorf("unexpected result: %s", current.State) } select { case <-ctx.Done(): return ctx.Err() case <-time.After(100 * time.Millisecond): } } stop() err = <-done waited = true if err != nil { return err } fmt.Printf("worker_drained=true running=%d\n", w.Stats().Running) return nil }