Spring Boot Microservice Orchestration with Temporal | Saga, Retries & Long-Running Workflows
Java Techie · 2025-08-03 · 50м 27с · 27 224 просмотров · YouTube ↗
Топики: durable-execution
🎧 Аудио
📝 Summary
model=deepseek-v4-flash · prompt=summary-v7 · 9 257→3 306 tokens · 2026-07-20 15:08:18
🎯 Главная суть
Temporal — workflow engine, который берёт на себя retry, timeout, долгоживущие процессы и компенсационные транзакции (Saga) в распределённых Spring Boot-приложениях. Разработчик пишет обычный Java-код, а движок автоматически восстанавливает состояние, повторяет упавшие шаги и откатывает уже выполненные операции при сбое.
Проблема ручного управления отказами в микросервисах
Типовой сценарий — система бронирования поездки с тремя микросервисами: flight, hotel, cab. Клиент вызывает оркестратор, который последовательно резервирует каждый сервис через REST, gRPC или Kafka. Пока всё работает — проблем нет. Но если hotel-сервис упал или cab-бронирование не ответило в таймаут, разработчику приходится вручную добавлять retry и rollback предыдущих успешных шагов (например, отменять билет, если отель отказал). Такой код сложен в тестировании, не надёжен и быстро разрастается.
Что такое Temporal и как он решает проблему
Temporal — workflow engine с открытым исходным кодом. Бизнес-логика пишется как обычный Java-код, но Temporal автоматически управляет состоянием, повторяет упавшие активности, обрабатывает таймауты и поддерживает долгоживущие workflows. Встроенная поддержка Saga (компенсаций) позволяет откатить выполненные шаги при отказе последующего. Всё это делается без сторонних библиотек вроде Resilience4j или Circuit Breaker — достаточно указать настройки через API Temporal.
Архитектура Temporal
В терминах Temporal: workflow — это процесс верхнего уровня (например, bookTrip), который вызывает activities — конкретные действия (bookFlight, bookHotel, arrangeTransport).
Внутреннее устройство:
- Приложение (workflow client) отправляет запрос Temporal Server через gRPC-стаб.
- Сервер содержит History Service (хранит метаданные и историю шагов workflow) и Matching Service (помещает задачи в task queue).
- Worker — компонент, который постоянно опрашивает task queue, забирает задания (workflow и activities) и выполняет их.
- После выполнения результат возвращается на сервер. Если произошёл сбой, Temporal автоматически делает retry или (при настройке) запускает компенсации.
Разработчику не нужно знать все внутренние компоненты — достаточно сконфигурировать WorkerFactory и gRPC-стаб.
Создание Spring Boot проекта с Temporal SDK
Проект создаётся через Spring Initializr с зависимостями spring-boot-starter-web и Lombok. Затем в pom.xml добавляется SDK: io.temporal:temporal-sdk:latest. Для Swagger UI используется springdoc-openapi-ui.
Docker Compose для локального запуска Temporal
Чтобы не устанавливать сервер вручную, используется Docker Compose с тремя сервисами:
- temporal (образ temporalio/server:latest, порт 7233 — gRPC).
- postgres (postgres:13, БД для хранения истории).
- temporal-ui (образ temporalio/ui:latest, порт 8088 — веб-интерфейс для визуализации workflows и activities).
После docker compose up сервер запущен, UI доступен по localhost:8088.
Конфигурация Temporal в Spring
В пакете config создаётся класс конфигурации:
- WorkerFactory, которому указывается task queue (в примере —
TravelTaskQueue). - gRPC-стаб (
WorkflowServiceStubs), соединяющийся с Temporal Server по адресу по умолчанию (127.0.0.1:7233). WorkflowClient, построенный на основе стаба, используется для запуска workflows.- WorkerFactory регистрирует реализацию workflow и activities (пока null, заполнится позже).
Реализация activities (TravelActivities)
Интерфейс помечается аннотацией @ActivityInterface. Методы: bookFlight(TravelRequest), bookHotel(TravelRequest), arrangeTransport(TravelRequest).
Класс реализации TravelActivitiesImpl аннотируется @Service. В демо-версии каждый метод просто логирует действие (можно заменить на REST/gRPC/Kafka-вызовы к реальным сервисам).
Пример:
log.info("Flight booked for user {} to {} on {}", request.getUserId(), request.getDestination(), request.getTravelDate());
Реализация workflow (TravelWorkflow)
Интерфейс с аннотацией @WorkflowInterface и методом bookTrip(TravelRequest). В реализации (TravelWorkflowImpl, аннотирован @Service):
- Получает объект activities через
Workflow.newActivityStub(ActivityOptions)с заданным таймаутом (например,StartToCloseTimeout10 секунд) и настройками retry. - Последовательно вызывает
bookFlight,bookHotel,arrangeTransport. - Логирует результат.
WorkerFactory в конфигурации регистрирует этот класс как реализацию workflow.
Запуск workflow через REST endpoint
Создаётся сервис-стартер TravelBookingWorkflowStarter (аннотирован @Service), в который инжектится WorkflowClient. Метод startWorkflow(TravelRequest):
- Строит экземпляр workflow (
TravelWorkflow) с помощьюclient.newWorkflowStub(WorkflowOptions), где указывается task queue и уникальный ID (например,"travel-" + userId). - Вызывает метод
bookTrip— синхронно (можно и асинхронно).
В контроллере (TravelController) создаётся endpoint POST /book-travel, который принимает TravelRequest и вызывает стартер. Дополнительно — endpoint POST /confirm-booking/{userId} (для сигнала, описан ниже).
Демонстрация auto retry
Без настройки Temporal по умолчанию делает 10 попыток для упавшей activity. В видео показано: при throw new RuntimeException("simulate failure") в arrangeTransport Temporal перезапускает эту activity 10 раз, показывая в UI жёлтую отметку и счётчик попыток.
Чтобы ограничить retry, в ActivityOptions добавляется setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build()). После этого при повторном сбое Temporal делает ровно 3 попытки, затем помечает workflow как завершённый с ошибкой. Весь процесс виден в UI — история шагов, коды ошибок.
Long-running workflows с Signal
Добавляется логика: после выполнения всех трёх бронирований workflow ждёт 2 минуты (в реальности — 24 часа) подтверждения от пользователя. Вводится булева переменная isUserConfirmed = false.
- Состояние workflow замораживается вызовом
Workflow.await(Duration.ofMinutes(2), () -> isUserConfirmed). - Если за 2 минуты не пришёл сигнал, вызывается
cancelBooking()(реализованная как activity), отменяющая все бронирования. - Для передачи сигнала используется аннотация
@SignalMethodв интерфейсе workflow (методreceiveUserConfirmationSignal()). - В стартере создаётся метод
sendConfirmation(String userId), который находит workflow по ID и вызываетreceiveUserConfirmationSignal. - В контроллере — endpoint
POST /confirm-booking/{userId}, который отправляет сигнал.
Демонстрация: после запуска workflow (с await(Duration.ofMinutes(2))) в консоли появляется "Waiting for user confirmation for 2 minutes". Если в течение 2 минут отправить запрос на confirm-booking, флаг меняется, workflow завершается подтверждением. Иначе через 2 минуты выполняется отмена.
Failure recovery (Saga)
При отказе одной из activities (например, arrangeTransport) уже выполненные bookFlight и bookHotel должны быть отменены. Temporal предоставляет паттерн Saga через класс Saga.
В реализации workflow:
Saga saga = new Saga(compensationOptions);
try {
saga.addCompensation(() -> activities.cancelFlight(request));
activities.bookFlight(request);
saga.addCompensation(() -> activities.cancelHotel(request));
activities.bookHotel(request);
// если arrangeTransport упадёт, предыдущие компенсации будут вызваны
activities.arrangeTransport(request);
} catch (Exception e) {
saga.compensate();
throw e;
}
Компенсационные методы (cancelFlight, cancelHotel) реализованы в TravelActivitiesImpl и просто логируют отмену. Temporal автоматически запускает их в порядке, обратном выполнению, при возникновении исключения.
Пример Saga в действии
При запуске workflow с throw new RuntimeException("force failure") в arrangeTransport Temporal сначала делает 3 retry (если настроено), затем после исчерпания попыток бросает исключение. Выполняется saga.compensate(), что в консоли выводит:
Booking hotel cancelled for user Raj
Booking flight cancelled for user Raj
В UI видно, что workflow завершился с ошибкой, а история содержит шаги отмены. Таким образом, данные остаются консистентными без ручного кода компенсации.
Итог
Temporal автоматизирует четыре критических сценария в распределённых системах на Spring Boot:
- Auto retry — по умолчанию или с настраиваемым числом попыток.
- Timeout — задаётся через
StartToCloseTimeout. - Long-running workflows — возможность приостановиться на произвольные промежутки времени и дождаться сигнала от пользователя.
- Distributed transactions (Saga) — встроенная компенсация через
Saga.addCompensation.
Весь процесс визуализируется в Temporal UI: история шагов, тайминги, ошибки. Для продакшена потребуется реальная интеграция с микросервисами (REST/gRPC/Kafka), но логика остаётся неизменной.
📜 Transcript
en · 6 532 слов · 94 сегментов · clean
Показать текст транскрипта
Hi everyone, welcome to Javateki. Have you ever struggled with handling retries, timeout or long running workflows in your Spring Boot application? If yes, then you already know how painful it can be to manage that logic manually in a distributed system, right? What if I told you there is a better way to make your service resilient, reliable and easy to manage, all without writing unorganized code? So well, In this video, we are going to explore about Temporal, which is a powerful workflow engine that takes care of all the tricky parts like retries, timeouts, and failure recovery. So you no need to take any additional headache. Okay. So as always, we'll break it down step by step with a hands-on example. So you'll clearly understand how Temporal works and how to plug it into your Spring Boot project. Okay. All right. so without any further delay let's get started so before we jump into the coding part first let's understand what temporal actually is and why so many companies are adopting it across the industry so let's take a simple example imagine you are building a trip booking system at high level you would probably have these basic features like book a flight reserve hotel schedule a cab for customer simple right To design this, you would typically create three separate microservices like flight service, hotel service and cab service and then build an orchestration layer to coordinate between them. So when a user make a booking request, it hit the orchestrator. Let's say you have something called book trip method and from there it calls these services internally. You might use REST API call, gRPC or Kafka. whatever works for your project requirement. This all looks good and usually this is how we normally design real world system right but hold on what happens if the hotel service goes down or the cab booking times out. Now things get tricky. Technically we need to add retry logic maybe even roll back the previous successful step like cancel the flight if hotel booking fail and all of that logic needs to be handled inside our code and even in every microservices because failure can happen in any place right let's be honest that's a big headhack and none of the developers want this so using plain spring boot for this kind of fault tolerance becomes complex hard to taste and definitely not reliable solution so how do we handle these failure scenarios seamlessly that's exactly where temporal comes in temporal is a workflow engine that handles all this complexity for you it lets you write your business logic as a regular java code just like we do in our springboard project but with some super powers like it will does auto retry for you if there is any failure it will handle the timeouts it also support for long running workflows and it also has built-in failure recovery and many more features like state management lot many features we have inside the temporal okay and the best part guess what you are still writing pure java code no magic tricks no yml drama basically temporal takes care of state orchestrator and fault tolerance under the hood out of the box isn't it so i hope by now you have got some context on why we need temporal but before jumping into the code It's equally important to have a high level understanding of how temporal actually works under the hood. Okay. So let me walk you through a quick architectural diagram. So in temporal term, all the action we are performing like booking the flight, booking the hotel, booking the cab considered as a activities. And who triggered those activities considered as a workflow. So in our case, user send request. to book the trip so book trip is my workflow and then book trip internally call these three action book the flight book the hotel and book the cab these are called my activities okay just remember these two terms activities and workflow because we are going to use them a lot throughout the rest of this video now let's move to the temporal internal workflow so that you will get some better context while writing the code it will be easy for you to sync what you are doing okay so at first we create our springboard application which will be act as a workflow client and it simply says hey temporal can you start a trip booking workflow for me so basically we will define a method called book trip which is nothing my starting point of workflow then the moment we make the call workflow use grpc to talk to the temporal server which you can think as the brain of the system. Okay. So once it receives the request, it delegates that request to history service, which is responsible for storing workflow metadata and history events, basically keeping track of every step of your workflow takes. Okay. Next, the request goes to the matching service, whose job is to push all the workflow and activity tasks into a task queue. Then we have the workers who constantly pull the task queue to see if there is something to do. So when a task appears like booking a flight or reserving a hotel, the workers immediately pick it up and execute it. And after the execution, it sends the request back to the temporal server. And more interesting part is that while execution, if something goes wrong, then temporal handles retry and rollback and recovery automatically okay i hope you got a clear picture and don't worry you don't have to remember all these internal components most of it is abstract away for us we just define a few configuration and let temporal do its magic behind the scene okay well so let's actually try this use case in action and see how it all works step by step okay let's go to the spring initializer we'll create a new project we'll define the workflow and activities then we'll see how temporal helps us to do the auto retries timeout handling failure recovery all the theory what we just discussed so if you want to explore more about temporal then you can go to the temporal.io and even you can start it for free so you can install the temporal server in your local machine and you can check how it handles the retries timeout failure recovery state management everything okay but in this example we are not going to install it we'll just use the docker compose to run this temporal server in locally using container now first let's create the project i'll name it spring temporal then we'll add only web dependency and let me add also lumbok that's it additionally we'll add the spring temporal sdk So let me generate the project. So here is our project. Now go to the pom.xml. Let's add the spring temporal SDK. You can use IOTemporal. Temporal SDK. This is the latest version I am using. And I also added the OpenAPI WebMVC UI for Swagger. Okay. Just update the project. All good. Now the next step. We want to start the temporal server in our local. Right. And for that, what I will do, I will create a Docker compose file. Then we will define these services. Now, if you see here, the services first one is temporal, which will be this version and the default port, it will run on 7233. Then if you remember to store the history and activity and workflow metadata, we need the database, right? So when I'm saying we need temporal server, need the database. so for that reason we have defined here postgres database okay and we are using postgres 13 this is the default username and password then we also added temporal ui where you can visualize your workflow and activities okay you no need to log into the db and check what is the state how many times it did retry what is the timeout handling scenario it does all the things you can figure it out from the UI. Okay. And the default port will be 8088. So we have added three component in our Docker Compose OML or we have added three services. One is the temporal. Second one is the database. And last service we added temporal UI. We are good with our setup. Then next, if you remember this particular flow, we have something called Q. Then we also need worker factory, group of worker. Then we also need grpc stock to communicate to the temporal server from my application or from workflow client. So those basic things we need to configure. So for that what I can do, I will create a config package. Then I will simply add this particular class. So if you observe, this is very simple configuration. So here at very first step, what we are doing? We are creating the worker factory. And if you check this flow, this WorkerFactory needs to listen to this particular queue. Right? So for that reason, I have defined a queue called TravelTaskQueue. So my WorkerFactory needs to listen to this particular queue. And also, in this task queue, he needs to pull the workflow I have created and activity I have created. For now, let's keep it null since we have not created it. Next, if we will check. we have created the stop so this stop is nothing a client to communicate to the temporal server okay so if i'll open this class you will see this is the grpc stop now from my application using this stop i can connect to the temporal server so if you see here now this is my workflow client to connect to the temporal server i need a grpc call so how can we make the grpc call by using the stop so that is what i have configured here okay next i just initialize the stop to the worker factory that's what the simple configuration you need to do once we created our workflow implementation and activity implementation will provide those two class here okay now what next let's go with the actual flow now what do you understand In this temporal server, all the action will be executed by the activities. Okay. So wherever you will write all the action, if you remember this particular flow, all the action will come under activities and who trigger that, that comes under workflow. Now, as per our use case, let's define the activities. Book the flight, book the hotel and book the cab. These are the three activities we can define. I will create a another package then what i will do i will simply create a new interface i'll name it travel activities and you must need to annotate this class with activity interface okay now what all action you want to perform as part of this activity simple action what is that public void you want to book the flight book the hotel public void Then arrange transport or book the transport. Anything you can define. Okay. Now you can pass the individual parameter. Better let me create a travel request class as an input. So I will create a package called DTO. And I will define few field like user ID, destination and travel date. So I am trying to make the example simple. You could add individual object for travel, hotel and cab everything. But let's make it simple by defining. couple of attributes okay now since we have added lambook i can use other data all argument constructor even no argument constructor go to the travel activity import this particular class all good now what we can do let's create the implementation for this particular interface okay i keep this class outside the package move it to the activities then i'll create another class which will implements from activities and you need to override all the methods and you need to annotate this let me zoom this you need to annotate this at the rate service and also i will add the log statement now what basically you need to do since this is just a demo i am not going to call the different microservices so in book flight you can do the rest call to flight microservice. Okay. Similarly, in book hotel, if you have grpc mechanism, you can simply do the grpc call to hotel service. Similarly, if you want to use Kafka, you can just simply write the actual logic, Kafka message to transport service. So you can implement different mechanism, whatever fit to your project. But for now, since this is the demo, i'll go with the log statement for book flight flight booked for the user and destination is this and date is this same i'll do for hotel same for booking cap fine now this is my activities now this particular activities needs to call from the workflow right but before that let's configure the activity what we created in our configuration So if you see here, we are telling to the worker, hey, you just execute new travel activities IMPL. Okay. Once we created the workflow, we will provide here. Now let me do one thing. Let's create another package workflow. Now I will just create another interface travel workflow. And you need to define this particular interface with the annotation workflow interface. okay now define a method also you need to annotate this particular method as a workflow method if you observe these two annotations came from io.temporal now from this book trip method you will call all the activities we have defined okay so for that what i will do i will create another implementation class for this travel workflow implements from next you just need to overwrite the methods book trip method first let me annotate this class with springbin service then i will also define sl4j then let me add the logs starting travel booking for user this now what we need to do from this book trip method we need to call these activities method right so go to the implementation first we need to create the activity object so workflow dot new activity now here what you can provide you can provide your activity class name which is travel activity dot class then no need to call the individual method at this moment you can set this option can you see here workflow activity options new builder Set start to close time. I mean the timeout window you can keep for 10 second if you want to keep any retry you can also do that Let me add it So what we are doing we are creating to I mean we are creating activity object and giving it to the workflow Saying that these are my basic parameter for now. Let me remove this retry. Okay, we'll try to understand without retry then we'll see the magic now this will give you the activity object now since you have the activities object you can call each method dot book flight give the travel request similarly you can call activities dot book hotel activities dot arrange transport okay we are calling all the three methods at the end i will add another log travel booking completed for the user this so the workflow will start from here then we are getting the activity subject then each activity will be called then the request will go to the each activity implementation from here currently we are printing the log but in real time you do call to the flight service rest call kapka grpc whatever is fine for your use case okay now we did all the setup basic setup we have created the workflow we have created the activities now i need to register this particular workflow to my worker go to the temporal config give him the class name now all set right so since we have defined the workflow this particular workflow we can directly call this particular workflow but wait do we need to this workflow or we will give this workflow to the temporal server to execute if you understand this this particular workflow we are making the temporal server grpc call using the stub we created right so for that to start the workflow what i can do i will create another class to just start the workflow to just make a grpc call to the temporal server okay so for that what i will do i will create another package now i will just create a another class called travel then annotate this with at the rate you can define service or component anything here the main thing see remember this from our workflow client we want to call the temporal server how we can call it using the grpc stop so you need to first inject the workflow service stop given by the temporal server private workflow service stop service stop now you can inject using auto add next step you write a method to start the workflow okay So simply you can define public and give the travel request as input. You have the stop with you. You have the travel request with you. You know you have the you already created the task, you work for everything. Okay. Just give all the information to begin the or to just start the workflow. Okay. So create the object of workflow client. Let me input this. So created the object of workflow client. Then this is my workflow. and we are telling to this particular workflow all the activity tasks will push to this particular task queue that is what our matching service does right he push all the tasks into the queue that is why that is why we have defined the queue details here and i just want to set the unique id for each workflow i just define travel then the user idea will pass and this is the book trip method to start the workflow once you call this inside this it will call all the activities this is the simple flow okay currently we are going with the synchronous call but you can make it concurrency or you can run it asynchronously that is different story that will cover in the next video but for now let's test the happy flow now since you have created the starter class let's create a rest endpoint to call this particular starter okay simply inject the starter object here private final travel booking workflow starter do the auto add or you can do the constructor injection as well up to you now simply define a endpoint to just start the workflow what is the method we have defined here start workflow all good i guess at this moment now this is what we are trying to understand the happy scenario there is no failure there is no need of retry nothing i just want to show you how the workflow and activities communicate to the temporal server and how we get the result i just want to give you the high level picture of it then slowly we'll go one by one to understand each and it every benefits let me start the application go to the resource i'll just change it to the server port to 9191 before that first you should start the docker once your docker is started you need to do the docker compose up so that it will create the temporal server i mean it will start the temporal server your web ui and postgres everything okay so i'll do docker compose up and let's see so looks good now let's go to the main class and simply start the application we are seeing something string main okay let's set it to the true now restart the application so if you observe It started on port 9191 now go to the browser and search swagger also I will parallel open the webby port of temporal that browser 808 I guess So if you see there is no workflow at this moment. We have not triggered anything Now what I will do I will go to the travel book and I will create some request to send the first workflow. Let's say user is busanth destination something usa travel date 15 25 okay meanwhile first let me clear this send the request travel booking workflow started for user busanth now if you see the console output starting travel booking for the user busan then it booked the flight for me then it booked the hotel for me then transport arrangements for user these then at the end the booking is completed now if you see the workflow just refresh it you can see your first workflow okay and workflow id we are just keeping travel as a prefix and we are appending the user id that is how you can see here okay open it you can see each activity how much time each activity took the complete graph okay this is the book flight then book hotel then arrange transport There is no failure, nothing to check in this particular UI at this moment. Okay. So there is one worker who picked the task and executed. History. What is this? There are so many things. Even I didn't explore all of them. You can start exploring each and every tab. Okay. Now let's come to the point where these temporal help us. Now to simulate the failure scenario, forcefully I am throwing some exception. let's go to the activity impl let's say arrange transport simulate a failure to demonstrate compensation just throw some exception now think about it once my workflow will start the request will come to the book trip then it execute the book flight then book hotel then arrange transport so in this arrange transport it will fail now what usually we do usually we write the retry logic there could be a chance of connection issue or network issue we usually do the retry but since we are using temporal we will expect temporal to handle that for us okay so first let's run the application so it started clear the console again go to the swagger i'll give some random name okay i mean i'm not focusing on the payload structure and all let's give something executed Travel booking workflow started. Workflow is initiated. Now if you see the console output, we got the simulate transport arrangement failure. See, we are keep getting this exception. Now if you go and check in the workflow UI, it's still running because we are keep getting the error. And if you see, it already attempt 6 times. It will max attempt 10 times if it will not configure the retry option for you. Now book flight, book hotel, these two are done. But you can see this yellow mark, right? It attempts 7 times. It will attempt 10 times and it will pass. So you need to tell to the temporal, hey, just if there is any failure, just retry for 3 times in every 10 second interval or whatever the configuration. So how you can do that? Now see if you observe the beauty here, we are not configure anywhere to do the retry. Since there is a failure, temporal internally do the retry, auto retry for you. That is the first benefit we understand using temporal. Right? You can see the count is 8. Now what we will do? We will overwrite this value. We will tell to the temporal, hey if there is any failure, please retry only for 3 times. So nothing to do, just here. dot set retry option and i want to set the maximum attempt to 3 that is what you can define so all good let me terminate this particular task restart the application so application started clear it go to the swagger i will send the request for john executed travel booking workflow started now if you see the console one 2 and 3 see in the UI see here it did the book flight then book hotel then it total attempt 3 times to this arrange transport method can you see the count here then after that it didn't get the result it marked the workflow as a field can you see here if you click on it you will find the complete error stack trace this is the main advantages of using this temporal Even though there is a failure, the retry, you no need to do it manually or you no need to use any third party library like Circuit Breaker or Resilience Forger. These things can be done by the temporal. Okay. So now let's discuss the next advantages of using this temporal. So we understand about auto retries and timeout handling. Now let's understand how temporal helps in long running workflows. Okay. For example, If you see the workflow, what we are doing here in the book trip workflow, we are just doing the book flight, book hotel and arrange transport. These are the three different activities we are in booking. Now what we want, we want to wait for 24 hour or you can say one day. Within this period, if user is not giving the confirmation about their booking, then simply we will go ahead and cancel the booking. okay wait for user confirmation if you won't get any within 24 hour then cancel it this is the simple validation you want to apply on the long running task if you want to apply the validation saying that wait for some time then within that period if there is no response from the user you want to roll back the entire flow How you can do that? That is where temporal helps you. And the approach is also very straightforward. Let me show you how we can do that. Okay. So you will tell the workflow. Just tell him. Can you please wait for 2 minute duration? I mean for demo purpose. I will keep it 2 minute. But in real time it could be 24 hour or 48 hour. Anything. Okay. Duration 2. Okay. Now what we want to define? We want to define a flag. If that flag is changed, wait for this 2 minutes. Otherwise just do the action. Okay. So for that I will just define another flag. So I will just define a Boolean flag. Private is user confirmed. Initially it is false. So here let me add a log statement. Waiting for user confirmation. for 24 hours change it to 2 minutes because i cannot run the application for 24 hour in this video right so let's keep it simple to simulate the long running task so i set it 2 minute now within 2 minute if user is confirmed is not changing to the true then it will roll back it so for that what i can do just define a variable okay now what you want to validate if user confirm then booking confirm the booking if user is not confirmed then cancel the booking okay so you can define if is not confirmed then what you want to do you want to cancel the booking correct so you will add some let's say add some log statement log dot user didn't confirm within two minutes cancelling the booking for user this okay Now we'll create another method called cancel flight. Okay. So go to the activities. Not cancel flight. You can define cancel booking itself. Okay. Now provide the implementation. Cancelling booking for user this. Okay. And also you need to define another method. Confirm booking. Just override it and add the log statement. Booking confirm for the user this. Okay. Simple two methods I just added. To show you that. the long running task can wait for hour day week month anytime okay and this will be handled by the temporal until unless it get the signal okay now what next we have defined these two methods go to your workflow impl call the activities cancel booking similarly else just define the log info User confirm the booking for user this. Not required. User confirm the booking. Remove this. And confirm booking. So very simple, right? So on confirm, we are just confirm the booking. If user didn't confirm within time, we are cancelling it. But you need to think how this value will be changed when the workflow is running. Initially, we define it as a false. When and how this value will be changed to true someone needs to signal to the workflow saying that okay user is interested to confirm it how you will pass that signal to the workflow or how you will pass that signal to the temporal server for that there is a annotation called signal method you can use this particular annotation okay you can define a method where you will turn this particular flag to true saying that receive user confirmation signal and this particular signal method you can define in your starter class so if you see we have created a starter right go to the starter in the starter class you can define the method saying that public send confirmation this okay and make sure you also need to define this signal method in your workflow so go to the workflow and just define signal method this is my method okay now go to the starter all looks good right now you can call this send confirmation signal method from your controller to trigger the endpoint to update the flag to true okay if user interested now go to your controller class define another endpoint endpoint to confirm the booking by sending a signal to the workflow Now let me see if I can add any other logs to make the things more visible. Go to your workflow MPL. So we start the travel. Then these things will call. Okay. Meanwhile, let me remove the forcefully exception we have raised here. Okay. Because we already see the behavior for retry. We no need to throw the exception at this moment. All good. Let's restart the application. So it started. Clear the console. Now simply go to the browser. This event history go back. Now here. Let me copy this. I will refresh. I should see another endpoint here. Can you see the two endpoint? Travel confirm user ID. And book the travel. So here I will change the user ID to something else. Now he don't want to go to USA. He want to go to UK. Some random bill you guys. Now what will happen? The request will go. all the three activity will execute then it will as per our configuration it will wait for two minute to get the user confirmation within two minute if user is not confirming the booking it should cancel the whole booking itself okay so since we added the log but if you have dv connection or if you are inserting records to the dv that will give the better picture okay let's go with this simple flow only click on execute Come here. Can you see here? Starting travel booking for user, book the flight, book the hotel, transport arranged, then waiting for user confirmation 2 minutes. Meanwhile, if you will go and check the workflow, it completed everything, then it will wait for 2 minutes. Can you see here? That is what we configure. Once it will complete the 2 minutes and if it didn't receive the user confirmation, it should terminate automatically. we'll see the console output can you see here user did not confirm within two minutes it didn't confirm within two minutes so cancel the booking now if you see here let me refresh the workflow again it wait for two minutes since you didn't confirm the booking it cancelled the booking can you see here the methods cancel booking okay and then it marked the workflow as a completed all good right Now what we can do, let's test one success scenario. Let's forcefully give the confirmation within two minutes and we'll see how it behaves. Okay. That is what our expectation, right? That is the reason the long running task you can keep or you can pause for long time. It could be one day or one month or even more time. Okay. So what I can do, let me go to the workflow again. clear the console first now in swagger i will send some different user xyz it started waiting for user confirmation 2 minute now what is the user xyz go to the this request confirm endpoint give xyz send the request booking confirmed by the user the flag will changed and immediately you will get the confirmation signal User confirm the booking XYZ. Booking confirm for the user this. All the log whatever we added. Okay. We are good at this long running task. You can simulate the number to long time as per your requirement. I am just giving the demo so I keep it 2. So basically if you remember in insurance domain you might need to wait for multi approval. Okay. Then in that kind of use case it will helpful to keep a signal. for your workflow fine now let's move to the next benefit of failure recovery when i'm saying failure recovery in distributed it is called saga or two-phase commit right now for example let's see now if hotel service failed here then whatever the details you persist to the flight service that also you need to roll back right or if cab service failed whatever the details you persist to hotel service and flight service both needs to revert or roll back how will you handle that failure scenario if there will be any failure don't keep any inconsistency data keep the data clean and consistent by applying the distributed transaction as far we know in microservices we can implement the distributed transaction using saga two-phase commit now let's see how this temporal helps to manage the distributed transaction or failure recovery okay so the approach is very straightforward what you can do go to your travel activities now for every success action create a method for cancel okay for example public cancel flight public cancel hotel then public cancel transport now in this approach what will happen if arranged transport will fail to compensate that the saga or the temporal internally call cancel hotel and cancel flight you no need to explicitly call it you just need to give that to the saga dot compensate in temporal okay i'll show you the syntax is very simple there is no rocket science but you also need to write a simple method to roll back the things from that specific service okay so we have defined this now what we can do go to the activities impl overwrite these methods simply i will add the log statement now to see this uh failure recovery again forcefully i will throw the exception in transport arrange transport method okay so if there will be any failure in the arrange transport then it should call cancel hotel and cancel flight Okay to handle the failure because before this arranged transport We are only executing two action book the hotel and book the flight and those two things needs to be rolled back That is the reason we have defined this now how you will tell to the temporal if there is a failure in the Arrange transport call only cancel hotel and cancel of flight. How will you define that? It's very simple Just go to your workflow. What you can do here? First, define the saga for compensating the failure. Saga options is available for you now. Now just create object of saga, new saga, give this option. Now all the logic, keep it inside try and catch. Now in the catch blog, what you can do? You will ask saga.compensate. okay and better i'll do one thing i'll keep this log statement outside if there is no error you will get this result all good right now here what you can do activities dot book flight here you can tell to the saga if there is anything wrong next to it just roll back it saga dot add compensate add compensation You can define lambda, give your activities. What is the activities you want to execute? Cancel the flight. Correct? Now, you want to do the same for hotel booking. So, you can define saga, add compensation, cancel hotel. Similarly, you can do this for cancel transport. For all the success, you have defined the, I mean, you already informed how to handle your failure scenario. Now, if there is any failure occurs, previously what? Activities will be executed before. That will be auto-rolled back by the temporal only. Okay. So, we'll validate this quickly. All good. That's the only change you need to do for handling the failure. Okay. Now, what we can do? Let me restart the application. Meanwhile, let's check the workflow. All looks good. So, it started. Clear everything. and if you see i i forgot to explain from the beginning if you see here workflow puller activity puller and both workflow and activity push to this particular task queue and from this task queue worker pull the task and execute it that is what we understand if you remember can you see this okay so that is what the log statement you can even see on the application startup just clear it Go to the swagger. Go to the booking. Give some better name. Raj. Execute it. See this output. Starting travel booking for user. Flight booked. Then hotel booked. Transport arranged. Then forcefully we are throwing the error. It will retry three times. Then error during travel booking for user. See the step it cancelled. Cancelling hotel booking. Cancelling flight booking. Because it failed on the RNG transport. All the previous two steps, two activities, what it executed, it revert those changes. This is what called saga, right? This is what called managing the distributed transaction. Since we are doing it in the same application, you might not get the clear picture of this saga. If you will try to create three different microservices for flight, for hotel and for cab, you will understand it. okay so i believe we almost cover each and every features auto retries timeout handling long running workflow failure recovery and state management okay at high level we have covered how to orchestrate long running workflows implement retries rollbacks and even handle human decision using signals on long running workflows so i believe you enjoy this session if yes don't forget to like share and subscribe and keep coding i'll see you in the next video
⚙️ Pipeline jobs
| Stage | Status | Att. | Updated | Error |
|---|---|---|---|---|
| download | done | 1/3 | 2026-07-20 15:06:54 | |
| transcribe | done | 1/3 | 2026-07-20 15:07:43 | |
| summarize | done | 1/3 | 2026-07-20 15:08:18 | |
| embed | done | 1/3 | 2026-07-20 15:08:19 |
📄 Описание YouTube
Показать
#javatechie #microservice #springboot #temporal 👉👉 In this video, we dive deep into Temporal Workflow with Spring Boot — bringing real-world automation to life with retry, rollback, and stateful orchestration! ⚙️🔥 Whether you're managing long-running microservice tasks or building fault-tolerant workflows, this video has everything you need! 💪 ✅ How Temporal works behind the scenes (Workflows, Activities, Workers) ✅ gRPC-powered communication with the Temporal Server ✅ Auto-retry, rollback, and fault-tolerance with zero boilerplate ✅ Full hands-on demo with trip-booking orchestration 🚀 🧨 Hurry-up & Register today itself!🧨 Devops for Developers course 🔥🔥: https://javatechie.ongraphy.com/courses/Devops-for-Developers-64f1e07be4b0bbd6b56f7c05 Spring boot microservice : https://javatechie.ongraphy.com/courses/Spring-Boot--Microservice-62c3e1f20cf25d97f28f9fa8 GitHub: https://github.com/Java-Techie-jt/spring-temporal Blogs: https://javatechie4u.medium.com/ Facebook: https://www.facebook.com/groups/javatechie 💡 Bookmark this page for quick access so you can easily find the right content whenever you need it. 📌 Don't forget to subscribe to the [JavaTechie YouTube Channel](https://www.youtube.com/c/JavaTechie) for more in-depth tutorials! 🎥🔥 --- ## 🚀 Core Java 🔹 Java 8 → (https://www.youtube.com/watch?v=-1XE__rrbjQ&list=PLVz2XdJiJQxzrdrpglCv_nWIO5CDIqOVj) ## 🌱 Spring Framework 🔥 Spring Boot Complete Course → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxw-jVLpBfVn2yqjvA1Ycceq) 🔐 Spring Security → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxynOpTm0DuufOkfWHNamJsF) 📦 Spring Data JPA → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxxdOhu-xmEUYDzY_Pz8cRGa) ⚡ Spring Batch → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxyC2LMLgDjFGJBX9TJAM4A2) 💰 Spring Transaction → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxxj_zMhm6zCPO6zhtOcq-wl) ☁️ Spring Cloud → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxz3L2Onpxbel6r72IDdWrJh) 🔄 Spring Reactive → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxyB4Sy29sAnU3Eqz0pvGCkD) ## 📩 Messaging Systems 📡 Kafka for Beginners → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwpWGoNokohsSW2CysI6lDc) ## 🏗️ Microservices 🌍 Microservices → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxxWhFkucZBoMxeYE6qTgEF8) 🏛 Microservice Design Patterns → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxw1H3JVhclHc__WYDaiS1uL) ## 🤖 GenAI for Beginners 🎭 Learn ChatGPT, Google Bard, and DeepSeek → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxx695lRqc4v3V-YitV6ERfn) ## ☁️ DevOps & Cloud 🐳 Docker → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxzMiFDnwxUDxmuZQU3igcBb) ☸️ Kubernetes → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxybsyOxK7WFtteH42ayn5i9) 🌍 AWS (Amazon Web Services) → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxxurKT1Dqz6rmiMuZNdClqv) ## 🎨 Frontend Development 🎨 Angular Full Course → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwAhzEZSpDqXlfT7XvNPDIE) ## 🎯 Interview Preparation 🎤 Interview FAQs → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwS8FyWnWyKyfILxHPLsiro) ## 🛠️ Tools 🖥 GitHub → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxxeLS2a9DPp0jY3SBgNw8NV) 🏗 Jenkins → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwS0BZUHX34ocLTJtRGSQzN) 🔍 Splunk → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwLTK4h387zBtxyKwMkhTXU) ## 🗄️ Databases 📊 NoSQL (MongoDB, Neo4j, Cassandra, Solr) → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxyxZK0ggHweWODoVTkXsWUA) ## 🌍 Web Services 🌐 REST Web Services → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxwXNIHPzUz8kP734ZYf8Avu) 🛠 SOAP Web Services → (https://www.youtube.com/playlist?list=PLVz2XdJiJQxyp_3TWxX3YdkPCwQPFTIXN) 📢 Stay Updated & Subscribe 🔔 [JavaTechie YouTube Channel](https://www.youtube.com/c/JavaTechie) Join this channel to get access to perks: https://www.youtube.com/javatechie/join 🔔 Guys, if you like this video, please do subscribe now and press the bell icon to not miss any update from Java Techie. Disclaimer/Policy: 📄 Note: All uploaded content in this channel is mine and it's not copied from any community, you are free to use source code from the above-mentioned GitHub account.