„CobbleDB“: DI paieškos saugyklos atstatymas mažesnei delsai ir sąnaudoms

Atskirta architektūra su padalinta patvaria būsena, paketiniais atnaujinimais ir paieška užklausos metu.

AutoriaiPerplexity Engineering

Dirbtinio intelekto pagrindu veikianti paieška kelia skirtingus reikalavimus tam, kaip žiniatinklio turinys turi būti apdorojamas, saugomas ir aptarnaujamas. Siekdami patenkinti šiuos poreikius dideliu mastu, iš naujo atkūrėme visą architektūrą nuo nulio, kad pagerintume našumą ir sumažintume išlaidas.

Sukūrėme savo paieškos vorų, indeksavimo ir reitingavimo infrastruktūrą, vektorių modelius ir išvedimo variklius, kad užtikrintume veiksmingą apdorojimą ir greitą aptarnavimą. Dabar šį požiūrį plečiame į saugyklą – jungiamąjį audinį, į kurį apdorojimas rašo, o aptarnavimas skaito.

Valdomos duomenų bazės, tokios kaip „DynamoDB“, užtikrino patikimą saugyklą, tačiau turėjome ribotą kontrolę, kaip duomenys saugomi ir kaip aptarnaujami nuskaitymai, o nuo naudojimo priklausoma kainodara augo kartu su mūsų indekso dydžiu ir užklausų apimtimi. Be to, mūsų ankstesnis konvejeris susiejo dokumentų apdorojimą tiesiogiai su aptarnaujančia duomenų baze, todėl buvo sunku valdyti atnaujinimus nepriklausomai nuo tiesioginių nuskaitymų.

Padedami naujo vidinio kodavimo agentų spiečiaus, atstatėme šį sluoksnį aplink „CobbleDB“ – raktų-reikšmių karštąją saugyklą, kurią palaiko „Pillar“ patvariai dokumento būsenai ir publikavimui bei „Lorry“ paketiniais atnaujinimams. Šis pertvarkymas atskiria dokumentų apdorojimą nuo karštosios saugyklos įrašymo ir leidžia pritaikyti aptarnavimą mūsų darbo eigai. Gamybiniai matavimai prieš ir po rodo maždaug 5 kartus mažesnę paketinio nuskaitymo delsą, o mūsų vidinis sąnaudų modelis vertina bent 20% sutaupymą lyginant su „DynamoDB“.

Infrastruktūra dirbtinio intelekto pagrindu veikiančiai paieškai

Klasikinė paieškos architektūra sukurta žmogui skaitytojui, o ne kalbos modeliui. Šis skirtumas keičia detalumo lygį, kuriuo sistema turi paruošti, saugoti ir pateikti žiniatinklio turinį.

Kad žalias HTML būtų paverstas modeliui paruoštu turiniu, mūsų apdorojimo konvejeris išvalo kiekvieną puslapį, padalija jį į semantiniu požiūriu nuoseklias ištraukas, apskaičiuoja jų vektorių atvaizdus ir kartu išsaugo ištraukas bei vektorių atvaizdus duomenų bazėje. Užklausos metu aptarnavimo konvejeris atrenka užklausai labiausiai tinkamą tekstą ir perduoda jį modeliui.

Dvi skirtingos darbo eigos

Todėl saugyklos sluoksnis turi palaikyti dvi darbo eigos su skirtingais reikalavimais: atnaujinimų gavimą iš apdorojimo konvejerio ir mažos delsos nuskaitymų aptarnavimą.

Įrašymo pusėje nuolat atsiranda naujų puslapių, keičiasi esami puslapiai, o skaidymo metodų ar vektorių modelių pakeitimai gali pareikalauti pakartotinio didelės korpuso dalies apdorojimo. Saugyklos sluoksnis turi įtraukti šiuos atnaujinimus į patvarią dokumento būseną, sekti dabartines versijas, publikuoti tinkamus atvaizdavimus pasroviui esančioms sistemoms ir palaikyti didelius užpildymus nekartojant viso konvejerio iš žalio paieškos voro nuskaitymo.

Priešingai, nuskaitymai užklausos metu reikalauja puslapių raktų paketų ir gauna atitinkamą ištraukos tekstą bei vektorių atvaizdus reitingavimui ir ištraukų parinkimui. Šie nuskaitymai yra kelio į galutinį atsakymą dalis, todėl jie turi būti atlikti greitai.

