![Go logo](img/cadence-logo.png) <!-- .element style="border: none; box-shadow: none; height: 100px" --> ## Intro workshop --- # Agenda - History - Brief review of Cadence - Examples --- # History -- ## Problem Potentially long running (and complex) interactions with transactional characteristics. -- ## Interaction <img src="img/interaction.svg" alt="Interaction example" class="image stretch"> -- ## Naive implementation ```go func placeOrder(o Order) { warehouse.ReserveItems(o.Items) payment.ProcessPayment(o) notifyBuyer(o, OrderPlaced) saveOrder(o) } ``` -- ## Failure <img src="img/interaction-failure.svg" alt="Interaction failure example" class="image stretch"> -- ## Queues FTW <img src="img/interaction-with-queue.svg" alt="Interaction with queue" class="image stretch"> -- ## Failure <img src="img/interaction-with-queue-failure.svg" alt="Interaction failure example" class="image stretch"> -- ## Failure modes ```go func placeOrder(o Order) { reserved, err := warehouse.ReserveItems(o.Items) if err != nil { // Warehouse service unavailable // Retry? } if !reserved { // Business error requiring user intervention notifySeller(o, OrderFailed) notifyBuyer(o, OrderFailed) return } // ... } ``` -- ## More queues <img src="img/interaction-with-more-queues.svg" alt="Interaction with more queues" class="image stretch"> -- ## Status? - User: what's going on with my order? - Seller: incoming orders? -- ## State! <img src="img/interaction-state.svg" alt="Interaction state" class="image stretch"> -- ## Reinventing wheels <img src="img/reinventing-wheels.png" alt="Reinventing wheels" class="image stretch"> *Source: [StackOverflow Blog](https://stackoverflow.blog/2020/11/23/the-macro-problem-with-microservices/)* <!-- .element style="font-size: 13px" --> <!-- .element style="margin-top: -30px;" --> -- ## Lessons learnt - No *one size fits all* solution (yet) - Fragile systems - Long running transactions? - Lack of orchestration -- ## Lack of orchestration - Changes affect a lot of components - Cancellations? - Additional interactions? - Troubleshooting? -- ## We want this ```go func placeOrder(o Order) { warehouse.ReserveItems(o.Items) payment.ProcessPayment(o) notifyBuyer(o, OrderPlaced) saveOrder(o) } ``` --- # Cadence -- <img src="img/cadence.svg" alt="Cadence" class="image stretch"> -- ## Orchestration as code - Orchestration framework - Write business logic as plain code - Tools for handling failures and timeouts -- <!-- .slide: data-transition="slide-in none-out" --> <img src="img/workflow.svg" alt="Workflow" class="image stretch"> -- <!-- .slide: data-transition="fade-in slide-out" --> <img src="img/workflow-with-activities.svg" alt="Workflow with activities" class="image stretch"> -- <img src="img/worker.svg" alt="Worker" class="image stretch"> -- <img src="img/worker-queues.svg" alt="Worker queues" class="image stretch"> -- ## Other notable concepts - **Domain**: unit of isolation (analogous to a database) - [**Task list**](https://cadenceworkflow.io/docs/concepts/task-lists/): routing mechanism to different kinds of workers --- # Preparation -- ## Prepare your computer 1. Git, Make, etc. 2. Make sure you have [Go](https://golang.org/) installed 3. Make sure [Docker](https://www.docker.com/get-started) and [docker-compose](https://docs.docker.com/compose/install/) are installed. -- ## Setup the project Checkout the following repository: [`https://github.com/sagikazarmark/cadence-intro-workshop`](https://github.com/sagikazarmark/cadence-intro-workshop) Follow the instructions in the readme. -- ## Check the tools - UI: http://127.0.0.1:8088 - CLI: `make shell` --- # Worker --- # Workflows -- - Single unit of orchestration logic - Write business logic as simple code - **MUST** be deterministic - Parameters **MUST** be serializable -- ## Reminder ```go func placeOrder(o Order) { warehouse.ReserveItems(o.Items) payment.ProcessPayment(o) notifyBuyer(o, OrderPlaced) saveOrder(o) } ``` -- ## Example 1 & 2 -- ## Example 3 - Input: list of integer numbers - Output: - total count of numbers - count of odd numbers - count of even numbers - total sum - Log every number divisible by 3 - Return an error if the list of numbers is empty -- ## Example 4 -- ## Example 5 Write a test for Example 3. -- ## Determinism > Output value is based entirely on the input. ```go func add(a, b int) int { return a + b } ``` ```go func add(a, b int) int { // This is not deterministic resp := http.Get(fmt.Sprintf("https://add.com/%d/%d", a, b)) return decodeBody(resp.Body) } ``` -- ## Forbidden in Go - Time functions `time.Now`, `time.Sleep` - Goroutines - Channels and selects - Iterating over maps Use [deterministic wrappers](https://cadenceworkflow.io/docs/go-client/create-workflows/#implementation) instead. -- ## Forbidden in general - Accessing external systems (usually over network) -- ## Example 6 & 7 -- ## Workflow determinism 1. Workflow is scheduled to run on a worker 2. Once it reaches an execution step, it stops 3. Cadence schedules the execution of that step 4. Cadence stores the result in the history 5. The workflow continues -- ## Workflow determinism ```go func Workflow(ctx workflow.Context, input Input) (Output, error) { //... encodedNumber := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { return rand.Intn(max) }) //... workflow.Sleep(ctx, 1*time.Second) //... } ``` -- ## Example 8 Write a query handler (for your workflow from examples 3, 5) that returns the current number in the loop. Add sleep at the beginning of the loop so you have time to query it using the CLI. -- ## Recap - Workflows implement business logic - Compared to queue based solutions they provide orchestration - They **MUST** be deterministic -- ## Uncovered topics - Child workflows - Signals - Versioning - Reset / Cancellation - Sessions - Cron - ... https://cadenceworkflow.io/docs/concepts/workflows/ https://cadenceworkflow.io/docs/go-client/ --- # Activities -- - Single task within a workflow - Can be non-deterministic - API calls, database access, etc - Just regular code with regular tests -- ## Example 9 & 10 -- ## Example 11 Rewrite the parity check (based on examples 3, 5, 8) as an activity (with retries and timeouts): - It should always fail on the first attempt - It should sleep for 5 seconds on the second attempt (and trigger a timeout) - Rewrite the tests so they continue to pass -- ## Uncovered topics - Cancellation - Task lists - Async completion - Local activities https://cadenceworkflow.io/docs/concepts/activities/ https://cadenceworkflow.io/docs/go-client/ --- # Read more -- https://stackoverflow.blog/2020/11/23/the-macro-problem-with-microservices/ -- https://cadenceworkflow.io/docs/concepts/ -- https://cadenceworkflow.io/docs/go-client/ -- https://github.com/uber-common/cadence-samples -- ## Prepare for the future https://docs.temporal.io --- # The End