Table of Contents

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.

CacheService.Keys
DistributedLock

Distribuirani lock — obezbeđuje da posao izvrši TAČNO JEDNA instanca.

Zašto je potreban: BackgroundService se pokreće u svakom procesu. Sa dve instance API-ja, BoostExpiryService bi u istom trenutku na obe krenuo da oduzima BoostScore istim oglasima — pa bi svaki oglas bio umanjen dvaput. MessageCleanupService bi 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 milisekundi

TTL 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:

  1. AbortOnConnectFail = false — bez toga ConnectAsync baca 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.

  2. IsAvailable — svaki pozivalac prvo pita da li Redis uopšte postoji, umesto da hvata izuzetke po celom kodu.

StackExchange.Redis je namerno singleton: ConnectionMultiplexer je 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.