Ana sayfa → Teknik
Rate Limit ve Fallback: Hata Değil, Plan
Pazartesi 10:14, halka arz talep toplamasının ilk sabahı. Destek asistanı müşteriye
“Şu an yardımcı olamıyorum, lütfen daha sonra tekrar deneyin” dedi. 38 dakika
boyunca her üç sorudan birine aynı cümleyi söyledi. Sağlayıcının cevabı tek satırdı:
429 Too Many Requests.
- 429 bir arıza değil, bir sözleşme maddesi. Kotan belli, trafiğin belli; ikisinin ne zaman çarpışacağı da önceden hesaplanabilir.
- İki kota var, ikisini ayrı izle. Dakikalık istek (RPM) ve dakikalık token (TPM). Biz istek sayısına bakıyorduk; dolan token kotası oldu.
- Beklemeden tekrar denemek, kotayı daha hızlı doldurur.
Retry-After’a uy, jitter ekle, toplam bekleme bütçesi koy. - Fallback bedava değil. Küçük model 120 soruda 108 yerine 91 doğru verdi; hangi sorunun fallback’e gidebileceğine önceden karar ver.
- Kademeli hizmet, “yardımcı olamıyorum”dan iyidir. Önce arka plan işini durdur, sonra cevabı kısalt, en son yönlendir.
- Alarm 429’da değil, %80’de çalar. 429 geldiğinde iş işten geçmiştir.
Sahadan: halka arz sabahı
O sabah olan şey sürpriz değildi, sadece hazırlıksız yakalandık. Halka arz talep toplaması başladığında destek sorusu her zaman artar: “Talebim alındı mı?”, “Kaç lot düşer?”, “Param neden bloke?” Normal bir sabah tepe noktada dakikada 90 soru gelir. O sabah 150 geldi.
Her soru ortalama 2.500 token harcıyordu: system prompt, müşteri bağlamı, konuşma geçmişi ve cevap. 150 soru, dakikada 375 bin token demek. Kotamız 400 bindi. Kâğıt üstünde sığıyordu. Sığmadı, çünkü aynı saatte çalışan bir işi unutmuştuk: gece gelen ticket’ları etiketleyen arka plan işi, her sabah 10:00’da başlıyordu ve tek başına dakikada 100 bin token yiyordu. Toplam 475 bin. Kotanın %119’u.
Asıl hasarı ise kendi kodumuz yaptı. İstemcimiz 429 alınca beklemeden üç kez tekrar deniyordu. Reddedilen her soru dört istek oldu. Dakikalık istek sayısı 270’ten 610’a çıktı ve bu sefer 500’lük istek kotası da doldu. Artık kısa sorular bile geçmiyordu. 38 dakikada gelen yaklaşık 5.700 sorunun 1.900’ü (%33) “yardımcı olamıyorum” cevabı aldı.
10:52’de her şey düzeldi. Biz bir şey yapmadık; yapacak bir şeyimiz yoktu. Trafik kendiliğinden düştü, etiketleme işi bitti. Olaydan sonraki toplantıda sorulan ilk soru “bunu nasıl düzelttiniz” idi. Dürüst cevap: düzeltmedik, geçmesini bekledik.
Kota ne demek: RPM, TPM ve senin payın
LLM sağlayıcısı sana iki şey satıyor: bir model ve o modelin bir dilimi. Dilimin sınırı çoğu sağlayıcıda iki sayıyla ifade ediliyor:
- RPM (requests per minute): dakikada kaç istek atabilirsin. Kısa isteklerle dolar.
- TPM (tokens per minute): dakikada kaç token işletebilirsin. Uzun prompt’larla dolar.
Bazı sağlayıcılarda bir de günlük sınır var. Hangisi önce dolar, iş yükünün şekline bağlı. Bizim canlı asistanımız az ama uzun istek atıyordu: TPM’e yakındı, RPM’den uzaktı. Etiketleme işi ise çok ve kısa istek atıyordu. İkisi aynı kotayı paylaşınca ikisinin en kötü yanı birleşti.
İlk ders basit: kotayı bir sayı olarak değil, bir bütçe olarak düşün. O bütçeyi kim harcıyor? Bizde cevap şuydu: canlı müşteri, iç araçlar ve arka plan işleri. Olaydan önce üçü aynı havuzdan, sıra beklemeden çekiyordu. Kimin önce geleceğini kimse yazmamıştı.
Retry: beklemeden değil, bütçeyle
429’a verilen en yaygın cevap, bizim verdiğimiz cevap: tekrar dene. Sorun tekrar denemede değil, nasıl denendiğinde. Üç kural koyduk:
- Sağlayıcı ne zaman gel diyorsa o zaman gel. Cevapta
Retry-Aftervarsa ona uy. Sağlayıcı kotanın ne zaman boşalacağını senden iyi biliyor. - Yoksa exponential backoff, jitter’lı. Aynı anda reddedilen yüz istek aynı anda geri gelirse ikinci bir dalga olur. Rastgele bir pay ekleyince dalga zamana yayılıyor.
- Toplam bekleme bir bütçedir. Müşteri ekranda bekliyor. Bizde bütçe 6 saniye: ilk token’ın gelmesi zaten 1–2 saniye sürüyor (latency yazısında anlattım), üstüne 6 saniyeden fazla bekleme “bozuk” hissi veriyor. Bütçe dolunca tekrar deneme biter, fallback başlar.
BEKLEME_BUTCESI = 6.0 # saniye; kullanici ekranda bekliyor
MAKS_DENEME = 2 # ilk istek + 2 tekrar
def cevapla(soru):
baslangic = simdi()
for deneme in range(MAKS_DENEME + 1):
yanit = ana_model.cagir(soru)
if yanit.durum != 429:
return yanit
# saglayici ne zaman gel diyorsa ona uy
bekle_sn = yanit.basliklar.get("Retry-After")
if bekle_sn is None:
taban = 0.5 * (2 ** deneme) # 0.5, 1, 2
bekle_sn = random.uniform(0, taban) # full jitter
# butce yetmiyorsa bekleme, dogrudan fallback'e gec
if simdi() - baslangic + bekle_sn > BEKLEME_BUTCESI:
break
uyu(bekle_sn)
metrik.artir("fallback", sebep="429")
return fallback(soru) # asagidaki kurallara gore
Kodun en önemli satırı break. Olay sabahı bizim istemcimizde bu satır yoktu:
tekrar deneme, kotanın boşalmasını beklemiyordu, kotayı dolduran şeyin kendisiydi.
Fallback: ikinci model, ikinci kalite
Olaydan sonra ilk refleks “ikinci bir model koyalım, biri dolunca öbürü cevaplasın” oldu. Doğru refleks, eksik plan. Fallback’in iki sorusu var: kalitesi ne kadar düşük ve hangi soruya açık.
Kaliteyi ölçmek için destek ekibinden iki kişiyle son ayın 120 gerçek sorusunu seçtik ve üç seçeneğe de sorduk. İki kişi cevapları ayrı ayrı puanladı, anlaşamadıkları 7 cevabı birlikte karara bağladılar:
| Seçenek | Doğru cevap (120 soruda) | Ortalama cevap süresi | Not |
|---|---|---|---|
| Ana model | 108 (%90) | 3,1 sn | Referans |
| Aynı sağlayıcının küçük modeli | 91 (%76) | 1,4 sn | Ayrı kota; hızlı ama vergi ve halka arz kurallarında zayıf |
| İkinci sağlayıcı | 101 (%84) | 2,6 sn | Ayrı prompt gerekti; kişisel veri gönderilemez |
Tablo bize üç karar aldırdı. Birincisi: küçük model sadece genel sorulara açık. “Hesap nasıl açılır”, “işlem saatleri ne” gibi soruları iyi cevaplıyor. Para çekme, emir iptali ve vergi soruları fallback’e gitmiyor; onlar canlı temsilciye yönleniyor. Yanlış cevap, bekletilen cevaptan pahalı.
İkincisi: ikinci sağlayıcıya müşteri verisi gitmiyor. Sözleşmemiz ve veri işleme onayımız ana sağlayıcıyla. İkinci sağlayıcı yalnızca kişisel veri içermeyen soruları alıyor. Bu da pratikte onu küçük modelin yedeği yapıyor, ana modelin değil.
Üçüncüsü: fallback sessiz olmaz. Her cevabın hangi modelden geldiği loglanıyor (neyi nasıl logladığımızı prompt loglama yazısında anlattım). Bir müşteri şikâyet ettiğinde ilk bakılan alan bu.
Kademeli hizmet: dört seviye
Asıl değişiklik, kotayı bir açma-kapama düğmesi olarak değil, bir gösterge olarak görmek oldu. Kullanım yükseldikçe sistem sırayla vazgeçiyor. Önce kimsenin fark etmeyeceği şeylerden, en son müşterinin fark edeceklerinden:
| Seviye | Tetik (dakikalık TPM kullanımı) | Ne değişir | Müşteri fark eder mi? |
|---|---|---|---|
| 0 — Normal | %70 altı | Her şey açık | — |
| 1 — Arka plan durur | %70–85 | Etiketleme ve gece özetleri kuyrukta bekler | Hayır |
| 2 — Kısa cevap | %85–95 | Konuşma geçmişi son 10 yerine son 4 mesaj; cevap sınırı 600 yerine 250 token; “konuşmayı özetle” düğmesi kapanır | Biraz |
| 3 — Yönlendirme | %95 üstü ya da 429 oranı %2 üstü | Genel sorular küçük modele; hassas sorular canlı temsilciye | Evet |
Seviye 1 tek başına olay sabahını kurtarırdı: etiketleme işi dursaydı toplam 375 bin token olacaktı, kotanın altında. Arka plan işleri artık ayrı bir kuyrukta ve kotanın en fazla %15’ini, yani dakikada 60 bin token’ı kullanabiliyor. Canlı müşteri her zaman önce geliyor.
Seviye 3’te bile müşteri “yardımcı olamıyorum” görmüyor. Ya daha kısa bir cevap alıyor ya da “Sizi temsilcimize bağlıyorum”. Hata mesajı, planın bittiği yerdir; bizde plan o noktaya varmadan bitmiyor.
İlk denemede sistem seviyeler arasında gidip geldi
İlk sürümde seviye, kullanım eşiğin altına iner inmez geri dönüyordu. Seviye 2 token kullanımını düşürüyor, kullanım %85’in altına iniyor, sistem seviye 1’e dönüyor, kullanım yine yükseliyor. Testte dakikada iki kez gidip geldi. Çözüm: yukarı çıkmak anlık, aşağı inmek için 10 dakika boyunca alt eşiğin altında kalmak gerekiyor.
- RPM ve TPM’i ayrı ayrı, kotanın yüzdesi olarak izle
Retry-After’a uy, jitter ekle, bekleme bütçesi koy- Arka plan işlerine kotadan ayrı, sınırlı bir pay ver
- Fallback kalitesini gerçek sorularla ölç, soru tipine göre aç
- Seviyeden inmek için bekleme süresi koy
- 429’da beklemeden tekrar denemek
- Canlı müşteriyle arka plan işini aynı havuzda bırakmak
- Fallback cevabını loglamadan vermek
- Kişisel veriyi sözleşmesi olmayan sağlayıcıya göndermek
- Alarmı 429’a kurmak
Ne izlemeli?
| Ne | Neden |
|---|---|
| Dakikalık TPM ve RPM, kotanın yüzdesi olarak | Hangisinin önce dolacağı iş yüküne göre değişir; ikisi ayrı çizgi |
| Kullanımın dağılımı: canlı / iç araç / arka plan | Kotayı kimin harcadığını bilmeden kimden keseceğini bilemezsin |
| 429 oranı ve ilk istek başına tekrar deneme sayısı | Tekrar deneme oranı yükseliyorsa kotayı kendi istemcin dolduruyor |
| Fallback’ten verilen cevap oranı | Sıfır değilse neden sıfır değil; kalıcıysa aslında kotan yetmiyor |
| Seviye 1, 2 ve 3’te geçen dakika / hafta | Artıyorsa sorun anlık tepe değil, kalıcı büyüme |
Alarm tek kural: dakikalık token kullanımı beş dakika boyunca %80’in üstündeyse nöbetçiye bildirim. 429 geldiğinde çalan alarm, müşterinin zaten gördüğü şeyi sana haber verir.
Bende işe yaramayanlar
- Sadece kota artışı istemek. Olaydan sonra ilk iş sağlayıcıdan artış istedik. İki hafta sonra 400 binden 500 bine çıktı. Plan yoksa yeni kota da bir sonraki tepede doluyor; artış sadece o günü öteliyor.
- 429’u circuit breaker’a bağlamak. İlk hafta 429 gelince ana modeli bir dakikalığına tamamen kapattık. Kota çoğu zaman %100 değil %103 doluydu; bir dakikalık tam kapatma, kaybettiğimizden fazlasını fallback’e itiyordu. Kademeli seviye daha iyi çalıştı.
- Tek prompt’u iki sağlayıcıda kullanmak. İkinci sağlayıcının modeli aynı prompt’la cevapları madde madde değil, uzun paragraf olarak yazdı. Ayrı prompt tutmak zorunda kaldık; iki prompt da artık birlikte test ediliyor.
Sahadan: bir ay sonra, ikinci halka arz
17 Mart Salı, bir sonraki talep toplamasının ilk sabahı. Soru sayısı bu sefer dakikada 170’e çıktı, ilk olaydan da yüksek. Yan yana:
| 16 Şubat | 17 Mart | |
|---|---|---|
| Tepe soru / dakika | 150 | 170 |
| TPM kotası | 400 bin | 500 bin |
| Tepe kullanım (dakikalık) | %119 | %97 (bir dakika) |
| 429 alan istek | 8.000’i aşkın (tekrar denemeler dahil) | 14 |
| “Yardımcı olamıyorum” cevabı | ~1.900 | 0 |
| Kademeli hizmette geçen süre | — | Seviye 1: 25 dk, seviye 2: 9 dk |
| Beğeni oranı (seviye 2 penceresi) | Ölçülmedi | %82 → %77 |
Son satır önemli: kademeli hizmet bedava değil. Kısa cevap veren 9 dakikada müşterinin beğeni oranı 5 puan düştü. Ama müşteri kısa bir cevap aldı, kapalı bir kapı değil. Seviye 3’e hiç çıkmadık; 14 isteğin hepsi tekrar denemede geçti.
Kontrol listesi
- RPM ve TPM kotamın yüzde kaçını, kim kullanıyor — biliyor muyum?
- Canlı müşteri ile arka plan işleri aynı havuzdan mı çekiyor?
- İstemcim 429’da
Retry-After’a uyuyor mu, beklemeden mi deniyor? - Tekrar denemelerin toplam bir bekleme bütçesi var mı?
- Fallback modelin kalitesini gerçek sorularla ölçtüm mü?
- Hangi soru tipleri fallback’e asla gitmez — yazılı mı?
- İkinci sağlayıcıya hangi veri gidebilir, hangisi gidemez?
- Kota %80’e geldiğinde kime haber gidiyor?
- Müşterinin gördüğü son cümle “yardımcı olamıyorum” mu, yoksa bir yönlendirme mi?
Sonuç
16 Şubat sabahı 38 dakika boyunca müşteriye kapıyı kapattık. Sebep kotanın küçük olması değildi. Sebep, kota dolduğunda ne olacağına hiç karar vermemiş olmamızdı. Karar verilmeyince sistem en kötü seçeneği kendi seçti: herkesi aynı anda reddetti, sonra herkesi aynı anda tekrar denetti.
Kota bir sınırdır ve sınır bir gün dolacak. Soru dolup dolmayacağı değil, dolduğunda neyin önce susacağı. Etiketleme işi mi, uzun cevap mı, müşteri mi?
O sırayı sakin bir günde sen yazarsın. Yazmazsan, halka arz sabahı sağlayıcı senin yerine yazar.