Jak przestać naprawiać systemy rozproszone i zacząć żyć z Restate
Wyobraź sobie taką sytuację: piszesz mikroserwis do przetwarzania płatności. W połowie procesu sieć zawodzi, baza danych przekracza limit czasu, albo kontener po prostu restartuje się. Co dzieje się z transakcją? W klasycznym scenariuszu musisz ręcznie zaimplementować logikę ponawiania, śledzić idempotentność, konfigurować kolejki wiadomości, a najprawdopodobniej wprowadzić jakąś złożoną sagę. To bolesne, czasochłonne i zamienia Twój kod w labirynt obsługi błędów.
Niedawno odkryłem Restate, projekt rozwiązujący ten problem na poziomie infrastruktury. Deweloperzy nazywają to „Durable Execution" — odporne na błędy wykonywanie kodu. Idea jest taka, że Twój kod staje się nieśmiertelny: jeśli proces ulegnie awarii, Restate wznawia go dokładnie w miejscu, w którym się zatrzymał, zachowując wszystkie zmienne i stan.
Co to w ogóle jest
Restate to binarny plik w Rust, który działa jako serwer proxy i koordynator dla Twoich usług. Przejmuje całą brudną robotę związaną z zarządzaniem stanem i wywołaniami. Piszesz zwykły kod w TypeScript, Python lub Go, używasz SDK i nagle Twoje funkcje zamieniają się w niezawodne przepływy pracy.
Główna różnica w porównaniu z ciężkimi rozwiązaniami jak Temporal polega na tym, że Restate jest znacznie łatwiejszy do rozpoczęcia pracy. Nie potrzebujesz masywnej konfiguracji z bazami danych i złożonymi workerami. Możesz po prostu uruchomić pojedynczy plik binarny lub kontener Docker i zacząć pracę.
Jak Restate pomaga w praktyce
Projekt zawiera kilka ciekawych koncepcji, które naprawdę upraszczają życie.
Gwarantowane wykonanie
Jeśli wywołałeś funkcję przez Restate, wykona się ona do końca. Kropka. Jeśli serwer z Twoim kodem ulegnie awarii, Restate poczeka na jego powrót i kontynuuje wykonanie od ostatniego pomyślnego kroku. Nie musisz się już martwić o to, czy email został wysłany do użytkownika dwukrotnie lub czy pieniądze zostały pobrane dwukrotnie.
Inteligentne timery i obietnice
Zwykle implementacja opóźnienia w systemie rozproszonym to quest. Musisz umieścić wiadomość w kolejce z opóźnieniem lub skonfigurować zadanie cron. W Restate po prostu piszesz ctx.sleep(duration). Wątek nie blokuje się bezcelowo: usługa może się całkowicie wyłączyć, a trzy dni później Restate ją „obudzi" i kontynuuje wykonanie.
Stan bezpośrednio w kodzie
Restate pozwala przechowywać stan K/V powiązany z konkretną encją (na przykład ID użytkownika). Wygląda to jak zwykła praca z obiektami, ale pod maską Restate gwarantuje, że dane są spójne i zawsze dostępne wraz z żądaniem. Jest to szczególnie wygodne dla architektur serverless, gdzie funkcje typowo nie mają pamięci.
Jak to wygląda w kodzie
Powiedzmy, że musimy zaimplementować proces rejestracji użytkownika z potwierdzeniem emailem. W TypeScript z użyciem Restate SDK wyglądałoby to mniej więcej tak:
import * as restate from "@restatedev/restate-sdk";
const userService = restate.service({
name: "users",
handlers: {
register: async (ctx: restate.Context, user: { id: string, email: string }) => {
// Сохраняем состояние
ctx.set("status", "pending");
// Отправляем письмо (Restate гарантирует, что это случится 1 раз)
await ctx.run(() => sendWelcomeEmail(user.email));
// Ждем подтверждения или таймаута в 24 часа
const confirmed = await ctx.awakeable<boolean>("email-confirmed");
if (confirmed) {
ctx.set("status", "active");
}
}
}
});
Tutaj ctx.run gwarantuje, że efekt uboczny (wysłanie emaila) wykona się pomyślnie, a wynik zostanie zbuforowany. Jeśli funkcja ulegnie awarii po wysłaniu, przy restarcie Restate po prostu pomija ten krok, wiedząc że jest już wykonane.
Strona techniczna
Projekt jest napisany w Rust, co daje doskonałą wydajność. Architektonicznie Restate działa jako invoker. Odbiera przychodzące żądania przez HTTP/gRPC, zapisuje je do swojego logu i wywołuje Twoje handlery.
Ciekawe jest to, że Restate może „zawieszać" wykonanie. Jeśli Twój kod czeka na odpowiedź z zewnętrznego API lub timera, Restate zwalnia zasoby. Gdy nastąpi zdarzenie, przywraca kontekst wykonania. Pozwala to na uruchamianie tysięcy długotrwałych procesów na skromnym sprzęcie.
Kto powinien spróbować
Restate idealnie wypełni luki w projektach, gdzie:
- Jest wiele łańcuchów wywołań między mikroserwisami.
- Musisz budować złożone łańcuchy działań (sagi, przepływy pracy).
- Używasz agentów AI, którzy muszą długo czekać na odpowiedzi LLM i zachować kontekst rozmowy.
- Istnieją zadania z opóźnionym wykonaniem (przypomnienie o porzuconym koszyku za 2 godziny).
Projekt jest aktywnie rozwijany, z niemal 4 000 gwiazdek na GitHub. SDK są dostępne dla TypeScript/JavaScript, Java/Kotlin, Python, Go i Rust.
Oczywiście nie powinieneś się spieszyć, żeby jutro wciągać nowy komponent infrastruktury do produkcji dużego banku — najpierw musisz wypróbować lokalnie. Ale dla startupów lub nowych funkcji w istniejących projektach może zaoszczędzić tygodnie developmentu.
Możesz wypróbować dosłownie w kilka minut:
brew install restatedev/tap/restate-server
restate-server
I to wszystko, masz lokalne środowisko do uruchamiania aplikacji odpornych na błędy. Być może to obecnie najniższy próg wejścia do świata Durable Execution.
Powiązane projekty