Skip to content

Commit

Permalink
First collection of Restate patterns (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanEwen authored Dec 5, 2023
1 parent c337efe commit ca40202
Show file tree
Hide file tree
Showing 10 changed files with 841 additions and 0 deletions.
20 changes: 20 additions & 0 deletions typescript/patterns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Restate Sample Patterns

A collections of useful patterns in distributed applications, and how to
implement them with [Restate](https://github.com/restatedev/).
This is a continuously evolving list.

The patterns currently exist only in TypeScript/JavaScript, other languages
will be added soon.

All patterns are described in one file and have a comment block at the top
that explains them.

### List of Patterns

* [**Durable Event-based Asynchronous Communication as RPC**](src/async_calls_as_rpc.ts)
* [**Idempotency Tokens**](src/deterministic_idempotency_tokens.ts)
* [**Single-writer Concurrency**](src/single_writer_concurrency.ts)
* [**Dual Writes**](src/dual_writes.ts)
* [**Distributed Locks**](src/distributed_locks.ts)
* [**Idempotent DynamoDB Updates**](src/dynamo_db_idempotency.ts)
199 changes: 199 additions & 0 deletions typescript/patterns/package-lock.json

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

15 changes: 15 additions & 0 deletions typescript/patterns/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "restate-pattern-collection",
"version": "0.0.1",
"description": "A collection of useful patterns for distributed applications with Restate (https://github.com/restatedev/)",
"type": "commonjs",
"scripts": {
"build": "tsc --noEmitOnError"
},
"dependencies": {
"@restatedev/restate-sdk": "^0.5.1"
},
"devDependencies": {
"typescript": "^5.0.2"
}
}
64 changes: 64 additions & 0 deletions typescript/patterns/src/async_calls_as_rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as restate from "@restatedev/restate-sdk";

// ----------------- -----------------
// Durable Event-based Asynchronous Communication as RPC
// ----------------- -----------------
//
// !!! Durability of communication end-to-end, without having to deal with !!!
// !!! message queues and message sending semantics. !!!
//
// Calling asynchronously via sending events through a persistent message queue,
// is very powerful, because it ensures the call happens regardless of failures
// or temporary unavailability of caller or callee.
//
// Restate makes this a breeze: Its RPC construct is as simple as the req/resp
// RPC that you know. But it is backed by durable event channels, and the durable
// promises and durable execution make sure everything wires up correctly after
// any failure. The ability to suspend when awaiting means that the caller
// doesn't need to stay up while waiting for the callee's response.
//
// Additionally, also RPC calls from external clients to Restate-backed services
// have durability of requests (even when the deployed service/handler is down,
// the call will be delivered once the service comes back), and allow the caller
// to resume / re-connect to invocations.


// Reliably call / re-connect a call via
//
// curl -X POST http://<restate-runtime>/caller/makeCall \
// -H 'idempotency-key: ABC'
// -H 'content-type: application/json'
// -d '{"request": 12345}'
//
// can reconnect to this invocation by curl-ing with the same idempotency token again.


const caller = {

makeCall: async (ctx: restate.RpcContext, request: number) => {
// this call happens asynchronous through a durable message channel
// and completes reliably despite any caller or callee failure.
// The caller can even suspend while waiting for the callee to respond.
const result = await ctx.rpc(calleeApi).beCalled("hello " + request);

// ...
}
}

const callee = {
beCalled: async (ctx: restate.RpcContext, arg: string) => {
// this will reliably receive the call from 'caller'
}
}

const calleeService = restate.router(callee);
const calleeApi = { path: "callee" } as restate.ServiceApi<typeof calleeService>;

const callerService = restate.router(caller);
const callerApi = { path: "caller" } as restate.ServiceApi<typeof callerService>;

restate
.createServer()
.bindRouter(calleeApi.path, calleeService)
.bindRouter(callerApi.path, callerService)
.listen();
Loading

0 comments on commit ca40202

Please sign in to comment.