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
Persistent K/V state
Reliable RPC
Microservice Orchestration with Restate
Turn functions into durable handlers with the Restate SDK.
- TypeScript
- Java
- Kotlin
- Go
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.
const roleUpdater = restate.object({name: "roleUpdate",handlers: {update: async function (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.
@Servicepublic class RoleUpdateService {@Handlerpublic void applyRoleUpdate(Context ctx, UpdateRequest update) {UserRole previousRole =ctx.run(JacksonSerdes.of(UserRole.class), () -> getCurrentRole(update.getUserId()));ctx.run(() -> tryApplyUserRole(update.getUserId(), update.getRole()));List<Permission> previousPermissions = new ArrayList<>();for (Permission permission : update.getPermissions()) {try {Permission previous =ctx.run(JacksonSerdes.of(Permission.class),() -> tryApplyPermission(update.getUserId(), permission));previousPermissions.add(previous); // remember the previous setting} catch (TerminalException err) {rollback(ctx, update.getUserId(), 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.
@Serviceclass RoleUpdateService {@Handlersuspend fun applyRoleUpdate(ctx: Context, update: UpdateRequest) {val previousRole = ctx.runBlock { getCurrentRole(update.userId) }ctx.runBlock { tryApplyUserRole(update.userId, update.role) }val previousPermissions = mutableListOf<Permission>()for (permission in update.permissions) {try {val previous: Permission = ctx.runBlock { tryApplyPermission(update.userId, permission) }previousPermissions.add(previous) // remember the previous setting} catch (err: TerminalException) {rollback(ctx, update.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.
type RoleUpdate struct{}func (RoleUpdate) Update(ctx restate.Context, update UpdateRequest) error {previousRole, err := restate.Run(ctx, func(ctx restate.RunContext) (UserRole, error) {return getCurrentRole(ctx, update.UserId)})if err != nil {return err}if _, err := restate.Run(ctx, func(ctx restate.RunContext) (bool, error) {return applyUserRole(ctx, update.UserId, update.Role)}); err != nil {return err}var previousPermissions []Permissionfor _, permission := range update.Permissions {previous, err := restate.Run(ctx, func(ctx restate.RunContext) (Permission, error) {return applyPermission(ctx, update.UserId, permission)})if err != nil {if restate.IsTerminalError(err) {if err := rollback(ctx, update.UserId, previousRole, previousPermissions); err != nil {return err}}return err}previousPermissions = append(previousPermissions, previous)}return nil}
Proxy RPC calls to other services via Restate and get durability and idempotency for free.
- TypeScript
- Java
- Kotlin
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.
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;// withClass(1:5) highlight-lineconst 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.
Client rs = Client.connect(Config.RESTATE_URL);public void reserveProduct(String productId, String reservationId) {ProductServiceClient.fromClient(rs, productId).send().reserve(CallRequestOptions.DEFAULT.withIdempotency(reservationId));}
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.
val rs = Client.connect(Config.RESTATE_URL)suspend fun reserveProduct(productId: String, reservationId: String) {ProductServiceClient.fromClient(rs, productId).send().reserve(CallRequestOptions.DEFAULT.withIdempotency(reservationId))}
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.
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.
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.