Asosiy kontentga o‘tish

Maqolalar

Maqola · 2026-08-16 · ~3 daqiqa o‘qiladi · o'rta

Negative caching tuzog'i: xatolikni ham keshlab qo'ymang

#backend #performance

Mundarija

Kecha shu saytning o'zida qiziq bug bo'ldi: bosh sahifadagi "open-source loyihalar" soni GitHub'dan jonli olinadi, lekin production'da u eskirgan zaxira qiymatni ko'rsatib turaverdi. Lokalda esa hammasi to'g'ri. Sabab — men bilib turib qazib qo'ygan chuqurga o'zim tushdim: xatolikni ham muvaffaqiyat bilan bir xil muddatga keshlagan edim.

Kod qanday edi#

def repo_count() -> int:
    cached = cache.get(CACHE_KEY)
    if cached is not None:
        return cached
    value = fetch_from_github()  # xato bo'lsa None
    if value is None:
        value = FALLBACK  # zaxira qiymat
    cache.set(CACHE_KEY, value, 60 * 60 * 12)  # 12 soat!
    return value

Bir qarashda mantiqiy: qiymat topilmasa zaxirani olamiz, keshga yozamiz, 12 soat tinch yashaymiz. Muammo shundaki, cache.set ikkala holatda ham 12 soatga yozadi.

Nima bo'ldi#

Server qayta ishga tushganda birinchi so'rov GitHub API'ga 3 soniyalik timeout bilan ketdi. O'sha paytda tarmoq bir soniya sekinroq edi — so'rov o'tmadi, None qaytdi, zaxira qiymat keshga tushdi. Tamom: keyingi 12 soat davomida hech kim GitHub'ga qayta so'rov yubormaydi, chunki kesh "to'la". Bitta omadsiz soniya — kun bo'yi eskirgan raqam.

Bu holatning nomi bor: negative caching — salbiy natijani (xato, bo'sh javob, 404) keshlab qo'yish. O'z-o'zidan yomon narsa emas, DNS ham shunday qiladi. Xato — salbiy natijani ijobiy natija muddatiga keshlash.

To'g'ri yechim#

Ikkala holatga alohida muddat:

TTL = 60 * 60 * 12  # muvaffaqiyat: 12 soat
FAILURE_TTL = 60 * 15  # xatolik: 15 daqiqadan keyin qayta urinamiz

value = fetch_from_github()
if value is None:
    cache.set(CACHE_KEY, FALLBACK, FAILURE_TTL)
    return FALLBACK
cache.set(CACHE_KEY, value, TTL)
return value

Endi omadsiz so'rov ko'pi bilan 15 daqiqa yashaydi — foydalanuvchi zaxira qiymatni qisqa vaqt ko'radi, tizim esa o'zini o'zi tuzatadi.

Qoidalar#

  • Muvaffaqiyat va xatolik — har doim alohida TTL. Xatolik uchun qisqa (soniyalar–daqiqalar), muvaffaqiyat uchun uzun.
  • Timeout'ni juda qattiq qilmang. 3 soniya tashqi API uchun kam — bitta sekin javob butun keshni zaharlaydi.
  • Zaxira qiymat — vaqtinchalik yamoq, holat emas. U keshda uzoq yashasa, foydalanuvchi uchun "haqiqat"ga aylanadi.
  • Bunday bug'ni topish qiyin: kod ishlayapti, xato loglari yo'q, faqat raqam "qotib" qolgan. Shuning uchun keshga yozayotgan har bir joyda o'zingizdan so'rang: "bu yerga xato qiymat tushsa, qachon tuzaladi?"

Kichik bug, katta saboq — endi bu savol mening har bir code review ro'yxatimda.