-
Notifications
You must be signed in to change notification settings - Fork 241
Expand file tree
/
Copy pathaccumulate_and_batch_workflow.go
More file actions
117 lines (97 loc) · 2.92 KB
/
Copy pathaccumulate_and_batch_workflow.go
File metadata and controls
117 lines (97 loc) · 2.92 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
package batch_queue
import (
"context"
"fmt"
"os"
"strings"
"time"
"go.temporal.io/sdk/workflow"
)
const (
SIGNAL_READ_VALS = "read_vals"
SIGNAL_COMMIT_BATCH = "commit_batch"
)
func GetAccumulateAndBatchWorkflowID() string {
return "AccumulateAndBatchWorkflowID"
}
type WorkflowTicker struct {
d time.Duration
C workflow.Channel
}
func NewWorkflowTicker(ctx workflow.Context, d time.Duration) *WorkflowTicker {
wt := WorkflowTicker{
d: d,
C: workflow.NewChannel(ctx),
}
workflow.Go(ctx, func(ctx workflow.Context) {
for {
t := workflow.NewTimer(ctx, d)
err := t.Get(ctx, nil)
if err != nil {
workflow.GetLogger(ctx).Error("timer failed, restarting...", "error", err)
continue
}
wt.C.Send(ctx, nil)
}
})
return &wt
}
// AccumulateAndBatchWorkflow keeps accumulating data via signals. A timer
// decides when the accumulated data should be processed as a batch.
func AccumulateAndBatchWorkflow(ctx workflow.Context, vals []string) error {
logger := workflow.GetLogger(ctx)
logger.Info("AccumulateAndBatchWorkflow workflow started", "vals", vals)
// listen for incoming data
readValsCh := workflow.GetSignalChannel(ctx, SIGNAL_READ_VALS)
// batch every 10 seconds
t := NewWorkflowTicker(ctx, 10*time.Second)
eventsReceived := 0
selector := workflow.NewSelector(ctx)
selector.AddReceive(readValsCh, func(c workflow.ReceiveChannel, more bool) {
eventsReceived += 1
var incoming string
c.Receive(ctx, &incoming)
vals = append(vals, incoming)
})
selector.AddReceive(t.C, func(c workflow.ReceiveChannel, more bool) {
eventsReceived += 1
c.Receive(ctx, nil)
logger.Info("commiting batch...")
ao := workflow.ActivityOptions{
StartToCloseTimeout: 1 * time.Minute,
}
actx := workflow.WithActivityOptions(ctx, ao)
err := workflow.ExecuteActivity(actx, WriteBatchToFile, vals).Get(ctx, nil)
if err != nil {
// Couldn't write the batch, so do not discard the data. Note that
// activity should be idempotent, so you may need to dedupe. This
// example doesn't dedupe.
logger.Error("failed to write batch", "error", err)
return
}
// Discard the written data.
vals = []string{}
})
// Batch when you received enough events. Using a low number just to
// illustrate the batching, you can go much higher.
for {
selector.Select(ctx)
if eventsReceived > 15 {
// Pass in the existing vals since the signal history is no longer
// available.
return workflow.NewContinueAsNewError(ctx, AccumulateAndBatchWorkflow, vals)
}
}
}
func WriteBatchToFile(ctx context.Context, vals []string) error {
// Write the values to this file. We can compare values here to values sent
// to the workflow to see the durability.
f, err := os.OpenFile("values_received.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()
f.WriteString(strings.Join(vals, "\n"))
f.WriteString("\n")
return nil
}