Šiems darbo krūviams reikalingi skirtingi dizainai. Apdorojimui naudingos patvarios eilės ir galimybė efektyviai pristatyti atnaujinimus, pakartotinai bandyti esant klaidoms ir masteliu atnaujinti esamus įrašus. Aptarnavimui naudingi iš anksto apskaičiuoti įrašai, šiltos talpyklos ir greiti paketiniai nuskaitymai su minimaliu darbu užklausos metu. Glaudus ryšys tarp jų gali priversti atkūrimo darbą ir didelius atnaujinimus konkuruoti su deltrai jautriais nuskaitymais.

Kur valdoma duomenų bazė pasirodo esanti nepakankama

„DynamoDB“ suteikė mums didelio pasiekiamumo saugyklą be naštos valdyti paskirstytąją duomenų bazę. Specialioje paruoštų puslapių lentelėje buvo saugomos ištraukos ir vektorių atvaizdai nuskaitymui užklausos metu, o visas puslapio turinys ir metaduomenys buvo saugomi atskirai. Tačiau augant darbo eigai, trys apribojimai tapo svarbūs.

Antra, nuo naudojimo priklausoma kainodara apmokestina kiekvieną perskaitytą ar įrašytą baitą. Viena Paieškos API užklausa apima 100–120 raktų, atitinkančių puslapius, kuriuos turime gauti. Duomenų gavimo procesas padalija užklausą į mažesnius 10–20 puslapių raktų paketus, o vidutinis elemento dydis yra apie 50 KB. Pakartotinis šių įrašų nuskaitymas sunaudojo didelius pajėgumus, o nuolatiniai nuskaitymai ir skaidymo ar vektorių modelių pakeitimai generavo daugiau įrašų. Ekonomika taip pat priklausė nuo įrašų dydžio ir dažnumo, o ne tik nuo API užklausų skaičiaus.

Antra, valdomai paslaugai trūksta lankstumo, kurio mums reikia našumo derinimo tikslais. Mūsų reikalinga operacija buvo siaura: turint puslapių identifikatorių paketą, greitai grąžinti jų paruoštą turinį. Norėjome valdyti, kuri mašina laiko skirsnį, kiek atminties skirta jos talpyklai ir kuri kopija apdoroja užklausą. Šie valdikliai yra svarbūs, nes talpykloje nesančių duomenų nuskaitymas, šuolis per zonas arba lėta kopija gali vėluoti visą paketą. „DynamoDB“ valdo savo saugyklą ir kopijų elgseną už API. Mes galėjome konfigūruoti paslaugą, tačiau negalėjome suderinti jos vidinio išdėstymo, saugyklos variklio talpyklų ar kopijos pasirinkimo politikos pagal mūsų darbo eigą.

Trečia, apdorojimas buvo tiesiogiai susietas su karštosios saugyklos įrašais. Mūsų ankstesnis apdorojimo konvejeris įrašė kiekvieną paruoštą puslapį tiesiai į „DynamoDB“. Todėl didelė pakartotinio apdorojimo užduotis tapo individualių karštosios saugyklos atnaujinimų banga, o apdorojimas turėjo prisitaikyti prie karštosios saugyklos pralaidumo ribų ir pakartotinių bandymų elgsenos. Negalėjome paleisti „MapReduce“ visoje duomenų bazėje per 1–2 dienas, kad išbandytume naują skaidymo metodą, pakeistume savo vektorių modelį arba pridėtume naują lauką. Nebuvo nepriklausomo išsaugotų puslapių atnaujinimų srauto, kurį atskira paslauga galėtų sugrupuoti į paketus ir pristatyti į karštąją saugyklą savo tempu.

Šių apribojimų sprendimas reikalavo daugiau nei tik duomenų bazės pakeitimo. Turėjome atskirti patvarią dokumento būseną ir atnaujinimų pristatymą nuo saugyklos užklausų metu, o tada optimizuoti karštąją saugyklą būtent greitam paketiniam nuskaitymui.

Atskirkite dokumento būseną, atnaujinimų pristatymą ir aptarnavimą

