← все видео

The Simplest Way to Build Resilient Applications | Giselle van Dongen (EN)

JAVAPRO · 2025-07-19 · 44м 25с · 379 просмотров · YouTube ↗

Топики: durable-execution

🎧 Аудио

📝 Summary

model=deepseek-v4-flash · prompt=summary-v7 · 9 489→3 519 tokens · 2026-07-20 15:01:07

🎯 Главная суть

Durable execution (на примере проекта Restate) предлагает единый слой отказоустойчивости для микросервисов вместо набора разрозненных инструментов (очереди, KV-хранилища, workflow-оркестраторы). Restate перехватывает вызовы к сервису, логирует каждый шаг выполнения в durable-журнал и при сбоях восстанавливает прогресс, не повторяя уже совершённые действия. Это резко сокращает объём инфраструктурного кода и количество угловых случаев распределённых систем.


Проблема: микроархитектура без durable execution

Типичный пример — метод addSubscriptions в сервисе-реселлере Netflix/Prime/Hulu. Он создаёт recurring payment и подписки. При сбое сервера запрос теряется — требуется очередь для повторной подачи. При повторной обработке возникает дублирование платежа — нужна idempotency через KV-хранилище (например, Redis/DynamoDB). Уже для пяти строк кода понадобились очередь, key-value store, логика retry и очистка данных. Дополнительные углы: race conditions, network partitions, zombie-процессы, timeouts, падения API. В production коде таких мест десятки, и разработчики тратят усилия на «клей» между очередями, БД, workflow-оркестраторами (Camunda) и планировщиками. Наблюдаемость (observability) не сокращает сложность — лишь помогает диагностировать проблемы.


Идея durable execution: запись прогресса

Restate — это бинарник на Rust, который работает как прокси перед stateless-сервисами. Когда поступает RPC-запрос, он записывает событие старта в durable-журнал и начинает выполнение. Каждый шаг, требующий отказоустойчивости (например, ctx.run()), добавляет запись в журнал: сначала возвращаемое значение (payment ID), потом результаты вызовов API. При сбое Restate видит, что журнал не завершён, и повторно отправляет запрос сервису. Сервис при ретрае проходит по журналу: если для шага есть запись — возвращается сохранённое значение (действие не выполняется); если записи нет — шаг выполняется. Так достигается идемпотентность на уровне вызовов без ручного хранения requestID.


Демонстрация: отказ Netflix API и восстановление

Разработчик запускает Spring Boot приложение с методом add. Restate регистрирует его и через playground отправляет запрос с подпиской на Prime, Hulu, Netflix. Запрос «зависает» — в UI видно, что он в состоянии retrying. Причина: вызов Netflix возвращает исключение (имитация недоступности API). Restate повторяет с exponential backoff. Разработчик исправляет код (комментирует throw) и перезапускает сервис. При очередном ретрае сервис получает тот же запрос, но на основе журнала пропускает уже выполненные шаги (payment, Prime, Hulu) и выполняет только Netflix. Запрос завершается успешно.


Saga и компенсирующие действия

В durable execution «проталкивание вперёд» — приоритет, но бизнес-логика может потребовать отката (saga). Restate реализует saga через список компенсирующих действий. Если произошла терминальная ошибка (исключение типа TerminalException), сервис в блоке catch вызывает все накопленные компенсации в обратном порядке. Restate гарантирует, что каждая компенсация будет выполнена (при сбое — ретраи). Пример: тот же addSubscriptionSaga создал payment и подписки, но Disney не доступен. Выбрасывается TerminalException, и запускаются компенсации — удаление подписок и отмена платежа. После этого запрос помечается как завершённый (invocation исчезает из UI).


Асинхронные задачи и durable futures

Пример email-верификации при регистрации. Сервис генерирует уникальный ID, отправляет письмо с ссылкой и создаёт durable future, которая «ждёт» подтверждения от пользователя. Параллельно устанавливаются два таймера: один на 24 часа (верификация истекает), другой — с периодичностью 10 секунд для напоминаний. Все три события («пользователь кликнул», «истёк верификационный таймер», «время напоминания») соревнуются через конструкцию select (аналог Promise.race). Если приходит напоминание — сервис отправляет ещё одно письмо и ждёт снова. Если наступает таймер истечения — выбрасывается TerminalException. Если пользователь кликнул — future резолвится, процесс завершается. Durable future хранится в журнале; если сервис упадёт до клика, после подъёма он получит сохранённое состояние future и продолжит ожидание.


