· kaynak Hacker News – Front Page (hnrss.org)
Conviva Rust sorgu motorunda mmap yerine io_uring'e geçti ve motor yavaşladı
Conviva, Rust sorgu motorundaki gecikme sıçramalarının kaynağını mmap'in page-cache çalkantısında ve çekirdek kilit çekişmesinde buldu; ardından io_uring'e geçişin daha hızlı değil, daha yavaş olduğunu gördü.

Conviva'daki kurulum
Hacker News'in ana sayfasında öne çıkan bir Conviva mühendislik yazısına göre şirket, son kullanıcı deneyimini teşhis etmek için günde trilyonlarca olay analizliyor; bunu DataFusion, Arrow, Rust, Rayon ve Tokio üzerine kurulu bir olay ve örüntü analiz motoruyla yapıyor. Ham olaylar dönüştürülüyor, çoğunlukla sayısal tescilli bir formatta kodlanıyor ve bulutta depolanıyor, ardından yerel NVMe'ye kopyalanıp yaklaşık 3–5 GB boyutunda büyük Arrow IPC dosyaları olarak okunuyor. Tipik bir sorgu, sekiz batch dosyası boyunca altı kolona dokunuyor; batch başına yaklaşık 1,6 GB ve günlük kabaca 13 GB veri ediyor.
Arrow IPC'nin seçilmesinin nedeni, bellek içi ve disk üstü düzenlerinin aynı olması sayesinde kod çözme maliyetinin minimumda kalması ve arrow-rust'ın mmap üzerinden zero-copy okumaları doğal olarak desteklemesiydi. Bu, mmap'i açık ara varsayılan seçim yaptı: Çok sayıda dosya, uygulamanın belleği kendisi yönetmesine gerek kalmadan tembel biçimde okunabiliyordu.
mmap'in çöktüğü yer
Daha hafif yüklerde mmap iyi çalışıyor, sorgulara ham olaylardan saniyeler içinde yanıt veriyordu. Gerçek eşzamanlı yük altında p95 gecikme yaklaşık 30 saniyeden 150 saniyenin üzerine çıktı, çekirdek başına taranan satır sayısı eşzamanlılık düzeltmesi yapıldıktan sonra bile keskin biçimde düştü, pod'lar daha fazla private memory tükettikçe OS page cache küçüldü ve pod eklemek performansı iyileştirmek yerine kötüleştirdi.
Test donanımı, yaklaşık 750 GB RAM'e sahip 192 çekirdekli bir makineydi; ya kabaca 5,5 GB/s fio tavanlı iki diskli bir NVMe LVM şeridi ya da yaklaşık 21 GB/s'lik 32 diskli bir RAID-0 kullanıldı. İnceleme çekirdek 5.15 üzerinde koştu; üretimde 6.x var. Conviva, Linux 6.4+'ın hızlı per-VMA kilidi kazandığını, önceki çekirdeklerin ise daha yavaş mmap kilidine geri düştüğünü belirtiyor.
Etkiyi yalıtmak için ekip, aynı ana makine üzerinde aynı sorgu yüküyle bir pod ile dört pod'u karşılaştırdı. Page cache'i doldurmaya yetecek kadar uzun olan 14 günlük sorgularda tek pod fark atmıştı: en yüksekte %41, p95'te ise %20'den fazla hızlıydı. Teşhis şu: mmap page cache, ana makine düzeyinde örtük bir paylaşılan durumdur; tek cache, tek kilit hiyerarşisi ve her podun paylaştığı tek tahliye politikası vardır, dolayısıyla pod'lar CPU için değil cache için birbirleriyle mücadele ediyordu. Aynı koşuda alınan perf record, çekirdekte %100 kilit çekişmesi gösterdi.
Sayılarla bir page-fault fırtınası
Stresli bir koşu sırasında rezidant bellek RAM'in %98,91'ine tırmandı ve bu noktada çekirdek hâlâ ihtiyaç duyulan sayfaları tahliye etmeye başladı. Fiziksel I/O gerektiren major fault'lar ardışık örneklerde 571/s, 1352/s ve 975/s'ye sıçradı. Minor fault'lar saniyede milyonlar düzeyinde sürdü ve yaklaşık 2,35 milyonda zirve yaptı. Yazı, çekişmesiz bir minor fault'ın kabaca 0,5–1 mikrosaniye tuttuğunu, bunun saniyede iki milyonun mmap'in tavanına yakın olduğunu ve her fault'ın atomik işlemlerle bir cache line'a dokunduğunu, yani büyük cache-resident arama tablolarına bel bağlayan bir uygulama için L1/L2'yi çalkaladığını tahmin ediyor.
Sanal adres alanı, o kadar çok Arrow dosyası eşlendiği için yaklaşık 3 TB'a büyümişti; context switch'ler, thread'ler sürekli fault'ta bloklanıp yeniden planlandığından, sıcak cache'li bir koşudaki 14.000'e karşılık saniyede iki milyonu aştı; 150 katlık bir fark.
Darboğaz diskler değil, çekirdekti
perf top karşıtlığı çıplak gözle görünür kıldı. Soğuk bir koşuda, page cache'e bir sayfa ekleyen çekirdek fonksiyonu __filemap_add_folio örneklerin %78'ini yutarken gerçek sorgu kodu yaklaşık %5'e düştü; sıcak bir koşuda ise sorgu kodu kabaca %45 aldı. bpftrace ile yapılan off-CPU analizi, bloklanan sürenin %30,9'unu futex'lere — başka bir thread'in page-fault handler'ının arkasında sıraya giren thread'ler — %29,3'ünü çekirdek readahead işinin öncelikli olarak devreye girmesine ve yalnızca %6,9'unu gerçek disk I/O'suna atfetti.
Donanıma kalan mesafe büyüktü. fio, 32 diskli dizide io_uring motoruyla 20,2 GiB/s (21,7 GB/s) ölçtü ve tüm diskler tam kullanıma yakınken mmap 3,44 GB/s'de zirve yaptı; bu, depolamanın verebileceğinin yaklaşık %16'sı. Conviva sorunun mmap'e özgü olmadığını vurguluyor; herhangi bir buffered I/O yolu benzer page-cache ve kilit çekişmesine girebilir.
io_uring dönümü
Conviva ardından kısmen page cache'i atlayabilen doğrudan kullanıcı I/O'su vaadiyle io_uring'e geçti. Sonuç, yazının başlığında açıkça ifade ediliyor: yavaşladı. Yayınlanan analiz, mmap'in yük altında neden çöktüğünü derinlemesine belgeliyor; io_uring sonuçları ve page fault yönetimine dair söz verilen ikinci bölüm hikâyeyi ileri taşıyor.
Neden önemli
Veri mühendisleri için bu yazı iki varsayıma yararlı bir düzeltme: mmap'in zero-copy kolaylığının eşzamanlılıkla ölçeklendiği ve io_uring gibi daha hızlı bir I/O arayüzünün otomatik olarak daha hızlı sorgulara çevrildiği varsayımları. Conviva'nın ölçümleri, bir sorgu motorunun CPU'sunun büyük çoğunluğunu çekirdek page-cache defter tutarken harcayabileceğini ve NVMe'nin çoğunlukla boş bekleyebildiğini, ayrıca çekişen kaynak paylaşımlı bir ana makine düzeyi cache olduğunda yatay ölçeklemenin aktif biçimde zarar verebileceğini gösteriyor. Pratik öneri şu: soğuk ve sıcak koşuları profilleyin, major ve minor fault oranlarını artı off-CPU süresini izleyin ve I/O yolunu salt bir throughput sayısı olarak değil, bir eşzamanlılık kararı olarak ele alın.
- #rust
- #io-uring
- #mmap
- #linux-kernel
- #query-engine
- #data-engineering