Skip to content

Commit

Permalink
Update basics go
Browse files Browse the repository at this point in the history
  • Loading branch information
gvdongen committed Jan 8, 2025
1 parent f0ef9f8 commit 843de52
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 7 deletions.
20 changes: 13 additions & 7 deletions go/basics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,34 @@ about how they work and how they can be run.

### Examples

* **[Durable Execution](src/1_durable_execution.ts):** Running code cleanly
* **[Durable Execution](part0/durableexecution.go):** Running code cleanly
to the end in the presence of failures. Automatic retries and recovery of previously
finished actions. The example applies creates a subscription to movie streaming services
by first creating a recurring payment and then adding the subscriptions.

* **[Workflows](src/2_workflows.ts):** Workflows are durable execution tasks that can
* **[Building blocks](part1/buildingblocks.go):** Restate gives you a durable version
of common building blocks like queues, promises, RPC, state, and timers.
This example shows a handler which processes payment failure events from a payment provider.
The handler reminds the customer for 3 days to update their payment details, and otherwise cancels the subscriptions.

* **[Virtual Objects](part2/virtualobjects.go):** Stateful serverless objects
to manage durable consistent state and state-manipulating logic.

* **[Workflows](part3/workflows.go):** Workflows are durable execution tasks that can
be submitted and awaited. They have an identity and can be signaled and queried
through durable promises. The example is a user-signup flow that takes multiple
operations, including verifying the email address.

* **[Virtual Objects](src/3_virtual_objects.ts):** Stateful serverless objects
to manage durable consistent state and state-manipulating logic.

### Running the examples

1. [Start the Restate Server](https://docs.restate.dev/develop/local_dev) in a separate shell:
`restate-server`

2. Start the relevant example:
- `go run ./part1` for the Durable Execution example
- `go run ./part2` for the Workflows example
- `go run ./part3` for the Virtual Objects example
- The building blocks example is not runnable and more like a reference of what you can do with the API
- `go run ./part2` for the Virtual Objects example
- `go run ./part3` for the Workflows example

3. Register the example at Restate server by calling
`restate -y deployment register --force "localhost:9080"`.
Expand Down
File renamed without changes.
File renamed without changes.
92 changes: 92 additions & 0 deletions go/basics/part1/buildingblocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
restate "github.com/restatedev/sdk-go"
"log/slog"
"time"
)

type SubscriptionRequest struct {
UserID string `json:"userId"`
}

type MyService struct{}

func (MyService) Run(ctx restate.Context) error {
// 1. IDEMPOTENCY: Add an idempotency key to the header of your requests
// Restate deduplicates calls automatically. Nothing to do here.

// 2. DURABLE RPC: Call other services without manual retry and deduplication logic
// Restate persists all requests and ensures execution till completion
response, err := restate.Object[string](ctx, "SubscriptionService", "my-sub-123", "Add").
Request(SubscriptionRequest{UserID: "123"})
if err != nil {
return err
}
slog.Info("Response was: " + response)

// 3. DURABLE MESSAGING: send (delayed) messages to other services without deploying a message broker
// Restate persists the timers and triggers execution
restate.ObjectSend(ctx, "SubscriptionService", "my-sub-123", "Add").
Send(SubscriptionRequest{UserID: "123"})

// 4. DURABLE PROMISES: tracked by Restate, can be moved between processes and survive failures
// Awakeables: block the workflow until notified by another handler
awakeable := restate.Awakeable[string](ctx)
// Wait on the result
result, err := awakeable.Result()
if err != nil {
return err
}
slog.Info("Promise resolved", "result", result)
// Another process can resolve an awakeable with its ID
restate.ResolveAwakeable[string](ctx, awakeable.Id(), "hello")

// 5. DURABLE TIMERS: sleep or wait for a timeout, tracked by Restate and recoverable
// When this runs on FaaS, the handler suspends and the timer is tracked by Restate
// Example of durable recoverable sleep
// If the service crashes two seconds later, Restate will invoke it after another 3 seconds
err = restate.Sleep(ctx, 5*time.Second)
if err != nil {
return err
}
// Example of waiting on a promise (awakeable/call/...) or a timeout
timeout := restate.After(ctx, 5*time.Second)
selector := restate.Select(ctx, awakeable, timeout)
switch selector.Select() {
case awakeable:
result, err := awakeable.Result()
if err != nil {
return err
}
slog.Info("Awakeable won with result: " + result)
case timeout:
if err := timeout.Done(); err != nil {
return err
}
slog.Info("Sleep won")
}
// Example of scheduling a handler for later on
restate.ObjectSend(ctx, "SubscriptionService", "my-sub-123", "Cancel").
Send(nil, restate.WithDelay(24*time.Hour))

// 7. PERSIST RESULTS: avoid re-execution of actions on retries
// Use this for non-deterministic actions or interaction with APIs, DBs, ...
// For example, generate idempotency keys that are stable across retries
// Then use these to call other APIs and let them deduplicate
paymentDeduplicationID := restate.Rand(ctx).UUID().String()
success, err := restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
return chargeBankAccount(paymentDeduplicationID, 100)
})
if err != nil {
return err
}
slog.Info("Payment was successful: " + success)

return nil
}

func chargeBankAccount(paymentDeduplicationID string, amount int64) (string, error) {
// Implementation here
return "", nil
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 843de52

Please sign in to comment.