Skip to main content

Microservice Orchestration

The simplest way to build resilient applications.

Regular functions and services, but now resilient, consistent, and scalable.
Restate serves as the resilience and durability layer for distributed apps.

Durable Execution
Restate ensures code runs to the end, even in the presence of failures. Failures are retried, with recovery of the progress that had been made.
Persistent K/V state
Persistent state directly in your objects with a simple concurrency model. State is always consistent with execution progress.
Reliable RPC
Restate makes communication resilient: request-response calls, async messages, webhooks, and delayed calls. User can latch onto in-flight idempotent requests.

Microservice Orchestration with Restate

Turn functions into durable handlers with the Restate SDK.

Distributed, durable building blocks

Work with objects, functions, and promises as if failures don’t happen. Restate makes them distributed and durable out-of-the-box.

role_updater.ts

const roleUpdater = restate.object({
name: "roleUpdate",
handlers: {
updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) {
const { userId, role, permissions: permissions } = update;
const previousRole = await ctx.run(() => getCurrentRole(userId));
await ctx.run(() => applyUserRole(userId, role));
const previousPermissions: Permission[] = [];
for (const permission of permissions) {
try {
const previous = await ctx.run(() =>
applyPermission(userId, permission));
previousPermissions.push(previous);
} catch (err) {
if (err instanceof restate.TerminalError) {
await rollback(ctx, userId, previousRole, previousPermissions);
}
throw err;
}
}
}
},
})

Scalability, concurrency, consistency

Restate sequences requests per key, if desired. Scale out without fearing issues such as race conditions, multiple writers to state, etc.

This handler updates a user’s role in set of systems. Other updates to the same user are queued and processed in order. Updates either succeed or fail, but never leave the user in an inconsistent state.

role_updater.ts

const roleUpdater = restate.object({
name: "roleUpdate",
handlers: {
updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) {
const { userId, role, permissions: permissions } = update;
const previousRole = await ctx.run(() => getCurrentRole(userId));
await ctx.run(() => applyUserRole(userId, role));
const previousPermissions: Permission[] = [];
for (const permission of permissions) {
try {
const previous = await ctx.run(() =>
applyPermission(userId, permission));
previousPermissions.push(previous);
} catch (err) {
if (err instanceof restate.TerminalError) {
await rollback(ctx, userId, previousRole, previousPermissions);
}
throw err;
}
}
}
},
})

Persist progress

Store results of interaction with external systems or non-deterministic actions. On retries, the action does not get re-executed but the previous result will be returned.

role_updater.ts

const roleUpdater = restate.object({
name: "roleUpdate",
handlers: {
updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) {
const { userId, role, permissions: permissions } = update;
const previousRole = await ctx.run(() => getCurrentRole(userId));
await ctx.run(() => applyUserRole(userId, role));
const previousPermissions: Permission[] = [];
for (const permission of permissions) {
try {
const previous = await ctx.run(() =>
applyPermission(userId, permission));
previousPermissions.push(previous);
} catch (err) {
if (err instanceof restate.TerminalError) {
await rollback(ctx, userId, previousRole, previousPermissions);
}
throw err;
}
}
}
},
})

Sagas and rollbacks

Implement compensation logic with Restate’s durable building blocks and Restate takes care of running it till completion.

Here, we update the user’s role in multiple systems. If one fails, we rollback the changes in the other systems.

role_updater.ts

const roleUpdater = restate.object({
name: "roleUpdate",
handlers: {
updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) {
const { userId, role, permissions: permissions } = update;
const previousRole = await ctx.run(() => getCurrentRole(userId));
await ctx.run(() => applyUserRole(userId, role));
const previousPermissions: Permission[] = [];
for (const permission of permissions) {
try {
const previous = await ctx.run(() =>
applyPermission(userId, permission));
previousPermissions.push(previous);
} catch (err) {
if (err instanceof restate.TerminalError) {
await rollback(ctx, userId, previousRole, previousPermissions);
}
throw err;
}
}
}
},
})

Distributed, durable building blocks

Work with objects, functions, and promises as if failures don’t happen. Restate makes them distributed and durable out-of-the-box.

Scalability, concurrency, consistency

Restate sequences requests per key, if desired. Scale out without fearing issues such as race conditions, multiple writers to state, etc.

This handler updates a user’s role in set of systems. Other updates to the same user are queued and processed in order. Updates either succeed or fail, but never leave the user in an inconsistent state.

