Skip to content

Commit d8e239e

Browse files
authored
Merge pull request #29 from cschleiden/go118
Use Go1.18 generics
2 parents 6ba0c9c + 8ba2afd commit d8e239e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+575
-508
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Go
1717
uses: actions/setup-go@v2
1818
with:
19-
go-version: 1.17
19+
go-version: 1.18
2020

2121
- name: Build
2222
run: go build -v ./...

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.sqlite
2-
*.sqlite-journal
2+
*.sqlite-journal
3+
vendor

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@
3333
},
3434
"github-actions.workflows.pinned.workflows": [
3535
".github/workflows/go.yml"
36-
]
36+
],
37+
"go.alternateTools": {
38+
"go": "/Users/cschleiden/sdk/go1.18rc1/bin/go",
39+
},
40+
"go.testExplorer.showDynamicSubtestsInEditor": true,
3741
}

README.md

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
Borrows heavily from [Temporal](https://github.com/temporalio/temporal) (and since it's a fork also [Cadence](https://github.com/uber/cadence)) as well as [DTFx](https://github.com/Azure/durabletask).
66

7-
Note on go1.18 generics: many of the `Get(...)` operations will become easier with generics, an ongoing exploration is happening in branch [go118](https://github.com/cschleiden/go-workflows/tree/go118).
8-
97
See also:
108
- https://cschleiden.dev/blog/2022-02-13-go-workflows-part1/
119

10+
On Go support: the current version of the library requires **Go 1.18** or later. There is a version that doesn't require generics and relies more on `interface{}` instead, but I think the improved type safety is worth not supporting a version of Go before 1.18 for now.
11+
1212
## Simple example
1313

1414
### Workflow
@@ -17,15 +17,15 @@ Workflows are written in Go code. The only exception is they must not use any of
1717

1818
```go
1919
func Workflow1(ctx workflow.Context, input string) error {
20-
var r1, r2 int
21-
22-
if err := workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx, &r1); err != nil {
20+
r1, err := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx)
21+
if err != nil {
2322
panic("error getting activity 1 result")
2423
}
2524

2625
log.Println("A1 result:", r1)
2726

28-
if err := workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx, &r2); err != nil {
27+
r2, err := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx)
28+
if err != nil {
2929
panic("error getting activity 1 result")
3030
}
3131

@@ -99,7 +99,6 @@ func main() {
9999
}
100100

101101
c2 := make(chan os.Signal, 1)
102-
signal.Notify(c2, os.Interrupt)
103102
signal.Notify(c2, os.Interrupt)
104103
<-c2
105104
}
@@ -210,7 +209,8 @@ to call activities registered on a struct from a workflow:
210209
// ...
211210
var a *act
212211

213-
if err := workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, a.Activity1, 35, 12).Get(ctx, &r1); err != nil {
212+
r1, err := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, a.Activity1, 35, 12).Get(ctx)
213+
if err != nil {
214214
// handle error
215215
}
216216
// Output r1 = 47 + 12 (from the worker registration) = 59
@@ -232,8 +232,7 @@ if err != nil {
232232
From a workflow, call `workflow.ExecuteActivity` to execute an activity. The call returns a `Future` you can await to get the result or any error it might return.
233233
234234
```go
235-
var r1 int
236-
err := workflow.ExecuteActivity(ctx, Activity1, 35, 12, nil, "test").Get(ctx, &r1)
235+
r1, err := workflow.ExecuteActivity[int](ctx, Activity1, 35, 12, nil, "test").Get(ctx)
237236
if err != nil {
238237
panic("error getting activity 1 result")
239238
}
@@ -267,10 +266,9 @@ cancel()
267266
Sometimes scheduling an activity is too much overhead for a simple side effect. For those scenarios you can use `workflow.SideEffect`. You can pass a func which will be executed only once inline with its result being recorded in the history. Subsequent executions of the workflow will return the previously recorded result.
268267
269268
```go
270-
var id string
271-
workflow.SideEffect(ctx, func(ctx workflow.Context) interface{}) {
269+
id, _ := workflow.SideEffect[string](ctx, func(ctx workflow.Context) string) {
272270
return uuid.NewString()
273-
}).Get(ctx, &id)
271+
}).Get(ctx)
274272
```
275273
276274
### Running sub-workflows
@@ -279,8 +277,8 @@ Call `workflow.CreateSubWorkflowInstance` to start a sub-workflow.
279277
280278
```go
281279
func Workflow1(ctx workflow.Context, msg string) error {
282-
var wr int
283-
if err := workflow.CreateSubWorkflowInstance(ctx, workflow.SubWorkflowInstanceOptions{}, Workflow2, "some input").Get(ctx, &wr); err != nil {
280+
wr, err := workflow.CreateSubWorkflowInstance[int](ctx, workflow.SubWorkflowInstanceOptions{}, Workflow2, "some input").Get(ctx, &wr)
281+
if err != nil {
284282
return errors.Wrap(err, "could not get sub workflow result")
285283
}
286284

@@ -289,9 +287,8 @@ func Workflow1(ctx workflow.Context, msg string) error {
289287
}
290288

291289
func Workflow2(ctx workflow.Context, msg string) (int, error) {
292-
var r1 int
293-
294-
if err := workflow.ExecuteActivity(ctx, Activity1, 35, 12).Get(ctx, &r1); err != nil {
290+
r1, err := workflow.ExecuteActivity[int](ctx, Activity1, 35, 12).Get(ctx, &r1)
291+
if err != nil {
295292
return "", errors.Wrap(err, "could not get activity result")
296293
}
297294

@@ -320,19 +317,17 @@ if err != nil {
320317
Due its non-deterministic behavior you must not use a `select` statement in workflows. Instead you can use the provided `workflow.Select` function. It blocks until one of the provided cases is ready. Cases are evaluated in the order passed to `Select.
321318

322319
```go
323-
var f1 workflow.Future
324-
var c workflow.Channel
320+
var f1 workflow.Future[int]
321+
var c workflow.Channel[int]
325322
326323
workflow.Select(
327324
ctx,
328-
workflow.Await(f1, func (ctx workflow.Context, f Future) {
329-
var r int
330-
err := f.Get(ctx, &r)
325+
workflow.Await(f1, func (ctx workflow.Context, f Future[int]) {
326+
r, err := f.Get(ctx)
331327
// ...
332328
}),
333-
workflow.Receive(c, func (ctx workflow.Context, c Channel) {
334-
v, _ := c.Receive(ctx)
335-
// ...
329+
workflow.Receive(c, func (ctx workflow.Context, v int, ok bool) {
330+
// use v
336331
}),
337332
workflow.Default(ctx, func (ctx workflow.Context) {
338333
// ...
@@ -345,18 +340,16 @@ workflow.Select(
345340
`Await` adds a case to wait for a Future to have a value
346341

347342
```go
348-
var f1, f2 workflow.Future
343+
var f1, f2 workflow.Future[int]
349344
350345
workflow.Select(
351346
ctx,
352-
workflow.Await(f1, func (ctx workflow.Context, f Future) {
353-
var r int
354-
err := f.Get(ctx, &r)
347+
workflow.Await(f1, func (ctx workflow.Context, f Future[int]) {
348+
r, err := f.Get(ctx)
355349
// ...
356350
}),
357-
workflow.Await(f2, func (ctx workflow.Context, f Future) {
358-
var r int
359-
err := f.Get(ctx, &r)
351+
workflow.Await(f2, func (ctx workflow.Context, f Future[int]) {
352+
r, err := f.Get(ctx)
360353
// ...
361354
}),
362355
)
@@ -367,12 +360,11 @@ workflow.Select(
367360
`Receive` adds a case to receive from a given channel
368361

369362
```go
370-
var c workflow.Channel
363+
var c workflow.Channel[int]
371364
372365
workflow.Select(
373366
ctx,
374-
workflow.Receive(c, func (ctx workflow.Context, c Channel) {
375-
v, _ := c.Receive(ctx)
367+
workflow.Receive(c, func (ctx workflow.Context, v int, ok bool) {
376368
// ...
377369
}),
378370
)
@@ -383,13 +375,12 @@ workflow.Select(
383375
A `Default` case is executed if no previous case is ready and selected:
384376

385377
```go
386-
var f1 workflow.Future
378+
var f1 workflow.Future[int]
387379
388380
workflow.Select(
389381
ctx,
390-
workflow.Await(f1, func (ctx workflow.Context, f Future) {
391-
var r int
392-
err := f.Get(ctx, &r)
382+
workflow.Await(f1, func (ctx workflow.Context, f Future[int]) {
383+
r, err := f.Get(ctx, &r)
393384
// ...
394385
}),
395386
workflow.Default(ctx, func (ctx workflow.Context) {
@@ -415,15 +406,15 @@ func Workflow2(ctx workflow.Context, msg string) (string, error) {
415406
}
416407
}()
417408
418-
var r1 int
419-
if err := workflow.ExecuteActivity(ctx, ActivityCancel, 1, 2).Get(ctx, &r1); err != nil { // <---- Workflow is canceled while this activity is running
409+
r1, err := workflow.ExecuteActivity[int](ctx, ActivityCancel, 1, 2).Get(ctx)
410+
if err != nil { // <---- Workflow is canceled while this activity is running
420411
return errors.Wrap(err, "could not get ActivityCancel result")
421412
}
422413
423414
// r1 will contain the result of ActivityCancel
424415
// ⬇ ActivitySkip will be skipped immediately
425-
var r2 int
426-
if err := workflow.ExecuteActivity(ctx, ActivitySkip, 1, 2).Get(ctx, &r2); err != nil {
416+
r2, err := workflow.ExecuteActivity(ctx, ActivitySkip, 1, 2).Get(ctx)
417+
if err != nil {
427418
return errors.Wrap(err, "could not get ActivitySkip result")
428419
}
429420
@@ -471,12 +462,10 @@ For now, I've intentionally left our versioning. Cadence, Temporal, and DTFx all
471462
472463
```go
473464
func Workflow1(ctx workflow.Context) {
474-
var r1 int
475-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx, &r1)
465+
r1, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx)
476466
log.Println("A1 result:", r1)
477467
478-
var r2 int
479-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx, &r2)
468+
r2, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx)
480469
log.Println("A2 result:", r2)
481470
}
482471
```
@@ -486,15 +475,13 @@ to:
486475
```go
487476
func Workflow1(ctx workflow.Context) {
488477
var r1 int
489-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx, &r1)
478+
r1, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx)
490479
log.Println("A1 result:", r1)
491480
492-
var r3 int
493-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity3).Get(ctx, &r3)
481+
r3, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity3).Get(ctx)
494482
log.Println("A3 result:", r3)
495483
496-
var r2 int
497-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx, &r2)
484+
r2, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx)
498485
log.Println("A2 result:", r2)
499486
}
500487
```
@@ -511,18 +498,15 @@ the workflow will encounter an attempt to execute `Activity3` in-between event 2
511498
512499
```go
513500
func Workflow1(ctx workflow.Context) {
514-
var r1 int
515-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx, &r1)
501+
r1, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity1, 35, 12).Get(ctx)
516502
log.Println("A1 result:", r1)
517503
518504
if workflow.Version(ctx) >= 2 {
519-
var r3 int
520-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity3).Get(ctx, &r3)
505+
r3, _ := workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity3).Get(ctx)
521506
log.Println("A3 result:", r3)
522507
}
523508
524-
var r2 int
525-
workflow.ExecuteActivity(ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx, &r2)
509+
r2, _ := workflow.ExecuteActivity[int](ctx, workflow.DefaultActivityOptions, Activity2).Get(ctx)
526510
log.Println("A2 result:", r2)
527511
}
528512
```

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/cschleiden/go-workflows
22

3-
go 1.17
3+
go 1.18
44

55
require (
66
github.com/go-sql-driver/mysql v1.6.0

0 commit comments

Comments
 (0)