State / wykonanie i komunikacja
Co zostaje po zakończonym kroku?
Wyobraź sobie komponent, który wykonał krok pracy. Zanim rozpocznie się dalszy etap, zapisuje fakt: „pierwszy krok zakończony”.
Może odczytać zachowany fakt i odwołać się do wcześniej zatwierdzonego wyniku.
1. Zapisujemy fakt
W prostym przykładzie informacja o zakończonym kroku zostaje zatwierdzona w State. Żądanie ma własny identyfikator, aby można było rozpoznać jego późniejsze ponowienie.
2. Uruchamiamy komponent ponownie
Nowy proces odczytuje ten sam fakt. Informacja nie znika wraz z zakończeniem pierwszego procesu.
3. Rozpoznajemy ponowienie
Ten sam zapis zostaje przesłany jeszcze raz z tym samym identyfikatorem. Przykład odzyskuje wcześniej zatwierdzony wynik zamiast traktować go jako nową zmianę.
Sukces: zachowany fakt i wynik żądania są dostępne po ponownym uruchomieniu.
Mały fragment szerszej roli State
State nie wykonuje kroku za komponent. Zachowuje przekazaną informację o wykonaniu. To, kiedy i na jakiej podstawie wolno ją zapisać, jest częścią konkretnej integracji. Ten zachowany fakt jest przykładem stanu potrzebnego do kontynuacji; szersza rola State obejmuje też uzgadnianie aktualnego stanu i rozliczanie niepewności.
Ten mały przykład pokazuje mechanizm istniejącego kodu. Nie jest pełnym scenariuszem wznowienia systemu rozproszonego ani dowodem skutku w zewnętrznym urządzeniu.
Dla zainteresowanych: kod i wynik lokalnej próby
Rzeczywisty konsument Rust używa lokalnego magazynu State. Uruchomiono dwa osobne procesy: zapis oraz odczyt z ponowieniem. Nie uruchamia to Scheduler Runtime, sieci ani kontroli uprawnień.
Zatwierdzono: execution/first-step = completed, wersja 1
Po ponownym uruchomieniu: execution/first-step = completed, wersja 1
To samo zadanie zapisu: zachowano wersje 1
use junexis_state::{ExpectedVersion, RequestId, storage::{DurableStore, file::FileMedia}};
use std::path::PathBuf;
fn main() {
let mut args = std::env::args().skip(1);
let mode = args.next().expect("podaj write albo read");
let path = PathBuf::from(args.next().expect("podaj prywatny katalog proby"));
let key = "execution/first-step";
if mode == "write" {
let media = FileMedia::create(&path).expect("nowy, prywatny nosnik");
let mut store = DurableStore::initialize(media, [7; 16], 1).expect("inicjalizacja");
let version = store.put(RequestId::new(1), key.into(), "completed".into(), ExpectedVersion::Missing)
.expect("zatwierdzenie").expect("wynik zapisu");
println!("Zatwierdzono: {key} = completed, wersja {}", version.as_u64());
} else {
assert_eq!(mode, "read");
let media = FileMedia::open(&path).expect("otwarcie tego samego nosnika");
let mut store = DurableStore::open(media, [7; 16], 1).expect("odtworzenie");
let found = store.get(key).expect("odczyt").expect("zapisany fakt");
assert_eq!(found.value, "completed");
let version = found.version;
println!("Po ponownym uruchomieniu: {key} = {}, wersja {}", found.value, version.as_u64());
let repeated = store.put(RequestId::new(1), key.into(), "completed".into(), ExpectedVersion::Missing)
.expect("ponowienie").expect("zachowany wynik");
assert_eq!(repeated, version);
println!("To samo zadanie zapisu: zachowano wersje {}", repeated.as_u64());
}
}