Restate microservice orchestration

The Simplest Way to Build Resilient Applications

Giselle van Dongen
Restate is an open-source system that makes it easy to write resilient, consistent and scalable applications. Restate lets you write mission-critical applications, like payment workflows, user management, and AI agents, without worrying about resiliency. 

Restate is an open-source system that makes it easy to write resilient, consistent and scalable applications. Restate lets you write mission-critical applications, like payment workflows, user management, and AI agents, without worrying about resiliency.  All the hard stuff is abstracted away into a set of durable building blocks that you can use to write your applications.

You write regular functions and services and Restate makes them resilient, consistent and scalable

Why resiliency matters

It’s still too hard to write applications that are fully resilient. Wouldn’t it be great if you could stop worrying about all possible failure scenarios and race conditions in your applications such as charging customers twice, users withdrawing more cash than they have, and selling a single ticket multiple times? Application code is flooded with retry, recovery, and coordination logic across message queues, key-value stores, workflows, and cron jobs. This turns our applications into distributed systems that are hard to reason about and hard to debug.

A computer lets you make more mistakes faster than any invention in human history, with the possible exceptions of handguns and tequila. – Mitch Ratcliffe

Resilient code execution and communication

Restate solves this by capturing all application activity—RPC calls, timers, promises, API calls—into a single, reliable log. In case of failures, Restate retries the execution and recovers the process and all previous state. Restate automatically mitigates against a large class of infrastructure failures, race conditions, timeouts, and other inconsistencies.

Your code runs as Java applications on your existing infrastructure – whether that’s FaaS, Kubernetes, servers, or containers. The Restate SDK is embedded as a dependency.

The Restate Server which stores the execution log, sits in front of your services like a reverse proxy or Kafka broker. It’s a single binary (no extra dependencies like databases) that’s easy to deploy and operate.

Persist execution progress

The Restate SDKs provide programming constructs with built-in retries and recovery. You can work with objects, functions, and futures as if failures don’t exist – Restate can recover them anytime, anywhere.

These building blocks are made available via the Restate Context (ctx in the example). Whenever a line of code gets executed that uses the context, an event gets send to Restate to persist that this took place. When there is a failure halfway through, Restate triggers a retry, and recovers the previous results, instead of re-executing the actions.

Imagine we want to set up a list of subscriptions for a user, and create a recurring payment. With Restate this looks as follows:

@Service
public class SubscriptionService {

  @Handler
  public void add(Context ctx, SubscriptionRequest req) {
    // create an ID that's stable across retries
    var paymentId = ctx.random().nextUUID().toString();

    // use the ID to create exactly one recurring payment
    var payRef =
        ctx.run(() -> createRecurringPayment(req.creditCard(), paymentId));

    // Restate ensures all subscriptions will be created successfully
    for (String subscription : req.subscriptions()) {
      ctx.run(() -> createSubscription(req.userId(), subscription, payRef));
    }
  }
}

This code is fully resilient and will make sure every action happens without the need for retry-recovery logic, state stores, and workflow orchestrators.

Resilient RPC and messaging

Restate sits like a reverse-proxy or message broker in front of your services, proxying calls and ensuring they’re delivered and processed. It supports various communication patterns:

Calls can made over HTTP, Kafka, or with the SDK-generated clients. All calls are resilient out of the box without separate message queues, custom retry logic, or schedulers.

Idempotency for free

Restate handles deduplication of retried requests, by letting you add an idempotency token to your requests. Each request runs to completion exactly once.

Client restateClient = Client.connect(RESTATE_URL);
SubscriptionServiceClient.fromClient(restateClient)
    .send()
    .add(
        subscriptionRequest,
        CallRequestOptions.DEFAULT.withIdempotency(requestId));

Implementing sagas and distributed transactions

Restate guarantees code runs to completion, enabling you to implement resilient sagas in a try-catch block. Restate handles retries and recovery.

  1. Track compensations: As functions execute, undo operations are tracked in a list.
  2. Guaranteed rollback: When unrecoverable errors occur, Restate ensures all compensation actions run to completion.
