Yunus Emre Alpak

Çıkarım yönlendiricimiz neden çoğunlukla bir kuyruk

2026 · 04 · 1218 dk okuma

Akıllı olmaya çalıştığım bir yıl, önceliklerin, adil payların ve sabrın şaşırtıcı yeterliliğiyle bozuldu.

Yönlendirici, çoğu yönlendiricinin başladığı gibi, bir hırsla başladı. Yükü sürekli bir alan gibi modelleyecektik. Arka planda küçük bir doğrusal regresyonla her isteğin her replikada ne kadar süreceğini tahmin edecektik. Bir milisaniyenin altında neredeyse optimal bir atama bulacaktık ve bununla çok zekiyiz hissini yaşayacaktık.

On sekiz ay sonra yönlendirici, pratikte, üç öncelikli ve kiracı başına adil-pay token bucket'a sahip bir kuyruk. Akıllı kısımlar teknik olarak hâlâ orada, ama kimsenin çalıştırmadığı bir tanılama moduna sessizce indirildi. ¹ Onların yerini alan şeyi, nasıl olduğunu unutmadan önce yazmak istiyorum.

İlk sürpriz: varyans, işin kendisi

Çıkarım gecikmesi bir sayı değildir; uzun bir kuyruğu ve normal olmaya karşı inatçı bir direnişi olan bir dağılımdır. Yönlendirmek istediğin şey — beklenen replika süresi — sistemin en az kararlı özelliği. Bir çeyrek boyunca, ortalamadan toplamda yüzde on iki daha iyi bir tahminleyici inşa ettik. Dağıttık. p99 gecikmesi sıfır kadar hareket etti.

Sebebi, geriye dönüp bakınca, açık: kuyruğun ucunda, tahminleyici ve ortalama aynı şekilde yanılır. Kuyruk ucu, tahminleyicinin hiç görmediği olaylara aittir — ani bir cache miss, yavaş bir tokenizer yolu, aynı host'ta gürültülü bir komşu. Ortalamada yüzde on iki daha iyi, bir kuyruk-ucu hikâyesi değildir.

// Yerine koyduğumuz şey. Akıllı kısım.
fn assign(req: &Request, fleet: &Fleet) -> ReplicaId {
    fleet
        .replicas
        .iter()
        .min_by_key(|r| predict_time(req, r))
        .unwrap()
        .id
}

Şu anda kullandığımız şey daha kısa, daha az akıllı ve eski sürümün hiçbir zaman olmadığı bir şekilde kararlı. Bir replika kapasitesi olduğunda kuyruktan çekiyor. Öncelik bantları, küçük interaktif isteklerin büyük batch'ler tarafından açlığa terk edilmesini engelliyor. Kiracı başına bir token bucket, herhangi bir tek kiracının — fırsat verildiğinde kiracıların yapacağı şeyi — yapmasını engelliyor.

// Şu anda elimizdeki şey. Sıkıcı kısım.
loop {
    let req = queue.pop(priority).await;
    if !tenant_bucket.try_take(&req) {
        queue.push_back(req, priority);
        continue;
    }
    replica.run(req).await;
}

Önemli olan tek şeyle ölçülen performans (sözleşmedeki yükte uçtan uca gecikmenin p99'u) iyileşti. Operasyonel olarak yeni sürüm, sabah üçte aranan herhangi bir nöbetçi mühendisin kafasında tutabildiği bir sistem — eski sürüm değildi.

Kuyruğun şekli

Burada bir paragraftan çok küçük bir resim değerli. Kuyruk, yoldaki paylaşılan tek durum parçası. Geri kalan her şey — tahminleyiciler, sağlık probları, kapasite planlayıcıları — bu merkezî nesnenin etrafındaki süs.

reqreqreqreqreqP0 interaktifP1 varsayılanP2 batchr0r1r2

Şek. 1 — Üç öncelik bandı, üç replika. Replikalar kapasiteleri olduğunda çeker.

Kuyruğun yapamadığı

Kuyruk bedava bir öğle yemeği değil. Üç şeyde kötüdür ve bunun aksini iddia etmek, benzer sistemlerde en sık gördüğüm hata modudur:

Çok-adımlı koordinasyonda kötüdür. Eğer bir istek aslında bağımlılıkları olan alt-isteklerden oluşan küçük bir grafsa, kuyruk hepsini aynı anda aynı replikaya seve seve atayacak ve sen, yavaş yavaş ve mümkün olan en kötü anda, bir thundering herd yeniden icat ettiğini keşfedeceksin.

Heterojen filolarda kötüdür.Replikalarının yarısının iki katı belleği varsa, kuyruk bunu bilmez ve öğrenmez. Bunu, bir diyagramda daha çirkin ama denediğimiz daha akıllı fikirlerin herhangi birinden çıkarımı daha kolay olan, queue-per-tier'in gösterişsiz mekanizmasıyla çözdük.

Ne olduğunu sana söylemekte kötüdür.Production'da bir kuyruk, yalnızca kuyruk-ucunda görebildiğin bir şekildir. Gözlemlenebilirliği ayrıca inşa etmen ve ona bakmaya kendini adaman gerekecek. ²

Akıllılık üzerine bir not

Akıllı sistemlere karşı değilim. Onlardan epey yazdım ve dengede bu kariyerin en keyifli kısımları oldular. Ama şimdi altyapıda akıllılığın testinin tahtada ilginç olup olmaması olmadığını düşünüyorum. Test, sabah dörtte aranacak bir sonraki mühendisin tüm şeyi kafasında tutabilip tutamadığıdır.

Kuyruk bu testten geçer. Tahminleyici hiç geçmedi.

  1. Tanılama modu, geriye dönüp bakınca, akıllı kodun yaptığı en yararlı şeydi. Bir gözlemlenebilirlik aracı olarak dağıtıldı — zaten her zaman olması gereken şey buydu.
  2. Her öncelik bandı için pull-süresi üzerindeki histogramları birincil sinyal olarak kullanıyoruz. Söylendiğine göre, kesin dashboard herkesin sevdiği en sıkıcı grafik.