Come smettere di correggere i sistemi distribuiti e iniziare a vivere con Restate
Immagina questo: stai scrivendo un microservizio per l'elaborazione dei pagamenti. A metà del processo, la rete vacilla, il database va in timeout, o il container semplicemente si riavvia. Cosa succede alla transazione? In uno scenario classico, devi implementare manualmente la logica di retry, tracciare l'idempotenza, configurare code di messaggi, e molto probabilmente introdurre qualche saga complessa. È doloroso, richiede tempo, e trasforma il tuo codice in un labirinto di gestori di errori.
Ho scoperto di recente Restate, un progetto che affronta questo problema a livello di infrastruttura. Gli sviluppatori lo chiamano "Durable Execution" — esecuzione del codice fault-tolerant. L'idea è che il tuo codice diventa immortale: se un processo va in crash, Restate lo riprende esattamente da dove era stato interrotto, preservando tutte le variabili e lo stato.
Ma cosa diavolo è
Restate è un binario Rust che funziona come server proxy e coordinatore per i tuoi servizi. Si occupa di tutto il lavoro sporco della gestione dello stato e delle chiamate. Scrivi codice normale in TypeScript, Python, o Go, usi l'SDK, e improvvisamente le tue funzioni si trasformano in workflow affidabili.
La differenza principale rispetto a soluzioni pesanti come Temporal è che Restate è molto più facile da usare. Non hai bisogno di un'impostazione massiccia di database e worker complessi. Puoi semplicemente eseguire un singolo binario o container Docker e iniziare a lavorare.
Come Restate aiuta nella pratica
Il progetto include diversi concetti interessanti che semplificano genuinamente la tua vita.
Esecuzione garantita
Se hai chiamato una funzione attraverso Restate, verrà eseguita fino alla fine. Punto. Se il server che esegue il tuo codice va in crash, Restate aspetterà che torni online e continuerà l'esecuzione dall'ultimo step completato con successo. Non devi più preoccuparti se l'email è stata inviata due volte all'utente o se sono stati addebitati due volte i soldi.
Timer intelligenti e promise
Di solito, implementare un ritardo in un sistema distribuito è una quest. Devi mettere un messaggio in una coda di delay o configurare un cron job. In Restate, scrivi semplicemente ctx.sleep(duration). Il thread non si blocca inutilmente: il servizio può spegnersi completamente, e tre giorni dopo Restate lo "risveglierà" e continuerà l'esecuzione.
Stato direttamente nel codice
Restate ti permette di memorizzare stato K/V legato a un'entità specifica (per esempio, un ID utente). Sembra un lavoro normale con oggetti, ma sotto il cofano Restate garantisce che i dati siano consistenti e sempre disponibili insieme alla richiesta. Questo è particolarmente comodo per architetture serverless dove le funzioni tipicamente non hanno memoria.
Come appare nel codice
Diciamo che dobbiamo implementare un processo di registrazione utente con conferma email. In TypeScript usando l'SDK di Restate, sarebbe qualcosa del genere:
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");
}
}
}
});
Qui ctx.run garantisce che l'effetto collaterale (l'invio dell'email) verrà eseguito con successo, e il risultato verrà memorizzato nella cache. Se la funzione va in crash dopo l'invio, al riavvio Restate salta semplicemente questo step, sapendo che è già stato completato.
Il lato tecnico
Il progetto è scritto in Rust, il che garantisce eccellenti performance. Architetturalmente, Restate agisce come un invoker. Riceve richieste in entrata su HTTP/gRPC, le scrive nel suo log, e chiama i tuoi handler.
Interessante è che Restate può "sospendere" l'esecuzione. Se il tuo codice sta aspettando una risposta da un'API esterna o un timer, Restate libera le risorse. Quando l'evento si verifica, ripristina il contesto di esecuzione. Questo permette di eseguire migliaia di processi a lunga esecuzione su hardware modesto.
Chi dovrebbe provarlo
Restate colma perfettamente le lacune nei progetti dove:
- Ci sono molte catene di chiamate tra microservizi.
- Hai bisogno di costruire catene complesse di azioni (saga, workflow).
- Stai usando agenti AI che hanno bisogno di aspettare a lungo le risposte LLM e preservare il contesto della conversazione.
- Ci sono task di esecuzione ritardata (ricordare del carrello abbandonato tra 2 ore).
Il progetto è in sviluppo attivo, con quasi 4.000 stelle su GitHub. Gli SDK sono disponibili per TypeScript/JavaScript, Java/Kotlin, Python, Go, e Rust.
Ovviamente, non dovresti precipitarti a introdurre un nuovo componente di infrastruttura nella produzione di una grande banca domani — prima devi provarlo localmente. Ma per startup o nuove funzionalità in progetti esistenti, potrebbe farti risparmiare settimane di sviluppo.
Puoi provarlo in letteralmente un paio di minuti:
brew install restatedev/tap/restate-server
restate-server
E questo è tutto, hai un ambiente locale per eseguire applicazioni fault-tolerant. Forse questa è la barriera d'ingresso più bassa nel mondo della Durable Execution al momento.
Progetti correlati