Skip to content

Commit

Permalink
Apply formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanEwen committed Feb 29, 2024
1 parent 8dc0156 commit c50c3ff
Show file tree
Hide file tree
Showing 25 changed files with 323 additions and 337 deletions.
11 changes: 4 additions & 7 deletions basics/basics-typescript/src/1_durable_execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/

import * as restate from "@restatedev/restate-sdk";
import {UpdateRequest, applyUserRole, applyPermission} from "./utils/example_stubs";
import { UpdateRequest, applyUserRole, applyPermission } from "./utils/example_stubs";

// Durable execution ensures code runs to the end, even in the presence of
// failures. Use this for code that updates different systems and needs to
Expand All @@ -28,7 +28,7 @@ import {UpdateRequest, applyUserRole, applyPermission} from "./utils/example_stu
async function applyRoleUpdate(ctx: restate.Context, update: UpdateRequest) {
// parameters are durable across retries
const { userId, role, permissons } = update;

// apply a change to one system (e.g., DB upsert, API call, ...)
// the side effect persists the result with a consensus method so any
// any later code relies on a deterministic result
Expand All @@ -41,16 +41,13 @@ async function applyRoleUpdate(ctx: restate.Context, update: UpdateRequest) {
// each operation through the Restate context is journaled and recovery restores
// results of previous operations from the journal without re-executing them
for (const permission of permissons) {
await ctx.sideEffect(() => applyPermission(userId, permission));
await ctx.sideEffect(() => applyPermission(userId, permission));
}
}


// ---------------------------- deploying / running ---------------------------

const serve = restate
.endpoint()
.bindRouter("roleUpdate", restate.router({ applyRoleUpdate }))
const serve = restate.endpoint().bindRouter("roleUpdate", restate.router({ applyRoleUpdate }));

serve.listen(9080);
// or serve.lambdaHandler();
Expand Down
19 changes: 10 additions & 9 deletions basics/basics-typescript/src/2_durable_execution_compensation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
*/

import * as restate from "@restatedev/restate-sdk";
import { UpdateRequest, UserRole, Permission} from "./utils/example_stubs";
import { getCurrentRole, tryApplyUserRole, tryApplyPermission} from "./utils/example_stubs";
import { UpdateRequest, UserRole, Permission } from "./utils/example_stubs";
import { getCurrentRole, tryApplyUserRole, tryApplyPermission } from "./utils/example_stubs";

// Durable execution ensures code runs to the end, even in the presence of
// failures. That allows developers to implement error handling with common
Expand All @@ -34,7 +34,7 @@ async function applyRoleUpdate(ctx: restate.Context, update: UpdateRequest) {

// apply all permissions in order
// we collect the previous permission settings to reset if the process fails
const previousPermissions: Permission[] = []
const previousPermissions: Permission[] = [];
for (const permission of permissons) {
try {
const previous = await ctx.sideEffect(() => tryApplyPermission(userId, permission));
Expand All @@ -48,27 +48,28 @@ async function applyRoleUpdate(ctx: restate.Context, update: UpdateRequest) {
}
}

async function rollback(ctx: restate.Context, userId: string, role: UserRole, permissions: Permission[]) {
async function rollback(
ctx: restate.Context,
userId: string,
role: UserRole,
permissions: Permission[]
) {
console.log(">>> !!! ROLLING BACK CHANGES !!! <<<");
for (const prev of permissions.reverse()) {
await ctx.sideEffect(() => tryApplyPermission(userId, prev));
}
await ctx.sideEffect(() => tryApplyUserRole(userId, role));
}


// ---------------------------- deploying / running ---------------------------

const serve = restate
.endpoint()
.bindRouter("roleUpdate", restate.router({ applyRoleUpdate }))
const serve = restate.endpoint().bindRouter("roleUpdate", restate.router({ applyRoleUpdate }));

serve.listen(9080);
// or serve.lambdaHandler();
// or serve.http2Handler();
// or ...


//
// See README for details on how to start and connect Restate.
//
Expand Down
2 changes: 1 addition & 1 deletion basics/basics-typescript/src/3_workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
import * as restate from "@restatedev/restate-sdk";
import { maybeCrash } from "./utils/failures";

// ToDo
// ToDo
18 changes: 7 additions & 11 deletions basics/basics-typescript/src/4_virtual_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ import * as restate from "@restatedev/restate-sdk";
//
// Virtual Objects hold state and have methods to interact with the object.
// An object is identified by a unique id - only one object exists per id.
//
//
// Virtual Objects have their state locally accessible without requiring any database
// connection or lookup. State is exclusive, and atomically committed with the
// method execution.
//
// Virtual Objects are _Stateful Serverless_ constructs.
//
//

const greeterObject = restate.keyedRouter({

greet: async (ctx: restate.KeyedContext, name: string, request: { greeting?: string }) => {
const greeting = request?.greeting ?? "Hello";

Expand All @@ -41,22 +40,19 @@ const greeterObject = restate.keyedRouter({
count--;
}
ctx.set("count", count);
return `Dear ${name}, taking one greeting back: ${count}.`
return `Dear ${name}, taking one greeting back: ${count}.`;
},
});

// you can call this now through http directly the following way

example1: `curl localhost:8080/greeter/greet -H 'content-type: application/json' -d '{ "key": "Mary" }'`
example2: `curl localhost:8080/greeter/greet -H 'content-type: application/json' -d '{ "key": "Barack" }'`
example3: `curl localhost:8080/greeter/ungreet -H 'content-type: application/json' -d '{ "key": "Mary" }'`

example1: `curl localhost:8080/greeter/greet -H 'content-type: application/json' -d '{ "key": "Mary" }'`;
example2: `curl localhost:8080/greeter/greet -H 'content-type: application/json' -d '{ "key": "Barack" }'`;
example3: `curl localhost:8080/greeter/ungreet -H 'content-type: application/json' -d '{ "key": "Mary" }'`;

// --------------------------------- deploying --------------------------------

const serve = restate
.endpoint()
.bindKeyedRouter("greeter", greeterObject);
const serve = restate.endpoint().bindKeyedRouter("greeter", greeterObject);

serve.listen(9080);
// or serve.lambdaHandler();
Expand Down
2 changes: 1 addition & 1 deletion basics/basics-typescript/src/5_event_processing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
import * as restate from "@restatedev/restate-sdk";
import { maybeCrash } from "./utils/failures";

// ToDo
// ToDo
62 changes: 35 additions & 27 deletions basics/basics-typescript/src/utils/example_stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,50 @@
* https://github.com/restatedev/examples/blob/main/LICENSE
*/

import { applicationError, maybeCrash } from "./failures"
import { applicationError, maybeCrash } from "./failures";

export type UserRole = {
roleKey: string,
roleDescription: string
}
roleKey: string;
roleDescription: string;
};

export type Permission = {
permissionKey: string,
setting: string
}
permissionKey: string;
setting: string;
};

export type UpdateRequest = {
userId: string,
role: UserRole,
permissons: Permission[]
}
userId: string;
role: UserRole;
permissons: Permission[];
};

export async function getCurrentRole(userId: string): Promise<UserRole> {
// in this example, the previous role was always just 'viewer'
return { roleKey: "viewer", roleDescription: "User that cannot do much" }
return { roleKey: "viewer", roleDescription: "User that cannot do much" };
}

/*
* This function would call the service or API to record the new user role.
* For the sake of this example, we just fail with a random probability and
* otherwise return success.
*/
* This function would call the service or API to record the new user role.
* For the sake of this example, we just fail with a random probability and
* otherwise return success.
*/
export async function applyUserRole(userId: string, userRole: UserRole): Promise<boolean> {
maybeCrash(0.3);
console.log(`>>> Applied role ${userRole.roleKey} for user ${userId}`);
return true;
}

/*
* This function would call the service or API to apply a permission.
* For the sake of this example, we just fail with a random probability
* and otherwise return success.
*/
* This function would call the service or API to apply a permission.
* For the sake of this example, we just fail with a random probability
* and otherwise return success.
*/
export async function applyPermission(userId: string, permission: Permission): Promise<void> {
maybeCrash(0.2);
console.log(`>>> Applied permission ${permission.permissionKey}:${permission.setting} for user ${userId}`);
console.log(
`>>> Applied permission ${permission.permissionKey}:${permission.setting} for user ${userId}`
);
}

/*
Expand All @@ -72,13 +74,19 @@ export async function tryApplyUserRole(userId: string, userRole: UserRole): Prom
* For the sake of this example, we sometimes fail when applying an 'allow' permission
* and otherwise return success. Also, we sometimes crash the process.
*/
export async function tryApplyPermission(userId: string, permission: Permission): Promise<Permission> {
export async function tryApplyPermission(
userId: string,
permission: Permission
): Promise<Permission> {
const { permissionKey, setting } = permission;
maybeCrash(0.3); // sometimes infra goes away

if (setting !== "blocked") {
applicationError(0.4, `Could not apply permission ${permissionKey}:${setting} for user ${userId} due to a conflict.`);
applicationError(
0.4,
`Could not apply permission ${permissionKey}:${setting} for user ${userId} due to a conflict.`
);
}
console.log(`>>> Applied permission ${permissionKey}:${setting} for user ${userId}`);
return { permissionKey, setting: "blocked" }
}
return { permissionKey, setting: "blocked" };
}
3 changes: 1 addition & 2 deletions basics/basics-typescript/src/utils/failures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import * as restate from "@restatedev/restate-sdk";

const killProcess: boolean = Boolean(process.env.CRASH_PROCESS);


export function maybeCrash(probability: number = 0.5): void {
if (Math.random() < probability) {
console.error("A failure happened!");
Expand All @@ -29,7 +28,7 @@ export function maybeCrash(probability: number = 0.5): void {

export function applicationError(probability: number, message: string): void {
if (Math.random() < probability) {
console.error("Action failed: " + message)
console.error("Action failed: " + message);
throw new restate.TerminalError(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
* https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
*/

import * as dp from "./dp/durable_promises"
import * as dp from "./dp/durable_promises";

// The Durable Promises work like regular futures/promises, but are
// durable cross processes and failures.
//
// - A promise is uniquely identified by a name
// - An arbitrary number of awaiters (listeners) across different
// processes can await the promise.
// processes can await the promise.
// - That promise can be resolved/rejected once. If multiple attempts
// to resolve or reject are made, only the first will take effect. The
// resolution is idempotent.
//
//
// The promises are a simple but expressive way to signal across distributed
// processes:
//
Expand All @@ -29,34 +29,31 @@ import * as dp from "./dp/durable_promises"
// again after a failure/restart (await again, get the same value).
// - The result is durable once set. Completer and listeners do not need to
// be alive at the same time.
// - It does not matter whether listener or completer comes first.
// - It does not matter whether listener or completer comes first.

const promiseId = "my-durable-promise-id";
const restateUri = "http://localhost:8080";

async function run() {
// get a reference to a durable promise
const durablePromise = dp.durablePromise<string>(restateUri, promiseId);

// check the promise without blocking
const peeked = await durablePromise.peek();
console.log("Peeked value: " + peeked);

// awaiting the result
const resultProm = durablePromise.get();
resultProm
.then((val) => console.log("Result value: " + val))
.catch(console.error);

// completing the promise. if we are the first to complete, the actual result
// will be our completion value
const actualResult = await durablePromise.resolve("This promise will notify everyone");
console.log("Actual result after completing: " + actualResult);

// this will never make it, because the promise is already completed
const actualResult2 = await durablePromise.reject("Oh dear, rejected");
console.log("Actual result after rejection: " + actualResult2);
// get a reference to a durable promise
const durablePromise = dp.durablePromise<string>(restateUri, promiseId);

// check the promise without blocking
const peeked = await durablePromise.peek();
console.log("Peeked value: " + peeked);

// awaiting the result
const resultProm = durablePromise.get();
resultProm.then((val) => console.log("Result value: " + val)).catch(console.error);

// completing the promise. if we are the first to complete, the actual result
// will be our completion value
const actualResult = await durablePromise.resolve("This promise will notify everyone");
console.log("Actual result after completing: " + actualResult);

// this will never make it, because the promise is already completed
const actualResult2 = await durablePromise.reject("Oh dear, rejected");
console.log("Actual result after rejection: " + actualResult2);
}

run()
.catch((err) => console.error(err?.message));
run().catch((err) => console.error(err?.message));
Loading

0 comments on commit c50c3ff

Please sign in to comment.