@Service
public class SubscriptionSaga {

  @Handler
  public void add(Context ctx, SubscriptionRequest req) {

    List<Runnable> compensations = new ArrayList<>();

    try {
      var paymentId = ctx.random().nextUUID().toString();

      compensations.add(() -> removeRecurringPayment(paymentId));
      ctx.run(STRING, () -> createRecurringPayment(req.creditCard(), paymentId));

      for (String subscription : req.subscriptions()) {
        compensations.add(() -> removeSubscription(req.userId(), subscription));
        ctx.run(() -> createSubscription(req.userId(), subscription));
      }
    } catch (TerminalException e) {
      for (Runnable compensation : compensations) {
        ctx.run(() -> compensation.run());
      }
      throw e;
    }
  }
}

This lets you implement distributed transactions across multiple services and systems in simple sequential code, without having to resort to resiliency patterns like event sourcing.

Stateful entities and state machines

Restate has an embedded K/V store, which lets you implement stateful services by storing state directly in Restate.

  • Consistent state: State changes become just another type of execution progress. Restate makes sure that your state is always consistent with where your execution is at. You can implement durable state machines, stateful AI agents, or actors, without extra infrastructure.
  • Scalability, consistency, concurrency: Restate guards consistency by ensuring only one handler writes to a single state value at a time. You can scale your services without worrying about multiple writers, lost updates, race conditions, or inconsistencies.
  • Stateful serverless: Restate attaches the service’s K/V state to each request, allowing your handlers to work with local state. Your stateful functions can be deployed to serverless platforms like AWS Lambda and scale up and down as needed.
@VirtualObject
public class SubscriptionObject {

  StateKey<String> SUBSCRIPTION = StateKey.of("subscription", STRING);

  @Handler
  public void add(ObjectContext ctx, ObjectUtils.SubscriptionRequest req) {

    ctx.set(SUBSCRIPTION, "awaiting_payment");
    var paymentId = ctx.random().nextUUID().toString();
    boolean success =
        ctx.run(
            BOOLEAN,
            () -> createRecurringPayment(req.creditCard(), paymentId));

    if (!success) {
      ctx.set(SUBSCRIPTION, "payment_failed");
      return;
    }

    ctx.set(SUBSCRIPTION, "creating_subscription");
    ctx.run(() -> createSubscription(req.userId(), req.subscription()));

    ctx.set(SUBSCRIPTION, "created");
  }
}

What can you build?

Restate gives you everything you need to build resilient applications for a large variety of use cases:

  • Saga-based distributed transactions
  • Human-in-the-loop workflows
  • Durable state machines (for tracking factory equipment)
  • Consistent database interactions
  • Idempotent async payment processing
  • Stateful AI agents

Detailed observability

While traditional observability tools stop at the request boundary, Restate is aware of the code execution itself. It knows how services communicate and what the application state is. This gives it a unique position for observability.

Restate makes all the information it stores available to you via a UI, CLI, and built-in tracing capabilities. You can answer questions like

  • “Where in the call chain and at what line of code did my handler get stuck?”
  • “What step is failing with what error?”
  • “Which code path did my handler follow?”
  • “What other handlers are experiencing the same issues?”
  • “Are there still ongoing executions on this deployment or can I remove it safely?”

Try it out!

Restate is open source, so go to the documentation (docs.restate.dev) to get your first demo app running. If you have any questions, join Restate’s Discord or Slack community.

Restate is also available as a fully managed cloud service, if all you want is to use it and let us operate it.

Want to Dive Deeper?
Gisell van Dongen is speaker at JCON!
This article covers the topic of his JCON session. If you can’t attend live, the video of this session will be available after the conference – it’s worth checking out!

Total
0
Shares
Previous Post

IBM Architect Mark Stoodley Reveals JVM Optimization Techniques

Next Post

EclipseStore: The Evolution of Java Persistence

Related Posts