Namespace UsluzionicaServer.Infrastructure.Redis
Classes
- CacheInvalidator
Obaveštava SVE instance da je neki keš zastareo — preko Redis pub/sub-a.
PROBLEM KOJI REŠAVA: Neki keševi su u Redis-u i tamo ih je dovoljno obrisati jednom, jer ih sve instance dele. Ali CategorySearchIndex drži foldovana imena kategorija U MEMORIJI SVAKOG PROCESA (namerno — pogađa se na svakom tokenu svake pretrage, pa mrežni poziv po tokenu ne dolazi u obzir).
Kad admin preimenuje kategoriju, instanca koja je obradila taj zahtev očisti SVOJU kopiju. Druga instanca ne zna ništa i nastavlja da pretražuje po starom imenu — do isteka TTL-a od 10 minuta.
Pub/sub to rešava odmah: instanca koja je izmenila objavi poruku na kanal, sve instance (uključujući nju samu) je prime i očiste svoju kopiju.
FALLBACK: ako Redis nije dostupan, poruka se ne pošalje i svaka instanca se oslanja na svoj TTL. Zastarelost je tada ograničena na 10 minuta umesto na milisekunde — sporije, ali nikad trajno pogrešno.
- CacheInvalidator.Topics
Teme koje se objavljuju. Konstante da se ne prekucavaju.
- CacheService
Tanak sloj nad Redis-om za keširanje objekata.
Cela poenta ove klase je da pozivaoci NIKAD ne pišu try/catch oko keša. Svaka metoda ovde guta grešku i ponaša se kao promašaj:
• Redis mrtav pri čitanju → vrati null → pozivalac ide u bazu • Redis mrtav pri upisu → tiho ništa → sledeći zahtev opet ide u bazu
Rezultat je da gašenje Redis-a čini aplikaciju SPORIJOM, ne pokvarenom. Vidi RedisConnection za detalje.
Zašto ručno preko IDatabase, a ne IDistributedCache: IDistributedCache interfejs vraća bajtove i nema pojam o obrascu „uzmi ili izračunaj", pa bi se ista try/catch logika ponavljala na svakom pozivnom mestu.
- DistributedLock
Distribuirani lock — obezbeđuje da posao izvrši TAČNO JEDNA instanca.
Zašto je potreban:
BackgroundServicese pokreće u svakom procesu. Sa dve instance API-ja,BoostExpiryServicebi u istom trenutku na obe krenuo da oduzima BoostScore istim oglasima — pa bi svaki oglas bio umanjen dvaput.MessageCleanupServicebi dvaput brisao (bezopasno) ali bi i dvaput logovao i dvaput opteretio bazu.Mehanizam je Redis
SET key value NX PX ttl: NX → upiši SAMO ako ključ ne postoji (atomično, na strani Redis-a) PX → automatski istekni posle ttl milisekundiTTL je ono što ovo čini bezbednim. Ako instanca koja drži lock pukne ili joj neko povuče struju, lock se sam oslobodi posle TTL-a. Bez TTL-a bi jedan pad zauvek zaustavio taj posao na celom klasteru.
TTL zato mora biti DUŽI od najdužeg očekivanog trajanja posla. Ako posao traje duže, drugi ga preuzme dok prvi još radi — i vraćamo se na problem koji smo rešavali.
- RedisConnection
Jedna deljena veza ka Redis-u za ceo proces.
PRAVILO CELE FAZE B: Redis je UBRZANJE I DELJENO STANJE, NIKAD USLOV ZA RAD. Aplikacija mora da se digne i da radi i kad Redis uopšte nije konfigurisan (lokalni razvoj, integracioni testovi) i kad je konfigurisan ali mrtav (ispao je u produkciji). Nijedan korisnički zahtev ne sme pasti zbog keša.
Dve stvari to omogućavaju:
AbortOnConnectFail = false— bez togaConnectAsyncbaca izuzetak ako Redis nije tu u trenutku starta, i ceo proces umire. Sa njim, biblioteka vrati objekat koji je „trenutno nepovezan" i sama se povezuje u pozadini čim Redis oživi. Ovo je najvažnija jedna linija u celoj Fazi B.IsAvailable — svaki pozivalac prvo pita da li Redis uopšte postoji, umesto da hvata izuzetke po celom kodu.
StackExchange.Redis je namerno singleton:
ConnectionMultiplexerje thread-safe, multipleksira sve komande preko JEDNE TCP veze i skup je za pravljenje. Otvaranje veze po zahtevu je klasična greška koja obori Redis brže nego bilo koje opterećenje.