Večernja gužva: Izgradnja otporne kuhinje
Naš restoran je sada model moderne efikasnosti.
Možemo da obrađujemo složene porudžbine i upravljamo stanjem uživo (StateFlow) i događajima (SharedFlow).
Ali složimo se, do sada smo radili pod idealnim uslovima.
Šta se dešava kada nastane gužva u subotu uveče?
- Kuhinja (
producer) počinje da izbacuje jela mnogo brže nego što konobari (consumers) mogu da ih isporuče. - Specijalizovani zadatak poput sečenja mesa (
IO-intenzivan) obavlja se tik do delikatne stanice za serviranje (UI), izazivajući haos :(. - Jedno jelo zagori, i ceo švedski sto se zbog toga zatvara.
Da bi preživela gužvu, naša kuhinja mora biti više od efikasne. Mora biti otporna.
Backpressure: Kada je kuvar prebrz
Backpressure je pojava kada proizvođač (producer) emituje stavke brže nego što potrošač (consumer) može da ih obradi.
Podrazumevano, Flow je sekvencijalan. Kuvar čeka da konobar isporuči jedno jelo pre nego što počne sa sledećim. Ovo je sigurno, ali ne uvek i najefikasnije.
Pogledajmo primer brzog kuvara i sporog konobara:
| |
Izlaz:
| |
Kuvar je neprestano blokiran, čekajući sporog konobara. Ipak možemo to bolje.
Strategija 1: buffer() - Sto za podgrevanje
Operator buffer() pokreće korutinu proizvođača konkurentno sa potrošačem, sa baferom između.
Kuvar može da stavlja jela na sto za podgrevanje bez čekanja na konobara.

| |
Izlaz:
| |
Kuvar završava kuvanje skoro trenutno, a ukupno vreme sada zavisi samo od sporog konobara.
Strategija 2: conflate() - Tabla “Današnja preporuka”
Šta ako nam je stalo samo do najnovije vrednosti? conflate() je strategija gde spori potrošač preskače međuvrednosti.
Ako kuvar stavi tri nova jela dok je konobar zauzet, konobar će ignorisati prva dva i isporučiti samo poslednje.

| |
Izlaz:
| |
Strategija 3: collectLatest() - Polje za pretragu
Ovaj kolektor obrađuje samo poslednju vrednost, ali ide korak dalje. Ako nova vrednost stigne dok se prethodna obrađuje, on otkazuje stari blok za obradu i počinje iznova sa novom vrednošću. Ovo je savršen obrazac za obradu brzih UI događaja, poput upita u polju za pretragu.

| |
Izlaz:
| |
flowOn(): Održavanje reda u kuhinji
Podrazumevano, proizvođač i kolektor se izvršavaju u istoj korutini i na istom tredu.
Ovo može biti problem ako kuvar obavlja težak posao (poput sečenja mesa na IO tredu) koji ne bi trebalo da se dešava na delikatnoj stanici za serviranje (Main UI tred).
Operator flowOn() menja kontekst izvršavanja za upstream kod (proizvođača i sve operatore pre njega).

| |
Izlaz:
| |
flowOn deluje kao granica. Težak kuhinjski posao ostaje van glavnog treda.
catch(): Obrada zagorelog jela
Šta se dešava ako nešto krene po zlu u strimu? Podrazumevano, izuzetak će prekinuti flow i srušiti kolektor.
| |
Operator catch pruža deklarativan način za obradu upstream izuzetaka.
| |
Izlaz:
| |
Strim se nije srušio. Greška je elegantno obrađena.
Napomena:
catchmože da obradi samo izuzetke iz upstream operatora. Ne može da uhvati izuzetak u samomcollectbloku!
Zaključak
Kuhinja je sada zvanično otporna i spremna za večernju gužvu.
- Strategije za Backpressure: Upravlja brzim proizvođačima i sporim potrošačima pomoću buffer-a (konkurentnost), conflate-a (poslednja vrednost) i collectLatest-a (poništivi rad).
- flowOn: Dodeljuje određene delove strima ispravnom kontekstu, održavajući kod organizovanim i UI responzivnim.
- catch: Koristi se za deklarativnu obradu grešaka unutar strima, sprečavajući rušenja i omogućavajući oporavak.
Šta nas čeka u petom delu?
Dizajnirali smo neverovatan restoran, obučili osoblje i izgradili otporne sisteme. Ali kako da dokažemo da sve funkcioniše bez otvaranja za goste? Kako možemo biti sigurni da je kuvarov tajming ispravan i da je logika konobara dobra?
U poslednjem delu, zaronićemo u ključnu temu Testiranja korutina i Flow-ova.
Istražićemo biblioteku kotlinx-coroutines-test, naučiti kako da kontrolišemo virtuelno vreme i pišemo stabilne, pouzdane testove za naš asinhroni svet.