Hız sınırı bir savunma değil. Bir kapasite sözleşmesi.

Xaron'da hız sınırı olmayan endpoint merge olmuyor. Ama asıl mesele kuralın kendisi değil — neye göre saydığın, ve limite çarpan kullanıcıya ne söylediğin.

This post is in Turkish.

Xaron'daki kurallardan biri şu: yeni bir endpoint hız sınırı olmadan girmiyor. Eksikse bu bir sonraki bilet değil, doğrudan engel.

Bu kuralı koyduğumuzda gerekçe kötüye kullanımdı. Zamanla anladık ki hız sınırının asıl işi saldırganı durdurmak değil — sistemin kapasitesini açıkça beyan etmek. Saldırı zaten en kolay senaryo. Zor senaryo, iyi niyetli bir istemcinin bozuk bir döngüye girmesi.

Neye göre sayıyorsun

İlk ve en belirleyici karar bu. IP'ye göre saymak en kolay yol ve çoğu durumda yanlış.

Bir üniversite kampüsündeki yüz kullanıcı tek IP'nin arkasından geliyor. Aynı şekilde bir mobil operatörün NAT'ı yüz binlerce kişiyi birkaç adrese sıkıştırıyor. IP'ye göre sınır koyduğunda, o kampüsteki bir kişinin fazla isteği diğer doksan dokuz kişiyi cezalandırıyor.

Doğru sayaç kimliğe göre olan. Oturum açmış kullanıcıda kullanıcı kimliği, açmamışta cihaz veya oturum kimliği, ve IP yalnızca en dış katmanda kaba bir güvenlik ağı olarak. Yani tek bir sınır değil, katmanlı sınırlar.

Her endpoint aynı değil

İkinci karar: tek bir global sınır işe yaramıyor.

Bir mesaj göndermek ile bir arama yapmak aynı maliyette değil. Arama, veritabanına giden pahalı bir iş; mesaj göndermek ucuz ama sık. Şifre denemesi ise sıklığı düşük olması gereken bir iş — orada sınır kapasite değil, güvenlik kararı.

Bu yüzden sınırlar katmanlı: uç noktanın maliyet sınıfına göre farklı kotalar, üstüne kullanıcı başına genel bir tavan. Yeni bir endpoint yazan kişinin cevaplaması gereken soru "sınır koydum mu" değil, "bu hangi maliyet sınıfında".

Hız sınırı olmayan bir endpoint, kapasitesi sonsuz olduğunu iddia eden bir endpoint'tir.

Ani yük normaldir

Üçüncüsü, sabit pencere sayacının neden yetmediği.

Dakikada altmış istek diyorsan, kullanıcının saniyede bir istek atmasını beklemiyorsun. Gerçek istemciler öbek hâlinde çalışıyor: uygulama açılıyor, beş istek birden gidiyor, sonra bir dakika sessizlik. Bu normal davranış ve engellenmemeli.

Token bucket bunu doğal olarak çözüyor — birikmiş kredin varsa öbeği geçiyorsun, sürekli bastırırsan ortalama hızda sınırlanıyorsun. Sabit pencere ise pencere sınırında ikiye katlanma problemini getiriyor: pencerenin son saniyesinde altmış, ilk saniyesinde altmış — iki saniyede yüz yirmi istek.

Reddetmek de bir arayüz

Dördüncüsü ve en sık ihmal edileni: limite çarpan istemciye ne söylediğin.

Sadece hata kodu dönmek işi yarım bırakıyor. İstemcinin bilmesi gereken şey ne zaman tekrar deneyebileceği. Bu bilgi verilmediğinde istemci hemen tekrar deniyor, tekrar reddediliyor, ve sen zaten yüklüyken üstüne bir de gereksiz trafik alıyorsun.

Yani doğru cevap üç parça taşıyor: ne zaman tekrar denenebileceği, mevcut kotanın ne olduğu, ne zaman yenileneceği. İyi yazılmış bir istemci bu bilgiyle kendini yavaşlatıyor — ve o noktada hız sınırı bir ceza olmaktan çıkıp bir koordinasyon mekanizmasına dönüşüyor.

Realtime tarafı ayrı bir problem

Son olarak, bunların hiçbiri kalıcı bağlantılar için doğrudan geçerli değil.

Bir WebTransport oturumunda "istek" diye net bir birim yok. Sınırlaman gereken şey açılan akış sayısı, saniyedeki mesaj hacmi, ve toplam bağlantı süresi. Ve reddetme davranışı da farklı: bağlantıyı koparmak, bir HTTP isteğini reddetmekten çok daha ağır bir ceza.

Burada öğrendiğimiz şey, sınırı bağlantı seviyesinde değil akış seviyesinde uygulamak oldu. Kötü davranan bir akış kapanıyor, oturum ayakta kalıyor. Kullanıcı sesli sohbetten düşmüyor — sadece taşkınlık yapan kanalı susuyor.