-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlifecycle.go
More file actions
144 lines (126 loc) · 4.18 KB
/
Copy pathlifecycle.go
File metadata and controls
144 lines (126 loc) · 4.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package browse
import (
"go.klarlabs.de/statekit"
)
// Task lifecycle events
const (
EventStart statekit.EventType = "START"
EventSuccess statekit.EventType = "SUCCESS"
EventFail statekit.EventType = "FAIL"
EventRetry statekit.EventType = "RETRY"
EventAbort statekit.EventType = "ABORT"
EventReset statekit.EventType = "RESET"
)
// Task lifecycle states
const (
StatePending statekit.StateID = "pending"
StateRunning statekit.StateID = "running"
StateSuccess statekit.StateID = "success"
StateFailed statekit.StateID = "failed"
StateAborted statekit.StateID = "aborted"
StateRetrying statekit.StateID = "retrying"
)
// TaskLifecycleContext holds the context for a task's state machine.
type TaskLifecycleContext struct {
TaskName string
Attempt int
LastErr error
}
// NewTaskLifecycle creates a statekit machine modeling the task execution lifecycle.
//
// pending → START → running
// running → SUCCESS → success (final)
// running → FAIL → failed (final)
// running → ABORT → aborted (final)
// running → RETRY → retrying
// retrying → START → running
// failed → RESET → pending
func NewTaskLifecycle(taskName string) (*statekit.MachineConfig[TaskLifecycleContext], error) {
return statekit.NewMachine[TaskLifecycleContext]("task-lifecycle").
WithInitial(StatePending).
WithContext(TaskLifecycleContext{TaskName: taskName}).
WithAction("incrementAttempt", func(ctx *TaskLifecycleContext, event statekit.Event) {
ctx.Attempt++
}).
WithAction("recordError", func(ctx *TaskLifecycleContext, event statekit.Event) {
if err, ok := event.Payload.(error); ok {
ctx.LastErr = err
}
}).
// pending: waiting to be executed
State(StatePending).
On(EventStart).Target(StateRunning).Do("incrementAttempt").Done().
// running: actively executing
State(StateRunning).
On(EventSuccess).Target(StateSuccess).
On(EventFail).Target(StateFailed).Do("recordError").
On(EventAbort).Target(StateAborted).
On(EventRetry).Target(StateRetrying).Done().
// retrying: waiting before re-execution
State(StateRetrying).
On(EventStart).Target(StateRunning).Do("incrementAttempt").Done().
// terminal states
State(StateSuccess).Final().Done().
State(StateFailed).
On(EventReset).Target(StatePending).Done().
State(StateAborted).Final().Done().
Build()
}
// TaskTracker wraps a statekit Interpreter to track task execution state.
type TaskTracker struct {
interp *statekit.Interpreter[TaskLifecycleContext]
}
// NewTaskTracker creates a tracker for the given task name.
func NewTaskTracker(taskName string) (*TaskTracker, error) {
machine, err := NewTaskLifecycle(taskName)
if err != nil {
return nil, err
}
interp := statekit.NewInterpreter(machine)
interp.Start()
return &TaskTracker{interp: interp}, nil
}
// Start transitions the task to running state.
func (t *TaskTracker) Start() {
t.interp.Send(statekit.Event{Type: EventStart})
}
// Success transitions the task to success state.
func (t *TaskTracker) Success() {
t.interp.Send(statekit.Event{Type: EventSuccess})
}
// Fail transitions the task to failed state with an error.
func (t *TaskTracker) Fail(err error) {
t.interp.Send(statekit.Event{Type: EventFail, Payload: err})
}
// Abort transitions the task to aborted state.
func (t *TaskTracker) Abort() {
t.interp.Send(statekit.Event{Type: EventAbort})
}
// Retry transitions the task to retrying state.
func (t *TaskTracker) Retry() {
t.interp.Send(statekit.Event{Type: EventRetry})
}
// Reset transitions a failed task back to pending.
func (t *TaskTracker) Reset() {
t.interp.Send(statekit.Event{Type: EventReset})
}
// State returns the current state ID.
func (t *TaskTracker) State() statekit.StateID {
return t.interp.State().Value
}
// Context returns the current task lifecycle context.
func (t *TaskTracker) Context() TaskLifecycleContext {
return t.interp.State().Context
}
// IsDone returns true if the task is in a terminal state.
func (t *TaskTracker) IsDone() bool {
return t.interp.Done()
}
// Matches checks if the task is in the given state.
func (t *TaskTracker) Matches(state statekit.StateID) bool {
return t.interp.Matches(state)
}
// Stop cleans up the interpreter.
func (t *TaskTracker) Stop() {
t.interp.Stop()
}