← Natrag
Tehnologija

Zašto reranker pool sa 3 instance kao Postgres replicas

RiNET Engineering· 16. lipnja 2026.· 5 min čitanja· 22 pregleda
Zašto reranker pool sa 3 instance kao Postgres replicas
🎧 Poslušaj članak

Reranker je u modernom RAG pipelineu onaj tihi ubojica performansi. Dok embedding modeli generiraju vektore koji su relativno jeftini za izračun, cross-encoder reranker mora proći kroz svaki par upita i dokumenta, izračunati similarity score i vratiti rangiranu listu. To je računski skup posao, pogotovo kad vam je latencija ispod 500 milisekundi uvjet bez kojeg ne idete u production. Jedna instanca rerankera je single point bottleneck — i to ne samo u smislu throughputa, nego i u smislu pouzdanosti. Naš tim je dizajnirao reranker pool s tri instance na portovima 8099, 8100 i 8101, s round-robin dispečingom u kodu, upravo zato što smo naučili na teži način da jedan proces nije dovoljan čim prijeđete tisuću upita na sat. Ovo nije teorija, ovo je arhitektura koju smo složili jer smo imali krvave oči od debugiranja memory leakova i timeouta.

Zašto baš tri instance? Nije to magični broj, nego pragmatični kompromis. Jedna instance je premalo jer čim krene batch processing ili concurrent requests, dolazi do head-of-line blockinga. Dvije su već bolje, ali ako jedna padne, ostajete na 50% kapaciteta i opet imate problem. Tri instance znači da vam i dalje radi 66% kapaciteta čak i kad jedna ode u kvar. To je dovoljno da sistem ne kolabira, a opet ne trošite resurse na pet ili deset instanci koje bi vam pojele GPU memoriju bez potrebe. Svaka instance je cross-encoder model učitani u memoriju, i kad kažem u memoriji, mislim na 4 do 6 GB RSS-a po procesu, ovisno o veličini modela i batch sizeu. Tri puta po 6 GB je 18 GB samo za reranking — to je već ozbiljna alokacija, ali isplati se kad vidite da vam latencija ne raste eksponencijalno s brojem dokumenata.

Memory leak je kod rerankera kronični problem koji svi ignoriraju dok im ne sruši production. Cross-encoder modeli, pogotovo oni bazirani na Transformer arhitekturi, imaju tendenciju akumulirati cached tenzore i intermediate representations kroz vrijeme. To nije bug, nego inherentna karakteristika frameworka poput PyTorcha i TensorFlowa koji ne oslobađaju memoriju agresivno nakon svakog forward passa. Nakon nekoliko sati rada, RSS može skočiti s 4 GB na 8 ili čak 10 GB, i ako nemate monitoring, proces će jednostavno crknuti zbog OOM kill-a. Mi smo to riješili tako što imamo cron job koji svakih sat vremena provjerava RSS svake instance na portovima 8099, 8100 i 8101. Ako bilo koja instance prijeđe 6 GB, killamo je i restartamo. To nije elegantno, ali je efektivno.

Round-robin dispečing u kodu nije ništa posebno — običan cirkularni iterator koji šalje zahtjeve na portove 8099, 8100, 8101 redom. Ali trik je u tome što svaki zahtjev nosi timeout od 10 sekundi i retry logiku. Ako instance na portu 8099 ne odgovori u roku ili vrati 503, klijent automatski preusmjerava na sljedeću instancu u poolu. To znači da čak i kad jedna instance padne, korisnik ne vidi error — samo blagi porast latencije od 100 do 200 milisekundi dok se retry izvrši. Naravno, ovo podrazumijeva da je klijentski kod svjestan konfiguracije porta i da ima hardkodiranu listu endpointa. Nismo išli u service discovery ili Kubernetes servise jer je to overkill za tri instance — jednostavni config file s tri reda je dovoljan.

Što se desi kad jedna instance padne? Prvo, cron job koji prati RSS će je detektirati kao mrtvu ako je OOM killana, ili će je restartati ako je prešla 6 GB. Ali dok se to dogodi, preostale dvije instance preuzimaju sav promet. Tu dolazi do izražaja onaj pragmatični odabir tri umjesto dvije instance. Kod dvije instance, gubitak jedne znači 50% kapaciteta, što u praksi znači dupli queue i latenciju koja raste linearno s brojem zahtjeva. Kod tri instance, gubitak jedne znači 33% kapaciteta, što je dovoljno da sistem ostane responzivan čak i pod normalnim opterećenjem. Naravno, ako imate peak, recimo black Friday ili neki batch import, latencija će porasti, ali neće biti totalnog collapsea.

