@@ -2,9 +2,7 @@ package worker
2
2
3
3
import (
4
4
"context"
5
- "errors"
6
5
"log/slog"
7
- "sync"
8
6
"time"
9
7
10
8
"github.com/benbjohnson/clock"
@@ -14,197 +12,82 @@ import (
14
12
"github.com/cschleiden/go-workflows/backend/payload"
15
13
"github.com/cschleiden/go-workflows/internal/activity"
16
14
"github.com/cschleiden/go-workflows/internal/metrickeys"
17
- mi "github.com/cschleiden/go-workflows/internal/metrics"
15
+ im "github.com/cschleiden/go-workflows/internal/metrics"
18
16
"github.com/cschleiden/go-workflows/internal/workflow"
19
17
"github.com/cschleiden/go-workflows/internal/workflowerrors"
20
18
)
21
19
22
- type ActivityWorker struct {
23
- backend backend. Backend
20
+ func NewActivityWorker ( b backend. Backend , registry * workflow. Registry , clock clock. Clock , options WorkerOptions ) * Worker [backend. ActivityTask , history. Event ] {
21
+ ae := activity . NewExecutor ( b . Logger (), b . Tracer (), b . Converter (), b . ContextPropagators (), registry )
24
22
25
- options * Options
26
-
27
- activityTaskQueue chan * backend.ActivityTask
28
- activityTaskExecutor * activity.Executor
29
-
30
- wg sync.WaitGroup
31
- pollersWg sync.WaitGroup
32
-
33
- clock clock.Clock
34
- logger * slog.Logger
35
- }
36
-
37
- func NewActivityWorker (b backend.Backend , registry * workflow.Registry , clock clock.Clock , options * Options ) * ActivityWorker {
38
- return & ActivityWorker {
39
- backend : b ,
40
-
41
- options : options ,
42
-
43
- activityTaskQueue : make (chan * backend.ActivityTask ),
44
- activityTaskExecutor : activity .NewExecutor (b .Logger (), b .Tracer (), b .Converter (), b .ContextPropagators (), registry ),
45
-
46
- clock : clock ,
47
- logger : b .Logger (),
48
- }
49
- }
50
-
51
- func (aw * ActivityWorker ) Start (ctx context.Context ) error {
52
- aw .pollersWg .Add (aw .options .ActivityPollers )
53
-
54
- for i := 0 ; i < aw .options .ActivityPollers ; i ++ {
55
- go aw .runPoll (ctx )
23
+ tw := & ActivityTaskWorker {
24
+ backend : b ,
25
+ activityTaskExecutor : ae ,
26
+ clock : clock ,
27
+ logger : b .Logger (),
56
28
}
57
29
58
- go aw .runDispatcher ()
59
-
60
- return nil
30
+ return NewWorker [backend.ActivityTask , history.Event ](b , tw , & options )
61
31
}
62
32
63
- func (aw * ActivityWorker ) WaitForCompletion () error {
64
- // Wait for task pollers to finish
65
- aw .pollersWg .Wait ()
66
-
67
- // Wait for tasks to finish
68
- aw .wg .Wait ()
69
- close (aw .activityTaskQueue )
70
-
71
- return nil
72
- }
73
-
74
- func (aw * ActivityWorker ) runPoll (ctx context.Context ) {
75
- defer aw .pollersWg .Done ()
76
-
77
- ticker := time .NewTicker (aw .options .ActivityPollingInterval )
78
- defer ticker .Stop ()
79
- for {
80
- task , err := aw .poll (ctx , 30 * time .Second )
81
- if err != nil {
82
- aw .logger .ErrorContext (ctx , "error while polling for activity task" , "error" , err )
83
- }
84
- if task != nil {
85
- aw .wg .Add (1 )
86
- aw .activityTaskQueue <- task
87
- continue // check for new tasks right away
88
- }
89
-
90
- select {
91
- case <- ctx .Done ():
92
- return
93
- case <- ticker .C :
94
- }
95
- }
33
+ type ActivityTaskWorker struct {
34
+ backend backend.Backend
35
+ activityTaskExecutor * activity.Executor
36
+ clock clock.Clock
37
+ logger * slog.Logger
96
38
}
97
39
98
- func (aw * ActivityWorker ) runDispatcher () {
99
- var sem chan struct {}
100
- if aw .options .MaxParallelActivityTasks > 0 {
101
- sem = make (chan struct {}, aw .options .MaxParallelActivityTasks )
40
+ func (atw * ActivityTaskWorker ) Complete (ctx context.Context , event * history.Event , task * backend.ActivityTask ) error {
41
+ if err := atw .backend .CompleteActivityTask (ctx , task .WorkflowInstance , task .ID , event ); err != nil {
42
+ atw .backend .Logger ().Error ("completing activity task" , "error" , err )
102
43
}
103
44
104
- for task := range aw .activityTaskQueue {
105
- if sem != nil {
106
- sem <- struct {}{}
107
- }
108
-
109
- task := task
110
-
111
- go func () {
112
- defer aw .wg .Done ()
113
-
114
- // Create new context to allow activities to complete when root context is canceled
115
- taskCtx := context .Background ()
116
- aw .handleTask (taskCtx , task )
117
-
118
- if sem != nil {
119
- <- sem
120
- }
121
- }()
122
- }
45
+ return nil
123
46
}
124
47
125
- func (aw * ActivityWorker ) handleTask (ctx context.Context , task * backend.ActivityTask ) {
48
+ func (atw * ActivityTaskWorker ) Execute (ctx context.Context , task * backend.ActivityTask ) ( * history. Event , error ) {
126
49
a := task .Event .Attributes .(* history.ActivityScheduledAttributes )
127
- ametrics := aw .backend .Metrics ().WithTags (metrics.Tags {metrickeys .ActivityName : a .Name })
50
+ ametrics := atw .backend .Metrics ().WithTags (metrics.Tags {metrickeys .ActivityName : a .Name })
128
51
129
52
// Record how long this task was in the queue
130
53
scheduledAt := task .Event .Timestamp
131
54
timeInQueue := time .Since (scheduledAt )
132
55
ametrics .Distribution (metrickeys .ActivityTaskDelay , metrics.Tags {}, float64 (timeInQueue / time .Millisecond ))
133
56
134
- // Start heartbeat while activity is running
135
- if aw .options .ActivityHeartbeatInterval > 0 {
136
- heartbeatCtx , cancelHeartbeat := context .WithCancel (ctx )
137
- defer cancelHeartbeat ()
138
-
139
- go func (ctx context.Context ) {
140
- t := time .NewTicker (aw .options .ActivityHeartbeatInterval )
141
- defer t .Stop ()
142
-
143
- for {
144
- select {
145
- case <- ctx .Done ():
146
- return
147
- case <- t .C :
148
- if err := aw .backend .ExtendActivityTask (ctx , task .ID ); err != nil {
149
- if ! errors .Is (err , context .Canceled ) {
150
- aw .backend .Logger ().Error ("extending activity task" , "error" , err )
151
- panic ("extending activity task" )
152
- }
153
- }
154
- }
155
- }
156
- }(heartbeatCtx )
157
- }
158
-
159
- timer := mi .NewTimer (ametrics , metrickeys .ActivityTaskProcessed , metrics.Tags {})
57
+ timer := im .NewTimer (ametrics , metrickeys .ActivityTaskProcessed , metrics.Tags {})
160
58
defer timer .Stop ()
161
59
162
- result , err := aw .activityTaskExecutor .ExecuteActivity (ctx , task )
163
- event := aw .resultToEvent (task .Event .ScheduleEventID , result , err )
60
+ result , err := atw .activityTaskExecutor .ExecuteActivity (ctx , task )
61
+ event := atw .resultToEvent (task .Event .ScheduleEventID , result , err )
164
62
165
- if err := aw .backend .CompleteActivityTask (ctx , task .WorkflowInstance , task .ID , event ); err != nil {
166
- aw .backend .Logger ().Error ("completing activity task" , "error" , err )
167
- panic ("completing activity task" )
168
- }
63
+ return event , nil
64
+ }
65
+
66
+ func (atw * ActivityTaskWorker ) Extend (ctx context.Context , task * backend.ActivityTask ) error {
67
+ return atw .backend .ExtendActivityTask (ctx , task .ID )
169
68
}
170
69
171
- func (aw * ActivityWorker ) resultToEvent (ScheduleEventID int64 , result payload.Payload , err error ) * history.Event {
70
+ func (atw * ActivityTaskWorker ) Get (ctx context.Context ) (* backend.ActivityTask , error ) {
71
+ return atw .backend .GetActivityTask (ctx )
72
+ }
73
+
74
+ func (atw * ActivityTaskWorker ) resultToEvent (scheduleEventID int64 , result payload.Payload , err error ) * history.Event {
172
75
if err != nil {
173
76
return history .NewPendingEvent (
174
- aw .clock .Now (),
77
+ atw .clock .Now (),
175
78
history .EventType_ActivityFailed ,
176
79
& history.ActivityFailedAttributes {
177
80
Error : workflowerrors .FromError (err ),
178
81
},
179
- history .ScheduleEventID (ScheduleEventID ),
82
+ history .ScheduleEventID (scheduleEventID ),
180
83
)
181
84
}
182
85
183
86
return history .NewPendingEvent (
184
- aw .clock .Now (),
87
+ atw .clock .Now (),
185
88
history .EventType_ActivityCompleted ,
186
89
& history.ActivityCompletedAttributes {
187
90
Result : result ,
188
91
},
189
- history .ScheduleEventID (ScheduleEventID ))
190
- }
191
-
192
- func (aw * ActivityWorker ) poll (ctx context.Context , timeout time.Duration ) (* backend.ActivityTask , error ) {
193
- if timeout == 0 {
194
- timeout = 30 * time .Second
195
- }
196
-
197
- ctx , cancel := context .WithTimeout (ctx , timeout )
198
- defer cancel ()
199
-
200
- task , err := aw .backend .GetActivityTask (ctx )
201
- if err != nil {
202
- if errors .Is (err , context .DeadlineExceeded ) {
203
- return nil , nil
204
- }
205
-
206
- return nil , err
207
- }
208
-
209
- return task , nil
92
+ history .ScheduleEventID (scheduleEventID ))
210
93
}
0 commit comments