Долгие процессы: insurance claim processor с Kafka

Обработка страхового требования (claim): сначала создание записи в legacy-системе, затем ожидание pre-approval (циклическая проверка статуса с засыпанием), потом обновление другой системы. Такие долгие операции блокируют обычный Kafka-цикл (poll → loop). Restate решает это иначе: он сам читает из Kafka (в push-режиме) и направляет каждое сообщение отдельной инстанции сервиса. При этом гарантируется упорядоченность по ключу (per-key concurrency). В демо отправляются два события с разными userID — они обрабатываются параллельно. Если во время ожидания pre-approval сервис перезапускается, прогресс восстанавливается по журналу.


Stateful actors и state machine

Пример трекера посылок. Сервис имеет два метода: registerPackage (сохраняет конечный пункт назначения) и updateLocation (обновляет текущую локацию). Состояние хранится не во внешнем KV-хранилище, а прямо в durable-журнале Restate. Это исключает неконсистентность между кодом и состоянием. Обновления приходят как через RPC, так и через Kafka-топик — демонстрируется join событий из разных источников. Текущее состояние можно получить через отдельный handler (например, для дашборда пользователя). В UI видна история изменений локации.


Внутреннее устройство Restate

Restate — одиночный бинарник (на Rust), не требующий дополнительных СУБД (используется встроенное Raft-согласование и RoxDB). В распределённом режиме события реплицируются между нодами, а снапшоты сбрасываются в S3 для долговременной durabilility. Внутри — event loop: события приходят от ingress (RPC) или от сервиса (результаты, таймеры, обновления состояния). Event loop записывает записи в журнал, выполняет вызовы, сохраняет таймеры, обновляет состояние. Сервисы могут быть развёрнуты как угодно (Kubernete pod, AWS Lambda), общаются по HTTP. SDK: TypeScript, Java, Kotlin, Python, Go, Rust.


Устранение типовых проблем распределённых систем


Сравнение с workflow-движками (Camunda, Temporal)

Restate не требует описания статического DAG (как BPMN в Camunda). Каждый запрос строит свой recovery log динамически — нет ограничений на циклы и ветвления. Это даёт гибкость, особенно для AI-агентов, где агент сам решает, какой инструмент вызвать следующим. Restate — push-based, что даёт меньшую латентность (~миллисекунды) по сравнению с pull-моделью многих workflow-систем. Один бинарник без внешних баз данных упрощает эксплуатацию.


Ответы на вопросы

📜 Transcript

en · 6 662 слов · 100 сегментов · clean

