Skip to content

Commit

Permalink
Take all the methods out of ctx
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkleeman committed Aug 21, 2024
1 parent 6b76db0 commit 2d25302
Show file tree
Hide file tree
Showing 18 changed files with 339 additions and 423 deletions.
112 changes: 8 additions & 104 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,132 +3,36 @@ package restate
import (
"context"
"log/slog"
"time"

"github.com/restatedev/sdk-go/interfaces"
"github.com/restatedev/sdk-go/internal/futures"
"github.com/restatedev/sdk-go/internal/options"
"github.com/restatedev/sdk-go/internal/rand"
"github.com/restatedev/sdk-go/internal/state"
)

// Context is the base set of operations that all Restate handlers may perform.
// Context is passed to Restate service handlers and enables interaction with Restate
type Context interface {
RunContext

// Rand returns a random source which will give deterministic results for a given invocation
// The source wraps the stdlib rand.Rand but with some extra helper methods
// This source is not safe for use inside .Run()
Rand() *rand.Rand

// Sleep for the duration d. Can return a terminal error in the case where the invocation was cancelled mid-sleep.
Sleep(d time.Duration) error
// After is an alternative to Context.Sleep which allows you to complete other tasks concurrently
// with the sleep. This is particularly useful when combined with Context.Select to race between
// the sleep and other Selectable operations.
After(d time.Duration) interfaces.After

// Service gets a Service request client by service and method name
// Note: use module-level [Service] to deserialise return values
Service(service, method string, opts ...options.ClientOption) interfaces.Client

// Object gets an Object request client by service name, key and method name
// Note: use module-level [Object] to receive serialised values
Object(object, key, method string, opts ...options.ClientOption) interfaces.Client

// Run runs the function (fn), storing final results (including terminal errors)
// durably in the journal, or otherwise for transient errors stopping execution
// so Restate can retry the invocation. Replays will produce the same value, so
// all non-deterministic operations (eg, generating a unique ID) *must* happen
// inside Run blocks.
// Note: use module-level [Run] to get typed output values instead of providing an output pointer
Run(fn func(ctx RunContext) (any, error), output any, opts ...options.RunOption) error

// Awakeable returns a Restate awakeable; a 'promise' to a future
// value or error, that can be resolved or rejected by other services.
// Note: use module-level [Awakeable] to avoid having to pass a output pointer to Awakeable.Result()
Awakeable(options ...options.AwakeableOption) interfaces.Awakeable
// ResolveAwakeable allows an awakeable (not necessarily from this service) to be
// resolved with a particular value.
ResolveAwakeable(id string, value any, options ...options.ResolveAwakeableOption)
// ResolveAwakeable allows an awakeable (not necessarily from this service) to be
// rejected with a particular error.
RejectAwakeable(id string, reason error)

// Select returns an iterator over blocking Restate operations (sleep, call, awakeable)
// which allows you to safely run them in parallel. The Selector will store the order
// that things complete in durably inside Restate, so that on replay the same order
// can be used. This avoids non-determinism. It is *not* safe to use goroutines or channels
// outside of [Run] functions, as they do not behave deterministically.
Select(futs ...futures.Selectable) interfaces.Selector
inner() *state.Context
}

// RunContext methods are the only methods of [Context] that are safe to call from inside a .Run()
// Calling any other method inside a Run() will panic.
// RunContext is passed to [Run] closures and provides the limited set of Restate operations that are safe to use there.
type RunContext interface {
context.Context

// Log obtains a handle on a slog.Logger which already has some useful fields (invocationID and method)
// By default, this logger will not output messages if the invocation is currently replaying
// The log handler can be set with `.WithLogger()` on the server object
Log() *slog.Logger

// Request gives extra information about the request that started this invocation
Request() *Request
Request() *state.Request
}

type Request struct {
// The unique id that identifies the current function invocation. This id is guaranteed to be
// unique across invocations, but constant across reties and suspensions.
ID []byte
// Request headers - the following headers capture the original invocation headers, as provided to
// the ingress.
Headers map[string]string
// Attempt headers - the following headers are sent by the restate runtime.
// These headers are attempt specific, generated by the restate runtime uniquely for each attempt.
// These headers might contain information such as the W3C trace context, and attempt specific information.
AttemptHeaders map[string][]string
// Raw unparsed request body
Body []byte
}

// ObjectContext is an extension of [Context] which can be used in exclusive-mode Virtual Object handlers,
// ObjectContext is an extension of [Context] which is passed to exclusive-mode Virtual Object handlers.
// giving mutable access to state.
type ObjectContext interface {
Context
KeyValueReader
KeyValueWriter
ObjectSharedContext
}

// ObjectContext is an extension of [Context] which can be used in shared-mode Virtual Object handlers,
// ObjectContext is an extension of [Context] which is passed to shared-mode Virtual Object handlers,
// giving read-only access to a snapshot of state.
type ObjectSharedContext interface {
Context
KeyValueReader
}

// KeyValueReader is the set of read-only methods which can be used in all Virtual Object handlers.
type KeyValueReader interface {
// Get gets value associated with key and stores it in value
// If key does not exist, this function returns [ErrKeyNotFound]
// If the invocation was cancelled while obtaining the state (only possible if eager state is disabled),
// a cancellation error is returned.
// Note: Use GetAs generic helper function to avoid passing in a value pointer
Get(key string, value any, options ...options.GetOption) error
// Keys returns a list of all associated key
// If the invocation was cancelled while obtaining the state (only possible if eager state is disabled),
// a cancellation error is returned.
Keys() ([]string, error)
// Key retrieves the key for this virtual object invocation. This is a no-op and is
// always safe to call.
Key() string
}

// KeyValueWriter is the set of mutating methods which can be used in exclusive-mode Virtual Object handlers.
type KeyValueWriter interface {
// Set sets a value against a key, using the provided codec (defaults to JSON)
Set(key string, value any, options ...options.SetOption)
// Clear deletes a key
Clear(key string)
// ClearAll drops all stored state associated with this Object key
ClearAll()
}
16 changes: 3 additions & 13 deletions error.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package restate

import (
stderrors "errors"
"fmt"

"github.com/restatedev/sdk-go/internal/errors"
Expand Down Expand Up @@ -41,19 +40,10 @@ func TerminalErrorf(format string, a ...any) error {
// IsTerminalError checks if err is terminal - ie, that returning it in a handler or Run function will finish
// the invocation with the error as a result.
func IsTerminalError(err error) bool {
if err == nil {
return false
}
var t *errors.TerminalError
return stderrors.As(err, &t)
return errors.IsTerminalError(err)
}

// ErrorCode returns [Code] associated with error, defaulting to 500
func ErrorCode(err error) Code {
var e *errors.CodeError
if stderrors.As(err, &e) {
return e.Code
}

return 500
func ErrorCode(err error) errors.Code {
return errors.ErrorCode(err)
}
30 changes: 15 additions & 15 deletions examples/codegen/proto/helloworld_restate.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2d25302

Please sign in to comment.