https://restate.dev/ Announcing Restate 1.0, Restate Cloud, and our Seed Funding Round Read more [66552a0c5a] Get Started QuickstartTour of Restate Company AboutCareers DocsBlogCloud [66610ca9a5][665f46412f][66610ca90f] Get Restate The simplest way to build resilient applications. As regular functions and services, in your existing infrastructure. On FaaS, K8s, servers, containers. Self-hosted or fully managed. Restate meets you where you are. > Quickstart> Open Source Restate> Restate Cloud [665bd80206] [665bd72587fda1828156231c_workflows-tab] [665bd8b9738f3b3435eb8a29_async-tab] [665bd8ba692a82f0c59e20f6_event-tab] [665bd8b9f551b8f5641dd073_micro-tab] Workflows as code Async Tasks, Timers, Schedulers Event-driven Applications MicroService Orchestration Easy solutions for common challenges Tab 1 Tab 2 Tab 3 Workflows as code Workflows as code Durable Execution ensures code runs reliably to the end, even in the presence of failures. * Failures and errors are automatically retried (unless labeled as terminal errors) * Functions can memoize the results of code blocks, and actions like RPC, in a journal. Completed steps are not re-executed during retries, but replayed from the journal. * Workflows are built with regular code and control flow, no custom DSLs needed. * Durable sleeps let code wait and suspend for up to months > Learn More JS Java export default restate.service({ name: "roleUpdate", handlers: { applyRoleUpdate: async (ctx, update) => { const { userId, role, permissions } = update; const applied = await ctx.run("apply new role", () => applyUserRole(userId, role) ); if (!applied) { return; } for (const permission of permissions) { await ctx.run("apply permission", () => applyPermission(userId, permission) ); } } } }); @Service public class RoleUpdateService { @Handler public void applyRoleUpdate(Context ctx, Update update) { boolean success = ctx.run("apply new role", BOOLEAN, () -> applyUserRole(update.getUserId(), update.getRole())); if (!success) { return; } for (String permission : update.getPermissions()) { ctx.run("apply permission", () -> applyPermission(update.getUserId(), permission)); } } } API calls and webhooks API calls and webhooks Reliably join synchronous code and async events like webhooks * Webhooks/events are persisted in Restate's log and reliably delivered to services * Persistent Promises/Futures easily join synchronous and asynchronous code paths * Durable execution ensures reliable completion, whether webhooks come after milliseconds or months, and avoid re-execution of completed steps. > Learn More JS Java const paymentSvc = restate.service({ name: "payments", handlers: { processPayment: async (ctx, request) => { const webhookPromise = ctx.awakeable(); const paymentIntent = await ctx.run("stripe call", () => createPaymentIntent({ request, metadata: { restate_callback_id: webhookPromise.id } }) ); if (paymentIntent.status === "processing") { // synchronous response inconclusive, await webhook response const paymentIntentFromWebhook = await webhookPromise.promise; return verifyPayment(paymentIntentFromWebhook); } else { return verifyPayment(paymentIntent); } }, processWebhook: async (ctx) => { const paymentIntent = verifyAndParseEvent(ctx.request()); const webhookPromiseId = paymentIntent.metadata.restate_callback_id; ctx.resolveAwakeable(webhookPromiseId, paymentIntent); } } }); @Service public class PaymentService { @Handler public void processPayment(Context ctx, PaymentRequest request) { var webhookFuture = ctx.awakeable(SERDE); var payment = ctx.run("Stripe call", SERDE, () -> submitPayment( request, Map.of("restate_callback_id", webhookFuture.id()) )); if (payment.getStatus().equals("processing")) { // synchronous response inconclusive, await webhook response var updatedPayment = webhookFuture.await(); verifyPayment(updatedPayment); } else { verifyPayment(payment); } } @Handler public void processWebhook(Context ctx) { var paymentEvent = verifyAndParseEvent(ctx.request()); String callbackId = paymentEvent.getMetadata().get("restate_callback_id"); ctx.awakeableHandle(callbackId).resolve(SERDE, paymentEvent); } } Asynchronous Tasks Asynchronous Tasks All functions invoked through Restate are executed durably and asynchronous. * Deploy async functions serverless or as containers or processes. * Call functions synchronously, async, or delayed. Re-attach and await from anywhere. * Build async patterns like fan-out, fan-in, task chains, and subtasks simply with function calls and Futures/Promises. * Use persistent timers to schedule tasks into the future. * Use fine-grained virtual queues (via virtual objects) to enforce strict task order and concurrency > Learn More JS Java // ------ service (= worker) ------ const asyncTaskService = restate.service({ name: "taskWorker", handlers: { processPayment } }); // ------ client ------ const rs = clients.connect({ url: process.env.RESTATE_URL }); const taskWorker = rs.serviceSendClient({ name: "taskWorker" }); // submit the payment task app.post('/charge/:paymentId', async (req, res) => { const taskHandle = await taskWorker.processPayment( { request: req.params }, SendOpts.from({ idempotencyKey: req.params.paymentId }) ); res.json(taskHandle); }); // await the payment task app.get('/status', async (req,res) => { const taskHandle = req.body.json(); const paymentResult = await restate.result(taskHandle); res.join(paymentResult); }); // --- start payment task --- server.createContext("/charge", httpExchange -> { PaymentRequest req = parsePaymentRequest(httpExchange); SendResponse handle = AsyncTaskServiceClient .fromIngress(RESTATE_URI) .send() .processPayment(req, idempotencyKey(req.getPaymentId())); respondJson(httpExchange, handle); }); // --- connect to payment result --- server.createContext("/status", httpExchange -> { String handle = parseToHandle(httpExchange); String response = IngressClient.defaultClient(RESTATE_URI) .invocationHandle(handle, STRING) .attach(); respond(httpExchange, response); }); Stateful Event Processing Stateful Event Processing Process events (for example from Kafka) with durable functions as event handlers and get fine-grained retries and workflow-as-code semantics. * No queue subscriptions, no manual offset management, scaling, or balancing * Deploy the event processing logic as serverless functions on FaaS * Keep exactly-once state, delay events, run multiple asynchronous steps or API calls. * Restate's queue-per-key semantics mean no more head-of-the-line waiting effects > Learn More JS Java const eventEnricher = restate.object({ name: "eventEnricher", handlers: { userEvent: async (ctx, event) => { // remember event, time box 100 ms to collect features // before emitting result ctx.set("user", event); ctx.serviceSendClient(eventEnricher, { delay: 100 }).emit(); }, featureEvent: async (ctx, featureEvent) => { // merge feature into event const userEvent = (await ctx.get("user")) ?? {}; (userEvent.features ??= []).push(featureEvent); ctx.set("user", userEvent) }, emit: async (ctx) => { emit(ctx.key, await ctx.get("user")); ctx.clearAll(); } } }) @VirtualObject public class EventEnricher { static final StateKey USER = StateKey.of("user", of(User.class)); @Handler public void userEvent(ObjectContext ctx, User event) { ctx.set(USER, event); // time box 100 ms to collect features before emitting result EventEnricherClient.fromContext(ctx, ctx.key()) .send(ofMillis(100)).emit(); } @Handler public void featureEvent(ObjectContext ctx, Feature event) { User user = ctx.get(USER).orElse(new User()); user.addFeature(event); ctx.set(USER, user); } @Handler public void emit(ObjectContext ctx) { send(ctx.key(), ctx.get(USER)); ctx.clearAll(); } } Durable Signals Durable Signals Create workflows and event handlers that reliably handle external signals, events, human input. * Use durable Promises/Futures to intuitively model signals and conditions * Create signals from RPCs, webhooks, or Kafka events * Signals and events are persisted by Restate, no need for a queue > Learn More JS Java export default workflow({ name: "verify", handlers: { run: async (ctx, { email }) => { const secret = ctx.run("generate secret", () => crypto.randomUUID() ); await ctx.run("send email", () => sendEmail({ email, secret })); const clickSecret = await ctx.promise("email.clicked"); return clickSecret == secret; }, click: (ctx, { secret }) => { ctx.promise("email.clicked").resolve(secret); }, }, }); @Workflow public class SecretVerifier { static final DurablePromiseKey EMAIL_CLICKED = DurablePromiseKey.of("email_clicked", JsonSerdes.STRING); @Workflow public boolean run(WorkflowContext ctx, Email email) { String secret = ctx.random().nextUUID().toString(); ctx.run("send email", () -> sendEmailWithLink(email, secret)); String clickSecret = ctx.promise(EMAIL_CLICKED).awaitable().await(); return clickSecret.equals(secret); } @Handler public void click(SharedWorkflowContext ctx, String secret) { ctx.promiseHandle(EMAIL_CLICKED).resolve(secret); } } Idempotency Idempotency Add idempotency to any RPC- or event handler. * Every RPC- and event handler call accepts an idempotency key * Use idempotency keys to re-attach to an ongoing invocation * Calls from within a durable execution context are automatically idempotent > Learn More JS Java const rs = restate.connect({ url: process.env.RESTATE_URL }); 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); }) server.createContext("/reserve", httpExchange -> { ReservationRequest req = parseRequest(httpExchange.getRequestBody()); // derive an idempotency key from the parameters var idempotencyOps = CallRequestOptions.DEFAULT .withIdempotency(req.getReservationId()); // add idempotency opts to the request to let the service automatically // fuse repeated requests Reservation reservation = ProductServiceClient .fromIngress(RESTATE_RUNTIME_ENDPOINT) .reserve(req.getProduct(), idempotencyOps); sendResponse(httpExchange, reservation); }); Sagas Sagas Implements robust sagas and compensation patterns: long-running transactions that undo previous actions when they need to abort and roll back. * Reliably pick up after failures to trigger compensations * Ensure compensations happen even upon failures during the compensation phase * Use standard Exception/Error mechanisms and control flow rather than complex DSLs. > Learn More JS Java async function reservation(ctx, products) { const reservations = []; try { for (const product of products) { const reservation = await ctx.run(`reserve ${product}`, () => reserve(product)); reservations.push(reservation); } } catch (error) { if (error instanceof TerminalError) { for (const reservation of reservations) { await ctx.run("undo reserve", () => cancelReservation(reservation)); } } throw error; } } @Handler public void reserveAllProducts(Context ctx, Product[] products) { final List reservations = new ArrayList<>(); try { for (Product product : products) { Reservation res = ctx.run("Reserve " + product.getId(), RESERVE_SERDE, () -> reserve(product) ); reservations.add(res); } } catch (TerminalException e) { reservations.forEach(res -> { ctx.run("Undo reservation", () -> cancelReservation(res)); }); throw e; } } State machines State machines Create consistent and scalable State Machines without databases or transactions * Run millions of State Machines that maintain state directly in the context of their handlers * State changes commit atomically with function execution, for rock-solid consistency * Single-writer semantics for a dead simple concurrency model. A virtual queue per state machine for efficiency and scalability. * State transition can be workflows with all the features from durable execution > Learn More JS Java const paymentSvc = restate.object({ name: "payments", handlers: { makePayment: async (ctx, payment) => { const paymentId = ctx.key; switch (await ctx.get("status")) { case "CANCELLED": return `${paymentId} was cancelled before`; case "SUCCESS": return `${paymentId} previously completed`; } wireFunds(payment); ctx.set("status", "SUCCESS"); ctx.set("payment", payment); }, cancelPayment: async (ctx) => { const status = await ctx.get("status"); if (status === "SUCCESS") { const payment = await ctx.get("payment"); refund(payment); } ctx.set("status", "CANCELLED"); } } }); @VirtualObject public class PaymentStateMachine { @Handler public String makePayment(ObjectContext ctx, PaymentRequest payment) { String paymentId = ctx.key(); switch (ctx.get(STATE_STATUS).orElse(NEW)) { case CANCELLED: return paymentId + " was cancelled before"; case SUCCESS: return paymentId + " was previously completed"; } wireFunds(payment); ctx.set(STATE_STATUS, SUCCESS); ctx.set(STATE_PAYMENT_REQUEST, payment); return paymentId + " was successfully processed"; } @Handler public void cancelPayment(ObjectContext ctx) { Status status = ctx.get(STATE_STATUS).orElse(NEW); if (status == SUCCESS) { PaymentRequest payment = ctx.get(STATE_PAYMENT_REQUEST).get(); refund(payment); } ctx.set(STATE_STATUS, CANCELLED); } } A simple and powerful programming model Restate provides distributed durable version of your everyday building blocks. > See how it works Durable Execution Functions/Services that handle retries, recovery, asynchrony, idempotency. Virtual Objects Persistent state directly in your objects with a simple concurrency model. Durable Promises Transparent and fault-tolerant communication across services, processes, and time. [664fdfb3d3] Single binary, no dependencies, built in Rust. A system that runs locally and on-prem just as well as in the cloud. Restate server comes as a single binary. Simple to run, simple to operate. Fully self-contained, resource-efficient, resilient, thanks to Rust's magic. [664fdfb4b4] Stellar local dev-experience What's better than a local dev server? Running the real system on your laptop or in your CI pipeline. No subtle quirks and differences between dev- and prod setups. Your Restate-powered code is just functions/services. Develop them with the tools you know and love. Works with [665bf8e63b] TypeScript / JavaScript [665bf8e6ba] Java [665bf8e63b] Kotlin Restate Cloud: The zero-infrastructure option Get a fully serverless Restate experience, managed by the developers of the system. Sign in, generate keys, point your app, go! > Get Access [66551349e3] Baked-in powerful tools to operate applications Built-in OTel tracing, a powerful CLI, and a SQL engine help you understand what is happening in your distributed application. [6666310f14] Ready to get started? > Subscribe Want to get updates on releases and other news? Copyright (c) 2024 Restate. All rights reserved. T&Cs Privacy Imprint Contact