Naujoji architektūra priskiria kiekvieną atsakomybę skirtingam komponentui. „Pillar“ palaiko patvarią dokumento būseną ir nusprendžia, ką publikuoti. „Lorry“ paverčia šį eksportą suskirstytais į paketus duomenimis. „CobbleDB“ gauna paketus ir aptarnauja paruoštus įrašus pagal puslapio raktą.

Prieš ir po saugyklos migracijos. Pillar ir Lorry atskiria patvarią dokumentų būseną ir atnaujinimų pateikimą nuo CobbleDB teikiamų užklausos laiko nuskaitymų.

„CobbleDB“: reguliuojamas nuskaitymo kelias

„CobbleDB“ yra mūsų karštoji saugykla, optimizuota paruoštų įrašų aptarnavimui užklausos metu. Tai paskirstyta raktų-reikšmių saugykla: raktai yra puslapių identifikatoriai (maišos URL), o reikšmės – apdorota puslapio išraiška (iš anksto sukapotos ištraukos ir vektorių atvaizdai kiekvienam blokui).

Svarbus pokytis yra mūsų kontrolė virš saugyklos konfigūracijos.

Duomenys yra padalinti ir išskirstyti tarp duomenų mazgų. Šis paskirstytas formatas užtikrina, kad duomenų bazė gali horizontaliai plėstis, kad galėtų sutalpinti milžinišką apdorotų puslapių rinkinį. Kiekvienas skirsnis turi tris kopijas trijuose skirtinguose mazguose; jei viena kopija tampa nepasiekiama, kiti gali toliau aptarnauti nuskaitymus.

Kiekvienam duomenų mazgui priskiriamas dalinis skirsnių poaibis. Mazgas saugo savo duomenis naudodamas „RocksDB“ – atvirojo kodo įterptąją raktų-reikšmių saugyklos variklį, puikiai pritaikytą nuskaitymo apkrovoms, kurioms duomenys tiekiami paketais.

Talpykloje saugomi duomenys gali būti aptarnaujami iš atminties, o neįtraukti į talpyklą nuskaitymai naudoja vietinę NVMe saugyklą. Tai suteikia mums tiesioginį būdą suderinti atminties ir disko balansą stebėtai darbo eigai, o ne priimti valdomos paslaugos vidinę talpyklos politiką.

Bevalstis užklausų maršruto parinktuvas atlieka raktų maišą skirsniams ir lygiagrečiai nukreipia užklausas į mazgus. Architektūroje taip pat yra PostgreSQL pagrindu veikianti metaduomenų paslauga ir patvari objektų saugykla.

Šis dizainas palaiko nuskaitymo šablonus, kurių reikalauja mūsų darbo eiga. Užklausai praėjus gavimo ir reitingavimo etapus, kad būtų gautas tinkamų puslapių kandidatų rinkinys, karštojoje saugykloje paketu ieškoma tų puslapių turinio. Šios nuskaitymo užklausos pasiekia maršruto parinktuvą ir yra nukreipiamos į atitinkamą duomenų mazgą. Maršruto parinktuvas teikia pirmenybę užklausų siuntimui į tą patį pasiekiamumo zoną esantį mazgą, kad būtų sumažinta delsos trukmė tarp zonų, jei įmanoma. Kiekvienas duomenų mazgas gali gauti puslapio turinį naudodamas „MultiGet“ – „RocksDB“ paketinio nuskaitymo API, skirtą vienu metu maža delsa nuskaityti kelis raktus. Jei mazgas lėtai atsako, sistema taip pat gali užklausti kito skirsnio kopijos kitame mazge, pagerindama kraštstinę delsą.

Taip pat galime išvengti dažnų funkcijų, kurių mūsų darbo eiga nereikalauja, įgyvendinimo. Pavyzdžiui, dauguma duomenų bazių siūlo nuoseklumo garantijas ir tranzakcijų palaikymą, tačiau mūsų karštoji saugykla nereikalauja tranzakcijų ar sinchronizuotų kopijų. Yra priimtina, kad būtų trumpas laiko tarpas tarp dokumento įrašymo ir jo pasiekimui nuskaitymui, taip pat yra normalu, jeigu viena kopija gauna duomenis greičiau nei kita. Nereikalingų funkcijų atsisakymas sumažina mūsų sistemos papildomas išlaidas ir kaštus.