Persist progress

Store results of interaction with external systems or non-deterministic actions. On retries, the action does not get re-executed but the previous result will be returned.

Sagas and rollbacks

Implement compensation logic with Restate’s durable building blocks and Restate takes care of running it till completion.

Here, we update the user’s role in multiple systems. If one fails, we rollback the changes in the other systems.

role_updater.ts

const roleUpdater = restate.object({
name: "roleUpdate",
handlers: {
updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) {
const { userId, role, permissions: permissions } = update;
const previousRole = await ctx.run(() => getCurrentRole(userId));
await ctx.run(() => applyUserRole(userId, role));
const previousPermissions: Permission[] = [];
for (const permission of permissions) {
try {
const previous = await ctx.run(() =>
applyPermission(userId, permission));
previousPermissions.push(previous);
} catch (err) {
if (err instanceof restate.TerminalError) {
await rollback(ctx, userId, previousRole, previousPermissions);
}
throw err;
}
}
}
},
})

Proxy RPC calls to other services via Restate and get durability and idempotency for free.

Durable RPC

Send requests to other services and Restate ensures they are delivered and processed. Requests can be done as request-response, as message, or delayed action.

product_reservation.ts

const rs = restate.connect({ url: process.env.RESTATE_URL });
const productService: ProductService = {name: "product"};
app.get('/reserve/:product/:reservationId', async (req, res) => {
const { product, reservationId } = req.params;
const products = rs.serviceClient(productService);
const reservation = await products.reserve(
product,
Opts.from({ idempotencyKey : reservationId })
);
res.json(reservation);
})

Idempotency for free

Add an idempotency token to your request and let Restate take care of deduplication for you. Duplicate requests latch on to the previous one and see the same response.

Here, we reserve a product for a user. We connect to Restate and send a reserve request with an idempotency key so retries wouldn't lead to double reservations.

product_reservation.ts

const rs = restate.connect({ url: process.env.RESTATE_URL });
const productService: ProductService = {name: "product"};
app.get('/reserve/:product/:reservationId', async (req, res) => {
const { product, reservationId } = req.params;
const products = rs.serviceClient(productService);
const reservation = await products.reserve(
product,
Opts.from({ idempotencyKey : reservationId })
);
res.json(reservation);
})

Durable RPC

Send requests to other services and Restate ensures they are delivered and processed. Requests can be done as request-response, as message, or delayed action.

Idempotency for free

Add an idempotency token to your request and let Restate take care of deduplication for you. Duplicate requests latch on to the previous one and see the same response.

Here, we reserve a product for a user. We connect to Restate and send a reserve request with an idempotency key so retries wouldn't lead to double reservations.

product_reservation.ts

const rs = restate.connect({ url: process.env.RESTATE_URL });
const productService: ProductService = {name: "product"};
app.get('/reserve/:product/:reservationId', async (req, res) => {
const { product, reservationId } = req.params;
const products = rs.serviceClient(productService);
const reservation = await products.reserve(
product,
Opts.from({ idempotencyKey : reservationId })
);
res.json(reservation);
})

Detailed Observability

Understand what is happening in your distributed applications, by using built-in tracing, the CLI, and the SQL engine.
Restate tracks communication and execution, giving it a unique position for observability.

Detailed Observability

Regular functions and services, in your existing infra

Your code runs just like before: as Java or NodeJS applications on FaaS, K8s, servers, containers.
On-prem or in the cloud. Restate meets you where you are.

Regular functions and services, in your existing infra

What you can build with Microservice Orchestration and Restate

Idempotent async payments

Issue an idempotent payment to Stripe and process the response. The payment provider either responds immediately or notifies us later via a webhook.

Keeping systems in sync / sagas

Reserve a flight, then a car, then handle the payment. If the payment fails, rollback the reservations.

Human-in-the-loop workflows

Food ordering: handle payment, wait till desired delivery time, ask restaurant to start preparation, wait for restaurant to confirm, deliver.

Durable state machines

Define state transitions in your handlers and store the current state in Restate. Use single-writer per key and concurrency limits to simplify writing robust state machines.

Wondering about a specific use case?

Let’s brainstorm together on Discord

Developer Resources

Blog post

Why we built Restate

Learn

Follow the Tour of Restate to learn more.

Need help?

Join the Restate Discord channel