Bu makale yalnızca üçüncü taraf, tarihli kaynaklara dayanmaktadır; asla icat edilmiş bir Aurabase şifresine dayanmamaktadır. Arka ucumuza özel p99 gecikmesi dahil değildir. Uygulama çekirdeğimizi çöp toplayıcı olmadan Rust'ta yazıyoruz: doğrudan kod, çalışma alanı Kargo ve axum hizmetlerinde doğrulanabilecek bir gerçek. Ancak bunu rakamlarla gösterecek tekrarlanabilir bir p99 kıyaslama metodolojisini henüz yayınlamadık. Bu metin ölçülen bir sonucu değil, bir mekanizmayı açıklamaktadır.
Temeller
- p99, yüzlerce sorgu arasında en yavaş olanı ölçer; çöp toplayıcı (GC) duraklamasının en çok acı verdiği yer, ortalamada değil (Dean & Barroso, "The Tail at Scale", Google, 2013).
- Bir GC, kullanılmayan belleği boşaltmak için tüm programı kesintiye uğratır (“dünyayı durdur”). Rust'ta GC yoktur: bir değerin kapsam dışına çıktığı anda bellek serbest bırakılır ve derleyici tarafından doğrulanır.
- Discord, 2020'de Go'nun en az iki dakikada bir çöp toplama döngüsünü tetiklediği ve her döngünün gecikmede ani bir artışa neden olduğu bir önbellek hizmetini belgeledi (Discord Mühendislik Blogu).
- GC duraklamasını azaltmak, Google'da bile yıllar süren mühendislik gerektirir: GB toplayıcı, 2015 ile 2018 arasında hiçbir zaman sıfıra ulaşmadan 300-400 ms'den 500 µs'ye çıktı (go.dev).
- Aurabase'in arka uç çekirdeği, çöp toplayıcı olmadan Rust'ta yazılmıştır ve kodda doğrulanmıştır. Bugüne kadar hiçbir p99 Aurabase gecikme rakamı yayınlanmadı: bu bir ölçüm değil, bir mekanizma olarak kalıyor.
Neden p99 ortalama değil
Ortalama, esas olanı gizler. 100 istekten 99'u 5 ms'de yanıt verirse ve yalnızca biri 500 ms sürerse ortalama düşük kalır. Ancak yüz kullanıcıdan biri, yüz kat daha uzun bir bekleme deneyimi yaşıyor. p99 tam olarak bu isteği ölçer: en yavaş yüzüncü istek, ortalama gecikme süresi kontrol paneliniz yeşil kalırken SLA'nızı ihlal eden istek.
Google'da Jeffrey Dean ve Luiz André Barroso bu sorunu “Ölçekte Kuyruk” (Communications of the ACM, cilt 56, 2013) belgesinde resmileştirdi. O zamandan bu yana sıklıkla alıntılanan gözlemleri: "Orta büyüklükteki sistemlerde önemsiz olan geçici yüksek gecikme dönemleri, büyük ölçekte genel hizmet performansına hakim olabilir". Kısacası: Küçük ölçekte ihmal edilebilir düzeydeki ara sıra gecikme dönemleri, dağıtılmış bir sistemin algılanan performansına hakim olur.
Saniyede binlerce isteği işleyen bir arka ucun, GC duraklaması sırasında düşen bir isteği bir noktada göndermesi zorunludur. Büyük ölçekte bu alışılmadık bir durum değil. Bu istatistiksel bir kesinliktir.
Bir çöp toplayıcı ne yapar ve neden her şeyi duraklatır?
Bir çöp toplayıcı (GC), bir programın canlı nesnelerini (hala bir yere başvurulanları) sürekli olarak izler ve erişilemez hale gelen nesnelerin belleğini serbest bırakır. Bu izlemeye tracingadı verilir: GC referans grafiğinden geçer, hala kullanılanı işaretler ve geri kalanını tarar.
Sorun: Program yeni referanslar oluşturmaya devam ederken bu grafiğin üzerinden geçmek tutarsız sonuçlar doğuruyor. Birçok modern GC tarafından hâlâ son çare olarak kullanılan tarihsel yanıt stop-the-world'dir; işaretleme ve tarama sırasında tüm program duraklar. Yığın ne kadar büyük olursa, duraklatma da o kadar uzun olma eğilimindedir: süresi mevcut iş yüküne değil, canlı verinin boyutuna bağlıdır.
Modern GC'lerin çoğu nesiller arası bir strateji kullanıyor: nesnelerin çoğunun genç yaşta öldüğünü varsayıyorlar. Bu nedenle en son tahsisler küçük bir hafıza alanında sık sık ancak hızlı bir şekilde taranır. Birkaç döngüden sonra hayatta kalabilen nesneler daha geniş bir alana taşınır ve nadiren taranır; ancak bu alanın temizlenmesi gerektiğinde ilgili duraklama, boyutuyla birlikte büyür. Yüksek trafikli, yüksek tahsisli bir hizmetin p99'una hakim olan şey, küçük "küçük" molalar değil, bu "büyük" moladır.
Modern eş zamanlı ve nesilsel GC'ler programla paralel çalışarak bu molaların sıklığını ve süresini azaltır. Ancak neredeyse hepsinde sınırda kalan durumlar için dünyayı durduracak bir geri dönüş mekanizması bulunuyor ve bunu azaltmak yıllar süren mühendislik gerektiriyor. Bölüm 04'te sayısal ve kaynaklı bir örnek verilmektedir.
Discord, 2020: GC kesintisi üretim olayına dönüşüyor
Şubat 2020'de mühendis Jesse Howarth, sektörde referans haline gelen bir gönderi yayınladı: "Discord neden Go'dan Rust'a geçiyor?" (Discord Engineering Blogu). İlgili hizmet olan Okuma Durumları, saniyede yüzbinlerce güncellemeyle milyonlarca kullanıcının mesaj okuma durumunu (önbellek başına on milyonlarca giriş) yönetir.
Makalede belirtildiği gibi teşhis doğrudandır: "Go, çöp toplama işlemini en az 2 dakikada bir çalıştırmaya zorlayacaktır". Başka bir deyişle Go, bu hizmette en az iki dakikada bir çöp toplama döngüsünü tetikliyor ve her döngü, ekibin grafiklerinde görülebilen gecikmede bir artışa neden oluyor.
Ekip öncelikle ani artışları düzeltmek için önbellek boyutunu küçülttü. Uzlaşma olumsuz olmaya devam etti: daha az GC duraklaması, ancak veritabanına düşen daha fazla önbellek kaçırma isteği - dolayısıyla başka yerlerde genel olarak bozulmuş bir p99. Temel düzeltme, izlenecek bir çöp toplayıcı olmadan hizmetin Rust'ta yeniden yazılmasıydı.
Gönderi canlı bir teknik tartışmaya yol açtı: Hacker News hakkında yayınlandığı aynı günde (4 Şubat 2020) 1.580'den fazla puan ve 642 yorum - sorunun Discord davasının çok ötesine geçtiğine dair bir işaret.
Duraklatmayı 400 ms'den 500 µs'ye düşürmek için Google'da üç yıllık mühendislik
Go çöp toplayıcı, Google'daki özel bir ekibin kaynaklarıyla bile, GC duraklamasını yumuşatmak için gereken çabanın boyutunu gösteriyor. Go GC teknik lideri Rick Hudson, bu hikayeyi iki resmi Go blog yazısında belgeledi.
| Ağustos 2015'ten önce | 300-400ms | Tarihi Go koleksiyoncusu, yeniden tasarlanmadan önce |
|---|---|---|
| Ağustos 2015 · Go 1.5 | 30-40ms | İlk rakip toplayıcı, hedef < 10 ms belirlendi |
| 2016 · Git 1.6 | < 10 ms (SLO tutuldu) | Üretimde ilk hedefe ulaşıldı |
| Mart 2017 · Go 1.8 | milisaniyenin altında | Dünyayı durdur yığın taramasının kaldırılması |
| Ağustos 2017 · Go 1.9 | 100-200 µs (işaret) | Ekibin bahsettiği yeni resmi olmayan kriter |
| 2018 · SLO açıklandı | Döngü başına 500 µs | Rick Hudson tarafından resmileştirilen hizmet hedefi |
Kaynak: “Gitmeye Başlarken: Go'nun Çöp Toplayıcısının Yolculuğu”, go.dev, 12 Temmuz 2018; ve “Go GC: Düşük gecikme süresine ve basitliğe öncelik verme”, go.dev, 31 Ağustos 2015.
Üç yıllık özverili çalışma, tipik arayı bin kat azalttı. Ancak duraklama hiçbir zaman ortadan kalkmadı: bu bir hizmet hedefidir (SLO), mutlak sıfır garantisi değil. Bir GC izlemesi, yapısı gereği, zaman zaman canlı nesnelerin bir grafiğini geçmelidir. Ayarlanabilir tek değişken bu yolculuğun sıklığı ve süresidir, varlığı değil.
Bu öncelik seçimi tarafsız değildir. Go öncelikli olarak birkaç yüz milisaniyelik bir duraklamanın doğrudan kullanıcı deneyimini bozduğu ağ hizmetlerini ve web arka uçlarını hedefler; dolayısıyla ham GC verimi yerine gecikmeye harcanan büyük çaba budur. Diğer yönetilen çalışma zamanları, kendi düşük duraklamalı toplayıcılarıyla temel oluşturmadan önce, geçmişteki kullanım durumlarına göre şekillenen farklı ödünleşimleri miras aldı. Ortak nokta aynı kalıyor: hepsi GC izlemeden başlıyor, dolayısıyla en aza indirilecek ve asla inşaatla ortadan kaldırılmayacak bir duraklatma mekanizmasından başlıyor.
Rust'ta neden yapısal olarak bu sorun yok?
Pas, GC duraklamalarını azaltmaz; bunlara neden olan mekanizmayı ortadan kaldırır. Derleyici, derleme sonrasında her bellek değerinin kime ait olduğunu izler - buownership. Bir değerin sahibi kapsam dışına çıktığında Rust, bu belleği serbest bırakan çağrıyı ikili kodda aynı yere otomatik olarak ekler. Bu mekanizmaya RAII (Kaynak Edinimi Başlatmadır) adı verilir: sürüm deterministiktir, arka planda çalışan bir çöp toplayıcı tarafından planlanmaz.
Scala/Rust ekosisteminde tanınan teknik bir blogun yazarı Alexandru Nedelcu, yakın tarihli bir makalesinde bu ödünleşimi şöyle özetliyor: "Rust'un yaptığı ödünleşim, öngörülebilir gecikme ve güvenlik ile performans tercihi yerine kullanım kolaylığıdır" (alexn.org, 21 Temmuz 2026). Rust, öngörülebilir gecikme karşılığında yazmanın basitliğinden vazgeçiyor.
Aynı makale, modern GC'lerin neden her zaman yeterli olmadığını özetlemektedir: "Modern GC'ler, programı etkilemeden işlerini aşamalı ve eşzamanlı olarak yapmaya çalışırlar. Ancak yetenekleri sınırlıdır, tüm programı donduran ve dolayısıyla gecikmeyi etkileyen dünyayı durdurma GC döngüsüne geri dönerler".
Aurabase kodundan bir alıntı değil, genel bir örnek olan mekanizmayı yaklaşık on satır halinde burada bulabilirsiniz:
Önemli nüans: her şey bedava değildir. Referans sayma türleri (Rc, Arc) her klon ve sürüme küçük bir maliyet ekler. Bu maliyet yerel ve belirleyici olmaya devam ediyor. Hiçbir zaman bir bellek yığınından geçerken programın tamamını donduran bir duraklama olmaz.
Eşzamansız bir arka uç için yararlı açıklama: Rust eşzamansız çalışma zamanının (tüm Aurabase hizmetleri tarafından kullanılantokio) çöp toplayıcıyla hiçbir ilgisi yoktur. Bir iş parçacığı havuzu üzerinde ortak görevleri planlar, ancak belleği boşaltmak için hiçbir zaman canlı nesne grafiği boyunca yineleme yapmaz. Eşzamansız çalışma zamanının ve GC'nin aynı sanal makine tarafından yönetildiği ekosistemlerde kafa karışıklığı yaygındır.
Yüksek trafikli bir arka uç için bu neleri değiştirir?
Binlerce eşzamanlı isteğe hizmet veren bir arka uçta, GC'nin yokluğu, p99 denkleminden bir değişkeni kaldırır. Artık bir bellek yığınını boyutlandırmanıza, bir toplayıcının nesillerini ayarlamanıza veya en kötü zamanda gerçekleşebilecek bir döngüyü izlemenize gerek yok. Bireysel bir isteğin gecikmesi, programın başka bir yerindeki öngörülemeyen küresel bir olaya değil, kendi çalışmasına bağlıdır.
Aurabase'in arka uç çekirdeği bu prensibi uygular: tüm hizmetler (aura-gateway, aura-auth, aura-db, aura-realtime, aura-storage, aura-functions, aura-ai…) Rust'ta yazılır ve tek bir Kargo çalışma alanında düzenlenir. Bu doğrudan depoda doğrulanabilir:
Cargo.toml'den gerçek alıntı, çalışma alanı sürümü 2021, çözümleyici v2 — Aurabase deposunda doğrulandı.
Bu aşamada bu gerçek neyi kanıtlamıyor: Aurabase için ölçülen bir p99 gecikme rakamı. Henüz kendi arka ucumuz için tekrarlanabilir bir kıyaslama metodolojisi yayınlamadık; bu, bugün mevcut bir sonuç değil, devam eden bir çalışmadır. Çöp toplayıcının olmaması kodda doğrulanan bir mekanizmadır. Bu tek başına ölçülen p99 gecikmesinin kanıtı değildir. Bizimki de dahil olmak üzere konuyla ilgili herhangi bir pazarlama argümanı karşısında bu ayrımı aklınızda bulundurun — mimarinin ayrıntıları için Aurabase ve Supabase teknik karşılaştırmamıza bakın.
Bir p99'un doğru şekilde ölçülmesi kendi disiplinini gerektirir: temsili yük koşulları, yeterince geniş bir kayan pencere üzerinden hesaplanan yüzdelikler ve üretime yakın bir test ortamı. Bu metodoloji olmadan bir rakam yayınlamak, bir pazarlama rakamı yayınlamaya benzer. Bu makalede yapmayı reddettiğimiz şey tam olarak budur.
GC'nin yokluğunun çözemediği şey
Çöp toplayıcının kaldırılması, kuyruk gecikmesinin yalnızca bir kaynağını ortadan kaldırır; hepsini değil. Bir Rust arka ucu, ağ beklemesi, doymuş bir Postgres bağlantı havuzu, tartışmalı bir veritabanı kilidi, zayıf şekilde dizine eklenmiş bir SQL sorgusu veya yavaş bir üçüncü taraf API çağrısı nedeniyle hala bozulmuş bir p99 gösterebilir. Bu makalede anlatılan mekanizma yapısal bir nedeni ortadan kaldırmaktadır. Başkalarına karşı bağışıklık sağlamaz.
Örneğin Aurabase'de her hizmet Postgres ile bir bağlantı havuzu (sqlx) aracılığıyla ve diğer hizmetlerle NATS JetStreamaracılığıyla iletişim kurar. Küçük boyutlu bir havuz, yavaş tüketilen bir NATS aboneliği veya uygun bir dizine sahip olmayan bir SQL sorgusunun her biri, bir çöp toplayıcının yokluğuna bakılmaksızın kendi gecikme artışlarına neden olur.
Pratik sonuç: GC'nin yokluğu, p99 uyumlu bir sistem için Rust arka ucunu seçmek için iyi bir mimari nedendir. Bu, ne Aurabase'de ne de başka bir yerde kendi başına bir gecikme garantisi değildir. Önemli olan yöntem aynı kalıyor: Ölçün, metodolojiyi yayınlayın, ardından ölçümlerin ortaya çıkardığını düzeltin. GC ile bir arka uçtan geçiş yapıyorsanız, Supabase'den Aurabase'e geçiş kılavuzumuz neyin değiştiğini ve neyin aynı kaldığını ayrıntılarıyla anlatır.