Показать текст транскрипта
Hi everyone, welcome to this presentation on the simplest way to build resilient applications. Resiliency is not the lightest topic, so I hope you still have good energy levels on this Friday afternoon. So what we'll be talking about is a bit a new approach to how to tackle resiliency in microservices and in general applications. I am Giselle van Dong. I work for a project called Restate, which is one of the projects in the durable execution space. So we'll be talking about durable execution in general and how Restate fits in there and how it works. If you have any questions, you can use the QR code on the right top and I will answer them then after the talk. so this talk will be pretty demo heavy and i broke my wrist so i will be doing it with one hand so let's see how that goes but let's first get into a bit of context about what we will be talking so here is a small code snippet This code snippet does in broad lines the following. So imagine I'm a reseller of Netflix and Prime and Hulu subscriptions, so video streaming services, and I have in my application somewhere this method, add subscriptions, which lets me, for a request from a user, create a recurring payment and then create each of the subscriptions he asks for. So that could be Netflix and Hulu, for example. so now the first question is how can we actually make this reliable or what are the possible catches here does anybody see a few or one which wants to contribute silence Okay, so let's go a bit from top to bottom. So imagine this is a method which can be called over HTTP, a bit like an RPC call to this method. The first thing we need here is to make sure that if this service crashes due to some infrastructure failure or something, that we don't lose the requests that we were processing. So we need something like retry logic for the upstream caller or something like a queue if we want to do async communication. Now we have at least a persistent place to get our request back. But even if we process this retry, there are some difficulties that we need to make sure that we handle them correctly. How do we handle retries of the same request? We don't want our user to pay two times for the same subscription set and only get it one time. One thing we could do here, for example, is deploy a key value store where we keep track of the payment IDs that we generated and store them together with the request ID. And then if there is a crash, we go look in our key value store whether we already did this payment and what the ID was. So you see... Slowly, this is becoming already a bit more complex, right? We started with five lines of code and now we already need a queue and a key value store. We need retry logic around that payment. Probably we need something like cleanup of the data in that key value store to not make it grow out of bounds. And even then, we're actually only still solving the issues of these few lines of code and not yet looking at the full picture of how this interacts with the rest of our system. There are so many more ways in which these things actually can go wrong. For example, things like race conditions or network partitions, zombie processes, but also the slightly more common things like just timeouts or an API being down. So who of you has encountered any of these things already in their life as a developer? Okay, at least somehow it's a relevant talk, it seems. If you look at ways to solve this, often it involves a lot of different solutions for resilience of different parts of the system. So a few things you have there are, as I already showed, key value stores to store application state, maybe something like Amazon DynamoDB or Redis, message queues for asynchronous communication like Kafka and SQS. Also more complex systems like for example workflow orchestrators. Maybe you need to deploy something like Kamunda to actually track where in execution you are, what you need to retry and what already was successful. And then the last thing listed here is schedulers. So maybe I want to schedule something for later on and I want to be sure that this gets executed no matter what. And so what you see in code bases is that a lot of the code you're writing is glue between all these different solutions for resiliency of different parts of your system. And even then, it's pretty hard to make sure you cover all the corner cases. You can always end up with a new corner case you didn't think about it and then just send a voucher to the customer and apologize. So what you end up with is architectures like this that are hard to keep an overview of and have so many different things in different places that you're actually not anymore really sure where the problem is exactly so one thing you see here is that to solve this issue of losing overview of what actually is happening in the system and when something goes wrong, how everything ties together, is to use observability tooling. And it's pretty interesting to see the amount of new tools and startups in the observability space in the last 10 years, more or less, since we started switching more and more to microservices. And I think one of the reasons for this is just that our systems have become fragmented and quite complex to actually handle all these possible ways in which distributed systems can fail. So I will not be talking about something that replaces all of this. This is more like the context of what we got ourselves into. But what I'll be talking about is definitely something that can help reduce the complexity and gives you a bit more of a trustworthy basis to build your applications on top of. So what are we doing today? As you all know, you have some clients, maybe they send some HTTP calls, you have your backend code and your backend code interacts with maybe a database and some APIs. So what happens on a failure is that when this backend code or the service running that runs the backend code disappears, you lose everything that was in it. You lose the state, the partial progress of what it was working on and handling that resiliency is fully up. to the developer to actually tackle. So restate and durable execution as a whole have as a goal to give applications a extra component which takes care of resiliency, state and communication. So which takes care of actually the complexity for which we otherwise would have needed so many different solutions. So in the case of restate, the way that it looks is you have a components sitting in front of the backend, a bit like a workflow orchestrator or a message broker or a reverse proxy. It proxies requests through to the backend and the backend becomes just a stateless function doing some work and reporting its progress back to the main server. And the main server is a resilient server, which will make sure that that stands the test of time. So that sounds a bit abstract and a bit like magic, so I'll be showing... quite some demos and a bit more information of how this works. So today I'll be talking about a few different use cases, which are often particularly complex to get fully resilient and right. I'll be talking about workflows and sagas, concurrent async tasks, so racing futures, state machines, agents and actors, and then finally also a bit about Kafka event processing. But before I start with workflows and sagas, I first want to show a small animation of how it works a bit under the hood because it's easier to do this way than in a demo. So let's take again the same code snippet that I showed in the beginning. So the creation of these subscriptions to a set of streaming services. So what the durable execution does is it gives code built-in retries and recovery of progress. So the way that that looks is there is here this green context, which is an SDK in my plain Java service, which now is wrapping a few of these closures or calls that I do. And so the way this works is if I have a request for this function, so an RPC call, maybe I want to subscribe to Netflix and Disney, then what happens is the server writes down that this request took place. a bit like a message broker, but it will start a log for everything that this request will do. So it will forward it to the method. Then when it starts executing, every time you see ctx.run, what happens is a new item gets added to this execution progress log. So first you generate a payment ID. That payment ID is an entry in that log, meaning that this is now persisted as if I would persisted to a database. And you do that for actually any type of code execution where you feel you don't want or you need the resiliency. So Netflix, imagine now something goes wrong. How does this then actually recover? So the connection between here the Restate server and the service drops. Restate sees that this request took place and that it never ran till completion. so it knows this still needs to be finished so it sends a retry request once the service is back up it will be able to handle that retry request and then what it does is every time you see the ctx.run it will look can i find an entry for this action in the log if it finds one it will just return whatever it finds in the log and if it doesn't find anything then it will actually execute it so for example here this first one where i generate a unique identifier i will just get the same unique identifier that i got during my last run so it has basically turned it into a deterministic action across retries So same for my payment. It will not execute it again. Then there's Netflix. And then finally it sees that Disney is not yet in the lock. So it still needs to create the subscription. And then you have a successfully executed function. So this is with a little animation a bit how it works. So I can now show some demos of the different use cases that I talked about. So let's just start with running what I talked about in the animation. So I have here the same subscription service. This is a Spring Boot application. I have here the method add, which does those three things that I talked about. I can now run this. I'll run it in debug mode, because for some of the other demo examples, I will use a few breakpoints. So this has started up. So now the next thing I need to do is start the restate server. So that's that component running in front of it. So it's a single binary. I only need to start up this thing and then that's it. And now it has a UI. So I can go to the UI to tell it where my service is actually running. So my service is running at localhost 9080. And what will then happen is Restate will ask that service which things are... running there what is hosted there and so these are the different services that i'll be showing throughout the demo the first one that i'll start with is this subscription service and so you can see which handlers are being hosted there and also what kind of request that they want you to send so i don't know if this is big enough i can make it a bit bigger so in the case of this service i need to give it a credit card list of subscriptions and a user id So once I've registered this I can now call it and Restate will proxy those requests so I can go to the service to the playground and Here send requests to my service. I will copy this over because typing is not my greatest skill today Alright, so I'll be subscribing to Prime Hulu and Netflix So if we press send then this will get stuck and the reason for that is that i put a little problem in the code. So if we go to the invocations, you can now see that this request that I did 12 seconds ago is in a state retrying. So it has issues. If I click here, I see what the issue is. So the issue is that the Netflix subscription failed because the Netflix API is down. So it's constantly retrying to do that request until it will eventually succeed. And we can also have a look at that journal. So that is what I showed in the animation on the left. That is the progress. of what was done until now so first i created a payment then i subscribed to netflix hulu or prime hulu and now i'm at next netflix and there i have the issue that the api is down so let's solve this so in my case it's actually just if subscription contains netflix then throw an exception so if i comment this out and restart my application then it will retry it And everything should end alright. So it can take a few seconds because it retries with exponential backup. So what you see is that now it invoked my service again. And now the only thing it created was this subscription for Netflix. So it didn't redo the other steps. And this is also gone now. So that's the real life example of what I showed in the animation earlier. Let's now have a look at how this relates to sagas. Does anybody have an idea of how this could relate to sagas? No. So if you look at durable execution, in essence, what you do is you try to push execution always forward. When something goes wrong, you try to recover to that point and try to push execution forward from there on. While sagas... have as a main goal that if something goes wrong you try to roll back what you did to get back to a consistent state and then maybe try again or not depending on what your business logic is there so how does that relate together so for by default what you would want to do is push execution forward and only in certain cases where your business logic demands it you could decide i don't want to retry this i actually just want to roll back to a consistent state where i was at the beginning and just forget about this so durable execution is actually something that works nicely together with sagas because rolling back becomes another form of pushing the execution forward so with Durable execution, the way you can implement sagas is by just keeping a list of what you want to undo. Here is the same example of the payments. I generate my UUID, I create a recurring payment and then the subscriptions. What do I do now in order to roll this back if there's something that I don't want to retry? I add a compensating action to my list. This will be deterministic on retries as well. This list will then be all the actions I need to undo in my catch block. So here I catch an exception called TerminalException, which is a name for an exception that I don't want to retry. So the way you tell Restate to not retry is just throwing a TerminalException and then it runs these compensations in reverse order in this case. And so you have the guarantee here that all compensations will be run no matter what. And if something there goes wrong, it will be retried. So let's try that out. Now I'll just send my request via curl because it's a bit easier. So I will send a request here to restate because that's who will proxy it. I want to send it to my subscription saga, which is just the name of my service here. And I will send it to them. method called add which is the name there so i will do the same a credit card a set of subscriptions and a user id and if i execute this it will hit my last breakpoint here which gives me a second to show what it actually did so if we go here again to the ui to see what it did we see that so it's running so it's stuck on that breakpoint and if we go to see more information about it then we see that it created the payment, then it subscribed to these services, then it tried to do the Disney subscription, which had the error that that's not available in this region. So that's a reason to say you cannot create this recurring payment because otherwise this user will be paying for something that they can't use. So roll everything back and ask the user to do something else. So then you see the different steps of removing everything again. we move this break point then and we refresh this then okay invocation is not found because it has been removed zoom out a bit so close and if i now refresh it you see that it's not there anymore so that's how sagas relate to durable execution and a bit how these kind of workflows work and what you can use it for. But everything I did until now was pretty boring. It's just wrapping functions in ctx.run. So let's have a look at some other things you can actually do with this same mechanism. So the next thing I'll show is about concurrent async tasks. and about raising futures against each other. So imagine the use case where you sign up to some online service and they tell you open up your email and click on the link to verify your email address. A way this could be implemented is by here generating some ID and a link. We send an email and then we wait for the user to click this email. So the way we implement this here is by which is actually a durable future. So a future in Java is not durable in the sense that it lives in your JVM. If your JVM crashes, you lose this future as well. It's not something persisted anywhere. So with durable execution, you can have a future as just another line in the log and you can recover it anywhere you want on another process. in another moment, like tomorrow or in one year. It's a persistent thing. So the example here, what it does is you have this durable future where we wait on a user to click the link. And then we have two timeouts. One is a verification timeout. So we give the user 24 hours to click this link. After that, it's not valid anymore. and the second one is to send a few reminders so you could imagine that this should be hours in real life you don't want to send a reminder every 10 seconds but it makes the demo a bit more interesting but so this works well for this kind of like long running processes which take maybe a day maybe even longer weeks and where you want to be able to recover this after restarts or failures so The way you can raise them against each other is by the SELECT statement. I wait for either of these to complete, and depending on which one completes, I either return that it got verified, rejected, or I send a reminder and wait again, or I just throw a terminal exception that this is the end of my attempt and that this didn't get verified. So I will run this example. send here a request to my email verification service to verify email to verify my email and then if I run this it gets stuck on me clicking the link so if we look at the logs you see that it sent an email to me and in order to resolve this I need to click this link see that here it already sent the first reminder after this 10 second timeout So just for visualization purposes, we can have again a look at what this looks like here. So we see that we send the email, then we see the sleeps getting raised against each other. So this is the long-running sleep for the verification in general. And then you see the reminders being sent every 10 seconds. So if I now actually execute this curl command, then I... I'm doing the same principle as resolving this future, and this will have disappeared. So that's the idea. If I actually... What I actually wanted to do is make the service crash before resolving it. And what you would then see is that the resolving of this future would also be persisted. And when I bring the service back up, it just gets the information that this future got resolved in the meantime. And it will continue from there on. So we've talked a bit about sagas and workflows and timers. There's more you can do with timers that is quite nice. easy to implement here is an example of a claim processor so insurance claims so i have here a method called submit which takes an insurance claim and then it interacts with some legacy systems so first it creates a claim somewhere and then it waits for that system to finally have done the pre-approval so i'm calling a get status method somewhere there, and sleeping until this gets approved. And then when it gets approved, I update another system. So it's a bit like a transaction where I first want to do one thing, then wait for X amount of time, you don't know how long that will be, and then update another system. And the thing I want to show here is that actually any of these things can also be connected to Kafka. So I don't know if anyone here uses Apache Kafka somewhere in their infrastructure. Okay, quite a few. So the way you would usually read data from Kafka is either via a Kafka consumer or use a system like, for example, Flink or Kafka Streams. And if you look at how you implement a Kafka consumer, what you do is you pull a batch of messages from the queue. You get like a list of messages back and then you loop over those messages and process them one after the other. And Kafka as a system, has partitions so you read from a specific partition and you get the batch from that specific partition but a single partition can contain multiple keys so data gets partitioned and multiple keys can end up on the same partition and when I get a batch it contains data with different keys and so one of the problems you have in this kind of applications when reading from Kafka is that if I do this inside a loop Any long-running process will block my loop, so I would need to find another way of executing this somewhere else, because otherwise reading the next event will be blocked on my claim here being pre-approved. What Restate does is it reads from the queue and it lets you just scale out the execution of all these things in parallel. I think I still have a Kafka cluster running here. I have a Kafka cluster running with a single broker. Now I will tell Restate where it can find this cluster and where it needs to send the data. The claims topic, which is my Kafka topic, will have all events forwarded to the claim processor slash submit, which is this function here. If I execute this, this now creates that subscription and as you see i don't need to implement like a kafka consumer here because that happens on the restate side and events will get pushed so this doesn't work in a pool base but it events get pushed here so let's publish a few events to this topic so here i will publish for two different users different issues that they experienced for insurance claims and I will use a Kafka console producer to put them on my claims topic. If we execute this, what we see then in the log is that these different claims are getting processed in parallel. My first claim for user 1 and for user 2 are getting processed in parallel now, whereas if they would be on the same partition and I would be processing message after message, they would be processed sequentially. This is like the same as a QPerKey. And these long-running processes, if I restart this somewhere halfway through, this will just continue exactly where it was at that point of the execution. Yeah, so it will just start the pre-approval again, waiting on it instead of asking to do it again. All right, then let's go over to the last example, which adds one more... feature or problem to the statement here so that is keeping state in applications so writing stateful entities things like state machines or durable actors or agents or also things like what i have here which is a package tracker so what i want to do is i want to have a state machine of my delivery i have one method called register package which lets me store some data in a key value state so this key value state now doesn't live in a key value store but it's just another thing i do in my code so it just becomes another line in my log a state update is not anymore a different system than i'm talking to it's 100% consistent with where code is, so you cannot get into situations where your state is inconsistent with what your code is actually doing right now. So after a crash, it will know whether or not the state got changed, and it will set it again to the same thing. So I have here two functions, update location, we'll get data over Kafka, and we'll update that same package info. object in my state store. So this is effectively a join between RPC requests and Kafka events. So I can register the package that has as its final destination Amsterdam. This is like an RPC request via curl. And then the next thing I do is I just send some messages to my Kafka topic to say where the package is right now. So this is just time of where it was in the location and after I execute this I can expose this state to other interested parties in this and they can call here like another handler to retrieve that state again so maybe a dashboard where I want to visualize for my user what is the current status of this package so if I call this get package info I get this state back and One last thing there is that you can also just request what the state is from the UI and see where right now this package is. That's a bit the main features that are the main demos that I wanted to show. Let's go back to the slides. I started the presentation with this slide of all sorts of problems that can happen in applications in general. these kind of issues can be solved by durable execution as a concept which takes away a lot of complexity in systems that we have now so if we have a bit closer look things like half executed orchestration or distributed transactions gets helped by logging what has been done and not making sure that you don't do it again things like timeouts where you can use retries Duplicate requests is an example of item potency. So if you add an item potency header to a request, it automatically duplicates them to avoid that you process the same request twice. Things like zombie processes and network partitions are not an issue anymore because the ground truth of what your application does lives within Restate as a database. And if a system is cut off and cannot send progress updates anymore to the central server, then it will get cut off and it will be stopped from making progress. Corrupted state, as well as I showed in the last example, and problems like race conditions and concurrency. What does this server actually look like? It's a single binary written in Rust, so you don't need to have extra databases and queues next to it. You can run it in a distributed way with snapshots to S3, and it lets you do cloud-native failover support. It's open source, so you can just go to docs.restate.dev and try it out for yourself. So a bit about the internals, what does it look like? some way it's like a transactional stateful stream processing engine so what do i mean with that you have like two entry points for events either clients coming from the outside or updates coming from the service itself and what's inside the box is basically the big event loop which either takes events coming from the ingress so from the outside adding them to the log And the event loop then just processes, looks at the event, and based on what it is, does something else. If it's a call to another system, it does the call. If it's the setting of a timer, it stores the timer. If it's updating state, it stores it in RuxDB, etc. So that's a bit how this works. So from a bit a higher perspective, it's a distributed system in which the... Primary durability is storing snapshots to S3. The most recent events are replicated between the different restate servers for the durability in short periods of time. You can deploy services anywhere you want, so it doesn't require you to run on some specific type of worker. It can just be a JVM in a pod on Kubernetes or an AWS Lambda function. as long as it can be talked to via HTTP. So we currently have six SDKs, TypeScript, Java, Kotlin, Python, Go, and Rust. So different type of applications can benefit from different languages there. So these are currently the supported ones. The community of FreeState currently uses it for mostly these use cases. So I showed quite some of them, like the sagas, event processing. distributed transactions but also in general things like payment processing or types of use cases where you need high correctness and where you want things to be resilient and then two also interesting ones are things like AI agents or workflow interpreters because the big difference between durable execution and workflows as we traditionally know them is that most of traditional workflow orchestrators let you define a static deck and basically events get pushed through this deck What Restate does is every event builds up his own recovery log. So there's no static definition of what that event can do. It can go anywhere where it wants. So in the case of AI agents, the agent could decide to let it go to one tool execution to another one. And it will just be a new line in the log, whatever decision it took. The final type of use cases, which may be interesting, is control plane. So any use case where you have a lot of retries, long running, processes that which we try a lot until it finally succeeds these kind of things so that was it I hope you enjoyed it if you have questions I will have a look if someone asks any there and otherwise you can scan the QR code and check out the project we have a slack and discord server community channel where we are super active if you have any questions you can always show up there What if the restate server dies? So restate is a distributed system, so you can deploy several instances of it. So in most cases, never dies completely because the other instances would still be there. If it loses the majority of the servers and isn't able to make consensus anymore, then it will just... block there but so in a distributed setup or a multi-region setup you shouldn't run into this situation too often so yes it has a consensus algorithm at its core and based on that consensus algorithm it replicates events to the other servers that are running what happens if there is an error with the execution of the rollback of the saga so the same happens as if there is an error with any other part of the execution so it will just keep on retrying that specific step in the rollback until it eventually succeeds and it will keep retrying forever with exponential backoff how will the reference to the already executed steps work if there was a change in the code so that's like a common question is how to do versioning in combination with this so the way that we approach that is that we assume a endpoint is immutable so if you want to deploy a new version you deploy it with slash v2 for example you register it again and then restate will send all new invocations to the latest version all existing invocations will go to the previous version and so you need to keep the previous version around for as long as there are still invocations running on it. We have tooling which lets you see which deployments got drained. So if you go here in the UI, you can actually see which deployment is currently answering invocations here. And you could see different versions here. And then you can actually also go to this deployment and delete it when you see that it's drained. So you can also see in the invocation step, you can filter on deployment. check which one is still having invocations and when it's drained, you can remove it and then all requests will be processing on the newer one. But so one thing to keep in mind there is that if you have very long running processes, it's always preferable if you're able to split them up in multiple shorter tasks to make versioning a bit easier and not need to keep services around for months. How old is the ReState technology? ReState is developed by the same people that were behind Apache Flink. They saw Apache Flink being used sometimes mainly for... like its fault tolerance characteristics, but actually Flink is not a system which covers these kind of use cases. Flink is like a big data analytics tool where you actually also define a bit like a DAG in code and you push events through them and calculate things like windows over streaming data. They don't fit well for... this kind of more transactional use cases where I want to update one system then update another then wait for an hour and then do something else because Flink does not for example have durable side effects so you don't get any guarantees on the side effects only on the end-to-end execution Ah, how old is Restate? So Restate is around three years old, but it came out of Flink, a project called Stateful Functions they did there. And they used Stateful Functions as like a first POC version, but it was bolted on top of Flink and didn't fully nail everything. So they started a new project called Restate, which actually nails this kind of use cases for either event processing or for just general... RPC services. So it's around three years. The big launch was now one year and a half ago, I think. I think that's it. What happens when the request to the other system is successful, but the program crashes before sending it to restate? Yeah, so when you look at the code that I showed, if I do something like a payment, the way usually these APIs work is either I generate an ID myself, and this ID is then persisted and is then used here to let the other system deduplicate them. For example, Stripe allows adding an idempotency key. Many APIs allow adding some kind of idempotency key, and so what you can do is you have an idempotency key which is 100% stable across retries and then just use it with confidence. The other type of APIs that exist is where you first create some kind of proposal of what you want to do and then commit it, a bit like two-phase commit. For example, reserving a car, this kind of APIs often work like that. First, you create the proposal and then you say, OK, confirm this. So in that case, you also have these two different steps which let you make that durable. What are the advantages over workflow engines like Camunda? There are a few other options, like Kamunda, there's also Temporal. So I would say one of the cool things about Restate is that you just implement services in RPC style. You don't actually implement workflows. You have a service with a set of RPC handlers, and they can be called over HTTP. And this lets you stay a bit closer to the way you would normally implement these kind of services. you wouldn't use the workflow paradigm. So it actually just turns functions in your infrastructure into workflows. And one of the advantages here is that it's lower latency because we work in a push-based way and it's more event-driven implementation. So it changes a bit the idea of having workflows which are very slow, like most workflow. systems don't have many guarantees about when exactly your workflow starts running it can also take like 500 milliseconds or something another thing is that many of these systems also are not a single binary so they often require you to run the workflow orchestrator system and some persistent database next to it sometimes this is Cassandra sometimes Postgres like depends on which one you use but so that's a bit in general how it would relate to each other Specifically, Camunda is BPMN-like, so it also puts a bit of restrictions on what you can actually do. Whereas if your recovery log is just a set of entries below each other, you don't have any restrictions on cycles in there and taking different forks. It's a bit more flexible. I think I answered all the questions. How can we configure access permissions to the Restate UI? I guess that depends a bit on how it's deployed and everything. I think if you're really interested in that, it might be best to ask in our Slack or Discord channel, because I guess you would use the same mechanism that you use for other UIs that you have. Thank you. If there are any other questions, you can always come to me.