Būsenos neturintis maršrutizatorius sugrupuoja puslapių raktus pagal skaidinį ir lygiagrečiai nuskaito replikas. Lėti nuskaitymai gali būti nukreipiami į kitą repliką; pirmasis kiekvieno skaidinio atsakymas sujungiamas į rezultatą.

„Pillar“ ir „Lorry“: patvarus įrašymo kelias

Atskiras saugyklos sluoksnis fiksuoja viską, ką nuskaitome ir apdorojame, sujungdamas atnaujinimus su versijuojama dokumento būsena ir pateikdamas reikiamas atvaizdavimo formas į „CobbleDB“ bei paieškos indeksą. Tam sukūrėme savo dokumentų būsenos ir publikavimo sluoksnį „Pillar“.

„Pillar“ palaiko „YTsaurus“. Ji saugo puslapių komponentus (tokius kaip metaduomenys, blokai ir vektorių atvaizdai) atskirose „YTsaurus“ lentelių šeimose. Ji taip pat seka blokų ir vektorių atvaizdų versijas, leisdama keliems atvaizdavimams egzistuoti vienu metu be vienas kito perrašymo, kad pagrindiniai modeliai galėtų keistis. Kadangi „Pillar“ ir „YTsaurus“ naudoja pigią HDD saugyklą, galime saugoti kur kas daugiau dokumentų nei anksčiau saugojome „DynamoDB“.

„Pillar“ seka poaibius arba politikos apibrėžtas puslapių grupes, tokias kaip nauji puslapiai arba didelės vertės puslapiai. Poaibio narystė nustato, kuriuos atnaujinimus reikia perduoti pasroviui esantiems komponentams, pavyzdžiui, „CobbleDB“. „CobbleDB“ naudoja brangesnę NVMe saugyklą, todėl eksportuojame tik tuos dokumentus, kuriuos iš tikrųjų norime naudoti paieškos rezultatuose. Kai puslapio turinys atnaujinamas arba jis įtraukiamas į poaibį ar pašalinamas iš jo, „Pillar“ yra atsakinga už atitinkamo eksporto darbo įtraukimą į eilę.

Atominės „YTsaurus“ tranzakcijos apima būseną, eksporto ketinimus ir įvestą informaciją, kad būtų užtikrintas teisingumas. Kad tranzakcija sėkmingai pasibaigtų, sistema privalo atnaujinti patvarią būseną, pažymėti įvestį kaip apskaitytą ir suorganizuoti visą reikiamą eksportą; jei tranzakcija nutraukiama, jokiame sluoksnyje niekas nekeičiama.

„CobbleDB“ tikisi, kad atnaujinimai bus sugrupuoti į paketus pagal skirsnį. „Pillar“ eksporto duomenys yra konvertuojami į šį formatą per bevalstę (stateless) paslaugą, kurią vadiname „Lorry“. Jos vienintelė užduotis yra nuskaityti eksporto įrašus iš nuolatinės pagal skirsnius sulygiuotos eilės, sukaupti juos į paketinius failus kiekvienam skirsniui ir perduoti juos „CobbleDB“.

Tarp „Lorry“ ir „Cobble“ papildomas protokolas atskiria duomenų plokštumą (S3) nuo valdymo plokštumos. „Lorry“ įrašo paketą į S3 naudodama unikalų identifikatorių ir užregistruoja paketą „Cobble“ viduje. „Cobble“ skirsnio kopijos nepriklausomai užklausia paketą arba valdymo plokštumos API dėl kito S3 paketo gaunamų duomenų ir asynergiškai pritaiko paketo atnaujinimus chronologine tvarka. Tai reiškia, kad lėtas arba atsigaunantis mazgas gali pasivyti savo tempu, o ne lėtinti likusios sistemos.

Pillar publikuoja patvarius eksporto rinkinius, o Lorry rašo suskaidytas sekas į S3 ir užregistruoja jas valdymo plokštumoje. Kiekviena CobbleDB replikacija nepriklausomai aptinka ir įsisavina savo sekas chronologine tvarka.

Naujasis kelias atskiria tiesioginio apdorojimo tranzakciją nuo karštosios saugyklos duomenų gavimo ir suteikia atgaminamą kelią inkrementiniams atnaujinimams bei masiniams atstatymams. Šis darbų padalijimas užtikrina, kad patvarios dokumento būsenos atnaujinimai netrukdytų nuskaitymo kelio delsai.