Gradimo ovo svaki dan. Ako tvoja institucija troši mjesece na taj problem — razgovaraj s nama.

Graceful degrade je ključni koncept koji smo implementirali kroz eksplicitne timeoutove i fallback mehanizme. Kad reranker pool vrati rezultate, oni su rangirani po scoreu od najvišeg prema najnižem. Ali ako jedna instance padne, neke kombinacije upita i dokumenata mogu ostati bez scorea. U tom slučaju, naš sistem jednostavno preskače te dokumente ili ih stavlja na dno liste s defaultnim scoreom od 0.0. To nije idealno, ali je bolje nego vratiti prazan response ili error. Korisnik će dobiti manje relevantne rezultate, ali će ih dobiti — i to je bitno. U realnom scenariju, to znači da search functionality nikad nije potpuno mrtva, čak i kad jedan proces crkne.

Monitoring je ono što sve drži na okupu. Osim cron joba za memory, imamo i health check endpoint na svakoj instanci koji vraća status, trenutni RSS i broj obrađenih zahtjeva. Ti podaci idu u Grafana dashboard s alertima ako bilo koja instance padne ispod 3 GB RSS (što znači da je restartana) ili ako pređe 5.5 GB (što je predwarning). Bez toga, vi ste slijepi i nadate se da će sistem preživjeti noć. Mi smo imali situaciju gdje je jedna instance tiho curala memoriju 12 sati i pala u 3 ujutro, a druge dvije su preuzele promet bez problema. Da nismo imali pool, cijeli RAG pipeline bi bio mrtav do jutra.

Arhitektura s tri instance na fiksnim portovima nije moderna, nije cloud-native, nije ništa od toga. Ali radi. Dok drugi prave Kubernetes clustere s auto-scalingom i service meshe, mi imamo tri procesa koje ručno restartamo kad treba. I to je u redu, jer je jednostavno, transparentno i lako za debugirati. Kad vam padne produkcija u 2 u noći, ne želite petlju autoscalera i load balancera, želite znati koji port je mrtav i zašto. Portovi 8099, 8100, 8101 su lako pamtljivi, lako se testiraju s curl komandom, i lako se restartaju bez da dirate ostatak sistema.

Ono što smo naučili iz ovog setupa jest da high availability ne mora biti komplicirana. Ne trebate Kubernetes, ne trebate Redis, ne trebate ništa osim tri instance, round-robin dispečinga i jednog cron joba. Cijela stvar stane u 200 linija koda i config file od 10 redaka. A opet, rješava 90% problema s rerankerom. Preostalih 10% su edge casevi poput istovremenog pada dvije instance, što se događa iznimno rijetko, i u tom slučaju imamo ručni failover na treću instancu koja se diže u roku od 30 sekundi. To je dovoljno dobro za nas.

I za kraj, najvažnija lekcija: reranker pool nije samo o performansama, nego o predvidljivosti. Kad znate da će svaka instance obrađivati maksimalno 10 zahtjeva u paraleli, i da će svaki zahtjev trajati između 200 i 500 milisekundi, možete izračunati maksimalni throughput. Tri instance vam daju 30 konkurentnih zahtjeva, što je za nas sasvim dovoljno. A kad jedan padne, znate da će preostale dvije i dalje držati 20 konkurentnih zahtjeva, što je i dalje više nego što nam treba za 95% vremena. To je prava vrijednost ovog setupa — ne u tehnologiji, nego u mentalnom miru.

Ako te zanima ovakva arhitektura ili gradiš nešto slično za svoju instituciju — javi se preko rinet.one.

rerankerload balancinghigh availabilitycross-encoderRi.NETsovereign AIAI operativni sustavCroatian AItehnologija

Zanima vas ovakva AI tehnologija?

RiNET gradi sovereign AI rjesenja za javni sektor i poduzeca - od civic intelligence do automatizacije nabave.

Kontaktirajte nas →
0 komentara

Komentari