CobbleDB: Prestavba úložiska AI vyhľadávania pre nižšiu latenciu a náklady
Oddelená architektúra s oddeleným trvalým stavom, dávkovými aktualizáciami a vyhľadávaním v čase dopytu.
Vyhľadávanie natívne pre AI kladie odlišné požiadavky na to, ako je potrebné spracovávať, ukladat a obsluhovať webový obsah. Aby sme splnili tieto požiadavky vo veľkom meradle, prestavujeme celý zásobník od základu s cieľom zlepšiť výkon a znížiť náklady.
Vybudovali sme vlastnú infraštruktúru na prechádzanie, indexovanie a hodnotenie, modely vkladania a odvodzovacie motory na zabezpečenie efektivneho spracovania a rýchlej obsluhy. Tento prístup teraz rozširujeme na úložisko, spojivo, do ktorého spracovanie zapisuje a z ktorého obsluha číta.
Spravované databázy, ako napríklad DynamoDB, poskytovali spoľahlivé úložisko, ale mali sme len obmedzenú kontrolu nad tým, ako sa údaje ukladajú a čítania obsluhujú, pričom ceny založené na využití rástli s veľkosťou nášho indexu a objemom dopytov. Okrem toho náš predchádzajúci kanál spájal spracovanie dokumentov priamo s obsluhujúcou databázou, čo sťažovalo nezávislú správu aktualizácií oddelene od živého čítania.
S pomocou nového interného roja kódovacích agentov sme túto vrstvu prestavali okolo CobbleDB, aktívneho úložiska typu kľúč-hodnota, podporovaného Pillar pre trvalý stav dokumentov a publikovanie a Lorry pre dávkové aktualizácie. Redizajn oddeluje spracovanie dokumentov od príjmu v aktívnom úložisku a umožňuje nám prispôsobiť obsluhu nášmu pracovnému zaťaženiu. Merania pred a po v produkcii ukazujú približne 5× nižšiu latenciu dávkového čítania a náš interný model nákladov odhaduje úsporu najmenej 20 % v porovnaní s DynamoDB.
Úložná infraštruktúra pre vyhľadávanie natívne pre AI
Klasický vyhľadávací zásobník je vytvorený pre ľudského čitateľa, nie pre jazykový model. Tento rozdiel mení granularitu, v ktorej systém potrebuje pripravovať, ukladat a poskytovať webový obsah.
Aby sme premenili surové HTML na obsah pripravený pre model, náš spracovateľský kanál vyčistí každú stránku, rozdelí ju na sémanticky koherentné pasáže, vypočíta ich vloženia a uloží pasáže a vloženia spoločne v databáze. V čase dopytu obsluhujúci kanál vyberie text najrelevantnejší k dopytu a odošle ho modelu.
Dva rôzne pracovné postupy
Úložná vrstva musí preto podporovať dva pracovné postupy s rôznymi požiadavkami: príjem aktualizácií zo spracovateľského kanála a obsluhu čítania s nízkou latenciou.
Na strane zápisu prichádzajú nové stránky nepretržite, existujúce stránky sa menia a zmeny metód delenia alebo modelov vkladania môžu vyžadovať opätovné spracovanie veľkých častí korpusu. Úložná vrstva musí tieto aktualizácie začleniť do trvalého stavu dokumentu, sledovať aktuálne verzie, publikovať správne reprezentácie do nadväzujúcich systémov a podporovať veľké dopĺňania bez opakovania celého kanála zo surového prechádzania.
Naproti tomu čítania v čase dopytu vyžadujú dávky kľúčov stránok a načítavajú zodpovedajúci text pasáží a vloženia na účely hodnotenia a výberu pasáží. Tieto čítania sa nachádzajú na ceste k finálnej odpovedi, takže musia byť dokončené rýchlo.
Tieto pracovné zaťaženia si vyžadujú odlišné návrhy. Spracowanie ťaží z trvalých radov a schopnosti efektívne doručovať aktualizácie, opakovať zlyhania a uvádzať existujúce záznamy do aktuálneho stavu vo veľkom meradle. Obsluha ťaží z vopred vypočítaných záznamov, teplých vyrovnávacích pamätí a rýchleho dávkového čítania s minimálnou prácou v čase dopytu. Tesné prepojenie medzi nimi môže spôsobiť, že práca na obnove a veľké aktualizácie budú súťažiť s čítaniami citlivými na latenciu.
Kde spravovaná databáza nestačí
DynamoDB nám poskytla vysoko dostupné úložisko bez záťaže spojenej s prevádzkou distribuovanej databázy. Špeciálna tabuľka pripravených stránok uchovávala pasáže a vloženia na čítanie v čase dopytu, zatiaľ čo úplný obsah stránok a metadáda boli uložené osobitne. S rastúcim pracovným zaťažením sa však stali dôležitými tri obmedzenia.
Po prvé, ceny založené na využití účtujú každý prečítaný alebo zapísaný bajt. Jediná požiadavka na vyhľadávacie API obsahuje 100 – 120 kľúčov zodpovedajúcich stránkam, ktoré musíme získať. Proces získavania údajov rozdelí požiadavku na menšie dávky 10 – 20 kľúčov stránok, pričom priemerná veľkosť položky je približne 50 KB. Opakované čítanie týchto záznamov spotrebovávalo značnú kapacitu, zatiaľ čo neustále prechádzania a zmeny metód delenia alebo modelov vkladania generovali viac zápisov. Ekonomika tiež závisela od veľkosti a frekvencie záznamov, nielen od počtu volaní API.
Po druhé, spravovanej službe chýba flexibilita, ktorú potrebujeme na ladenie výkonu. Operácia, ktorú sme potrebovali, bola úzka: vzhľadom na dávku identifikátorov stránok rýchlo vrátiť ich pripravený obsah. Chceli sme kontrolu nad tým, ktorý stroj držal partition, koľko pamäte bolo venované jej vyrovnávacej pamäti a ktorá replika vybavovala požiadavku. Tieto ovládacie prvky sú dôležité, pretože čítanie mimo vyrovnávacej pamäte, skok medzi zónami alebo pomalá replika môžu oneskoriť celú dávku. DynamoDB spravuje vlastné úložisko a správanie replík za API. Službu sme mohli konfigurovať, ale nemohli sme vyladiť jej interné umiestnenie, vyrovnávacie pamäte úložného motora alebo politiku výberu replík vzhľadom na naše pracovné zaťaženie.
Po tretie, spracovanie bolo úzko spojené so zápismi do aktívneho úložiska. Náš predchádzajúci kanál spracovania zapisoval každú pripravenú stránku priamo do DynamoDB. Veľká úloha prepracovania sa preto stala vlnou jednotlivých aktualizácií aktívneho úložiska a spracovanie sa muselo prispôsobiť limitom priepustnosti aktívneho úložiska a správaniu pri opakovaní. Nemohli sme spustiť MapReduce nad celou databázou za 1 až 2 dni, aby sme vyskúšali novú metódu rozdeľovania, zmenili náš model vkladania alebo pridali nové pole. Neexistoval žiadny nezávislý prúd uložených aktualizácií stránok, ktorý by samostatná služba mohla dávkovať a doručovať do aktívneho úložiska vlastným tempom.
Riešenie týchto obmedzení si vyžadovalo viac než len výmenu databázy. Museli sme oddeliť trvalý stav dokumentu a doručovanie aktualizácií od úložiska pre čas dopytu a potom optimalizovať aktívne úložisko špeciálne pre rýchle dávkové čítanie.
Oddelený stav dokumentov, doručovanie aktualizácií a obsluha
Nová architektúra priraďuje každú zodpovednosť inému komponentu. Pillar udržiava trvalý stav dokumentu a rozhoduje, čo sa má publikovať. Lorry mení tieto exporty na rozdelené dávky. CobbleDB prijíma dávki a obsluhuje pripravené záznamy podľa kľúča stránky.
CobbleDB: cesta prispôsobiteľného čítania
CobbleDB je naše aktívne úložisko optimalizované na obsluhu pripravených záznamov v čase dopytu. Je to distribuované úložisko typu kľúč-hodnota: kľúče sú identifikátory stránok (zahashované adresy URL) a hodnoty sú spracované reprezentácie stránok (ich vopred rozdelené pasáže a vektorové vloženia pre každú časť).
Dôležitou zmenou je naša kontrola nad konfiguráciou úložiska.
Údaje sú rozdelené a rozložené medzi dátové uzly. Tento distribuovaný formát zabezpečuje, že databáza sa môže horizontálne škálovať tak, aby obsiahla obrovský korpus spracovaných stránok. Každá partition má tri repliky na troch rôznych uzloch; ak sa jedna kópia stane nedostupnou, ostatné môžu pokračovať v obsluhe čítaní.
Každému dátovému uzlu je priradená podmnožina partition. Uzol ukladá svoje údaje pomocou RocksDB, open-source vstrekovacieho úložného motora typu kľúč-hodnota, ktorý sa skvele hodí pre pracovné zaťaženia s náročným čítaním napájané dávkovým príjmom.
Údaje vo vyrovnávacej pamäti môžu byť obsluhované z pamäte, zatiaľ čo čítania mimo vyrovnávacej pamäte využívajú lokálne úložisko NVMe. To nám poskytuje priamy spôsob, ako doladiť rovnováhu medzí pamäťou a diskom pre pozorované pracovné zaťaženie namiesto prijímania politiky vnútornej vyrovnávacej pamäte spravovanej služby.
Bezstavový smerovač dopytov hashuje kľúče na partition a paralelne smeruje požiadavky na uzly. Architektúra zahŕňa aj metadátovú službu s podporou PostgreSQL a trvalé objektové úložisko.
Tento dizajn podporuje vzory čítania, ktoré si naše pracovné zaťaženie vyžaduje. Po tom, čo dopyt prejde vyhľadávaním a hodnotením s cieľom získať množinu kandidátov relevantných stránok, sa aktívny úložisko hromadne dopytuje na obsah týchto stránok. Tieto požiadavky na čítanie dorazia do smerovača a sú smerované do zodpovedajúceho dátového uzlu. Smerovač uprednostňuje odosielanie požiadaviek do uzlu v tej istej zóne dostupnosti, aby sa ak je to možné minimalizovala latencia medzi zónami. Každý dátový uzol môže načítať obsah stránky pomocou MultiGet, čo je dávkové rozhranie API pre čítanie v RocksDB určené na načítanie viacerých kľúčov naraz s nízkou latenciou. Ak uzol odpovedá pomaly, systém môže dopytovať aj inú repliku partition na inom uzle, čo zlepšuje chvostovú latenciu.
Môžeme sa tiež vyhnúť implementácii bežných funkcií, ktoré náš pracovný postup nevyžaduje. Napríklad väčšina databáz ponúka záruky konzistencie a podporu transakcií, ale naše aktívne úložisko nevyžaduje transakcie ani synchronizované repliky. Je prijateľné, ak existuje krátka medzera medzi zápisom dokumentu a jeho sprístupnením na čítanie, a takisto je v poriadku, ak jedna replika prijíma údaje rýchlejšie ako iná. Vynechanie funkcií, ktoré nepotrebujeme, znižuje réžiu a náklady nášho systému.
Pillar a Lorry: cesta odolných zápisov
Samostatná úložná vrstva sleduje všetko, čo prechádzame a spracúvame, pričom zlučuje aktualizácie do verzionovaného stavu dokumentov a publikuje potrebné reprezentácie do databázy CobbleDB a vyhľadávacieho indexu. Na tento účel sme vytvorili Pillar, našu vrstvu pre stav dokumentov a publikovanie.
Pillar je podporené systémom YTsaurus. Ukladá komponenty stránok (ako sú metadáta, časti a vloženia) do samostatných rodín tabuliek YTsaurus. Sleduje tiež verzie častí a vložení, čo umožňuje viacerým reprezentáciám koexistovať bez toho, aby sa navzájom prepísali, takže základné modely sa môžu meniť. Keďže Pillar a YTsaurus používajú lacné HDD úložisko, môžeme uložiť oveľa viac dokumentov, než sme predtým ukladali v DynamoDB.
Pillar sleduje podmnožiny alebo skupiny stránok definované politikou, ako sú čerstvé stránky alebo stránky s vysokou hodnotou. Členstvo v podmnožine určuje, ktoré aktualizácie je potrebné posunúť ďalej do nadväzujúcich komponentov, ako je CobbleDB. CobbleDB používa drahšie úložisko NVMe, takže exportujeme iba tie dokumenty, ktoré chceme reálne použiť vo výsledkoch vyhľadávania. Keď sa obsah stránky aktualizuje alebo ak je pridaná do podmnožiny či z nej odstránená, Pillar zodpovedá za zaradenie zodpovedajúcej exportnej práce do frontu.
Atómové transakcie v systéme YTsaurus zahŕňajú stav, zámery exportu a spracovaný vstup s cieľom zabezpečiť správnosť. Aby bola transakcia úspešne dokončená, systém musí aktualizovať trvalý stav, označiť vstup ako zaevidovaný a zaradiť do frontu všetky potrebné exporty; ak sa transakcia preruší, na žiadnej vrstve sa nevykoná žiadna zmena.
CobbleDB očakáva, že aktualizácie budú dávkované podľa partition. Exporty Pillar sú konvertované do tohto formátu bezstavovou službou, ktorú nazývame Lorry. Jej jedinou úlohou je čítať záznamy exportu z perzistentného radu zarovnaného na partition, kumulovať ich do dávkových súborov pre jednotlivé partition a odovzdať ich do CobbleDB.
Medzi Lorry a Cobble oddeľuje dodatočný protokol dátovú rovinu (S3) od riadiacej roviny. Lorry zapisuje dávku do S3 pod jedinečným identifikátorom a registruje dávku v rámci Cobble. Repliky partition Cobble nezávisle dopytujú dávku alebo API riadiacej roviny pre ďalšiu dávku S3 na príjem a asynchrónne aplikujú aktualizácie dávok v chronologickom poradí. To znamená, že pomalý alebo obnovujúci sa uzol môže dobehnúť zameškané vlastným tempom namiesto spomaľovania zvyšku systému.
Nová cesta oddeľuje okamžitú transakciu spracovania od príjmu v aktívnom úložisku a poskytuje opakovateľnú cestu pre prírastkové aktualizácie a hromadné obnovenia. Toto delenie prác zabezpečuje, že aktualizácie trvalého stavu dokumentu nenarúšajú latenciu cesty čítania.
Overenie
5x rýchlejšie
Po migrácii aktívneho úložiska klesla stredná latencia dávkového čítania z 31,4 ms na 5,60 ms. Percentil p90 klesol z 56,7 ms na 9,77 ms a p99 klesol z 123 ms na 24,2 ms. Pomer sa pohybuje od 5,08× do 5,80× so zlepšeniami pri bežných požiadavkách aj na chvoste distribúcie.
Tieto čísla sú odvodené z dávkového čítania, kde každý dopyt vyžaduje približne 10 – 15 kľúčov. Priemerná veľkosť položky v užitočnom zaťažení je 50 KB. V oboch systémoch sa dopytovalo rýchlosťou približne 200 000 rps, hoci sme vykonali aj záťažové testy do 500 000 rps bez akéhokoľvek zníženia výkonu.
Na izolovanie príspevku databázy CobbleDB boli všetky ostatné aspekty nastavenia zachované ako konštantné. Ide však o observačné porovnanie pred a po; systémy obsluhovali živú produkčnú prevádzku v rôznych časoch namiesto rovnakých požiadaviek za kontrolovaných podmienok. Preto sme odmerali aj výkon oboch systémov na syntetickom benchmarku. Na priblíženie požiadavkám v produkcii sme oba systémy testovali s dávkami 10 – 15 kľúčov a hodnotami od 100 B do 100 KiB.
DynamoDB v tomto kontexte zápasí s problémami, pretože jej spravovaná cesta čítania sa nedá prispôsobiť dávkovému čítaniu tejto veľkosti. Na rozdiel od toho je CobbleDB optimalizovaná presne pre toto nastavenie, keďže spravujeme umiestnenie, ukladanie do vyrovnávacej pamäte a ovládanie replík. Zoskupuje kľúče podľa partition, vykonáva čítania paralelne proti replikám partition a môže zaistiť pomalé čítania proti iným replikám. Naše výsledky ukazujú, že tieto konštrukčné voľby sa premietajú do rýchlejšieho čítania v produkčnom prostredí.
O 20 % lacnejšie
CobbleDB je tiež výrazne lacnejšia ako DynamoDB. Okrem úložiska účtuje DynamoDB aj každý prečítaný alebo zapísaný bajt. V meradle nášho korpusu a obsluhujúcej premávky sa tieto poplatky založené na využití stávajú podstatnou časťou celkových nákladov. Nepretržité aktualizácie spotrebúvajú kapacitu zápisu, zatiaľ čo každá dávka pripravených stránok načítaná pre dopyt spotrebúva kapacitu čítania.
Úsporu nákladov sme vypočítali na základe interných odhadov veľkosti úložiska, jednotiek kapacity zápisu a jednotiek kapacity čítania. Pri každej úrovni záväzku je CobbleDB minimálne o 20 % lacnejšia ako DynamoDB. Skutočná úspora je pravdepodobne ešte vyššia, pretože tieto výsledky nezohľadňujú pravdepodobnú úsporu na zálohovaní vďaka kompresii.
Ako nám roj AI agentov pomáha ho vybudovať
Vybudovanie databázy CobbleDB a okolitej infraštruktúry nebolo vôbec jednoduché. Samotná jadrová databáza CobbleDB má približne 40 000 riadkov v jazyku Rust a migrácia si vyžadovala aj rozsiahlu prácu na overení správnosti a správania pri zlyhaniach v produkčnom meradle, integráciu CobbleDB s cestami spracovania aj obsluhy a bezpečný prechod živej premávky.
Túto snahu sme urýchlili použitím interného systému, ktorý prevádzkuje roj kódovacích agentov. Systém je navrhnutý tak, aby bol vysoko perzistentný; uchováva ciele projektu, aktívne riziká, históriu repozitára a predchádzajúce rozhodnutia naprieč reláciami.
K projektu CobbleDB sme priradili kolektív takýchto proaktívnych agentov, ktorí sú neustále v pohotovosti. Ich prvou úlohou bolo vytvoriť presný model práce, ktorá už prebieha. Skontrolovali kanál projektu a aktívne vlákno, prepojili nevyriešené položky s ich pull requestmi a vlastníkmi a vytvorili aktuálny prehľad o stave systému a ďalších krokoch. Odtiaľ pôsobili vo viacerých častiach inžinierskej slučky:
- Skontrolované zmeny kódu a infraštruktúry.
- Odhalené blokátory, ktoré sa dali ľahko prehliadnuť, vrátane nebezpečných predpokladov obnovenia, zastaraných odkazov zostavenia a konfigurácií, ktoré by zlyhali za behu.
- Pripravené cielené opravy, testy, zmeny monitorovania a prevádzková dokumentácia.
- Sledované brány CI, kontroly a infraštruktúry pri zachovaní výslovne ľudskej zodpovednosti za produkčné akcie.
- Monitorované nasadenia a cvičenia zálohovania alebo obnovenia a následné porovnanie pozorovaného stavu systému s kritériami úspešnosti nasadenia.
- Udržiavané prehľady projektu, aby inžinieri videli, čo bolo odoslané, čo zostalo zablokované a kde sa stále vyžaduje ľudský úsudok.
Inžinieri stále stanovujú architektúru, kontrolujú dôsledné zmeny a schvaľujú produkčné operácie. Agenti zvládli veľkú časť nepretržitej kontroly a nadväznosti medzi týmito rozhodnutiami. To umožnilo malému tímu efektívne viesť projekt – základnú infraštruktúru CobbleDB vytvorili dvaja ľudskí inžinieri a stovky AI agentov za dva mesiace.
Záver
CobbleDB nie je všeobecná databáza; je špeciálne vytvorená pre naše špecifické pracovné zaťaženie tvorené opakovaným dávkovým čítaním záznamov pripravených stránok, poháňaným asynchrónnym kanálom spracovania. Oddelením trvalého stavu, doručovania aktualizácií a obsluhy v čase dopytu môžeme nezávisle optimalizovať každú zodpovednosť bez toho, aby sme nútili jediné databázové rozhranie prispôsobiť sa všetkým naraz alebo presúvali prácu spojenú s údržbou na cestu živých dopytov. Migrácia na CobbleDB výrazne zlepšila strednú aj chvostovú latenciu a zároveň znížila náklady na infraštruktúru.
Keďže naďalej interne vyvíjame infraštruktúru natívnu pre AI, môžeme optimalizovať výkon a náklady spôsobmi, ktoré nie sú možné, keď sme viazaní na implementácie tretích strán a modely cien. Výkonní agenti AI posúvajú výpočet ešte viac v prospech budovania namiesto nákupu.
CobbleDB plánujeme čoskoro sprístupniť ako open-source, aby ostatné tímy budujúce systémy vyhľadávania natívne pre AI mohli ťažiť z tej istej úložnej vrstvy, vďaka ktorej je vyhľadávanie v našich produktoch rýchlejšie a lacnejšie.
Citácia
@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}
}