Validavimas

5 kartus greičiau

Migravus karštąją saugyklą, mediana paketinio nuskaitymo delsa sumažėjo nuo 31,4 ms iki 5,60 ms. P90 vertė sumažėjo nuo 56,7 ms iki 9,77 ms, o P99 vertė – nuo 123 ms iki 24,2 ms. Santykiai svyruoja nuo 5,08× iki 5,80×, o patobulinimai matomi tiek įprastose užklausose, tiek kraštutinėse reikšmėse.

Gamybinė paketinio nuskaitymo delsa prieš migraciją ir po jos: P50 sumažėjo nuo 31,4 iki 5,60 ms, P90 nuo 56,7 iki 9,77 ms, o P99 nuo 123 iki 24,2 ms. Tai yra karštosios saugyklos nuskaitymo delsos iš gyvo srauto skirtingu metu, o ne sutapatintų užklausų ar galutinio atsakymo delsa.

Šie skaičiai yra gauti iš nuskaitymų paketais, kai kiekviena užklausa reikalauja maždaug 10–15 raktų. Vidutinis naudingosios apkrovos elemento dydis yra 50 KB. Abiems sistemoms buvo teikiamos užklausos maždaug 200k rps greičiu, nors mes taip pat vykdėme apkrovos testus iki 500k rps be jokio kokybės pablogėjimo.

Siekiant išskirti „CobbleDB“ indėlį, visi kiti sąrankos aspektai buvo palikti nepakeisti. Tačiau tai tebėra stebimasis palyginimas prieš ir po; sistemas, o ne identiškas užklausas kontroliuojamomis sąlygomis, aptarnavo tiesioginį gamybinį srautą skirtingu metu. Todėl mes taip pat išmatavome abiejų sistemų našumą pagal sintetinį etaloninį testą. Kad pritaikytume gamyboje matytas užklausas, užklausėme abiejų sistemų su 10–15 raktų paketais, o reikšmės svyravo nuo 100B iki 100 KiB.

Sintetinė paketinio nuskaitymo delsa paketams po 10 ir 15 raktų, esant verčių dydžiams nuo 100 B iki 100 KiB. Kiekvienas taškas yra dviejų kiekvieno backend’o ir konfigūracijos paleidimų runų lygmens P50, P90 arba P99 delsos vidurkis.

„DynamoDB“ šiame kontekste susiduria su sunkumais, nes jos valdomas nuskaitymo kelias negali būti suderintas tokio dydžio paketo nuskaitymams. Priešingai, „CobbleDB“ yra optimizuota būtent šiai aplinkai, nes mes valdome išdėstymą, spartųjį atmintinė ir kopijų valdiklius. Ji grupuoja raktus pagal skirsnius, vykdo nuskaitymus lygiagrečiai su skirsnių kopijomis ir gali apdrausti lėtus nuskaitymus kitų kopijų atžvilgiu. Mūsų rezultatai rodo, kad šie dizaino pasirinkimai užtikrina greitesnį nuskaitymą gamybinėje aplinkoje.

20% pigiau

„CobbleDB“ taip pat yra žymiai pigesnė nei „DynamoDB“. Be saugyklos, „DynamoDB“ taip pat apmokestina kiekvieną perskaitytą ar įrašytą baitą. Mūsų korpuso ir aptarnavimo srauto mastu šie nuo naudojimo priklausomi mokesčiai tampa didele visų išlaidų dalimi. Nuolatiniai atnaujinimai eikvoja įrašymo pajėgumą, o kiekvienas užklausai nuskaitytas paruoštų puslapių paketas eikvoja nuskaitymo pajėgumą.

Sąnaudų sutaupymus apskaičiavome remdamiesi vidiniais saugyklos dydžio, įrašymo pajėgumo vienetų ir nuskaitymo pajėgumo vienetų įverčiais. Kiekviename įsipareigojimo lygyje „CobbleDB“ yra bent 20% pigesnė nei „DynamoDB“. Realus sutaupymas tikriausiai yra dar didesnis, nes šie rezultatai neatsižvelgia į galimą atsarginių kopijų sutaupymą dėl glaudinimo.

Kaip dirbtinio intelekto agentų spiečius padeda mums tai sukurti

