Skip to content

Commit

Permalink
Add a readme and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
muhamadazmy committed Mar 18, 2024
1 parent cd9eee3 commit acc362b
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 1 deletion.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Restate Go SDK

[Restate](https://restate.dev/) is a system for easily building resilient applications using *distributed durable async/await*. This repository contains the Restate SDK for writing services in **Golang**.

This SDK is an individual effort to build a golang SDK for restate runtime. The implementation is based on the service protocol documentation found [here](https://github.com/restatedev/service-protocol/blob/main/service-invocation-protocol.md) and a lot of experimentation with the protocol.

This means that it's not granted that this SDK matches exactly what `restate` has intended but it's a best effort interpretation of the docs

Since **service discovery** was not documented (or at least I could not find any documentation for it), the implementation is based on reverse engineering the TypeScript SDK.

This implementation of the SDK **ONLY** supports `dynrpc`. There is noway yet that you can define your service interface with `gRPC`

Calling other services right now is done completely by name, hence it's not very safe since you can miss up arguments list/type for example but hopefully later on we can generate stubs or use `gRPC` interfaces to define services.

## Features implemented

- [x] Log replay (resume of execution on failure)
- [x] State (set/get/clear/clear-all/keys)
- [x] Remote service call over restate runtime
- [X] Delayed execution of remote services
- [X] Sleep
- [x] Side effects
- Implementation might differ from as intended by restate since it's not documented and based on reverse engineering of the TypeScript SDK
- [ ] Awakeable

## Basic usage

Please check [example](example) for a fully working example. The example tries to implement the same exact example provided by restate official docs and TypeScript SDK but with few changes. So I recommend you start there first before trying out this example.

### How to use the example

Download and run restate [v0.8](https://github.com/restatedev/restate/releases/tag/v0.8.0)

```bash
restate-server --wipe all
```

> Generally you don't have to use `--wipe all` but that is mainly for testing to make sure you starting from clean state
In another terminal run the example

```bash
cd restate-sdk-go/example
go run .
```

In yet a third terminal do the following steps

- Add tickets to basket

```bash
curl -v localhost:8080/UserSession/addTicket \
-H 'content-type: application/json' \
-d '{"key": "azmy", "request": "ticket-1"}'

# {"response":true}
curl -v localhost:8080/UserSession/addTicket \
-H 'content-type: application/json' \
-d '{"key": "azmy", "request": "ticket-2"}'
# {"response":true}
```

Trying adding the same tickets again should return `false` since they are already reserved. If you didn't check out the tickets in 15min (if you are inpatient like me change the delay in code to make it shorter)

Finally checkout

```bash
curl localhost:8080/UserSession/checkout \
-H 'content-type: application/json' \
-d '{"key": "azmy", "request": null}'
#{"response":true}
```
4 changes: 4 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ func WithErrorCode(err error, code Code) error {

// TerminalError returns a terminal error with optional code.
// code is optional but only one code is allowed.
// By default restate will retry the invocation forever unless a terminal error
// is returned
func TerminalError(err error, code ...Code) error {
if err == nil {
return nil
Expand All @@ -206,6 +208,7 @@ func TerminalError(err error, code ...Code) error {
return err
}

// IsTerminalError checks if err is terminal
func IsTerminalError(err error) bool {
if err == nil {
return false
Expand All @@ -214,6 +217,7 @@ func IsTerminalError(err error) bool {
return errors.As(err, &t)
}

// ErrorCode returns code associated with error or UNKNOWN
func ErrorCode(err error) Code {
var e *codeError
if errors.As(err, &e) {
Expand Down
1 change: 0 additions & 1 deletion example/ticket_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const (
)

func reserve(ctx restate.Context, ticketId string, _ restate.Void) (bool, error) {
log.Info().Str("ticket", ticketId).Msg("reserving ticket")
status, err := restate.GetAs[TicketStatus](ctx, "status")
if err != nil && !errors.Is(err, restate.ErrKeyNotFound) {
return false, err
Expand Down
2 changes: 2 additions & 0 deletions example/user_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func checkout(ctx restate.Context, userId string, _ restate.Void) (bool, error)
return false, err
}

log.Info().Strs("tickets", tickets).Msg("tickets in basket")

if len(tickets) == 0 {
return false, nil
}
Expand Down
6 changes: 6 additions & 0 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Service interface {
}

type Context interface {
// Context of request.
Ctx() context.Context
// Set sets key value to bytes array. You can
// Note: Use SetAs helper function to seamlessly store
Expand Down Expand Up @@ -80,21 +81,26 @@ type Handler interface {
sealed()
}

// Router interface
type Router interface {
Keyed() bool
// Set of handlers associated with this router
Handlers() map[string]Handler
}

// UnKeyedRouter implements Router
type UnKeyedRouter struct {
handlers map[string]Handler
}

// NewUnKeyedRouter creates a new UnKeyedRouter
func NewUnKeyedRouter() *UnKeyedRouter {
return &UnKeyedRouter{
handlers: make(map[string]Handler),
}
}

// Handler registers a new handler by name
func (r *UnKeyedRouter) Handler(name string, handler *UnKeyedHandler) *UnKeyedRouter {
r.handlers[name] = handler
return r
Expand Down
1 change: 1 addition & 0 deletions server/restate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Restate struct {
routers map[string]restate.Router
}

// NewRestate creates a new instance of Restate server
func NewRestate() *Restate {
return &Restate{
routers: make(map[string]restate.Router),
Expand Down

0 comments on commit acc362b

Please sign in to comment.