⚙️ Pipeline jobs

StageStatusAtt.UpdatedError
download done 1/3 2026-07-20 14:59:53
transcribe done 1/3 2026-07-20 15:00:22
summarize done 1/3 2026-07-20 15:01:07
embed done 1/3 2026-07-20 15:01:09

📄 Описание YouTube

Показать
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?

In the recent years, a new kid has appeared on the block of microservice orchestration: Durable Execution Engines. These engines persist the progress of code execution across services and time. They use this information to drive retries and recovery, and to ensure that code always runs till completion. Services that have Durable Execution enabled, can recover themselves to the exact point they were before a failure. Having this durability layer, speeds up the development of resilient applications.

In this talk, we will discuss how you can use Durable Execution to harden your applications in a few key areas: workflows, asynchronous tasks, microservice orchestration, and event processing. We will demonstrate each concept with live code examples, using Java and Restate as the Durable Execution Engine.

➜ Subscribe to JAVAPRO and never miss an update:
     https://bit.ly/YouTube-JAVAPRO-subscribe


▬ About us: ▬

JAVAPRO is the magazine for professional, real-world Java development.
Beyond the publication, JAVAPRO organises the annual developer conference JCON, which focuses on Core Java, Enterprise Java, Microservices, AI, APIs, and frameworks.

On this channel, you'll find interviews, sessions, tips & tricks, and everything a Java developer could ask for.

➜ Read JAVAPRO online or download the PDF:
      https://javapro.io

➜ Join the next live training session: 
     https://javapro.io/training/


▬ Follow us on social media: ▬

Twitter: https://x.com/JAVAPROmagazin
LinkedIn: https://www.linkedin.com/company/75505519

➜ Thanks for watching – subscribe for more Java!