„CobbleDB“ ir su ja susijusios infrastruktūros kūrimas toli gražu nebuvo paprastas. Vien tik pagrindinė „CobbleDB“ duomenų bazė sudaro maždaug 40 000 „Rust“ kodo eilučių, o migracijai taip pat reikėjo didelių pastangų norint patikrinti teisingumą ir trikčių elgseną gamybiniame mastu, integruoti „CobbleDB“ su apdorojimo ir aptarnavimo keliais bei saugiai perjungti gyvą srautą.

Paspartinome šį darbą naudodami vidinę sistemą, kuri valdo kodavimo agentų spiečių. Sistema sukurta taip, kad būtų itin atspari; ji išlaiko projekto tikslus, aktyvias rizikas, repozitorijos istoriją ir ankstesnius sprendimus per sesija.

„CobbleDB“ projektui priskirėme tokių proaktyvių, nuolat veikiančių agentų kolektyvą. Jų pirmoji užduotis buvo sukurti tikslų jau vykdomų darbų modelį. Jie auditavo projekto gija ir aktyvias gijas, susiejo neišspręstus elementus su jų pull request ir savininkais bei pateikė esamą sistemos sveikatos ir tolimesnių veiksmų ataskaitą. Iš ten jie veikė keliose inžinerinio ciklo srityse:

  • Peržiūrėtas kodas ir infrastruktūros pakeitimai.
  • Išryškintos kliūtys, kurias buvo lengva praleisti, įskaitant nesaugias atkūrimo prielaidas, pasenusias kompiliavimo nuorodas ir konfigūracijas, kurios sugrįstų vykdymo metu.
  • Paruošti tiksliniai pataisymai, testai, stebėsenos pakeitimai ir operacinė dokumentacija.
  • Stebėjo CI, peržiūros ir infrastruktūros vartus, kartu užtikrindami, kad gamybiniai veiksmai būtų vykdomi tik žmonių.
  • Stebėti diegimai ir atsarginių kopijų kūrimo ar atkūrimo pratimai, o po to stebėta sistemos būsena buvo palyginta su diegimo sėkmės kriterijais.
  • Palaikė projekto ataskaitas, kad inžinieriai galėtų matyti, kas buvo išleista, kas liko užblokuota ir kur vis dar reikalingas žmogaus vertinimas.

Inžinieriai vis dar nustato architektūrą, peržiūrėjo reikšmingus pakeitimus ir autorizavo gamybines operacijas. Agentai tvarkė didelę nuolatinio patikrinimo ir tęstinumo dalį tarp tų sprendimų. Tai leido mažai komandai efektyviai valdyti projektą – pagrindinė „CobbleDB“ infrastruktūra buvo sukurta dviejų žmonių inžinierių ir šimtų dirbtinio intelekto agentų per du mėnesius.

Išvada

„CobbleDB“ nėra bendros paskirties duomenų bazė; ji sukurta specialiai mūsų specifinei darbo eigai, apimančiai pakartotinį paruoštų puslapių įrašų nuskaitymą paketais, kurį maitina asynerginis apdorojimo konvejeris. Atskirdami patvarią būseną, atnaujinimų pristatymą ir aptarnavimą užklausų metu, galime nepriklausomai optimizuoti kiekvieną funkciją, neversdami vienos duomenų bazės sąsajos patenkinti visų poreikių ir nekeldami priežiūros naštos į tiesioginės užklausos kelią. Migracija į „CobbleDB“ smarkiai pagerino tiek medianinę, tiek kraštstinę delsą ir sumažino infrastruktūros išlaidas.

Tęsdami dirbtinio intelekto pagrindu veikiančios infrastruktūros kūrimą savo jėgomis, galime optimizuoti našumą ir kainą būdais, kurie neįmanomi esant pririštiems prie trečiųjų šalių sprendimų ir kainodaros modelių. Galingi dirbtinio intelekto agentai dar labiau pakreipia skaičiavimus kūrimo, o ne pirkimo naudai.

Planuojame greitai atverti „CobbleDB“ kodą, kad kitos komandos, kuriančios dirbtinio intelekto pagrindu veikiančias paieškos sistemas, galėtų pasinaudoti tuo pačiu saugyklos sluoksniu, kuris padeda paieškai tapti greitesnei ir pigesnei mūsų produktuose.

Citata

BibTeX
@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}
}