152 lines
4.5 KiB
Go
152 lines
4.5 KiB
Go
// history 创建一次受控的冲突报告,演示通过公开查询区分权威结果和追加证据。
|
|
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
|
|
}
|
|
op, err := orbit.NewWorkerMessageID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
handler := "history.inspect." + op
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
accepted, err := client.SubmitTask(ctx, "history-"+op, orbit.SubmitTaskRequest{QueueID: queue, Handler: handler, HandlerVersion: "v1", PayloadVersion: "v1", Payload: json.RawMessage(`{"object_ref":"examples/input"}`), TaskTimeoutMS: 60000, Retry: orbit.RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 10000}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registration, err := orbit.NewWorkerRegistration(queue, 1, []orbit.WorkerCapability{{Handler: handler, HandlerVersion: "v1", PayloadVersion: "v1"}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w, err := client.DialWorker(ctx, registration)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer w.Close()
|
|
nextID := func() (string, error) { return orbit.NewWorkerMessageID() }
|
|
id, err := nextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.Capacity(ctx, id, orbit.WorkerCapacity{Sequence: 1, Capacity: 1}); err != nil {
|
|
return err
|
|
}
|
|
a, err := w.NextAssignment(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if a.Task.ID != accepted.Task.ID {
|
|
return errors.New("unexpected task")
|
|
}
|
|
id, err = nextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
started, err := w.Start(ctx, id, a.Attempt())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, active := started.Lease.ExecutionDeadline(); !active {
|
|
return errors.New("missing current execution authority")
|
|
}
|
|
// 这是故障演示,不对外部系统执行操作;先提交固定的模拟处理结果。
|
|
id, err = nextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
original := json.RawMessage(`{"version":"v1","sequence":9007199254740993}`)
|
|
if _, err := w.Finish(ctx, id, orbit.WorkerFinish{Ref: a.Attempt(), Outcome: orbit.WorkerSucceeded, Result: original}); err != nil {
|
|
return err
|
|
}
|
|
// 使用另一消息身份模拟矛盾回报;只能追加证据,不能覆盖先前权威结果。
|
|
id, err = nextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conflict, err := w.Finish(ctx, id, orbit.WorkerFinish{Ref: a.Attempt(), Outcome: orbit.WorkerSucceeded, Result: json.RawMessage(`{"version":"v2"}`)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if conflict.Receipt.Disposition != "EVIDENCE_ONLY" {
|
|
return errors.New("conflicting report was not isolated as evidence")
|
|
}
|
|
attempts, err := client.ListTaskAttempts(ctx, a.Task.ID, orbit.HistoryPageRequest{Limit: 20})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(attempts.Items) != 1 {
|
|
return errors.New("expected one attempt")
|
|
}
|
|
detail, err := client.GetTaskAttempt(ctx, a.Task.ID, attempts.Items[0].ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
evidence, err := client.ListAttemptEvidence(ctx, a.Task.ID, detail.ID, orbit.HistoryPageRequest{Limit: 20})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(evidence.Items) != 1 {
|
|
return errors.New("expected one evidence record")
|
|
}
|
|
item, err := client.GetAttemptEvidence(ctx, a.Task.ID, detail.ID, evidence.Items[0].ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current, err := client.GetTask(ctx, a.Task.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if current.State != orbit.TaskSucceeded || string(current.Result) != string(original) || item.Reason != "RESULT_CONFLICT" {
|
|
return errors.New("history did not preserve authoritative outcome")
|
|
}
|
|
id, err = nextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.Capacity(ctx, id, orbit.WorkerCapacity{Sequence: 2, Capacity: 0, Drain: true}); err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("task=%s state=%s attempts=%d evidence=%d reason=%s disposition=%s\n", current.ID, current.State, len(attempts.Items), len(evidence.Items), item.Reason, item.Disposition)
|
|
return nil
|
|
}
|