CobbleDB: Ponovna izgradnja skladišta AI pretrage za nižu latenciju i troškove
Dekouplovana arhitektura sa odvojenim trajnim stanjem, grupnim ažuriranjima i pronalaženjem u vreme upita.
Pretraga zasnovana na veštačkoj inteligenciji nameće različite zahteve u pogledu načina na koji veb sadržaj treba da se obrađuje, skladišti i opslužuje. Da bismo odgovorili na te zahteve u velikoj skali, ponovo gradimo ceo stak od nule kako bismo poboljšali performanse i smanjili troškove.
Izgradili smo sopstvenu infrastrukturu za pretraživanje, indeksiranje i rangiranje, modele ugrađivanja i mašine za inferenciju kako bismo omogućili efikasnu obradu i brzo opsluživanje. Sada proširujemo taj pristup na skladište, vezivno tkivo u koje obrada upisuje podatke, a iz kojeg čitanje opslužuje.
Upravljane baze podataka kao što je DynamoDB obezbedile su pouzdano skladište, ali smo imali ograničenu kontrolu nad načinom na koji su podaci skladišteni i čitanja opsluživana, a cene zasnovane na korišćenju rasle su sa veličinom našeg indeksa i obimom upita. Odvojeno, naša prethodna cev je spajala obradu dokumenata direktno sa bazom podataka za opsluživanje, što je otežavalo upravljanje ažuriranjima nezavisno od čitanja uživo.
Uz pomoć novog internog roja agenata za kodiranje, ponovo smo izgradili ovaj sloj oko sistema CobbleDB, „hot“ skladišta ključ-vrednost, podržanog od strane Pillar-a za trajno stanje dokumenata i objavljivanje, i Lorry-ja za grupna ažuriranja. Redizajn odvaža obradu dokumenata od unosa u „hot“ skladište i omogućava nam da prilagodimo opsluživanje našem radnom opterećenju. Merenja pre i posle u produkciji pokazuju otprilike 5× manju latenciju grupnog čitanja, a naš interni model troškova procenjuje uštedu od najmanje 20% u odnosu na DynamoDB.
Infrastruktura skladišta za pretragu zasnovanu na veštačkoj inteligenciji
Klasičan stak za pretragu napravljen je za ljudskog čitaoca, a ne za jezički model. Ta razlika menja granularnost na kojoj sistem treba da pripremi, uskladišti i posluži veb sadržaj.
Da bi se sirovi HTML pretvorio u sadržaj spreman za model, naša cev za obradu čisti svaku stranicu, deli je na semantički koherentne odlomke, izračunava njihova ugrađivanja i skladišti odlomke i ugrađivanja zajedno u bazi podataka. U vreme upita, cev za opsluživanje bira tekst koji je najrelevantniji za upit i prosleđuje ga modelu.
Dva različita radna toka
Sloj za skladištenje mora stoga da podržava dva radna toka sa različitim zahtevima: unšenje ažuriranja iz cevi za obradu i opsluživanje čitanja sa niskom latencijom.
Sa strane upisa, nove stranice pristižu kontinuirano, postojeće stranice se menjaju, a promene u metodama deljenja na delove ili modelima ugrađivanja mogu zahtevati ponovnu obradu velikih delova korpusa. Sloj skladišta mora da ugradi ova ažuriranja u trajno stanje dokumenata, prati trenutne verzije, objavi prave reprezentacije ka sistemima nizvodno (downstream) i podrži velike popune bez ponavljanja cele cevi iz sirovog pretraživanja.
Nasuprot tome, čitanja u trenutku upita zahtevaju grupe ključeva stranica i preuzimaju odgovarajući tekst odlomaka i ugrađivanja za rangiranje i izbor odlomaka. Ova čitanja se nalaze na putu do konačnog odgovora, tako da moraju biti završena brzo.
Ova radna opterećenja zahtevaju različite dizajne. Obrada ima koristi od trajnih redova i sposobnosti efikasne isporuke ažuriranja, ponovnog pokušaja u slučaju grešaka i donošenja postojećih zapisa do datuma u velikoj meri. Opsluživanje ima koristi od unapred izračunatih zapisa, toplih keš memorija i brzih grupnih čitanja sa minimalnim poslom u vreme upita. Tesna sprega između ova dva elementa može prouzrokovati da posao oporavka i velika ažuriranja konkurišu sa čitanjima osetljivim na latenciju.
Gde upravljana baza podataka podbacuje
DynamoDB nam je obezbedio visoko dostupno skladište bez opterećenja upravljanja distribuiranom bazom podataka. Namenska tabela pripremljenih stranica sadržala je odlomke i ugrađivanja (embeddings) za čitanja u trenutku upita, dok su kompletan sadržaj stranice i metapodaci bili uskladišteni odvojeno. Međutim, kako je naše radno opterećenje raslo, tri ograničenja su postala važna.
Prvo, cene zasnovane na korišćenju naplaćuju svaki pročitani ili upisani bajt. Jedan zahtev API-ja za pretragu sadrži 100-120 ključeva koji odgovaraju stranicama koje moramo da preuzmemo. Proces preuzimanja podataka deli zahtev na manje grupe od 10-20 ključeva stranica sa prosečnom veličinom stavke od oko 50 KB. Ponavljano čitanje tih zapisa trošilo je znatan kapacitet, dok su kontinuirana pretraživanja i promena modela za deljenje na delove ili ugrađivanje generisali više upisa. Ekonomika je takođe zavisila od veličine i učestalosti zapisa, a ne samo od broja poziva API-ja.
Drugo, upravljani servis nema fleksibilnost koja nam je potrebna za podešavanje performansi. Operacija koja nam je bila potrebna bila je uska: s obzirom na grupu identifikatora stranica, brzo vratite njihov pripremljeni sadržaj. Želeli smo kontrolu nad tim koja mašina drži particiju, koliko je memorije posvećeno njegovom kešu i koja replika obrađuje zahtev. Te kontrole su važne zato što nepročitano keširanje, skok između zona ili spora replika mogu odložiti celu grupu. DynamoDB upravlja sopstvenim skladištem i ponašanjem replika iza API-ja. Mogli bismo da konfigurišemo servis, ali nismo mogli da podesimo njegovo interno smeštanje, keševe motora skladišta ili politiku izbora replika oko našeg radnog opterećenja.
Treće, obrada je bila direktno povezana sa upisima u „hot“ skladište. Naša prethodna cev za obradu upisivala je svaku pripremljenu stranicu direktno u DynamoDB. Veliki posao ponovne obrade stoga je postao talas pojedinačnih ažuriranja „hot“ skladišta, a obrada je morala da se prilagodi ograničenjima propusnog opsega „hot“ skladišta i ponašanju ponovnog pokušaja. Nismo mogli da pokrenemo MapReduce nad celom bazom podataka za 1-2 dana da bismo isprobali novu metodu deljenja na delove (chunking), promenili naš model ugrađivanja ili dodali novo polje. Nije postojao nezavisan tok sačuvanih ažuriranja stranica koji bi zaseban servis mogao da grupiše i isporuči u „hot“ skladište sopstvenim tempom.
Rešavanje ovih ograničenja zahtevalo je više od zamene baze podataka. Morali smo da odvojimo trajno stanje dokumenata i isporuku ažuriranja od skladišta za vreme upita, a zatim da optimizujemo „hot“ skladište specifično za brza grupna čitanja.
Odvojeno stanje dokumenata, isporuka ažuriranja i opsluživanje
Nova arhitektura dodeljuje svaku odgovornost drugoj komponenti. Pillar održava trajno stanje dokumenata i odlučuje šta da objavi. Lorry pretvara te eksporte u partionisane grupe. CobbleDB unosi grupe i opslužuje pripremljene zapise po ključu stranice.
CobbleDB: prilagodljiva putanja čitanja
CobbleDB je naše „hot“ skladište optimizovano za opsluživanje pripremljenih zapisa u trenutku upita. To je distribuirano skladište ključ-vrednost: ključevi su identifikatori stranica (heširani URL-ovi), a vrednosti su obrađena reprezentacija stranice (njihovi unapred isečeni odlomci i vektorska ugrađivanja po odlomku).
Važna promena je naša kontrola nad konfiguracijom skladišta.
Podaci su partionisani i podeljeni između čvorova podataka. Ovaj distribuirani format osigurava da baza podataka može horizontalno da se skalira kako bi primila ogroman korpus obrađenih stranica. Svaka particija ima tri replike na tri različita čvora; ako jedna kopija postane nedostupna, ostale mogu da nastave sa opsluživanjem čitanja.
Svakom čvoru podataka dodeljen je podskup particija. Čvor skladišti svoje podatke koristeći RocksDB, ugrađeni opensource mehanizam za skladištenje ključ-vrednost koji je dobro prilagođen radnim opterećenjima sa intenzivnim čitanjem hranjenim grupnim unosom.
Keširani podaci mogu se opsluživati iz memorije, dok čitanja koja nisu keširana koriste lokalno NVMe skladište. Ovo nam daje direktan način da prilagodimo balans memorije i diska za uočeno radno opterećenje, umesto da prihvatamo internu politiku keširanja upravljanog servisa.
Besstatni ruter upita hešira ključeve u particije i paralelno usmerava zahteve ka čvorovima. Arhitektura takođe uključuje servis metapodataka podržan sa PostgreSQL-om i trajno skladište objekata.
Ovaj dizajn podržava obrasce čitanja koje naše radno opterećenje zahteva. Nakon što upit prođe kroz pronalaženje i rangiranje kako bi se dobio kandidatski skup relevantnih stranica, od „hot“ skladišta se grupno traži sadržaj tih stranica. Ovi zahtevi za čitanje stižu do ruter-a i usmeravaju se na odgovarajući čvor podataka. Ruter daje prioritet slanju zahteva čvoru u istoj zoni dostupnosti kako bi se smanjila kašnjenja između zona, ako je to moguće. Svaki čvor podataka može da preuzme sadržaj stranice koristeći MultiGet, RocksDB API za grupno čitanje dizajniran za preuzimanje više ključeva istovremeno uz nisku latenciju. Ako je čvor spor u odgovoru, sistem takođe može da upita drugu repliku particije na drugom čvoru, poboljšavajući latenciju krajnjih vrednosti (tail latency).
Takođe možemo izbeći implementaciju uobičajenih funkcija koje naš radni tok ne zahteva. Na primer, većina baza podataka nudi garancije konzistentnosti i podršku za transakcije, ali naše „hot“ skladište ne zahteva transakcije ni sinhronizovane replike. Prihvatljivo je da postoji kratak vremenski razmak između trenutka kada je dokument upisan i kada je dostupan za čitanje, a takođe je u redu i da jedna replika unosi podatke brže od druge. Izostavljanje funkcija koje nam nisu potrebne smanjuje opterećenje i troškove našeg sistema.
Pillar i Lorry: trajna putanja upisa
Poseban sloj za skladištenje prati sve što pretražujemo i obrađujemo, spajajući ažuriranja u verzionisano stanje dokumenata i objavljujući neophodne reprezentacije u CobbleDB i indeks pretrage. U tu svrhu izgradili smo Pillar, naš sloj za stanje dokumenata i objavljivanje.
Pillar je podržan sistemom YTsaurus. On skladišti komponente stranica (kao što su metapodaci, delovi i ugrađivanja) u odvojenim porodicama tabela YTsaurus-a. Takođe prati verzije delova i ugrađivanja, omogućavajući da više reprezentacija postoji istovremeno bez prepisivanja jednih preko drugih, tako da se osnovni modeli mogu menjati. Pošto Pillar i YTsaurus koriste jeftino HDD skladište, možemo uskladištiti daleko više dokumenata nego što smo prethodno skladištili u DynamoDB-u.
Pillar prati podskupove, odnosno grupe stranica definisane politikom, kao što su sveže stranice ili stranice visoke vrednosti. Pripadnost podskupu određuje koja ažuriranja treba da se proslede komponentama nizvodno kao što je CobbleDB. CobbleDB koristi skuplje NVMe skladište, tako da izvozimo samo dokumente koje zaista želimo da koristimo u rezultatima pretrage. Kada se sadržaj stranice ažurira, ili ako se doda ili ukloni iz podskupa, Pillar je odgovoran za stavljanje u red odgovarajućeg posla eksporta.
Atomske YTsaurus transakcije obuhvataju stanje, namere eksporta i obrađeni unos kako bi se osigurala tačnost. Da bi se transakcija uspešno završila, sistem mora da ažurira trajno stanje, označi unos kao obrađen i stavi u red sve potrebne eksporte; ako se transakcija prekine, nijedna promena se ne vrši ni na jednom sloju.
CobbleDB očekuje da ažuriranja budu grupisana prema particiji. Pillar eksporti se konvertuju u ovaj format pomoću besstatnog servisa koji zovemo Lorry. Njegov jedini zadatak je da čita zapise eksporta iz trajnog reda usklađenog sa particijama, akumulira ih u datoteke grupa po particijama i prosleđuje ih u CobbleDB.
Između sistema Lorry i Cobble, dodatni protokol odvaža ravan podataka (S3) od upravljačke ravni. Lorry upisuje grupu u S3 pod jedinstvenim identifikatorom i registruje grupu unutar Cobble-a. Replike particija Cobble-a nezavisno upituju grupu ili API upravljačke ravni za sledeću S3 grupu za unos i asinhrono primenjuju ažuriranja grupa hronološkim redosledom. To znači da spor ili oporavljajući čvor može da sustigne zaostatak sopstvenim tempom, umesto da usporava ostatak sistema.
Nova putanja odvaža neposrednu transakciju obrade od unosa u „hot“ skladište i pruža putanju koja se može ponovo reprodukovati za inkrementalna ažuriranja i masovne obnove. Ova podela rada osigurava da ažuriranja trajnog stanja dokumenata ne ometaju latenciju putanje čitanja.
Validacija
5x brže
Nakon migracije „hot“ skladišta, medijana latencije grupnog čitanja pala je sa 31,4 ms na 5,60 ms. P90 je pao sa 56,7 ms na 9,77 ms, a p99 sa 123 ms na 24,2 ms. Odnosi se kreću od 5,08× do 5,80×, uz poboljšanja kako kod tipičnih zahteva, tako i kod krajnjih vrednosti (tail).
Ovi brojevi su izvedeni iz grupnih čitanja, gde svaki upit zahteva otprilike 10-15 ključeva. Prosečna veličina stavke u prenosu podataka je 50KB. Oba sistema su upitana sa oko 200k rps, iako smo takođe pokrenuli testove opterećenja do 500k rps bez degradacije.
Da bi se izolovao doprinos sistema CobbleDB, svi drugi aspekti podešavanja su zadržani konstantnim. Međutim, ovo ostaje opservaciono poređenje pre i posle; sistemi su služili produkcijski saobraćaj uživo u različito vreme, umesto da obrađuju iste zahteve pod kontrolisanim uslovima. Stoga smo takođe izmerili performanse oba sistema na sintetičkom benčmarku. Da bismo se približili zahtevima viđenim u produkciji, uputilosmo upite obema platformama sa grupama od 10–15 ključeva i vrednostima u opsegu od 100B do 100 KiB.
DynamoDB se muči u ovom kontekstu jer se njegova upravljana putanja čitanja ne može prilagoditi za grupna čitanja ove veličine. Nasuprot tome, CobbleDB je optimizovan upravo za ovo okruženje, pošto upravljamo smeštanjem, keširanjem i kontrolama replika. On grupše ključeve po particiji, izvršava čitanja paralelno u odnosu na replike particija i može da zaštiti spore operacije čitanja u odnosu na druge replike. Naši rezultati pokazuju da se ovi izbori dizajna pretvaraju u brža čitanja u produkcijskom okruženju.
20% jeftinije
CobbleDB je takođe znatno jeftiniji od DynamoDB-a. Pored skladišta, DynamoDB takođe naplaćuje svaki pročitani ili upisani bajt. Na skali našeg korpusa i saobraćaja opsluživanja, ove naknade zasnovane na korišćenju postaju značajan deo ukupnih troškova. Kontinuirana ažuriranja troše kapacitet upisa, dok svaka grupa pripremljenih stranica preuzetih za upit troši kapacitet čitanja.
Izračunali smo uštedu troškova na osnovu internih procena veličine skladišta, jedinica kapaciteta upisa i jedinica kapaciteta čitanja. Na svakom nivou posvećenosti, CobbleDB je najmanje 20% jeftiniji od DynamoDB-a. Stvarne uštede su verovatno još veće, pošto ovi rezultati ne uzimaju u obzir verovatnu uštedu rezervnih kopija usled kompresije.
Kako nam roj AI agenata pomaže da ga izgradimo
Izgradnja sistema CobbleDB i prateće infrastrukture bila je daleko od jednostavne. Sama jezgro baze podataka CobbleDB ima oko 40.000 linija koda u Rrust-u, a migracija je takođe zahtevala obsežan rad na proveri tačnosti i ponašanja u slučaju otkaza u produkcijskoj skali, integraciju sistema CobbleDB sa putanjama obrade i opsluživanja, kao i bezbedan prelazak na saobraćaj uživo.
Ubrzali smo ovaj napor koristeći interni sistem koji upravlja rojem agenata za kodiranje. Sistem je dizajniran da bude izuzetno perzistentan; zadržava ciljeve projekta, aktivne rizike, istoriju skladišta i prethodne odluke kroz različite sesije.
Dodelili smo kolektiv takvih proaktivnih, stalno aktivnih agenata projektu CobbleDB. Njihov prvi zadatak bio je da izgrade tačan model posla koji je već u toku. Revidirali su kanal projekta i aktivne niti, povezali istaknute stavke sa njihovim pull request-ovima i vlasnicima, i proizveli trenutni pregled zdravlja sistema i sledećih radnji. Odatle su delovali u nekoliko delova inženjerske petlje:
- Pregledane promene koda i infrastrukture.
- Iscrtani blokatori koje je bilo lako propustiti, uključujući nesigurne pretpostavke vraćanja u prethodno stanje, zastarele reference gradnje i konfiguracije koje bi pale u izvršavanju.
- Pripremljene fokusirane ispravke, testovi, promene praćenja i operativna dokumentacija.
- Praćeni CI, pregled i infrastrukturne kapije dok su produkcijske radnje eksplicitno ostale pod ljudskim vlasništvom.
- Praćena uvođenja i vežbe rezervnih kopija ili vraćanja u prethodno stanje, a zatim upoređeno uočeno stanje sistema sa kriterijumima uspešnosti uvođenja.
- Održavani izveštaji o projektu tako da inženjeri mogu da vide šta je isporučeno, šta je ostalo blokirano i gde je ljudska procena i dalje bila potrebna.
Inženjeri i dalje postavljaju arhitekturu, pregledaju posledične promene i odobravaju produkcijske operacije. Agenti su obavili veći deo kontinuirane inspekcije i praćenja između tih odluka. Ovo je omogućilo malom timu da efikasno vodi projekat – osnovna infrastruktura sistema CobbleDB izgrađena je od strane dva ljudska inženjera i stotina AI agenata za dva meseca.
Zaključak
CobbleDB nije baza podataka opšte namene; napravljena je namenski za naše specifično radno opterećenje ponovljenih grupnih čitanja pripremljenih zapisa stranica, podržana asinhronom cevi za obradu. Odvajanjem trajnog stanja, isporuke ažuriranja i opsluživanja u trenutku upita, možemo nezavisno optimizovati svaku odgovornost bez primoravanja jedinstvenog interfejsa baze podataka da ih sve prilagodi ili prenošenja posla održavanja na putanju upita uživo. Migracija na CobbleDB značajno je poboljšala i medijanu i latenciju krajnjih vrednosti (tail latency) uz smanjenje troškova infrastrukture.
Dok nastavljamo da interno razvijamo infrastrukturu izvornu za veštačku inteligenciju (AI-native), možemo optimizovati performanse i troškove na načine koji nisu mogući kada smo vezani za implementacije i modele cena trećih strana. Moćni AI agenti pomeraju računicu još više u korist pravljenja umesto kupovine.
Planiramo da uskoro otvorimo kod sistema CobbleDB (open-source), tako da drugi timovi koji grade sisteme pretrage zasnovane na veštačkoj inteligenciji mogu da imaju koristi od istog sloja skladišta koji pretragu čini bržom i jeftinijom unutar naših proizvoda.
Citiranje
@online{perplexity2026Cobbledb,
author = {{Perplexity} and Alexandr Yarats and Marco Montagna and Aleksei Bykov and Alexey Gusev and Ali Abdullaev and Amir Mokhammed-Ali and Andrey Zosimov and Denis Kremko and Gleb Vishnevsky and Micah Zirn and Phi Hoang and Claire Beamer and Jeremy Yang and Jerry Ma and Denis Yarats},
title = {{CobbleDB: Rebuilding AI search storage for lower latency and cost}},
date = {2026-09-14},
url = {https://www.perplexity.ai/hub/blog/cobbledb}
}