├── etc ├── temporal │ └── dynamicconfig │ │ ├── docker.yaml │ │ └── development-sql.yaml └── reveal │ └── template │ └── reveal.html ├── .github ├── .editorconfig ├── dependabot.yaml └── workflows │ ├── ci.yaml │ └── slides.yaml ├── examples ├── example08 │ ├── activity.go │ └── workflow.go ├── example09 │ ├── activity.go │ ├── workflow.go │ └── workflow_test.go ├── example10 │ ├── activity.go │ ├── workflow.go │ └── workflow_test.go ├── example01 │ └── workflow.go ├── example02 │ └── workflow.go ├── example03 │ └── workflow.go ├── example04 │ ├── workflow.go │ └── workflow_test.go ├── example05 │ └── workflow.go ├── example06 │ └── workflow.go └── example07 │ ├── workflow.go │ └── workflow_test.go ├── slides ├── favicon.ico ├── assets │ ├── fonts │ │ └── Virgil.woff2 │ └── img │ │ ├── reinventing-wheels.png │ │ ├── old │ │ ├── temporal-high-level-abstracted-relationships.png │ │ ├── workflow.svg │ │ ├── workflow-with-activities.svg │ │ ├── interaction.svg │ │ ├── drawio.xml │ │ ├── interaction-with-queues.svg │ │ ├── interaction-state.svg │ │ └── interaction-failure.svg │ │ ├── temporal-workflow-task-loop.puml │ │ ├── temporal-workflow-task.puml │ │ ├── temporal-logo.svg │ │ ├── temporal-workflow-task-loop.svg │ │ ├── temporal-workflow-task.svg │ │ └── workflow-activity-relations.orig.svg ├── custom.css ├── theme.css └── index.md ├── .gitignore ├── .envrc ├── .editorconfig ├── solutions ├── example08 │ ├── activity.go │ └── workflow.go ├── example01 │ └── workflow.go ├── example03 │ ├── workflow.go │ └── workflow_test.go ├── example09 │ ├── activity.go │ ├── workflow_test.go │ └── workflow.go ├── example02 │ └── workflow.go ├── example04 │ ├── workflow.go │ └── workflow_test.go ├── example10 │ ├── activity.go │ ├── workflow_test.go │ └── workflow.go ├── example06 │ └── workflow.go ├── example07 │ └── workflow.go └── example05 │ └── workflow.go ├── docker-compose.override.yml.dist ├── cmd └── worker │ ├── main.go │ └── register.go ├── LICENSE ├── flake.nix ├── go.mod ├── flake.lock ├── docker-compose.yml ├── Makefile ├── README.md └── go.sum /etc/temporal/dynamicconfig/docker.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/.editorconfig: -------------------------------------------------------------------------------- 1 | [{*.yaml,*.yml}] 2 | indent_size = 2 3 | -------------------------------------------------------------------------------- /examples/example08/activity.go: -------------------------------------------------------------------------------- 1 | package example08 2 | 3 | func Activity08() error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /examples/example09/activity.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | func Activity09() error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /examples/example10/activity.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | func Activity10() error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /slides/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagikazarmark/temporal-intro-workshop/HEAD/slides/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.direnv/ 2 | /bin/ 3 | /build/ 4 | /docker-compose.override.yml 5 | /public/ 6 | /slides.pdf 7 | /var/ 8 | -------------------------------------------------------------------------------- /slides/assets/fonts/Virgil.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagikazarmark/temporal-intro-workshop/HEAD/slides/assets/fonts/Virgil.woff2 -------------------------------------------------------------------------------- /slides/assets/img/reinventing-wheels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagikazarmark/temporal-intro-workshop/HEAD/slides/assets/img/reinventing-wheels.png -------------------------------------------------------------------------------- /examples/example01/workflow.go: -------------------------------------------------------------------------------- 1 | package example01 2 | 3 | import "go.temporal.io/sdk/workflow" 4 | 5 | func Workflow(ctx workflow.Context) error { 6 | return nil 7 | } 8 | -------------------------------------------------------------------------------- /slides/assets/img/old/temporal-high-level-abstracted-relationships.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagikazarmark/temporal-intro-workshop/HEAD/slides/assets/img/old/temporal-high-level-abstracted-relationships.png -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | labels: 7 | - dependencies 8 | schedule: 9 | interval: daily 10 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | if ! has nix_direnv_version || ! nix_direnv_version 2.1.1; then 2 | source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.1.1/direnvrc" "sha256-b6qJ4r34rbE23yWjMqbmu3ia2z4b2wIlZUksBke/ol0=" 3 | fi 4 | use flake 5 | -------------------------------------------------------------------------------- /examples/example02/workflow.go: -------------------------------------------------------------------------------- 1 | package example02 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 02") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example03/workflow.go: -------------------------------------------------------------------------------- 1 | package example03 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 03") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example04/workflow.go: -------------------------------------------------------------------------------- 1 | package example04 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 04") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example05/workflow.go: -------------------------------------------------------------------------------- 1 | package example05 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 05") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example06/workflow.go: -------------------------------------------------------------------------------- 1 | package example06 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 06") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example07/workflow.go: -------------------------------------------------------------------------------- 1 | package example07 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 07") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example08/workflow.go: -------------------------------------------------------------------------------- 1 | package example08 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 08") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example09/workflow.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 09") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /examples/example10/workflow.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 10") 9 | 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /etc/temporal/dynamicconfig/development-sql.yaml: -------------------------------------------------------------------------------- 1 | limit.maxIDLength: 2 | - value: 255 3 | constraints: {} 4 | system.forceSearchAttributesCacheRefreshOnRead: 5 | - value: true # Dev setup only. Please don't turn this on in production. 6 | constraints: {} 7 | -------------------------------------------------------------------------------- /slides/custom.css: -------------------------------------------------------------------------------- 1 | .reveal .slides section ol, 2 | .reveal .slides section dl, 3 | .reveal .slides section ul { 4 | line-height: 1.7; 5 | } 6 | 7 | .reveal .slides section table.valign-middle > tbody > tr > td { 8 | vertical-align: middle; 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.nix] 12 | indent_size = 2 13 | 14 | [{Makefile,*.mk}] 15 | indent_style = tab 16 | 17 | [*.go] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /solutions/example08/activity.go: -------------------------------------------------------------------------------- 1 | package example08 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type ActivityInput struct { 8 | A int 9 | B int 10 | } 11 | 12 | type ActivityOutput struct { 13 | Result int 14 | } 15 | 16 | func Activity08(ctx context.Context, input ActivityInput) (ActivityOutput, error) { 17 | return ActivityOutput{input.A + input.B}, nil 18 | } 19 | -------------------------------------------------------------------------------- /solutions/example01/workflow.go: -------------------------------------------------------------------------------- 1 | package example01 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | type Input struct { 8 | A int 9 | B int 10 | } 11 | 12 | type Output struct { 13 | Result int 14 | } 15 | 16 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 17 | workflow.GetLogger(ctx).Info("starting example 01") 18 | 19 | return Output{input.A + input.B}, nil 20 | } 21 | -------------------------------------------------------------------------------- /solutions/example03/workflow.go: -------------------------------------------------------------------------------- 1 | package example03 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | type Input struct { 8 | A int 9 | B int 10 | } 11 | 12 | type Output struct { 13 | Result int 14 | } 15 | 16 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 17 | workflow.GetLogger(ctx).Info("starting example 03") 18 | 19 | return Output{input.A + input.B}, nil 20 | } 21 | -------------------------------------------------------------------------------- /solutions/example09/activity.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "go.temporal.io/sdk/activity" 8 | ) 9 | 10 | type ActivityInput struct { 11 | A int 12 | B int 13 | } 14 | 15 | type ActivityOutput struct { 16 | Result int 17 | } 18 | 19 | func Activity09(ctx context.Context, input ActivityInput) (ActivityOutput, error) { 20 | activityInfo := activity.GetInfo(ctx) 21 | 22 | if activityInfo.Attempt < 3 { 23 | return ActivityOutput{}, errors.New("attempts under 3") 24 | } 25 | 26 | return ActivityOutput{input.A + input.B}, nil 27 | } 28 | -------------------------------------------------------------------------------- /docker-compose.override.yml.dist: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | postgres: 5 | # needed for Linux users 6 | # user: "${uid}:${gid}" 7 | ports: 8 | - 127.0.0.1:5432:5432 9 | volumes: 10 | - ./var/docker/volumes/postgres:/var/lib/postgresql/data 11 | 12 | elasticsearch: 13 | ports: 14 | - 127.0.0.1:9200:9200 15 | 16 | temporal: 17 | # needed for Linux users 18 | # user: "${uid}:${gid}" 19 | ports: 20 | - 127.0.0.1:7233:7233 21 | 22 | temporal-ui: 23 | ports: 24 | - 127.0.0.1:8080:8080 25 | -------------------------------------------------------------------------------- /slides/assets/img/temporal-workflow-task-loop.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | participant Temporal as "Temporal Cluster" 3 | participant Worker 4 | 5 | loop Until workflow completes 6 | 7 | Temporal -> Temporal: Schedule Workflow Task 8 | 9 | Worker -> Temporal: Poll for Workflow Task 10 | Temporal -> Worker: Receive Workflow Task 11 | 12 | activate Worker 13 | Worker -> Worker: Execute Workflow function 14 | Worker -> Temporal: Return Commands 15 | deactivate Worker 16 | note right: Workflow function\nterminates 17 | 18 | Temporal -> Temporal: Execute Commands 19 | Temporal -> Temporal: Record Events in the history 20 | 21 | end 22 | @enduml 23 | -------------------------------------------------------------------------------- /slides/assets/img/temporal-workflow-task.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | participant User 3 | participant Temporal as "Temporal Cluster" 4 | participant Worker 5 | 6 | User -> Temporal: Start Workflow 7 | 8 | Temporal -> Temporal: Schedule Workflow Task 9 | 10 | Worker -> Temporal: Poll for Workflow Task 11 | Temporal -> Worker: Receive Workflow Task 12 | 13 | activate Worker 14 | Worker -> Worker: Execute Workflow function 15 | Worker -> Temporal: Return Commands 16 | deactivate Worker 17 | note right: Workflow function\nterminates 18 | 19 | Temporal -> Temporal: Execute Commands 20 | Temporal -> Temporal: Record Events in the history 21 | @enduml 22 | -------------------------------------------------------------------------------- /examples/example04/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example04 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/example07/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example07 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/example09/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/example10/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | -------------------------------------------------------------------------------- /solutions/example02/workflow.go: -------------------------------------------------------------------------------- 1 | package example02 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | type Input struct { 8 | Number int 9 | } 10 | 11 | type Output struct { 12 | Result int 13 | } 14 | 15 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 16 | workflow.GetLogger(ctx).Info("starting example 02") 17 | 18 | if input.Number < 1 { 19 | workflow.GetLogger(ctx).Info("invalid number", "number", input.Number) 20 | } 21 | 22 | result := 1 23 | 24 | for i := 1; i <= input.Number; i++ { 25 | result *= i 26 | } 27 | 28 | output := Output{ 29 | Result: result, 30 | } 31 | 32 | return output, nil 33 | } 34 | -------------------------------------------------------------------------------- /solutions/example04/workflow.go: -------------------------------------------------------------------------------- 1 | package example04 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | type Input struct { 8 | Number int 9 | } 10 | 11 | type Output struct { 12 | Result int 13 | } 14 | 15 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 16 | workflow.GetLogger(ctx).Info("starting example 04") 17 | 18 | if input.Number < 1 { 19 | workflow.GetLogger(ctx).Info("invalid number", "number", input.Number) 20 | } 21 | 22 | result := 1 23 | 24 | for i := 1; i <= input.Number; i++ { 25 | result *= i 26 | } 27 | 28 | output := Output{ 29 | Result: result, 30 | } 31 | 32 | return output, nil 33 | } 34 | -------------------------------------------------------------------------------- /solutions/example10/activity.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "go.temporal.io/sdk/activity" 8 | ) 9 | 10 | type ActivityInput struct { 11 | Number int 12 | } 13 | 14 | type ActivityOutput struct { 15 | Result int 16 | } 17 | 18 | func Activity10(ctx context.Context, input ActivityInput) (ActivityOutput, error) { 19 | activityInfo := activity.GetInfo(ctx) 20 | 21 | if activityInfo.Attempt < 1 { 22 | return ActivityOutput{}, errors.New("first attempt") 23 | } 24 | 25 | result := 1 26 | 27 | for i := 1; i <= input.Number; i++ { 28 | result *= i 29 | } 30 | 31 | return ActivityOutput{result}, nil 32 | } 33 | -------------------------------------------------------------------------------- /solutions/example06/workflow.go: -------------------------------------------------------------------------------- 1 | package example06 2 | 3 | import ( 4 | "go.temporal.io/sdk/workflow" 5 | ) 6 | 7 | func Workflow(ctx workflow.Context) error { 8 | workflow.GetLogger(ctx).Info("starting example 06") 9 | 10 | var number int 11 | 12 | err := workflow.SetQueryHandler(ctx, "current_number", func() (int, error) { 13 | return number, nil 14 | }) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | signalChan := workflow.GetSignalChannel(ctx, "set_number") 20 | 21 | s := workflow.NewSelector(ctx) 22 | 23 | s.AddReceive(signalChan, func(c workflow.ReceiveChannel, more bool) { 24 | c.Receive(ctx, &number) 25 | 26 | workflow.GetLogger(ctx).Info("Received number", "number", number) 27 | }) 28 | 29 | for { 30 | s.Select(ctx) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /solutions/example07/workflow.go: -------------------------------------------------------------------------------- 1 | package example07 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | type Input struct { 10 | Number int 11 | } 12 | 13 | type Output struct { 14 | Result int 15 | } 16 | 17 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 18 | workflow.GetLogger(ctx).Info("starting example 07") 19 | 20 | result := 1 21 | 22 | err := workflow.SetQueryHandler(ctx, "current_result", func() (int, error) { 23 | return result, nil 24 | }) 25 | if err != nil { 26 | return Output{}, err 27 | } 28 | 29 | for i := 1; i <= input.Number; i++ { 30 | workflow.Sleep(ctx, 10*time.Second) 31 | 32 | result *= i 33 | } 34 | 35 | output := Output{ 36 | Result: result, 37 | } 38 | 39 | return output, nil 40 | } 41 | -------------------------------------------------------------------------------- /cmd/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "time" 7 | 8 | "go.temporal.io/sdk/client" 9 | "go.temporal.io/sdk/worker" 10 | ) 11 | 12 | const ( 13 | address = "127.0.0.1:7233" 14 | namespace = "default" 15 | taskQueue = "workshop" 16 | ) 17 | 18 | func init() { 19 | rand.Seed(time.Now().UnixNano()) 20 | } 21 | 22 | func main() { 23 | // Create client 24 | c, err := client.Dial(client.Options{ 25 | HostPort: address, 26 | Namespace: namespace, 27 | }) 28 | if err != nil { 29 | log.Fatalln("unable to create Temporal client", err) 30 | } 31 | defer c.Close() 32 | 33 | // Create worker 34 | w := worker.New(c, taskQueue, worker.Options{}) 35 | 36 | // Register workflows and activities 37 | register(w) 38 | 39 | // Start worker 40 | err = w.Run(worker.InterruptCh()) 41 | if err != nil { 42 | log.Fatalln("unable to start worker", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solutions/example05/workflow.go: -------------------------------------------------------------------------------- 1 | package example05 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | "go.temporal.io/sdk/workflow" 8 | ) 9 | 10 | type Input struct { 11 | Number int 12 | } 13 | 14 | type Output struct { 15 | Result int 16 | } 17 | 18 | const max = 10 19 | 20 | func Workflow(ctx workflow.Context, input Input) (Output, error) { 21 | workflow.GetLogger(ctx).Info("starting example 05") 22 | 23 | number := input.Number 24 | 25 | if number < 1 { 26 | workflow.GetLogger(ctx).Info("generating random number") 27 | 28 | encodedNumber := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { 29 | return rand.Intn(max) 30 | }) 31 | 32 | err := encodedNumber.Get(&number) 33 | if err != nil { 34 | return Output{}, err 35 | } 36 | } 37 | 38 | result := 1 39 | 40 | for i := 1; i <= number; i++ { 41 | workflow.Sleep(ctx, 5*time.Second) 42 | 43 | result *= i 44 | } 45 | 46 | output := Output{ 47 | Result: result, 48 | } 49 | 50 | return output, nil 51 | } 52 | -------------------------------------------------------------------------------- /solutions/example03/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example03 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | 29 | func (s *WorkflowTestSuite) Test_Success() { 30 | s.env.RegisterWorkflow(Workflow) 31 | s.env.ExecuteWorkflow(Workflow, Input{3, 4}) 32 | 33 | s.Require().True(s.env.IsWorkflowCompleted()) 34 | s.Require().NoError(s.env.GetWorkflowError()) 35 | 36 | var output Output 37 | s.Require().NoError(s.env.GetWorkflowResult(&output)) 38 | 39 | s.Equal(7, output.Result) 40 | } 41 | -------------------------------------------------------------------------------- /solutions/example04/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example04 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/suite" 7 | "go.temporal.io/sdk/testsuite" 8 | ) 9 | 10 | func TestWorkflowTestSuite(t *testing.T) { 11 | suite.Run(t, new(WorkflowTestSuite)) 12 | } 13 | 14 | type WorkflowTestSuite struct { 15 | suite.Suite 16 | testsuite.WorkflowTestSuite 17 | 18 | env *testsuite.TestWorkflowEnvironment 19 | } 20 | 21 | func (s *WorkflowTestSuite) SetupTest() { 22 | s.env = s.NewTestWorkflowEnvironment() 23 | } 24 | 25 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 26 | s.env.AssertExpectations(s.T()) 27 | } 28 | 29 | func (s *WorkflowTestSuite) Test_Success() { 30 | s.env.RegisterWorkflow(Workflow) 31 | s.env.ExecuteWorkflow(Workflow, Input{5}) 32 | 33 | s.Require().True(s.env.IsWorkflowCompleted()) 34 | s.Require().NoError(s.env.GetWorkflowError()) 35 | 36 | var output Output 37 | s.Require().NoError(s.env.GetWorkflowResult(&output)) 38 | 39 | expectedOutput := Output{ 40 | Result: 120, 41 | } 42 | 43 | s.Equal(expectedOutput, output) 44 | } 45 | -------------------------------------------------------------------------------- /solutions/example08/workflow.go: -------------------------------------------------------------------------------- 1 | package example08 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/workflow" 7 | ) 8 | 9 | type WorkflowInput struct { 10 | A int 11 | B int 12 | } 13 | 14 | type WorkflowOutput struct { 15 | Result int 16 | } 17 | 18 | func Workflow(ctx workflow.Context, input WorkflowInput) (WorkflowOutput, error) { 19 | workflow.GetLogger(ctx).Info("starting example 08") 20 | 21 | ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 22 | TaskQueue: "workshop", 23 | ScheduleToCloseTimeout: 3*time.Second + 3*time.Second, 24 | ScheduleToStartTimeout: 3 * time.Second, 25 | StartToCloseTimeout: 3 * time.Second, 26 | HeartbeatTimeout: 0 * time.Second, 27 | WaitForCancellation: false, 28 | ActivityID: "", 29 | RetryPolicy: nil, 30 | }) 31 | 32 | var activityOutput ActivityOutput 33 | 34 | err := workflow.ExecuteActivity(ctx, Activity08, ActivityInput{input.A, input.B}).Get(ctx, &activityOutput) 35 | if err != nil { 36 | return WorkflowOutput{}, err 37 | } 38 | 39 | return WorkflowOutput{activityOutput.Result}, nil 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Márk Sági-Kazár 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | code: 11 | name: Code 12 | runs-on: ubuntu-latest 13 | defaults: 14 | run: 15 | shell: nix develop .#ci -c bash {0} 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | 21 | - name: Set up Nix 22 | uses: cachix/install-nix-action@v21 23 | with: 24 | extra_nix_config: | 25 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 26 | 27 | - name: Test 28 | run: go test -v ./... 29 | 30 | slides: 31 | name: Slides 32 | runs-on: ubuntu-latest 33 | defaults: 34 | run: 35 | shell: nix develop .#ci -c bash {0} 36 | 37 | steps: 38 | - name: Checkout code 39 | uses: actions/checkout@v3 40 | 41 | - name: Set up Nix 42 | uses: cachix/install-nix-action@v21 43 | with: 44 | extra_nix_config: | 45 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 46 | 47 | - name: Build 48 | run: make build-slides 49 | -------------------------------------------------------------------------------- /solutions/example09/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/suite" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func TestWorkflowTestSuite(t *testing.T) { 12 | suite.Run(t, new(WorkflowTestSuite)) 13 | } 14 | 15 | type WorkflowTestSuite struct { 16 | suite.Suite 17 | testsuite.WorkflowTestSuite 18 | 19 | env *testsuite.TestWorkflowEnvironment 20 | } 21 | 22 | func (s *WorkflowTestSuite) SetupTest() { 23 | s.env = s.NewTestWorkflowEnvironment() 24 | } 25 | 26 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 27 | s.env.AssertExpectations(s.T()) 28 | } 29 | 30 | func (s *WorkflowTestSuite) Test_Success() { 31 | s.env.RegisterWorkflow(Workflow) 32 | s.env.RegisterActivity(Activity09) 33 | 34 | s.env.OnActivity(Activity09, mock.Anything, ActivityInput{3, 4}).Return(ActivityOutput{7}, nil) 35 | 36 | s.env.ExecuteWorkflow(Workflow, WorkflowInput{3, 4}) 37 | 38 | s.Require().True(s.env.IsWorkflowCompleted()) 39 | s.Require().NoError(s.env.GetWorkflowError()) 40 | 41 | var output WorkflowOutput 42 | s.Require().NoError(s.env.GetWorkflowResult(&output)) 43 | 44 | s.Equal(7, output.Result) 45 | } 46 | -------------------------------------------------------------------------------- /solutions/example10/workflow_test.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/mock" 7 | "github.com/stretchr/testify/suite" 8 | "go.temporal.io/sdk/testsuite" 9 | ) 10 | 11 | func TestWorkflowTestSuite(t *testing.T) { 12 | suite.Run(t, new(WorkflowTestSuite)) 13 | } 14 | 15 | type WorkflowTestSuite struct { 16 | suite.Suite 17 | testsuite.WorkflowTestSuite 18 | 19 | env *testsuite.TestWorkflowEnvironment 20 | } 21 | 22 | func (s *WorkflowTestSuite) SetupTest() { 23 | s.env = s.NewTestWorkflowEnvironment() 24 | } 25 | 26 | func (s *WorkflowTestSuite) AfterTest(suiteName, testName string) { 27 | s.env.AssertExpectations(s.T()) 28 | } 29 | 30 | func (s *WorkflowTestSuite) Test_Success() { 31 | s.env.RegisterWorkflow(Workflow) 32 | s.env.RegisterActivity(Activity10) 33 | 34 | s.env.OnActivity(Activity10, mock.Anything, ActivityInput{5}).Return(ActivityOutput{120}, nil) 35 | 36 | s.env.ExecuteWorkflow(Workflow, WorkflowInput{5}) 37 | 38 | s.Require().True(s.env.IsWorkflowCompleted()) 39 | s.Require().NoError(s.env.GetWorkflowError()) 40 | 41 | var output WorkflowOutput 42 | s.Require().NoError(s.env.GetWorkflowResult(&output)) 43 | 44 | expectedOutput := WorkflowOutput{ 45 | Result: 120, 46 | } 47 | 48 | s.Equal(expectedOutput, output) 49 | } 50 | -------------------------------------------------------------------------------- /solutions/example10/workflow.go: -------------------------------------------------------------------------------- 1 | package example10 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/temporal" 7 | "go.temporal.io/sdk/workflow" 8 | ) 9 | 10 | type WorkflowInput struct { 11 | Number int 12 | } 13 | 14 | type WorkflowOutput struct { 15 | Result int 16 | } 17 | 18 | func Workflow(ctx workflow.Context, input WorkflowInput) (WorkflowOutput, error) { 19 | workflow.GetLogger(ctx).Info("starting example 10") 20 | 21 | ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 22 | TaskQueue: "workshop", 23 | ScheduleToCloseTimeout: 3*time.Second + 3*time.Second, 24 | ScheduleToStartTimeout: 3 * time.Second, 25 | StartToCloseTimeout: 3 * time.Second, 26 | HeartbeatTimeout: 0 * time.Second, 27 | WaitForCancellation: false, 28 | ActivityID: "", 29 | RetryPolicy: &temporal.RetryPolicy{ 30 | InitialInterval: time.Second, 31 | BackoffCoefficient: 1.0, 32 | MaximumInterval: 10 * time.Second, 33 | MaximumAttempts: 5, 34 | }, 35 | }) 36 | 37 | var activityOutput ActivityOutput 38 | 39 | err := workflow.ExecuteActivity(ctx, Activity10, ActivityInput{input.Number}).Get(ctx, &activityOutput) 40 | if err != nil { 41 | return WorkflowOutput{}, err 42 | } 43 | 44 | return WorkflowOutput{activityOutput.Result}, nil 45 | } 46 | -------------------------------------------------------------------------------- /solutions/example09/workflow.go: -------------------------------------------------------------------------------- 1 | package example09 2 | 3 | import ( 4 | "time" 5 | 6 | "go.temporal.io/sdk/temporal" 7 | "go.temporal.io/sdk/workflow" 8 | ) 9 | 10 | type WorkflowInput struct { 11 | A int 12 | B int 13 | } 14 | 15 | type WorkflowOutput struct { 16 | Result int 17 | } 18 | 19 | func Workflow(ctx workflow.Context, input WorkflowInput) (WorkflowOutput, error) { 20 | workflow.GetLogger(ctx).Info("starting example 09") 21 | 22 | ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ 23 | TaskQueue: "workshop", 24 | ScheduleToCloseTimeout: 3*time.Second + 3*time.Second, 25 | ScheduleToStartTimeout: 3 * time.Second, 26 | StartToCloseTimeout: 3 * time.Second, 27 | HeartbeatTimeout: 0 * time.Second, 28 | WaitForCancellation: false, 29 | ActivityID: "", 30 | RetryPolicy: &temporal.RetryPolicy{ 31 | InitialInterval: time.Second, 32 | BackoffCoefficient: 1.0, 33 | MaximumInterval: 10 * time.Second, 34 | MaximumAttempts: 5, 35 | }, 36 | }) 37 | 38 | var activityOutput ActivityOutput 39 | 40 | err := workflow.ExecuteActivity(ctx, Activity09, ActivityInput{input.A, input.B}).Get(ctx, &activityOutput) 41 | if err != nil { 42 | return WorkflowOutput{}, err 43 | } 44 | 45 | return WorkflowOutput{activityOutput.Result}, nil 46 | } 47 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Temporal intro workshop"; 3 | 4 | inputs = { 5 | nixpkgs.url = "nixpkgs/nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | nur = { 8 | url = "github:sagikazarmark/nur-packages"; 9 | inputs.nixpkgs.follows = "nixpkgs"; 10 | }; 11 | }; 12 | 13 | outputs = { self, nixpkgs, flake-utils, nur, ... }: 14 | flake-utils.lib.eachDefaultSystem (system: 15 | let 16 | pkgs = import nixpkgs { 17 | inherit system; 18 | 19 | overlays = [ 20 | (final: prev: { 21 | reveal-md = nur.packages.${system}.reveal-md; 22 | decktape = nur.packages.${system}.decktape; 23 | }) 24 | ]; 25 | }; 26 | in 27 | rec 28 | { 29 | devShells = { 30 | default = pkgs.mkShell { 31 | buildInputs = with pkgs; [ 32 | git 33 | gnumake 34 | reveal-md 35 | go 36 | 37 | temporalite 38 | temporal-cli 39 | ]; 40 | }; 41 | 42 | ci = pkgs.mkShell { 43 | buildInputs = with pkgs; [ 44 | git 45 | gnumake 46 | reveal-md 47 | go 48 | ]; 49 | }; 50 | }; 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/slides.yaml: -------------------------------------------------------------------------------- 1 | name: Slides 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | build: 21 | name: Build 22 | runs-on: ubuntu-latest 23 | defaults: 24 | run: 25 | shell: nix develop .#ci -c bash {0} 26 | 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v3 30 | 31 | - name: Setup Pages 32 | id: pages 33 | uses: actions/configure-pages@v3 34 | 35 | - name: Set up Nix 36 | uses: cachix/install-nix-action@v21 37 | with: 38 | extra_nix_config: | 39 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 40 | 41 | - name: Build 42 | run: make ABSOLUTE_URL=${{ steps.pages.outputs.base_url }} build-slides 43 | 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v1 46 | with: 47 | path: ./public/ 48 | 49 | deploy: 50 | name: Deploy 51 | environment: 52 | name: github-pages 53 | url: ${{ steps.deployment.outputs.page_url }} 54 | runs-on: ubuntu-latest 55 | needs: build 56 | 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v2 61 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sagikazarmark/temporal-intro-workshop 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.0 7 | go.temporal.io/sdk v1.17.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect 13 | github.com/gogo/googleapis v1.4.1 // indirect 14 | github.com/gogo/protobuf v1.3.2 // indirect 15 | github.com/gogo/status v1.1.1 // indirect 16 | github.com/golang/mock v1.6.0 // indirect 17 | github.com/golang/protobuf v1.5.2 // indirect 18 | github.com/google/uuid v1.3.0 // indirect 19 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect 20 | github.com/pborman/uuid v1.2.1 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/robfig/cron v1.2.0 // indirect 23 | github.com/stretchr/objx v0.4.0 // indirect 24 | go.temporal.io/api v1.11.1-0.20220907050538-6de5285cf463 // indirect 25 | go.uber.org/atomic v1.9.0 // indirect 26 | golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect 27 | golang.org/x/sys v0.0.0-20220906165534-d0df966e6959 // indirect 28 | golang.org/x/text v0.3.7 // indirect 29 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect 30 | google.golang.org/genproto v0.0.0-20220902135211-223410557253 // indirect 31 | google.golang.org/grpc v1.49.0 // indirect 32 | google.golang.org/protobuf v1.28.1 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1667395993, 6 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1667901915, 21 | "narHash": "sha256-IkSou5ox/yZ2YUhGpk8vxd2TNU2pwRlYtir5k55NaxE=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "093268502280540a7f5bf1e2a6330a598ba3b7d0", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "id": "nixpkgs", 29 | "ref": "nixos-unstable", 30 | "type": "indirect" 31 | } 32 | }, 33 | "nur": { 34 | "inputs": { 35 | "nixpkgs": [ 36 | "nixpkgs" 37 | ] 38 | }, 39 | "locked": { 40 | "lastModified": 1668078676, 41 | "narHash": "sha256-tZ1CZjKWs4BHUQ9qp4yZ4tlt4PnEgEg+OVEJ6qdhOms=", 42 | "owner": "sagikazarmark", 43 | "repo": "nur-packages", 44 | "rev": "a4386fda9d3b9161805f1446dceb5877c2fced01", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "owner": "sagikazarmark", 49 | "repo": "nur-packages", 50 | "type": "github" 51 | } 52 | }, 53 | "root": { 54 | "inputs": { 55 | "flake-utils": "flake-utils", 56 | "nixpkgs": "nixpkgs", 57 | "nur": "nur" 58 | } 59 | } 60 | }, 61 | "root": "root", 62 | "version": 7 63 | } 64 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | postgres: 5 | image: postgres:13.8 6 | environment: 7 | POSTGRES_USER: temporal 8 | POSTGRES_PASSWORD: temporal 9 | POSTGRES_DB: temporal 10 | 11 | elasticsearch: 12 | image: elasticsearch:7.16.2 13 | environment: 14 | - cluster.routing.allocation.disk.threshold_enabled=true 15 | - cluster.routing.allocation.disk.watermark.low=512mb 16 | - cluster.routing.allocation.disk.watermark.high=256mb 17 | - cluster.routing.allocation.disk.watermark.flood_stage=128mb 18 | - discovery.type=single-node 19 | - ES_JAVA_OPTS=-Xms100m -Xmx100m 20 | - xpack.security.enabled=false 21 | 22 | temporal: 23 | image: temporalio/auto-setup:1.18.4 24 | environment: 25 | - DB=postgresql 26 | - DB_PORT=5432 27 | - POSTGRES_USER=temporal 28 | - POSTGRES_PWD=temporal 29 | - POSTGRES_SEEDS=postgres 30 | - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml 31 | - ENABLE_ES=true 32 | - ES_SEEDS=elasticsearch 33 | - ES_VERSION=v7 34 | volumes: 35 | - ./etc/temporal/dynamicconfig:/etc/temporal/config/dynamicconfig 36 | depends_on: 37 | - postgres 38 | - elasticsearch 39 | 40 | temporal-admin-tools: 41 | image: temporalio/admin-tools:1.18.4 42 | environment: 43 | - TEMPORAL_CLI_ADDRESS=temporal:7233 44 | profiles: 45 | - cli 46 | depends_on: 47 | - temporal 48 | stdin_open: true 49 | tty: true 50 | 51 | temporal-ui: 52 | image: temporalio/ui:2.8.1 53 | environment: 54 | - TEMPORAL_ADDRESS=temporal:7233 55 | depends_on: 56 | - temporal 57 | -------------------------------------------------------------------------------- /cmd/worker/register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example01" 5 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example02" 6 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example03" 7 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example04" 8 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example05" 9 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example06" 10 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example07" 11 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example08" 12 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example09" 13 | "github.com/sagikazarmark/temporal-intro-workshop/examples/example10" 14 | "go.temporal.io/sdk/activity" 15 | "go.temporal.io/sdk/worker" 16 | "go.temporal.io/sdk/workflow" 17 | ) 18 | 19 | func register(w worker.Registry) { 20 | w.RegisterWorkflow(example01.Workflow) 21 | w.RegisterWorkflowWithOptions(example01.Workflow, workflow.RegisterOptions{Name: "example01"}) 22 | 23 | w.RegisterWorkflowWithOptions(example02.Workflow, workflow.RegisterOptions{Name: "example02"}) 24 | 25 | w.RegisterWorkflowWithOptions(example03.Workflow, workflow.RegisterOptions{Name: "example03"}) 26 | 27 | w.RegisterWorkflowWithOptions(example04.Workflow, workflow.RegisterOptions{Name: "example04"}) 28 | 29 | w.RegisterWorkflowWithOptions(example05.Workflow, workflow.RegisterOptions{Name: "example05"}) 30 | 31 | w.RegisterWorkflowWithOptions(example06.Workflow, workflow.RegisterOptions{Name: "example06"}) 32 | 33 | w.RegisterWorkflowWithOptions(example07.Workflow, workflow.RegisterOptions{Name: "example07"}) 34 | 35 | w.RegisterWorkflowWithOptions(example08.Workflow, workflow.RegisterOptions{Name: "example08"}) 36 | w.RegisterActivity(example08.Activity08) 37 | w.RegisterActivityWithOptions(example08.Activity08, activity.RegisterOptions{Name: "example08"}) 38 | 39 | w.RegisterWorkflowWithOptions(example09.Workflow, workflow.RegisterOptions{Name: "example09"}) 40 | w.RegisterActivity(example09.Activity09) 41 | w.RegisterActivityWithOptions(example09.Activity09, activity.RegisterOptions{Name: "example09"}) 42 | 43 | w.RegisterWorkflowWithOptions(example10.Workflow, workflow.RegisterOptions{Name: "example10"}) 44 | w.RegisterActivity(example10.Activity10) 45 | w.RegisterActivityWithOptions(example10.Activity10, activity.RegisterOptions{Name: "example10"}) 46 | } 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html 2 | 3 | SHELL = /bin/bash 4 | OS = $(shell uname | tr A-Z a-z) 5 | 6 | .PHONY: up 7 | up: start ## Spin up the environment 8 | 9 | .PHONY: down 10 | down: ## Destroy the environment 11 | docker compose down -v 12 | @ if [[ "$$OSTYPE" == "linux-gnu" ]]; then sudo rm -rf var/docker/volumes/; else rm -rf var/docker/volumes/; fi 13 | 14 | docker-compose.override.yml: ## Create docker compose override file 15 | @ if [[ "$$OSTYPE" == "linux-gnu" ]]; then cat docker-compose.override.yml.dist | sed -e 's/# user: "$${uid}:$${gid}"/user: "$(shell id -u):$(shell id -g)"/' > docker-compose.override.yml; else cp docker-compose.override.yml.dist docker-compose.override.yml; fi 16 | 17 | .PHONY: start 18 | start: docker-compose.override.yml ## Start services 19 | @ if [ docker-compose.override.yml -ot docker-compose.override.yml.dist ]; then diff -u docker-compose.override.yml* || (echo "!!! The distributed docker-compose.override.yml example changed. Please update your file accordingly (or at least touch it). !!!" && false); fi 20 | mkdir -p var/docker/volumes/postgres 21 | docker compose up -d 22 | 23 | .PHONY: stop 24 | stop: ## Stop services 25 | docker compose stop 26 | 27 | .PHONY: ps 28 | ps: ## Check the status of services services 29 | docker compose ps 30 | 31 | .PHONY: shell 32 | shell: ## Start a shell with the Temporal CLI 33 | docker compose exec --profile cli temporal-admin-tools bash 34 | 35 | .PHONY: worker 36 | worker: ## Start the worker 37 | go run ./cmd/worker/ 38 | 39 | .PHONY: test 40 | test: ## Run tests 41 | go test ./... 42 | 43 | .PHONY: slides 44 | slides: ## Open slides in the browser 45 | reveal-md --template etc/reveal/template/reveal.html --theme slides/theme.css --css slides/custom.css slides/index.md -w 46 | 47 | .PHONY: build-slides 48 | build-slides: ABSOLUTE_URL := http://localhost 49 | build-slides: 50 | reveal-md --template etc/reveal/template/reveal.html --theme slides/theme.css --css slides/custom.css --static public/ --static-dirs slides/assets --absolute-url ${ABSOLUTE_URL} slides/index.md 51 | 52 | .PHONY: print-slides 53 | print-slides: 54 | decktape --size='2048x1536' https://sagikazarmark.github.io/temporal-intro-workshop slides.pdf 55 | 56 | .PHONY: deps 57 | deps: 58 | npm install -g reveal-md 59 | 60 | .PHONY: help 61 | .DEFAULT_GOAL := help 62 | help: 63 | @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-10s\033[0m %s\n", $$1, $$2}' 64 | -------------------------------------------------------------------------------- /slides/assets/img/old/workflow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Workflow
Workflow
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /etc/reveal/template/reveal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{{title}}} 8 | {{#absoluteUrl}} 9 | 10 | 11 | 12 | 13 | {{/absoluteUrl}} 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{#cssPaths}} 21 | 22 | {{/cssPaths}} 23 | 24 | {{#watch}} 25 | 26 | 34 | {{/watch}} 35 | 36 | 37 |
38 |
{{{slides}}}
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 85 | 86 | {{#scriptPaths}} 87 | 88 | {{/scriptPaths}} 89 | 90 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Temporal Intro Workshop 2 | 3 | This repository contains example code for my [Temporal Intro Workshop](https://sagikazarmark.github.io/temporal-intro-workshop/). You can create your own Workshop with [my Workshop template](https://github.com/sagikazarmark/workshop-template). 4 | 5 | Record: https://youtu.be/UwdGmdTO3Ts 6 | 7 | Record (HUN): https://youtu.be/IGAnWQ52xOI 8 | 9 | 10 | ## Prerequisites 11 | 12 | 1. Git, Make, etc. 13 | 2. Make sure you have the latest [Go](https://golang.org/) and [Docker](https://www.docker.com/get-started) installed 14 | 15 | Alternatively, install [nix](https://nixos.org) and [direnv](https://direnv.net), then run `direnv allow` once you checked out the repository. 16 | 17 | _Note: you should still install Docker using your native package manager._ 18 | 19 | 20 | ## Usage 21 | 22 | 1. Checkout this repository 23 | 2. Run `make up` 24 | 3. Wait for Temporal to start 25 | 4. Check if Temporal is running with `make ps` 26 | 5. Start a new shell with `make shell` 27 | 28 | Alternatively, you can use the following alias for the `tctl` commands instead of opening a new shell: 29 | 30 | ```shell 31 | alias tctl='docker compose exec --profile cli temporal-admin-tools tctl' 32 | ``` 33 | 34 | 35 | ## Running a workflow using the CLI 36 | 37 | You can run a workflow using the CLI with the following command: 38 | 39 | ```bash 40 | tctl workflow run --taskqueue workshop --execution_timeout 60 --workflow_type WORKFLOW_TYPE -i 'arg1 arg2...' 41 | ``` 42 | 43 | As a best practice, workflows generally have a single input struct (to remain compatible with other languages). 44 | By default, Temporal uses JSON encoding, so such workflow execution looks like this: 45 | 46 | ```bash 47 | tctl workflow run --taskqueue workshop --execution_timeout 60 --workflow_type example01 -i '{"A": 1, "B": 2}' 48 | ``` 49 | 50 | You can shorten the command a lot by using shorthands for commands and options: 51 | 52 | ```bash 53 | tctl wf run --tq workshop --et 60 --wt example01 -i '{"A": 1, "B": 2}' 54 | ``` 55 | 56 | Last, but not least, if you want to start a workflow without waiting for its result, 57 | you can do so by using the `start` command instead of `run`: 58 | 59 | ```bash 60 | tctl wf start --tq workshop --et 60 --wt example01 -i '{"A": 1, "B": 2}' 61 | ``` 62 | 63 | 64 | ## Quering workflow state using the CLI 65 | 66 | Workflows can register query handlers to expose state about themselves. You can query that state using the following command: 67 | 68 | ```bash 69 | tctl workflow query --workflow_id 72daa600-3cac-49b0-9e86-277a47c80a87 --query_type current_number 70 | ``` 71 | 72 | Or using a shorter version: 73 | 74 | ```bash 75 | tctl wf query --wid 72daa600-3cac-49b0-9e86-277a47c80a87 --qt current_number 76 | ``` 77 | 78 | There is a special query type called `__stack_trace` that gives you the current stack trace of the workflow. 79 | Useful if a workflow is stuck for a long time and you want to check where it stopped. 80 | 81 | 82 | ## Signaling a workflow using the CLI 83 | 84 | Workflows can register query handlers to expose state about themselves. You can query that state using the following command: 85 | 86 | ```bash 87 | tctl workflow signal --workflow_id 72daa600-3cac-49b0-9e86-277a47c80a87 --name set_number --input '2' 88 | ``` 89 | 90 | Or using a shorter version: 91 | 92 | ```bash 93 | tctl wf signal --wid 72daa600-3cac-49b0-9e86-277a47c80a87 -n set_number -i '2' 94 | ``` 95 | 96 | There is a special query type called `__stack_trace` that gives you the current stack trace of the workflow. 97 | Useful if a workflow is stuck for a long time and you want to check where it stopped. 98 | 99 | 100 | ## Cleaning up 101 | 102 | Once you are finished with the workshop, you can clean up all resources (containers) by running the following command: 103 | 104 | ```bash 105 | make down 106 | ``` 107 | 108 | 109 | ## Development 110 | 111 | Make sure [nix](https://nixos.org) and [direnv](https://direnv.net) are installed, then run `direnv allow`. 112 | 113 | To work on the slides, run `make slides`. 114 | It will open a browser window and automatically refresh the page when you make changes to the slides. 115 | 116 | 117 | ## License 118 | 119 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 120 | -------------------------------------------------------------------------------- /slides/assets/img/temporal-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /slides/assets/img/old/workflow-with-activities.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Activitiy
Activitiy
Activitiy
Activitiy
Activitiy
Activitiy
Activitiy
Activitiy
Workflow
Workflow
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /slides/assets/img/temporal-workflow-task-loop.svg: -------------------------------------------------------------------------------- 1 | Temporal ClusterTemporal ClusterWorkerWorkerloop[Until workflow completes]Schedule Workflow TaskPoll for Workflow TaskReceive Workflow TaskExecute Workflow functionReturn CommandsWorkflow functionterminatesExecute CommandsRecord Events in the history -------------------------------------------------------------------------------- /slides/assets/img/temporal-workflow-task.svg: -------------------------------------------------------------------------------- 1 | UserUserTemporal ClusterTemporal ClusterWorkerWorkerStart WorkflowSchedule Workflow TaskPoll for Workflow TaskReceive Workflow TaskExecute Workflow functionReturn CommandsWorkflow functionterminatesExecute CommandsRecord Events in the history -------------------------------------------------------------------------------- /slides/assets/img/old/interaction.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Place order
Place order
User
User
Shop
Shop
Warehouse
Warehouse
Payment
Payment
Notification
Notification
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /slides/theme.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | [ robot-lung ] 4 | 5 | A hot pink theme for Reveal.js with Roboto fonts and a colorful border. 6 | By Josh Dzielak, https://dzello.com/, License MIT 7 | 8 | Original source: https://github.com/dzello/revealjs-themes 9 | 10 | To change the border color in your custom CSS: 11 | 12 | .reveal { 13 | border-color: ; 14 | } 15 | 16 | */ 17 | @import url(https://fonts.googleapis.com/css?family=Roboto+Slab:300,700); 18 | @import url(https://fonts.googleapis.com/css?family=Roboto:700); 19 | :root { 20 | --r-progress-color: #FF80A1; 21 | } 22 | 23 | /********************************************* 24 | * GLOBAL STYLES 25 | *********************************************/ 26 | :root { 27 | --r-background-color: #fff; 28 | --r-main-font: Roboto Slab, serif; 29 | --r-main-font-size: 32px; 30 | --r-main-color: #363636; 31 | --r-block-margin: 20px; 32 | --r-heading-margin: 0 0 20px 0; 33 | --r-heading-font: Roboto, sans-serif; 34 | --r-heading-color: #141414; 35 | --r-heading-line-height: 1.2; 36 | --r-heading-letter-spacing: normal; 37 | --r-heading-text-transform: uppercase; 38 | --r-heading-text-shadow: none; 39 | --r-heading-font-weight: 700; 40 | --r-heading1-text-shadow: none; 41 | --r-heading1-size: 2.6em; 42 | --r-heading2-size: 2.2em; 43 | --r-heading3-size: 1.7em; 44 | --r-heading4-size: 1.4em; 45 | --r-code-font: monospace; 46 | --r-link-color: #FF4081; 47 | --r-link-color-dark: #f30053; 48 | --r-link-color-hover: #ff8db3; 49 | --r-selection-background-color: #ffc0d5; 50 | --r-selection-color: #fff; 51 | } 52 | 53 | .reveal-viewport { 54 | background: #fff; 55 | background-color: var(--r-background-color); 56 | } 57 | 58 | .reveal { 59 | font-family: var(--r-main-font); 60 | font-size: var(--r-main-font-size); 61 | font-weight: normal; 62 | color: var(--r-main-color); 63 | } 64 | 65 | .reveal ::selection { 66 | color: var(--r-selection-color); 67 | background: var(--r-selection-background-color); 68 | text-shadow: none; 69 | } 70 | 71 | .reveal ::-moz-selection { 72 | color: var(--r-selection-color); 73 | background: var(--r-selection-background-color); 74 | text-shadow: none; 75 | } 76 | 77 | .reveal .slides section, 78 | .reveal .slides section > section { 79 | line-height: 1.3; 80 | font-weight: inherit; 81 | } 82 | 83 | /********************************************* 84 | * HEADERS 85 | *********************************************/ 86 | .reveal h1, 87 | .reveal h2, 88 | .reveal h3, 89 | .reveal h4, 90 | .reveal h5, 91 | .reveal h6 { 92 | margin: var(--r-heading-margin); 93 | color: var(--r-heading-color); 94 | font-family: var(--r-heading-font); 95 | font-weight: var(--r-heading-font-weight); 96 | line-height: var(--r-heading-line-height); 97 | letter-spacing: var(--r-heading-letter-spacing); 98 | text-transform: var(--r-heading-text-transform); 99 | text-shadow: var(--r-heading-text-shadow); 100 | word-wrap: break-word; 101 | } 102 | 103 | .reveal h1 { 104 | font-size: var(--r-heading1-size); 105 | } 106 | 107 | .reveal h2 { 108 | font-size: var(--r-heading2-size); 109 | } 110 | 111 | .reveal h3 { 112 | font-size: var(--r-heading3-size); 113 | } 114 | 115 | .reveal h4 { 116 | font-size: var(--r-heading4-size); 117 | } 118 | 119 | .reveal h1 { 120 | text-shadow: var(--r-heading1-text-shadow); 121 | } 122 | 123 | /********************************************* 124 | * OTHER 125 | *********************************************/ 126 | .reveal p { 127 | margin: var(--r-block-margin) 0; 128 | line-height: 1.3; 129 | } 130 | 131 | /* Remove trailing margins after titles */ 132 | .reveal h1:last-child, 133 | .reveal h2:last-child, 134 | .reveal h3:last-child, 135 | .reveal h4:last-child, 136 | .reveal h5:last-child, 137 | .reveal h6:last-child { 138 | margin-bottom: 0; 139 | } 140 | 141 | /* Ensure certain elements are never larger than the slide itself */ 142 | .reveal img, 143 | .reveal video, 144 | .reveal iframe { 145 | max-width: 95%; 146 | max-height: 95%; 147 | } 148 | 149 | .reveal strong, 150 | .reveal b { 151 | font-weight: bold; 152 | } 153 | 154 | .reveal em { 155 | font-style: italic; 156 | } 157 | 158 | .reveal ol, 159 | .reveal dl, 160 | .reveal ul { 161 | display: inline-block; 162 | text-align: left; 163 | margin: 0 0 0 1em; 164 | } 165 | 166 | .reveal ol { 167 | list-style-type: decimal; 168 | } 169 | 170 | .reveal ul { 171 | list-style-type: disc; 172 | } 173 | 174 | .reveal ul ul { 175 | list-style-type: square; 176 | } 177 | 178 | .reveal ul ul ul { 179 | list-style-type: circle; 180 | } 181 | 182 | .reveal ul ul, 183 | .reveal ul ol, 184 | .reveal ol ol, 185 | .reveal ol ul { 186 | display: block; 187 | margin-left: 40px; 188 | } 189 | 190 | .reveal dt { 191 | font-weight: bold; 192 | } 193 | 194 | .reveal dd { 195 | margin-left: 40px; 196 | } 197 | 198 | .reveal blockquote { 199 | display: block; 200 | position: relative; 201 | width: 70%; 202 | margin: var(--r-block-margin) auto; 203 | padding: 5px; 204 | font-style: italic; 205 | background: rgba(255, 255, 255, 0.05); 206 | box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); 207 | } 208 | 209 | .reveal blockquote p:first-child, 210 | .reveal blockquote p:last-child { 211 | display: inline-block; 212 | } 213 | 214 | .reveal q { 215 | font-style: italic; 216 | } 217 | 218 | .reveal pre { 219 | display: block; 220 | position: relative; 221 | width: 90%; 222 | margin: var(--r-block-margin) auto; 223 | text-align: left; 224 | font-size: 0.55em; 225 | font-family: var(--r-code-font); 226 | line-height: 1.2em; 227 | word-wrap: break-word; 228 | box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); 229 | } 230 | 231 | .reveal code { 232 | font-family: var(--r-code-font); 233 | text-transform: none; 234 | tab-size: 2; 235 | } 236 | 237 | .reveal pre code { 238 | display: block; 239 | padding: 5px; 240 | overflow: auto; 241 | max-height: 400px; 242 | word-wrap: normal; 243 | } 244 | 245 | .reveal .code-wrapper { 246 | white-space: normal; 247 | } 248 | 249 | .reveal .code-wrapper code { 250 | white-space: pre; 251 | } 252 | 253 | .reveal table { 254 | margin: auto; 255 | border-collapse: collapse; 256 | border-spacing: 0; 257 | } 258 | 259 | .reveal table th { 260 | font-weight: bold; 261 | } 262 | 263 | .reveal table th, 264 | .reveal table td { 265 | text-align: left; 266 | padding: 0.2em 0.5em 0.2em 0.5em; 267 | border-bottom: 1px solid; 268 | } 269 | 270 | .reveal table th[align="center"], 271 | .reveal table td[align="center"] { 272 | text-align: center; 273 | } 274 | 275 | .reveal table th[align="right"], 276 | .reveal table td[align="right"] { 277 | text-align: right; 278 | } 279 | 280 | .reveal table tbody tr:last-child th, 281 | .reveal table tbody tr:last-child td { 282 | border-bottom: none; 283 | } 284 | 285 | .reveal sup { 286 | vertical-align: super; 287 | font-size: smaller; 288 | } 289 | 290 | .reveal sub { 291 | vertical-align: sub; 292 | font-size: smaller; 293 | } 294 | 295 | .reveal small { 296 | display: inline-block; 297 | font-size: 0.6em; 298 | line-height: 1.2em; 299 | vertical-align: top; 300 | } 301 | 302 | .reveal small * { 303 | vertical-align: top; 304 | } 305 | 306 | .reveal img { 307 | margin: var(--r-block-margin) 0; 308 | } 309 | 310 | /********************************************* 311 | * LINKS 312 | *********************************************/ 313 | .reveal a { 314 | color: var(--r-link-color); 315 | text-decoration: none; 316 | transition: color .15s ease; 317 | } 318 | 319 | .reveal a:hover { 320 | color: var(--r-link-color-hover); 321 | text-shadow: none; 322 | border: none; 323 | } 324 | 325 | .reveal .roll span:after { 326 | color: #fff; 327 | background: var(--r-link-color-dark); 328 | } 329 | 330 | /********************************************* 331 | * Frame helper 332 | *********************************************/ 333 | .reveal .r-frame { 334 | border: 4px solid var(--r-main-color); 335 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 336 | } 337 | 338 | .reveal a .r-frame { 339 | transition: all .15s linear; 340 | } 341 | 342 | .reveal a:hover .r-frame { 343 | border-color: var(--r-link-color); 344 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); 345 | } 346 | 347 | /********************************************* 348 | * NAVIGATION CONTROLS 349 | *********************************************/ 350 | .reveal .controls { 351 | color: var(--r-link-color); 352 | } 353 | 354 | /********************************************* 355 | * PROGRESS BAR 356 | *********************************************/ 357 | .reveal .progress { 358 | background: rgba(0, 0, 0, 0.2); 359 | color: var(--r-link-color); 360 | } 361 | 362 | /********************************************* 363 | * PRINT BACKGROUND 364 | *********************************************/ 365 | @media print { 366 | .backgrounds { 367 | background-color: var(--r-background-color); 368 | } 369 | } 370 | /********************************************* 371 | * PROGRESS BAR (override) 372 | *********************************************/ 373 | .reveal .progress { 374 | color: var(--r-progress-color); 375 | } 376 | 377 | section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { 378 | color: #141414; 379 | } 380 | 381 | /********************************************* 382 | * BORDER 383 | *********************************************/ 384 | .reveal { 385 | box-sizing: border-box; 386 | border: 30px solid #FF4081; 387 | } 388 | @media (max-width: 840px) { 389 | .reveal { 390 | border-width: 15px; 391 | } 392 | } 393 | 394 | body.reveal-no-slide-border .reveal, 395 | .reveal.has-dark-background { 396 | border: none; 397 | } 398 | body.reveal-no-slide-border .reveal .controls, 399 | .reveal.has-dark-background .controls { 400 | right: 42px; 401 | bottom: 42px; 402 | } 403 | @media (max-width: 840px) { 404 | body.reveal-no-slide-border .reveal .controls, 405 | .reveal.has-dark-background .controls { 406 | right: 27px; 407 | bottom: 27px; 408 | } 409 | } 410 | 411 | .reveal .progress { 412 | z-index: 1000; 413 | } 414 | -------------------------------------------------------------------------------- /slides/assets/img/workflow-activity-relations.orig.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | Workflow DefinitionWorkflowTypeWorkflow ExecutionActivity DefinitionActivityTypeActivity ExecutionRefers toAwaitsIdentifiesIdentifiesRefers toRefers to -------------------------------------------------------------------------------- /slides/assets/img/old/drawio.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /slides/assets/img/old/interaction-with-queues.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Place order
Place order
User
User
Shop
Shop
Warehouse
Warehouse
Payment
Payment
Notification
Notification
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /slides/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Temporal intro workshop 3 | theme: sky 4 | highlightTheme: monokai 5 | separator: "\n---\n" 6 | verticalSeparator: "\n--\n" 7 | revealOptions: 8 | controls: true 9 | progress: true 10 | history: true 11 | hash: true 12 | transition: "slide" 13 | slideNumber: false 14 | --- 15 | 16 | ![Temporal logo](assets/img/temporal-logo.svg) 17 | ## Intro workshop 18 | 19 | --- 20 | 21 | # Agenda 22 | 23 | - History 24 | - Temporal 25 | - Workshop 26 | 27 | --- 28 | 29 | # History 30 | 31 | -- 32 | 33 | ## Problem 34 | 35 | Long running, complex interactions... 36 | 37 | ...in distributed systems... 38 | 39 | ...with transactional characteristics. 40 | 41 | -- 42 | 43 | ## Example 44 | 45 | Interaction example 46 | 47 | -- 48 | 49 | ## Naive implementation 50 | 51 | ```go 52 | func placeOrder(o Order) { 53 | warehouse.ReserveItems(o.Items) 54 | 55 | payment.ProcessPayment(o) 56 | 57 | notifyBuyer(o, OrderPlaced) 58 | 59 | saveOrder(o) 60 | } 61 | ``` 62 | 63 | -- 64 | 65 | ## Failure 66 | 67 | Interaction failure 68 | 69 | -- 70 | 71 | ## Failure modes 72 | 73 | ```go 74 | func placeOrder(o Order) { 75 | reserved, err := warehouse.ReserveItems(o.Items) 76 | if err != nil { // Warehouse service unavailable 77 | // Retry? 78 | } 79 | 80 | if !reserved { // Business error requiring user intervention 81 | notifySeller(o, OrderFailed) 82 | notifyBuyer(o, OrderFailed) 83 | 84 | return 85 | } 86 | 87 | // ... 88 | } 89 | ``` 90 | 91 | -- 92 | 93 | ## Queues FTW 94 | 95 | Interaction with queues 96 | 97 | -- 98 | 99 | ## Event choreography 100 | 101 | | **Service** | **Input** | **Output** | 102 | |--------------------------------------------------|----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| 103 | | Shop | `PlaceOrder`\* | `OrderPlaced` | 104 | | Warehouse | `OrderPlaced` | `ItemsReserved` / `ItemReservationFailed` | 105 | | Payment | `ItemsReserved` | `OrderPaid` / `PaymentFailed` | 106 | | Notification | `OrderPaid` / `PaymentFailed` / `ItemReservationFailed` | `BuyerNotified` | 107 | 108 | 109 | -- 110 | 111 | ## Failure with queues 112 | 113 | Interaction failure with queues 114 | 115 | -- 116 | 117 | ## Status? 118 | 119 | - **User:** what's going on with my order? 120 | - **Seller:** incoming orders? 121 | - **Developer:** debug a single execution? 122 | 123 | Interaction with queues status 124 | 125 | -- 126 | 127 | ## Cancellation? 128 | 129 | - **User:** I changed my mind, give me my money back! 130 | - **Payment failed:** User failed to pay the order, cancel it! 131 | 132 | Interaction with queues cancellation 133 | 134 | -- 135 | 136 | ## State! 137 | 138 | Interaction state 139 | 140 | -- 141 | 142 | ## Reinventing wheels 143 | 144 | Reinventing wheels 145 | 146 | *Source: [StackOverflow Blog](https://stackoverflow.blog/2020/11/23/the-macro-problem-with-microservices/)* 147 | 148 | 149 | -- 150 | 151 | ## Lessons learnt 152 | 153 | - No *one size fits all* solution (yet) 154 | - Fragile systems 155 | - Lack of orchestration 156 | 157 | -- 158 | 159 | ## Lack of orchestration 160 | 161 | - Fractured business processes 162 | - Tight coupling between components 163 | - Cancellations? 164 | - Compensating actions? 165 | - Additional interactions? 166 | - Troubleshooting? 167 | 168 | -- 169 | 170 | ## We want this 171 | 172 | ```go 173 | func placeOrder(o Order) { 174 | warehouse.ReserveItems(o.Items) 175 | 176 | payment.ProcessPayment(o) 177 | 178 | notifyBuyer(o, OrderPlaced) 179 | 180 | saveOrder(o) 181 | } 182 | ``` 183 | 184 | --- 185 | 186 | # Temporal 187 | 188 | -- 189 | 190 | ## What is Temporal? 191 | 192 | - Temporal service 193 | - Temporal SDK *(insert your programming language here)* 194 | 195 | -- 196 | 197 | ## Orchestration as code 198 | 199 | - Write business logic as plain code 200 | - Orchestration framework 201 | - Durability and reliability out-of-the-box 202 | 203 | -- 204 | 205 | ## Concepts 206 | 207 | - Workflow 208 | - Activity 209 | - Worker 210 | 211 | _[Documentation](https://docs.temporal.io/concepts)_ 212 | 213 | -- 214 | 215 | ## Workflow 216 | 217 | - Definition 218 | - Type 219 | - Execution 220 | 221 | _[Documentation](https://docs.temporal.io/workflows)_ 222 | 223 | -- 224 | 225 | ## Workflow Definition 226 | 227 | - aka. _Workflow Function_ 228 | - Encapsulates business logic 229 | - Required to be **deterministic** 230 | - Implemented in the _Worker_ 231 | 232 | _[Documentation](https://docs.temporal.io/workflows#workflow-definition)_ 233 | 234 | -- 235 | 236 | ## Determinism 237 | 238 | > Output is based entirely on the input. 239 | 240 | ```go 241 | func add(a, b int) int { 242 | return a + b 243 | } 244 | ``` 245 | 246 | ```go 247 | func add(a, b int) int { 248 | // This is not deterministic 249 | resp := http.Get(fmt.Sprintf("https://add.com/%d/%d", a, b)) 250 | 251 | return decodeBody(resp.Body) 252 | } 253 | ``` 254 | 255 | -- 256 | 257 | ## Workflow Type 258 | 259 | Identifies a _Workflow Definition_ (in the scope of a _Task Queue_) 260 | 261 | _[Documentation](https://docs.temporal.io/workflows#workflow-type)_ 262 | 263 | -- 264 | 265 | ## Workflow Execution 266 | 267 | - Durable and reliable execution of a _Workflow Definition_ 268 | - Runs once to completion 269 | - Executed by the _Worker_ 270 | 271 | _[Documentation](https://docs.temporal.io/workflows#workflow-execution)_ 272 | 273 | -- 274 | 275 | ## Activity 276 | 277 | - Definition 278 | - Type 279 | - Execution 280 | 281 | _[Documentation](https://docs.temporal.io/activities)_ 282 | 283 | -- 284 | 285 | ## Activity Definition 286 | 287 | - aka. _Activity Function_ 288 | - Building blocks for _Workflow( Definition)s_ 289 | - No restrictions on the code (ie. can be non-deterministic) 290 | - Asynchronously executed 291 | - Generally **idempotent** 292 | 293 | _[Documentation](https://docs.temporal.io/activities#activity-definition)_ 294 | 295 | -- 296 | 297 | ## Idempotence 298 | 299 | > Applying an operation multiple times does not change the result beyond the initial application. 300 | 301 | -- 302 | 303 | ## Activity Type 304 | 305 | Identifies an _Activity Definition_ (in the scope of a _Task Queue_) 306 | 307 | _[Documentation](https://docs.temporal.io/activities#activity-type)_ 308 | 309 | -- 310 | 311 | ## Activity Execution 312 | 313 | - Execution of an _Activity Definition_ 314 | - Can timeout 315 | - Can be retried 316 | - At least once execution guarantee 317 | - Runs to completion or exhausts timeouts/retries 318 | - Executed by the _Worker_ 319 | 320 | _[Documentation](https://docs.temporal.io/activities#activity-execution)_ 321 | 322 | -- 323 | 324 | Workflow activity relations 325 | 326 | -- 327 | 328 | ## Example 329 | 330 | ```go 331 | // workflow type: "placeOrder" 332 | func placeOrder(o Order) { 333 | for _, item := range o.Items { 334 | err := temporal.ExecuteActivity("warehouse.ReserveItem", item) 335 | // handle error 336 | } 337 | 338 | // ... 339 | } 340 | 341 | // activity type: "warehouse.ReserveItem" 342 | func reserveItem(item OrderItem) error { 343 | if !items.IsAvailable(item.Quantity) { return ErrItemNotAvailable } 344 | 345 | // .. 346 | 347 | return nil 348 | } 349 | ``` 350 | -- 351 | 352 | ## Worker 353 | 354 | - Implemented and operated by the user 355 | - Executes _Workflows_ and _Activities_ 356 | - Listens to _Task Queues_ 357 | 358 | _[Documentation](https://docs.temporal.io/workers)_ 359 | 360 | -- 361 | 362 | 363 | 364 | Worker queues 365 | 366 | -- 367 | 368 | Temporal highlevel 369 | 370 | -- 371 | 372 | ## Other notable concepts 373 | 374 | - [**Namespace**](https://docs.temporal.io/namespaces): unit of isolation and replication domain (analogous to a database) 375 | - [**Task Queue**](https://docs.temporal.io/tasks#task-queue): routing mechanism to different kinds of _Workers_ 376 | 377 | --- 378 | 379 | # Preparation 380 | 381 | -- 382 | 383 | ## Prepare your environment 384 | 385 | 1. Git, Make, etc. 386 | 2. Make sure you have the latest [Go](https://golang.org/) and [Docker](https://www.docker.com/get-started) installed 387 | 388 | -- 389 | 390 | ## Setup the project 391 | 392 | Checkout the following repository: 393 | 394 | [`https://github.com/sagikazarmark/temporal-intro-workshop`](https://github.com/sagikazarmark/temporal-intro-workshop) 395 | 396 | Follow the instructions in the README. 397 | 398 | -- 399 | 400 | ## Check the tools 401 | 402 | - UI: http://127.0.0.1:8080 403 | - CLI: `make shell` 404 | 405 | --- 406 | 407 | # Workflows 408 | 409 | -- 410 | 411 | - Write business logic as code 412 | - **MUST** be deterministic 413 | - Parameters **MUST** be serializable 414 | 415 | -- 416 | 417 | ## Reminder 418 | 419 | ```go 420 | func placeOrder(o Order) { 421 | warehouse.ReserveItems(o.Items) 422 | 423 | payment.ProcessPayment(o) 424 | 425 | notifyBuyer(o, OrderPlaced) 426 | 427 | saveOrder(o) 428 | } 429 | ``` 430 | 431 | -- 432 | 433 | ## Example 1 434 | 435 | Simple workflow function. 436 | 437 | -- 438 | 439 | ## Example 2 440 | 441 | - Input: number (integer) 442 | - Output: factorial of the number 443 | 444 | -- 445 | 446 | ## Example 3 447 | 448 | Writing unit tests for a workflow. 449 | 450 | -- 451 | 452 | ## Example 4 453 | 454 | Write a test for [Example 2](#/5/4). 455 | 456 | -- 457 | 458 | ## Determinism 459 | 460 | > Output value is based entirely on the input. 461 | 462 | ```go 463 | func add(a, b int) int { 464 | return a + b 465 | } 466 | ``` 467 | 468 | ```go 469 | func add(a, b int) int { 470 | // This is not deterministic 471 | resp := http.Get(fmt.Sprintf("https://add.com/%d/%d", a, b)) 472 | 473 | return decodeBody(resp.Body) 474 | } 475 | ``` 476 | 477 | -- 478 | 479 | ## Forbidden in Go 480 | 481 | - Time functions `time.Now`, `time.Sleep` 482 | - Goroutines 483 | - Channels and selects 484 | - Iterating over maps 485 | 486 | Use [deterministic wrappers](https://docs.temporal.io/application-development/foundations#workflow-logic-requirements) instead. 487 | 488 | -- 489 | 490 | ## Forbidden in general 491 | 492 | - Accessing external systems (usually over network) 493 | - Accessing the filesystem 494 | - Generating random values 495 | 496 | -- 497 | 498 | ## Example 5 499 | 500 | Side-effects in a workflow. 501 | 502 | -- 503 | 504 | ## Example 6 505 | 506 | Communicating with a running workflow. 507 | 508 | -- 509 | 510 | ## Example 7 511 | 512 | Write a query handler (for your workflow from examples 2, 4) that returns the current result in the loop. 513 | 514 | **Tip:** Add sleep at the beginning of the loop so you have time to query it using the CLI. 515 | 516 | -- 517 | 518 | ## Log-based execution 519 | 520 | - Record a history of events 521 | - _Replay_ events to get to the current state 522 | 523 | -- 524 | 525 | Temporal workflow task 526 | 527 | -- 528 | 529 | Temporal workflow task loop 530 | 531 | -- 532 | 533 | ## Workflow replay 534 | 535 | ```go [|2|4|6] 536 | func Workflow(ctx workflow.Context) error { 537 | foo := workflow.ExecuteActivity(ctx, "foo") 538 | 539 | workflow.Sleep(ctx, 10 * time.Second) 540 | 541 | workflow.ExecuteActivity(ctx, "bar", foo) 542 | } 543 | 544 | ``` 545 | 546 | -- 547 | 548 | ## Recap 549 | 550 | - Workflows implement business logic 551 | - They **MUST** be deterministic (due to log-based execution) 552 | 553 | -- 554 | 555 | ## Undiscussed topics 556 | 557 | - Child workflows 558 | - Versioning 559 | - Reset / Cancellation 560 | - Search attributes 561 | - Sessions 562 | - Cron 563 | - ... 564 | 565 | _[Documentation: Workflows](https://docs.temporal.io/workflows)_ 566 | 567 | _[Documentation: Workflow development in Go](https://docs.temporal.io/go/develop-workflows)_ 568 | 569 | --- 570 | 571 | # Activities 572 | 573 | -- 574 | 575 | - Single task within a workflow 576 | - Can be non-deterministic 577 | - API calls, database access, etc 578 | - Just regular code with regular tests 579 | 580 | -- 581 | 582 | ## Example 8 583 | 584 | Simple activity function. 585 | 586 | -- 587 | 588 | ## Example 9 589 | 590 | Activity retry. 591 | 592 | -- 593 | 594 | ## Example 10 595 | 596 | Rewrite the factorial calculation (based on examples 2, 4, 7) as an activity (with retries and timeouts): 597 | 598 | - It should always fail on the first attempt 599 | - Rewrite the tests so they continue to pass 600 | 601 | -- 602 | 603 | ## Undiscussed topics 604 | 605 | - Cancellation 606 | - Async completion 607 | - Local activities 608 | 609 | _[Documentation: Activities](https://docs.temporal.io/activities)_ 610 | 611 | _[Documentation: Activity development in Go](https://docs.temporal.io/go/develop-activities)_ 612 | 613 | --- 614 | 615 | # Further reading 616 | 617 | -- 618 | 619 | https://stackoverflow.blog/2020/11/23/the-macro-problem-with-microservices/ 620 | 621 | -- 622 | 623 | [Documentation: Concepts](https://docs.temporal.io/concepts) 624 | 625 | [Documentation: Glossary](https://docs.temporal.io/glossary) 626 | 627 | -- 628 | 629 | [Documentation: Developer's guide](https://docs.temporal.io/application-development/) 630 | 631 | -- 632 | 633 | [Temporal learning materials](https://learn.temporal.io/) 634 | 635 | --- 636 | 637 | # The End 638 | -------------------------------------------------------------------------------- /slides/assets/img/old/interaction-state.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Place order
Place order
User
User
Shop
Shop
Warehouse
Warehouse
Payment
Payment
Notification
Notification
Database
Database
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 5 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 6 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 7 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 8 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 9 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 10 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 11 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 12 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 13 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 14 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 19 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 20 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 21 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 22 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 23 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 24 | github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= 25 | github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= 26 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 27 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 28 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 29 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 30 | github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 31 | github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= 32 | github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= 33 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 34 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 35 | github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= 36 | github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= 37 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 38 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 39 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 40 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 41 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 42 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 43 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 44 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 45 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 46 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 47 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 48 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 49 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 50 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 51 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 52 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 53 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 54 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 55 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 56 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 57 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 58 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 59 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 60 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 61 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 62 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 63 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 64 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 65 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 66 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 67 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= 68 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= 69 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 70 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 71 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 72 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 73 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 74 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 75 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 76 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 77 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 78 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 79 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 80 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 81 | github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= 82 | github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 83 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 84 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 85 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 86 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 87 | github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= 88 | github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= 89 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 90 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 91 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 92 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 93 | github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= 94 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 95 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 96 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 97 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 98 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 99 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 100 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 101 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 102 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 103 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 104 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 105 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 106 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 107 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 108 | go.temporal.io/api v1.11.1-0.20220907050538-6de5285cf463 h1:ylSAQ8ldG7ZRu/0nuvcHBAvfSEZr/T4I9ZnSWe5xpWQ= 109 | go.temporal.io/api v1.11.1-0.20220907050538-6de5285cf463/go.mod h1:yZGA2AVWUri9TUol58DTosjQnQBLEMDnchA4u+v1i6E= 110 | go.temporal.io/sdk v1.17.0 h1:5zF4nBg35R7V+J/m8ke2tIWJNpiqRK2EpWZBQkVQCEc= 111 | go.temporal.io/sdk v1.17.0/go.mod h1:EybaEzZSigK4Pr0wBbP1cb4VrCEf8ZowcigT7LCgsMU= 112 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 113 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 114 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 115 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 116 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 117 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 118 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 119 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 120 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 121 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 122 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 123 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 124 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 125 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 126 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 127 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 128 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 129 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 130 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 131 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 132 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 133 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 134 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 135 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 136 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 137 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 138 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 139 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 140 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 141 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 142 | golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo= 143 | golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 144 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 145 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 146 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 147 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 148 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 149 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 150 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 151 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 152 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 153 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 154 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 155 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 156 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 157 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 158 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 159 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 160 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 161 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 162 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 163 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 164 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 165 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 166 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 167 | golang.org/x/sys v0.0.0-20220906165534-d0df966e6959 h1:qSa+Hg9oBe6UJXrznE+yYvW51V9UbyIj/nj/KpDigo8= 168 | golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 169 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 170 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 171 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 172 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 173 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 174 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 175 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 176 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 177 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= 178 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 179 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 180 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 181 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 182 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 183 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 184 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 185 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 186 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 187 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 188 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 189 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 190 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 191 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 192 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 193 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 194 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 195 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 196 | google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 197 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 198 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 199 | google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 200 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 201 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 202 | google.golang.org/genproto v0.0.0-20220902135211-223410557253 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4= 203 | google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 204 | google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 205 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 206 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 207 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 208 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 209 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 210 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 211 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 212 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 213 | google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= 214 | google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 215 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 216 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 217 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 218 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 219 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 220 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 221 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 222 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 223 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 224 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 225 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 226 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 227 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 228 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 229 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 230 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 231 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 232 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 233 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 234 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 235 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 236 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 237 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 238 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 239 | -------------------------------------------------------------------------------- /slides/assets/img/old/interaction-failure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Place order
Place order
User
User
Shop
Shop
Warehouse
Warehouse
Payment
Payment
Notification
Notification
Viewer does not support full SVG 1.1
--------------------------------------------------------------------------------