CobbleDB: Membangun Kembali Penyimpanan Pencarian AI untuk Latensi dan Biaya yang Lebih Rendah
Arsitektur yang didekopel dengan status tahan lama terpisah, pembaruan batch, dan pengambilan waktu kueri.
Pencarian asli AI memberlakukan persyaratan yang berbeda tentang bagaimana konten web perlu diproses, disimpan, and disajikan. Untuk memenuhi permintaan tersebut dalam skala besar, kami telah membangun kembali seluruh tumpukan dari awal untuk meningkatkan performa dan mengurangi biaya.
Kami membangun sendiri infrastruktur crawling, pengindeksan, dan peringkat, model embedding, dan mesin inferensi untuk mendukung pemrosesan yang efisien dan penyajian yang cepat. Kini kami memperluas pendekatan itu ke storage, jaringan penghubung yang ditulis oleh pemrosesan dan dibaca oleh penyajian.
Basis data terkelola seperti DynamoDB menyediakan penyimpanan yang andal, tetapi kami memiliki kontrol terbatas atas cara data disimpan dan pembacaan disajikan, serta penetapan harga berbasis penggunaan bertambah seiring ukuran indeks dan volume kueri kami. Secara terpisah, alur kami sebelumnya menggabungkan pemrosesan dokumen secara langsung ke basis data penyajian, sehingga sulit untuk mengelola pembaruan secara independen dari pembacaan langsung.
Dengan bantuan dari kawanan agen coding internal baru, kami membangun kembali lapisan ini di sekitar CobbleDB, penyimpanan panas (hot store) key-value, yang didukung oleh Pillar untuk status dokumen yang tahan lama (durable) dan penerbitan, serta Lorry untuk pembaruan batch. Desain ulang ini memisahkan pemrosesan dokumen dari penelusuran (ingestion) penyimpanan panas (hot store) dan memungkinkan kami menyetel penyajian sesuai dengan beban kerja kami. Pengukuran produksi sebelum dan sesudah menunjukkan latensi pembacaan batch sekitar 5× lebih rendah, dan model biaya internal kami memperkirakan penghematan setidaknya 20% relatif terhadap DynamoDB.
Infrastruktur penyimpanan untuk pencarian asli AI
Tumpukan pencarian klasik dibangun untuk pembaca manusia, bukan model bahasa. Perbedaan tersebut mengubah granularitas tempat sistem perlu menyiapkan, menyimpan, dan menyajikan konten web.
Untuk mengubah HTML mentah menjadi konten siap model, alur pemrosesan kami membersihkan setiap halaman, membaginya menjadi bagian (passage) yang koheren secara semantik, menghitung embedding-nya, dan menyimpan bagian (passage) serta embedding tersebut bersama-sama di dalam basis data. Pada waktu kueri, alur penyajian memilih teks yang paling relevan dengan kueri dan meneruskannya ke model.
Dua alur kerja yang berbeda
Oleh karena itu, lapisan penyimpanan harus mendukung dua alur kerja dengan permintaan yang berbeda: menyerap pembaruan dari alur pemrosesan dan menyajikan pembacaan latensi rendah.
Di sisi penulisan, halaman baru terus berdatangan, halaman yang ada berubah, dan perubahan pada metode segmentasi (chunking) atau model embedding dapat memerlukan pemrosesan ulang sebagian besar korpus. Lapisan penyimpanan harus memasukkan pembaruan ini ke dalam status dokumen yang tahan lama (durable), melacak versi saat ini, mempublikasikan representasi yang tepat ke sistem hilir, dan mendukung backfill besar tanpa mengulangi seluruh alur dari penelusuran (crawl) mentah.
Sebaliknya, pembacaan saat kueri meminta batch key halaman dan mengambil teks bagian (passage) yang sesuai serta embedding untuk pemeringkatan dan pemilihan bagian (passage). Pembacaan ini berada di jalur menuju jawaban akhir, sehingga harus diselesaikan dengan cepat.
Beban kerja ini memerlukan desain yang berbeda. Pemrosesan mendapat manfaat dari antrean yang tahan lama (durable) dan kemampuan untuk mengirimkan pembaruan secara efisien, mencoba ulang kegagalan, dan membawa catatan yang ada ke status terbaru pada skala besar. Penyajian mendapat manfaat dari catatan yang dihitung sebelumnya, cache yang hangat (warm cache), dan pembacaan batch yang cepat dengan pekerjaan waktu kueri yang minimal. Penggabungan yang erat antara keduanya dapat menyebabkan pekerjaan pemulihan dan pembaruan besar bersaing dengan pembacaan yang sensitif terhadap latensi.
Di mana basis data terkelola gagal memenuhi harapan
DynamoDB memberi kami penyimpanan yang sangat tersedia tanpa beban mengoperasikan basis data terdistribusi. Tabel halaman yang disiapkan khusus memuat bagian (passage) dan embedding untuk pembacaan saat kueri, sementara konten halaman lengkap dan metadata disimpan secara terpisah. Namun, seiring bertambahnya beban kerja kami, tiga kendala menjadi penting.
Pertama, penetapan harga berbasis penggunaan mengenakan biaya untuk setiap byte yang dibaca atau ditulis. Permintaan API Pencarian tunggal berisi 100-120 key yang sesuai dengan halaman yang perlu kami ambil. Proses pengambilan data membagi permintaan menjadi batch yang lebih kecil berisi 10-20 key halaman dengan ukuran item rata-rata sekitar 50 KB. Membaca catatan tersebut secara berulang menggunakan kapasitas yang besar, sementara penelusuran (crawl) berkelanjutan dan perubahan pada model segmentasi (chunking) atau embedding menghasilkan lebih banyak penulisan. Aspek ekonomi juga bergantung pada ukuran dan frekuensi catatan, bukan hanya jumlah panggilan API.
Kedua, layanan terkelola tidak memiliki fleksibilitas yang kita butuhkan untuk penyetelan performa. Operasi yang kita butuhkan sangat terbatas: mengingat sekumpulan pengidentifikasi halaman, kembalikan konten yang disiapkan dengan cepat. Kami ingin mengontrol mesin mana yang memegang partisi, berapa banyak memori yang dicurahkan untuk cache-nya, dan replika mana yang menangani permintaan. Kontrol tersebut penting karena pembacaan yang tidak di-cache, hop lintas zona, atau replika yang lambat dapat menunda seluruh batch. DynamoDB mengelola penyimpanannya sendiri dan perilaku replika di balik API. Kami dapat mengonfigurasi layanan tersebut, tetapi kami tidak dapat menyetel penempatan internalnya, cache mesin penyimpanan, atau kebijakan pemilihan replika di sekitar beban kerja kami.
Ketiga, pemrosesan terikat langsung dengan penulisan penyimpanan panas (hot store). Alur pemrosesan kami sebelumnya menulis setiap halaman yang disiapkan secara langsung ke DynamoDB. Oleh karena itu, pekerjaan pemeprosesan ulang yang besar menjadi gelombang pembaruan penyimpanan panas (hot store) individual, dan pemrosesan harus mengakomodasi batas throughput dan perilaku percobaan ulang dari penyimpanan panas (hot store). Kami tidak dapat menjalankan MapReduce di seluruh basis data dalam 1-2 hari untuk mencoba metode segmentasi (chunking) baru, mengubah model embedding kami, atau menambahkan kolom baru. Tidak ada aliran independen dari pembaruan halaman yang disimpan yang dapat dibandrol oleh layanan terpisah dan dikirimkan ke penyimpanan panas (hot store) dengan kecepatannya sendiri.
Mengatasi kendala-kendala ini memerlukan lebih dari sekadar mengganti basis data. Kami perlu memisahkan status dokumen yang tahan lama (durable) dan pengiriman pembaruan dari penyimpanan saat kueri, lalu mengoptimalkan penyimpanan panas (hot store) secara khusus untuk pembacaan batch yang cepat.
Pisahkan status dokumen, pengiriman pembaruan, dan penyajian
Arsitektur baru ini menetapkan setiap tanggung jawab ke komponen yang berbeda. Pillar mempertahankan status dokumen yang tahan lama (durable) dan memutuskan apa yang akan dipublikasikan. Lorry mengubah ekspor tersebut menjadi batch yang dipartisi. CobbleDB menyerap batch dan menyajikan catatan yang disiapkan berdasarkan key halaman.
CobbleDB: jalur baca yang dapat disetel
CobbleDB adalah penyimpanan panas (hot store) kami yang dioptimalkan untuk menyajikan catatan yang disiapkan pada waktu kueri. Ini adalah penyimpanan key-value terdistribusi: key adalah pengidentifikasi halaman (URL yang di-hash), dan nilainya adalah representasi halaman yang diproses (bagian yang telah dipotong sebelumnya dan embedding vektor per potongan).
Perubahan penting adalah kontrol kami atas konfigurasi penyimpanan.
Data dipartisi dan dibagi di seluruh node data. Format terdistribusi ini memastikan bahwa basis data dapat menskalakan secara horizontal untuk mengakomodasi korpus halaman yang diproses dalam jumlah besar. Setiap partisi memiliki tiga replika pada tiga node yang berbeda; jika satu salinan menjadi tidak tersedia, yang lain dapat terus melayani pembacaan.
Setiap node data diberi subset dari partisi. Node tersebut menyimpan datanya menggunakan RocksDB, sebuah mesin penyimpanan key-value tersemat sumber terbuka yang sangat cocok untuk beban kerja padat baca yang didukung oleh penelusuran (ingestion) batch.
Data yang di-cache dapat disajikan dari memori, sementara pembacaan yang tidak di-cache menggunakan penyimpanan NVMe lokal. Ini memberi kami cara langsung untuk menyetel keseimbangan memori-ke-disk untuk beban kerja yang diamati, alih-alih menerima kebijakan cache internal layanan terkelola.
Router kueri tanpa status (stateless) melakukan hash key ke partisi dan merujuk permintaan ke node secara paralel. Arsitektur ini juga menyertakan layanan metadata yang didukung PostgreSQL dan penyimpanan objek yang tahan lama (durable).
Desain ini mendukung pola baca yang dituntut oleh beban kerja kami. Setelah kueri melewati pengambilan dan pemeringkatan untuk mendapatkan kumpulan kandidat halaman yang relevan, penyimpanan panas (hot store) dikueri secara massal untuk konten halaman tersebut. Permintaan baca ini tiba di router dan dirujuk ke node data yang sesuai. Router memprioritaskan pengiriman permintaan ke node di zona ketersediaan yang sama untuk meminimalkan latensi lintas zona, jika memungkinkan. Setiap node data dapat mengambil konten halaman menggunakan MultiGet, sebuah API baca-batch RocksDB yang dirancang untuk mengambil beberapa key secara bersamaan pada latensi rendah. Jika sebuah node lambat merespons, sistem juga dapat menanyai replika partisi lain pada node yang berbeda, yang meningkatkan latensi tail.
Kami juga dapat menghindari penerapan fitur umum yang tidak memerlukan alur kerja kami. Sebagai contoh, sebagian besar basis data menawarkan jaminan konsistensi dan dukungan transaksi, tetapi penyimpanan panas (hot store) kami tidak memerlukan transaksi atau replika yang disinkronkan. Dapat diterima jika ada jeda singkat antara saat dokumen ditulis dan saat dokumen tersedia untuk dibaca, dan juga tidak masalah bagi satu replika untuk menyerap data lebih cepat daripada yang lain. Menghilangkan fitur yang tidak kita butuhkan mengurangi overhead dan biaya sistem kita.
Pillar dan Lorry: jalur tulis yang tahan lama (durable)
Lapisan penyimpanan terpisah melacak segala hal yang kita telusuri (crawl) dan proses, menggabungkan pembaruan ke dalam status dokumen berversi dan mempublikasikan representasi yang diperlukan ke CobbleDB dan indeks pencarian. Kami membangun Pillar, dokumen status dan lapisan publikasi kami, untuk tujuan ini.
Pillar didukung oleh YTsaurus. Pillar menyimpan komponen halaman (seperti metadata, potongan (chunk), dan embedding) dalam keluarga tabel YTsaurus yang terpisah. Pillar juga melacak versi potongan (chunk) dan embedding, memungkinkan beberapa representasi untuk hidup berdampingan tanpa menimpa satu sama lain sehingga model yang mendasarinya dapat berubah. Karena Pillar dan YTsaurus menggunakan penyimpanan HDD yang murah, kami dapat menyimpan lebih banyak dokumen daripada yang kami simpan sebelumnya di DynamoDB.
Pillar melacak subset, atau kelompok halaman yang ditentukan kebijakan seperti halaman segar atau halaman bernilai tinggi. Keanggotaan subset menentukan pembaruan mana yang perlu diteruskan ke komponen hilir seperti CobbleDB. CobbleDB menggunakan penyimpanan NVMe yang lebih mahal, jadi kami hanya mengekspor dokumen yang benar-benar ingin kami gunakan dalam hasil pencarian. Ketika konten halaman diperbarui, atau jika halaman ditambahkan ke atau dihapus dari subset, Pillar bertanggung jawab untuk mengantrekan pekerjaan ekspor yang sesuai.
Transaksi YTsaurus atomik mencakup status, maksud ekspor, dan input yang dikonsumsi guna memastikan kebenaran. Agar transaksi berhasil diselesaikan, sistem harus memperbarui status yang tahan lama (durable), menandai input sebagai yang telah diperhitungkan, dan mengantrekan semua ekspor yang diperlukan; jika transaksi dibatalkan, tidak ada perubahan yang dibuat pada lapisan mana pun.
CobbleDB mengharapkan pembaruan untuk dibandrol sesuai dengan partisi. Ekspor Pillar dikonversi ke format ini oleh layanan tanpa status (stateless) yang kami sebut Lorry. Satu-satunya tugasnya adalah membaca catatan ekspor dari antrean selaras partisi yang persisten, mengumpulkannya ke dalam file batch per partisi, dan menyerahkannya ke CobbleDB.
Antara Lorry dan Cobble, protokol tambahan memisahkan bidang data (S3) dari bidang kontrol. Lorry menulis batch ke S3 di bawah pengidentifikasi unik dan mendaftarkan batch tersebut di dalam Cobble. Replika partisi Cobble secara mandiri menanyai batch atau API bidang kontrol untuk batch S3 berikutnya yang akan ditelusuri (ingest) dan secara asinkron menerapkan pembaruan batch secara kronologis. Ini berarti bahwa node yang lambat atau sedang pulih dapat mengejar ketertinggalan dengan kecepatannya sendiri, alih-alih memperlambat sisa sistem.
Jalur baru ini memisahkan transaksi pemrosesan langsung dari penelusuran (ingestion) penyimpanan panas (hot store) dan menyediakan jalur yang dapat diputar ulang (replayable) untuk pembaruan inkremental dan pembangunan ulang massal. Pembagian kerja ini memastikan bahwa pembaruan pada status dokumen yang tahan lama (durable) tidak mengganggu latensi jalur baca.
Validasi
5x lebih cepat
Setelah memigrasikan penyimpanan panas (hot store), latensi pembacaan batch median turun dari 31,4 md menjadi 5,60 md. P90 turun dari 56,7 md menjadi 9,77 md, dan p99 turun dari 123 md menjadi 24,2 md. Rasio berkisar dari 5,08× hingga 5,80×, dengan peningkatan baik pada permintaan tipikal maupun tail.
Angka-angka ini berasal dari pembacaan batch, di mana setiap kueri meminta sekitar 10-15 key. Ukuran item payload rata-rata adalah 50KB. Kedua sistem dikueri pada sekitar 200rb rps, meskipun kami juga menjalankan uji beban hingga 500rb rps tanpa penurunan.
Untuk mengisolasi kontribusi CobbleDB, semua aspek penyiapan lainnya dijaga agar tetap konstan. Namun, ini tetap merupakan perbandingan observasional sebelum dan sesudah; sistem melayani trafik produksi langsung pada waktu yang berbeda alih-alih permintaan yang sama dalam kondisi terkontrol. Oleh karena itu, kami juga mengukur performa kedua sistem pada tolok ukur sintetik. Untuk mendekati permintaan yang terlihat dalam produksi, kami menanyai keduanya dengan batch yang berisi 10-15 key dan nilai yang berkisar dari 100B hingga 100 KiB.
DynamoDB mengalami kesulitan dalam konteks ini karena jalur baca terelolanya tidak dapat disetel untuk pembacaan batch dengan ukuran ini. Sebaliknya, CobbleDB dioptimalkan untuk pengaturan ini, karena kami mengelola penempatan, penelusuran dalam cache (caching), dan kontrol replika. CobbleDB mengelompokkan key berdasarkan partisi, mengeksekusi pembacaan secara paralel terhadap replika partisi, dan dapat melindungi (hedge) pembacaan lambat terhadap replika lainnya. Hasil kami menunjukkan bahwa pilihan desain ini diterjemahkan ke pembacaan yang lebih cepat di lingkungan produksi.
20% lebih murah
CobbleDB juga jauh lebih murah daripada DynamoDB. Selain penyimpanan, DynamoDB juga mengenakan biaya untuk setiap byte yang dibaca atau ditulis. Pada skala korpus dan trafik penyajian kami, biaya berbasis penggunaan ini menjadi bagian substansial dari total biaya. Pembaruan berkelanjutan menghabiskan kapasitas tulis, sementara setiap batch halaman yang disiapkan yang diambil untuk kueri menghabiskan kapasitas baca.
Kami menghitung penghematan biaya berdasarkan estimasi internal ukuran penyimpanan, unit kapasitas tulis, dan unit kapasitas baca. Pada setiap tingkatan komitmen, CobbleDB setidaknya 20% lebih murah daripada DynamoDB. Penghematan nyata bahkan mungkin lebih tinggi, karena hasil ini tidak memperhitungkan kemungkinan penghematan pencadangan dari kompresi.
Bagaimana kawanan agen AI membantu kami membangunnya
Membangun CobbleDB dan infrastruktur di sekitarnya sama sekali bukan hal yang sepele. Basis data inti CobbleDB saja berukuran sekitar 40.000 baris Rust, dan migrasi tersebut juga memerlukan pekerjaan ekstensif untuk memverifikasi kebenaran dan perilaku kegagalan pada skala produksi, mengintegrasikan CobbleDB dengan jalur pemrosesan dan penyajian, serta mentransisikan trafik langsung secara aman.
Kami mempercepat upaya ini menggunakan sistem internal yang mengoperasikan kawanan agen coding. Sistem ini dirancang agar sangat persisten; sistem ini mempertahankan tujuan proyek, risiko aktif, riwayat repositori, dan keputusan sebelumnya di seluruh Sesi.
Kami menugaskan sekumpulan agen proaktif yang selalu aktif ke proyek CobbleDB. Tugas pertama mereka adalah membangun model akurat dari pekerjaan yang sudah berjalan. Mereka mengaudit kanal proyek dan utas aktif, menghubungkan item yang luar biasa ke pull request dan pemiliknya, serta menghasilkan pembacaan saat ini tentang kesehatan sistem dan tindakan selanjutnya. Dari sana, mereka beroperasi di beberapa bagian dari lingkaran teknik:
- Meninjau perubahan kode dan infrastruktur.
- Memunculkan pemblokir yang mudah terlewatkan, termasuk asumsi pemulihan yang tidak aman, referensi build yang usang, dan konfigurasi yang akan gagal saat runtime.
- Menyiapkan perbaikan terfokus, pengujian, perubahan pemantauan, dan dokumentasi operasional.
- Melacak gerbang CI, ulasan, dan infrastruktur sambil menjaga tindakan produksi secara eksplisit dimiliki oleh manusia.
- Peluncuran yang dipantau serta latihan pencadangan atau pemulihan, kemudian membandingkan status sistem yang diamati dengan kriteria keberhasilan peluncuran.
- Mempertahankan pembacaan proyek sehingga para insinyur dapat melihat apa yang telah dikirimkan, apa yang masih diblokir, dan di mana penilaian manusia masih diperlukan.
Para insinyur tetap menetapkan arsitektur, meninjau perubahan konsekuensial, dan mengotorisasi operasi produksi. Para agen menangani sebagian besar inspeksi berkelanjutan dan tindak lanjut di antara keputusan tersebut. Hal ini memungkinkan tim kecil untuk mendorong proyek secara efisien—infrastruktur inti CobbleDB dibangun oleh dua insinyur manusia dan ratusan agen AI dalam dua bulan.
Kesimpulan
CobbleDB bukan basis data serbaguna; basis data ini dibuat khusus untuk beban kerja spesifik kami berupa pembacaan batch berulang dari catatan halaman yang disiapkan, yang diberi makan oleh alur pemrosesan asinkron. Dengan memisahkan status yang tahan lama (durable), pengiriman pembaruan, and penyajian saat kueri, kami dapat mengoptimalkan setiap tanggung jawab secara mandiri tanpa memaksa satu antarmuka basis data untuk mengakomodasi semuanya atau mendorong pekerjaan pemeliharaan ke jalur kueri langsung. Bermigrasi ke CobbleDB secara signifikan meningkatkan latensi median dan tail sekaligus mengurangi biaya infrastruktur.
Saat kami terus mengembangkan infrastruktur asli AI secara internal, kami dapat mengoptimalkan performa dan biaya dengan cara yang tidak mungkin dilakukan saat kami terikat dengan implementasi pihak ketiga dan model penetapan harga. Agen AI yang canggih menggeser kalkulus lebih jauh untuk mendukung pembangunan daripada pembelian.
Kami berencana untuk membuka sumber (open-source) CobbleDB segera, sehingga tim lain yang membangun sistem pencarian asli AI dapat mengambil manfaat dari lapisan penyimpanan yang sama yang membuat pencarian lebih cepat dan lebih murah di dalam produk kami.
Sitiran
@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}
}