Eventual consistency brzmi rozsądnie technicznie, ale w praktyce to często UX-owy koszmar. Zobacz, dlaczego i jak się przed tym bronić.
Czym jest eventual consistency?
Eventual consistency (czyli „ostateczna spójność”) to model spójności danych, który mówi tyle: „Nie gwarantuję, że dane są spójne teraz, ale będą… kiedyś.”
W praktyce oznacza to, że różne części systemu mogą przez chwilę widzieć różne wersje tych samych danych – i to jest akceptowalne. Warunkiem jest to, że po odpowiednim czasie wszystko się wyrówna, a dane staną się spójne globalnie.
Ten model stosuje się głównie w systemach rozproszonych, gdzie kluczowe są:
- wysoka dostępność,
- odporność na błędy,
- szybkość działania lokalnych operacji.
Jeśli trzeba wybierać między dostępnością a natychmiastową spójnością – eventual consistency mówi: wybierz dostępność. Przetwarzanie odbywa się asynchronicznie, czasem w wielu krokach, czasem w kolejce, czasem z opóźnieniami.
Dlaczego eventual consistency to problem front-u?
Z perspektywy back-end-u, eventual consistency to sprytne rozwiązanie. Systemy rozproszone, mikroserwisy, skalowanie, odporność na błędy – wszystko się zgadza. Ale jeśli spojrzeć na to z poziomu użytkownika? Mamy mały dramat.
Wyobraź sobie taką sytuację:
Zamawiasz produkt w sklepie internetowym. Klikasz „Kup teraz”, pojawia się komunikat „Dziękujemy za zamówienie”, ale po odświeżeniu strony produkt nadal leży w koszyku. A w historii zamówień – cisza.
Minęły dopiero dwie sekundy. Back-end jeszcze „nie doszedł” do konsystencji. Technicznie wszystko działa. Ale dla użytkownika… coś tu się ewidentnie zepsuło. Problem jednak nie leży w samej eventual consistency jako takiej. To mechanizm o sensownych podstawach. Użyty bez refleksji, może sprawić, że doświadczenie użytkownika stanie się dziwnie… niespójne.
Oto klasyczna ścieżka bólu:
- Coś idzie nie tak (timeout, rollback, błąd walidacji).
- UI zostaje z niespełnioną obietnicą.
- Użytkownik wykonuje akcję (np. dodaje komentarz).
- Interfejs reaguje optymistycznie – pokazuje wynik od razu.
- Back-end przetwarza żądanie asynchronicznie.
Talk is cheap, show me the code
Poniżej prosty przykład, który pokazuje, jak można zasymulować optymistyczne UI z konsekwencjami eventual consistency:
class Comment {
constructor(
public readonly authorId: string,
public readonly postId: string,
public readonly content: string
) {}
}
class CommentService {
private readonly pendingComments = new Map<string, Comment>();
add(comment: Comment): void {
this.pendingComments.set(comment.postId, comment);
this.sendToBackend(comment);
}
private sendToBackend(comment: Comment): void {
setTimeout(() => {
// Backend responds with success or failure
const success = Math.random() > 0.2;
if (success) {
this.pendingComments.delete(comment.postId);
return;
}
console.warn("Failed to add comment");
}, 1500);
}
}
Z punktu widzenia użytkownika komentarz pojawił się od razu. Ale co, jeśli po wywołaniu REST API back-end go odrzuci? UI tego nie wie, a użytkownik dostaje fałszywy sygnał, że wszystko się udało.
Jak zminimalizować koszmar?
Nie da się całkowicie pozbyć eventual consistency, ale można nauczyć się z nią żyć – projektując system tak, by nie szkodziła UX-owi.
Trzy zasady do bycia wygrywem z eventual consistency:
- Zachowuj spójność lokalnie – cache’uj tymczasowe dane i pokazuj je jako „wstępne”, ale jasno oznaczone.
- Komunikuj status – pokazywanie spinnera to za mało; potrzebne są stany: „oczekujące”, „w trakcie przetwarzania”, „niepowodzenie”.
- Ostrożnie z optimistic UI – jeśli nie możesz pewnie przewidzieć sukcesu, nie obiecuj za wcześnie.
Podsumowanie: technologia to nie wszystko
Eventual consistency jest jak niewidzialna siła – działa w tle, ale ma realny wpływ na to, co widzi (i czuje) użytkownik. Jeśli tworzymy systemy, które mają być nie tylko poprawne technicznie, ale też intuicyjne i przyjazne, musimy pamiętać o jednym: użytkownika nie obchodzi, że back-end „jeszcze nie zdążył przetworzyć”. On po prostu chce, żeby to działało. Od razu.


Dodaj komentarz