Mundarija (23)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nimani kuzatish kerak: to'rt qatlam
- 2.2. Belgilar kechikishi va proksi metrikalar
- 2.3. Metrikalar: hisoblagich, gauge, gistogramma
- 2.4. Persentil taxmini xatosi
- 2.5. Strukturali loglar va so'rov id
- 2.6. Ogohlantirish qoidalari
- 2.7. SLI, SLO va xato byudjeti
- 2.8. Dashboard (Grafana) — nazariyada
- 2.9. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Metrikalar noldan: counter, gauge, gistogramma va persentil
- Misol 2 — Strukturali loglar, so'rov id va belgilar kechikishi
- Misol 3 — Ogohlantirish qoidalari: precision, recall va alert fatigue
- Misol 4 — SLO, xato byudjeti va burn-rate ogohlantirishlari
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
27.11-dars: Monitoring
27-QISM — MLOPS VA DEPLOY · 11-dars
1. Kirish va motivatsiya
Oldingi darsda 27.10-bob model API sini bulutga chiqardik: replikalar sonini hisobladik, avtomasshtablashni sozladik va yangi versiyani canary bilan chiqardik. Canary ning butun g'oyasi bitta narsaga tayanardi — metrikalar: xato ulushi, kechikish, yangi versiya va eski versiya taqqoslanishi. Bu darsda o'sha metrikalar qayerdan keladi, qanday yig'iladi va qachon odamni uyg'otishi kerakligini ko'ramiz.
Oddiy dasturiy ta'minotda monitoring asosan bitta savolga javob beradi: "tizim ishlayaptimi?". ML tizimida bu yetarli emas. Model ishlashi mumkin — 200 kodi qaytaradi, kechikish past, xatolar yo'q — lekin noto'g'ri ishlashi mumkin: kirish ma'lumotlari o'zgargan, belgilar quvuri buzilgan, bashoratlar siljigan. Va eng yomoni: to'g'ri javob (belgi) ko'pincha haftalar o'tib keladi. Mijoz ketdimi yoki yo'qmi — bir oydan keyin ma'lum bo'ladi; kredit qaytarildimi — yildan keyin.
Real vaziyat. Bank skoring modeli uch hafta davomida "sog'lom" ko'rindi: kechikish 40 ms, xatolar 0.1%, dashboard yashil. Keyin risk bo'limi kelishuvlar ulushi keskin oshganini payqadi. Sabab: yangi mobil ilova versiyasi daromadni ming so'mda yubora boshlagan edi, eski versiya — so'mda. Model uchun barcha mijozlar birdaniga "kam daromadli" bo'lib qoldi. Belgilar (kredit qaytarildimi) oylar keyin kelardi, lekin kirish taqsimoti va bashorat taqsimoti birinchi kuniyoq o'zgargan edi — faqat ularni hech kim kuzatmagan edi. 2-misol aynan shu holatni simulyatsiya qiladi.
Bu darsda ML tizimini to'rt qatlamda kuzatishni o'rganamiz: tizim, ma'lumot, model va biznes — va qachon ogohlantirish kerakligini hisoblaymiz.
Bu darsda:
- Nimani kuzatish kerak: tizim, ma'lumot, model, biznes metrikalari
- Belgilar kechikishi va proksi metrikalar
- Metrikalar noldan: hisoblagich, gauge, gistogramma (Prometheus g'oyasi)
- Persentilni gistogrammadan taxminlash va uning xatosi
- Strukturali loglar va so'rov id
- Ogohlantirish qoidalari: statik va statistik chegara, yolg'on signal va o'tkazib yuborish, "alert fatigue"
- SLI, SLO va xato byudjeti, burn-rate ogohlantirishlari
- Dashboard (Grafana) — nazariyada
ℹ Barcha misollar virtual vaqtda (daqiqa, kun — hisoblagich) va urug' bilan deterministik. Prometheus va Grafana ishga tushirilmaydi — ularning g'oyasi kichik Python kodi bilan noldan quriladi.
2. Nazariya — chuqur tushuntirish
2.1. Nimani kuzatish kerak: to'rt qatlam
QATLAM SAVOL MISOL METRIKALAR
-------------- ----------------------------- ------------------------------------
1. TIZIM xizmat ishlayaptimi, tezmi? so'rovlar/s, xato ulushi (5xx),
kechikish p50/p95/p99, CPU, xotira,
navbat uzunligi, replikalar soni
2. MA'LUMOT kirish kutilganidek mi? null ulushi, sxema buzilishi (tur,
oraliq), kategoriyalar ulushi,
taqsimot siljishi 27.12-bob
3. MODEL bashoratlar oqilonami? bashorat taqsimoti (o'rtacha, ulush
> chegara), ishonch, model versiyasi;
belgilar kelganda - AUC, xato
4. BIZNES foyda keltiryaptimi? konversiya, kelishuvlar ulushi,
firibgarlik zarari, shikoyatlar
TIZIM USULLARI:
RED (so'rovga asoslangan xizmatlar): Rate, Errors, Duration
USE (resurslar): Utilization, Saturation, ErrorsBirinchi qatlam har qanday xizmatda bor. ML ning o'ziga xosligi — 2 va 3-qatlamlar: tizim metrikalari mukammal bo'lsa ham model jim holda noto'g'ri ishlashi mumkin. To'rtinchi qatlam — oxirgi hakam: model metrikasi yaxshi, biznes yomon bo'lsa, noto'g'ri narsani optimallashtirayotgan bo'lishimiz mumkin.
"200 OK" — to'g'ri javob degani emas: ML xizmati tizim, ma'lumot, model va biznes qatlamlarida alohida kuzatiladi.
2.2. Belgilar kechikishi va proksi metrikalar
VAZIFA BELGI QACHON MA'LUM KECHIKISH
mijoz ketishi keyingi oyda ~ 30 kun
kredit defolti to'lov muddatlari o'tgach oylar - yil
firibgarlik chargeback kelganda kunlar - haftalar
tavsiya (bosish) darhol soniyalar
talab prognozi davr tugagach kun - hafta
BELGI YO'Q PAYTDA NIMA KUZATILADI (PROKSI):
kirish ma'lumotlari: null, oraliq, taqsimot
bashorat taqsimoti: o'rtacha ball, "ijobiy" ulushi, ishonch
qisman belgilar: erta signal (1-to'lov o'tkazib yuborildi)
biznes proksi: tasdiqlangan arizalar ulushi
MUHIM:
proksi signal "nimadir o'zgardi" deydi, "model yomonlashdi" demaydi
-> tekshiruv boshlanadi; sifat esa belgilar kelganda tasdiqlanadi
belgilar kelgach - sifat metrikasi QAYTA hisoblanadi (orqaga qarab)Belgi kechiksa, birinchi signal — kirish va bashorat taqsimoti; sifat metrikasi keyin kelib, xulosani tasdiqlaydi yoki rad etadi.
2.3. Metrikalar: hisoblagich, gauge, gistogramma
COUNTER (hisoblagich): faqat o'sadi
api_sorovlar_jami{yol="/predict", status="500"} 90
foydali narsa - TEZLIK: rate = (qiymat2 - qiymat1) / vaqt
qayta ishga tushishda 0 ga tushadi -> "reset" ni hisobga olish kerak
GAUGE: istalgan tomonga o'zgaradi
api_navbat_uzunligi 3, xotira_bayt, yuklangan_model_versiya
HISTOGRAM: bucketlar (kumulyativ)
api_kechikish_soniya_bucket{le="0.1"} 12815 <- 100 ms gacha
api_kechikish_soniya_bucket{le="+Inf"} 20000 <- hammasi
api_kechikish_soniya_sum / _count <- o'rtacha uchun
persentil TAXMINI: kerakli bucketni topib, ichida chiziqli interpolyatsiya
YORLIQLAR (labels): yol, status, model_versiya
DIQQAT: foydalanuvchi id, so'rov id kabi yuqori kardinallikli yorliqlar
YO'Q - har qiymat alohida vaqt qatori, xotira portlaydiPROMETHEUS MODELI (pull):
ilova /metrics sahifasida joriy qiymatlarni matn sifatida beradi
Prometheus har 15-30 soniyada "tortib oladi" (scrape) va saqlaydi
so'rov tili (PromQL) misollari:
rate(api_sorovlar_jami[5m]) so'rov/s
sum(rate(api_sorovlar_jami{status=~"5.."}[5m]))
/ sum(rate(api_sorovlar_jami[5m])) xato ulushi
histogram_quantile(0.99,
sum by (le) (rate(api_kechikish_soniya_bucket[5m]))) p99# Haqiqiy loyihada: prometheus_client (bu darsda o'rnatilmagan, g'oya uchun)
from prometheus_client import Counter, Histogram
SOROVLAR = Counter("api_sorovlar_jami", "Jami so'rovlar", ["yol", "status"])
KECHIKISH = Histogram("api_kechikish_soniya", "Kechikish",
buckets=[0.02, 0.05, 0.1, 0.2, 0.3, 0.5, 1.0])
with KECHIKISH.time():
natija = model_bashorat(kirish)
SOROVLAR.labels(yol="/predict", status="200").inc()Persentillarni o'rtachalamang — replikalar yoki vaqt oralig'i bo'yicha gistogramma bucketlari qo'shiladi, keyin persentil hisoblanadi.
2.4. Persentil taxmini xatosi
histogram_quantile bucket ICHIDAGI taqsimotni bilmaydi -> chiziqli taxmin
xato <= bucket kengligi
standart bucketlar (5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, ...)
100-250 ms oralig'ida bitta bucket -> p99 = 320 ms bo'lsa, xato katta
SLA chegarasi atrofida ZICH bucketlar kerak (masalan, 250, 300, 400 ms)
OXIRGI CHEKLI BUCKETDAN KATTA persentil:
Prometheus oxirgi chekli chegarani qaytaradi -> p99 "1 s" bo'lib qotib qoladi
-> bucketlar kutilgan maksimumdan yuqorigacha borsin
ALTERNATIVA: summary (klientda persentil) - lekin replikalar bo'yicha
QO'SHIB BO'LMAYDI; shuning uchun odatda histogram afzalBucketlar SLA ga moslanadi: persentil aniqligi bucket kengligidan yaxshi bo'la olmaydi.
2.5. Strukturali loglar va so'rov id
ODDIY LOG: "2026-09-24 10:15 bashorat 0.83 mijoz uchun, 41ms"
-> odam o'qiydi, mashina qiynaladi (regex, format o'zgaradi)
STRUKTURALI LOG: {"sorov_id": "70fd086adf5b", "hodisa": "bashorat",
"model_versiya": "churn-v3", "ms": 31.5, "ball": 0.83}
-> filtr, guruhlash, metrikaga aylantirish oson
SO'ROV ID (correlation / trace id):
gateway da yaratiladi -> har xizmatga sarlavhada uzatiladi
-> har log qatorida bor -> bitta so'rovning butun izi bitta filtr bilan
(to'liq variant: distributed tracing - OpenTelemetry, span lar)
QOIDALAR:
har log qatorida: vaqt, daraja, xizmat, sorov_id, model_versiya
shaxsiy ma'lumot (ism, telefon, karta) - NIQOBLANADI yoki yozilmaydi
sirlar - hech qachon 27.9-bob
bashorat loglari = kelajakdagi o'quv/baholash ma'lumoti:
kirish + bashorat + versiya saqlanadi -> belgi kelganda birlashtiriladiBashorat logi — kelajakdagi baholash to'plami: kirish, bashorat va model versiyasi so'rov id bilan saqlanadi, belgi kelganda shu id orqali birlashtiriladi.
2.6. Ogohlantirish qoidalari
STATIK CHEGARA: xato ulushi > 2%
+ sodda, tushunarli - so'rovlar kam paytda (tun) shovqin katta
- mavsumiylikni bilmaydi
"FOR" SHARTI: xato ulushi > 2% ketma-ket 15 daqiqa
+ bir martalik sakrashni e'tiborsiz qoldiradi
- qisqa insidentni butunlay o'tkazib yuboradi, kechikish qo'shadi
STATISTIK (dinamik): x > o'rtacha(oxirgi kun) + k * std
+ moslashadi - std ichida tun/kunduz aralash, insidentlar ham
asosga "singib" ketadi
SO'ROVLAR SONIGA MOS (binomial z):
z = (xato - n * p) / sqrt(n * p * (1 - p)), p - tarixiy ulush
+ tunda n kichik -> maxraj kichik -> bitta xato "yong'in" emas
- n juda kichik bo'lsa normal yaqinlashuv buziladi -> minimal n yoki
ketma-ketlik sharti
BAHOLASH (ma'lum insidentlar tarixida):
precision = haqiqiy ogohlantirishlar / barcha ogohlantirishlar
recall = topilgan insidentlar / barcha insidentlar
aniqlash kechikishi, haftasiga yolg'on ogohlantirishlarALERT FATIGUE:
haftasiga 20 yolg'on signal -> odamlar ogohlantirishni o'qimay qo'yadi
-> haqiqiy insident ham e'tiborsiz qoladi
QOIDA: har ogohlantirish = odam HOZIR nimadir qilishi kerak
harakat talab qilmasa - dashboard yoki kunlik hisobot, ogohlantirish emas
PAGE (tunda uyg'otadi) vs TICKET (ish vaqtida ko'riladi)Ogohlantirish qoidasi ham model: uni tarixdagi ma'lum insidentlar ustida precision, recall va kechikish bilan baholang.
2.7. SLI, SLO va xato byudjeti
SLI (indicator): o'lchanadigan ko'rsatkich
"yaxshi so'rovlar / barcha so'rovlar"
yaxshi = status < 500 VA kechikish < 300 ms
SLO (objective): maqsad: 30 kunda SLI >= 99.7%
SLA (agreement): mijoz bilan shartnoma (odatda SLO dan yumshoqroq, jarima bilan)
XATO BYUDJETI = 1 - SLO = 0.3% so'rovlar "yomon" bo'lishi MUMKIN
30 kunda 10 mln so'rov -> 30 000 yomon so'rovga ruxsat
to'liq uzilish ekvivalenti: 0.003 * 30 * 24 * 60 = 130 daqiqa
BYUDJET SIYOSATI:
byudjet bor -> yangi funksiyalar, tajribalar, tez deploy
byudjet tugadi -> "muzlatish": faqat ishonchlilik ishlari
BURN RATE = (joriy yomon ulush) / (byudjet ulushi)
burn = 1 -> byudjet aynan 30 kunda tugaydi
burn = 14.4 -> 1 soatda oylik byudjetning 2% i (o'rtacha trafikda)
KO'P OYNALI OGOHLANTIRISH (Google SRE Workbook g'oyasi):
page: burn(1 soat) > 14.4 VA burn(5 daq) > 14.4
page: burn(6 soat) > 6 VA burn(30 daq) > 6
ticket: burn(3 kun) > 1 VA burn(6 soat) > 1
uzun oyna - ahamiyatlilik, qisqa oyna - "hali davom etyaptimi"Xato byudjeti — ishonchlilik va tezlik orasidagi kelishuv; ogohlantirish yomon ulushga emas, byudjet yonish tezligiga qaratiladi.
2.8. Dashboard (Grafana) — nazariyada
BITTA XIZMAT UCHUN DASHBOARD (yuqoridan pastga - umumiydan tafsilotga):
+------------------------------------------------------------------+
| SLO: 99.72% (maqsad 99.7%) | byudjet qoldi: 6% | burn(1h): 0.4 | <- holat
+------------------------------------------------------------------+
| so'rov/s (RED: Rate) | xato ulushi (Errors) | p50/p95/p99 | <- tizim
+------------------------------------------------------------------+
| null ulushi, sxema xatolari | kirish taqsimoti (asosiy belgilar) | <- ma'lumot
+------------------------------------------------------------------+
| o'rtacha ball, ball > 0.5 | model versiyasi bo'yicha trafik | <- model
| AUC (belgilar kelgan kunlar, kechikish bilan belgilangan) |
+------------------------------------------------------------------+
| biznes: konversiya, kelishuvlar ulushi | <- biznes
+------------------------------------------------------------------+
| deploy belgilari (annotatsiya) - grafiklarda vertikal chiziq |
QOIDALAR:
har panelda: birlik, chegara chizig'i (SLO / ogohlantirish), vaqt oralig'i
deploy va insidentlar annotatsiya bilan - "nima o'zgardi" darhol ko'rinadi
dashboard - tahlil uchun; ogohlantirish - harakat uchun (2.6)Dashboard savolga javob beradi, ogohlantirish harakatga chaqiradi — deploy annotatsiyalari "nima o'zgardi" savolini bir qarashda hal qiladi.
2.9. Tuzoqlar
Asosiy tuzoqlar: faqat tizim metrikalarini kuzatish ("200 OK" — model to'g'ri degani emas); belgilar kelishini kutib, proksi metrikalarsiz qolish; persentillarni o'rtachalash; SLA atrofida siyrak bucketlar; yuqori kardinallikli yorliqlar (foydalanuvchi id); counter reset ni hisobga olmaslik; matnli, tuzilmasiz loglar; so'rov id siz loglar; loglarda shaxsiy ma'lumot va sirlar; tungi kam trafikda statik chegara; harakat talab qilmaydigan ogohlantirishlar (alert fatigue); ogohlantirish qoidalarini tarixiy insidentlarda baholamaslik; fon xato darajasi byudjetning katta qismini yeydigan darajada qattiq SLO.
3. Tez ma'lumotnoma
import json
import math
def rate(oldingi, joriy, soniya):
"""Counter dan tezlik; reset bo'lsa joriy qiymat 0 dan boshlangan."""
farq = joriy - oldingi if joriy >= oldingi else joriy
return farq / soniya
def binomial_z(xato, n, p):
return (xato - n * p) / math.sqrt(n * p * (1 - p))
def burn_rate(yomon, jami, slo):
return (yomon / jami) / (1 - slo)
def log_json(**maydonlar):
print(json.dumps(maydonlar, ensure_ascii=False))
log_json(sorov_id="70fd086adf5b", hodisa="bashorat", ms=31.5, ball=0.83)# Prometheus ogohlantirish qoidasi (g'oya)
groups:
- name: churn-api
rules:
- alert: TezByudjetYonishi
expr: |
(sum(rate(api_yomon_jami[1h])) / sum(rate(api_sorovlar_jami[1h]))) / 0.003 > 14.4
and
(sum(rate(api_yomon_jami[5m])) / sum(rate(api_sorovlar_jami[5m]))) / 0.003 > 14.4
labels: {severity: page}
annotations:
summary: "churn-api: xato byudjeti tez yonmoqda"Tuzilma xulosasi
to'rt qatlam: tizim (RED/USE) -> ma'lumot -> model -> biznes
belgi kechikadi -> proksi: null, sxema, bashorat taqsimoti
counter (rate, reset), gauge, histogram (bucketlar SLA atrofida zich)
persentil: bucketlarni qo'shib, keyin histogram_quantile
loglar: JSON, sorov_id, model_versiya, shaxsiy ma'lumotsiz
ogohlantirish: n ga mos (binomial z) + ketma-ketlik; tarixda baholash
SLO -> byudjet -> burn rate (1h/5m, 6h/30m, 3d/6h)4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14); vaqt — virtual (daqiqa va kun hisoblagichlari).
Misol 1 — Metrikalar noldan: counter, gauge, gistogramma va persentil
Uch metrika turi Prometheus g'oyasi bo'yicha kichik klasslar sifatida yoziladi va /metrics matn formatida chiqariladi. 20000 ta virtual kechikish (lognormal) ikki xil bucket to'plamida yig'iladi va histogram_quantile taxmini haqiqiy persentil bilan taqqoslanadi. Keyin counter dan tezlik hisoblanadi (pod qayta ishga tushishi bilan) va uch replikaning persentillari ikki usulda birlashtiriladi.
"""Metrikalar noldan: Counter, Gauge, Histogram (Prometheus g'oyasi),
persentilni bucketlardan taxminlash va uning xatosi. Virtual vaqt."""
import bisect
import math
import numpy as np
class Counter:
"""Faqat o'sadi. Tezlik (rate) - ikki o'lchov orasidagi farqdan."""
def __init__(self, nom, yordam):
self.nom, self.yordam, self.qiymatlar = nom, yordam, {}
def inc(self, n=1.0, **yorliq):
k = tuple(sorted(yorliq.items()))
self.qiymatlar[k] = self.qiymatlar.get(k, 0.0) + n
class Gauge:
"""Istalgan tomonga o'zgaradi: navbat uzunligi, xotira, yuklangan model."""
def __init__(self, nom, yordam):
self.nom, self.yordam, self.qiymatlar = nom, yordam, {}
def set(self, v, **yorliq):
self.qiymatlar[tuple(sorted(yorliq.items()))] = v
class Histogram:
"""Kumulyativ bucketlar: le=chegara -> shu chegaragacha nechta."""
def __init__(self, nom, yordam, chegaralar):
self.nom, self.yordam = nom, yordam
self.chegaralar = list(chegaralar) + [math.inf]
self.sanoq = [0] * len(self.chegaralar)
self.yigindi = 0.0
self.soni = 0
def observe(self, v):
self.sanoq[bisect.bisect_left(self.chegaralar, v)] += 1
self.yigindi += v
self.soni += 1
def kumulyativ(self):
return np.cumsum(self.sanoq)
def birlashtir(self, boshqa):
assert self.chegaralar == boshqa.chegaralar, "bucketlar bir xil bo'lsin"
h = Histogram(self.nom, self.yordam, self.chegaralar[:-1])
h.sanoq = [a + b for a, b in zip(self.sanoq, boshqa.sanoq)]
h.yigindi = self.yigindi + boshqa.yigindi
h.soni = self.soni + boshqa.soni
return h
def histogram_quantile(q, h):
"""Prometheus dagi kabi: bucket ichida chiziqli interpolyatsiya."""
kum = h.kumulyativ()
nishon = q * kum[-1]
i = int(np.searchsorted(kum, nishon))
if h.chegaralar[i] == math.inf: # oxirgi chekli chegara qaytadi
return h.chegaralar[i - 1]
past = 0.0 if i == 0 else h.chegaralar[i - 1]
oldingi = 0 if i == 0 else kum[i - 1]
ichida = kum[i] - oldingi
return past + (h.chegaralar[i] - past) * (nishon - oldingi) / ichida
def ekspozitsiya(metrikalar):
"""/metrics sahifasining matn formati (qisqartirilgan)."""
qatorlar = []
for m in metrikalar:
tur = type(m).__name__.lower()
qatorlar += [f"# HELP {m.nom} {m.yordam}", f"# TYPE {m.nom} {tur}"]
if isinstance(m, Histogram):
for le, s in zip(m.chegaralar, m.kumulyativ()):
le_s = "+Inf" if le == math.inf else f"{le:g}"
qatorlar.append(f'{m.nom}_bucket{{le="{le_s}"}} {s}')
qatorlar.append(f"{m.nom}_sum {m.yigindi:.3f}")
qatorlar.append(f"{m.nom}_count {m.soni}")
else:
for k in sorted(m.qiymatlar):
y = ",".join(f'{a}="{b}"' for a, b in k)
qatorlar.append(f"{m.nom}{{{y}}} {m.qiymatlar[k]:g}")
return qatorlar
STANDART = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
MOSLANGAN = [0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.15, 0.2, 0.25, 0.3, 0.4,
0.5, 0.75, 1.0, 1.5, 2.5]
def main() -> None:
rng = np.random.default_rng(0)
print("=== 1. Yig'ish: 20000 so'rov (virtual kechikishlar) ===")
sorovlar = Counter("api_sorovlar_jami", "Jami so'rovlar")
navbat = Gauge("api_navbat_uzunligi", "Navbatdagi so'rovlar")
kech = Histogram("api_kechikish_soniya", "So'rov kechikishi", STANDART)
kechikishlar = rng.lognormal(math.log(0.08), 0.6, 20_000)
for v in kechikishlar:
kech.observe(float(v))
for yol, ulush in [("/predict", 0.9), ("/health", 0.1)]:
for status, p in [("200", 0.985), ("422", 0.01), ("500", 0.005)]:
sorovlar.inc(round(20_000 * ulush * p), yol=yol, status=status)
navbat.set(3)
matn = ekspozitsiya([sorovlar, navbat, kech])
tanlangan = [q for q in matn if "predict" in q or "navbat" in q
or q.startswith(("api_kechikish_soniya_sum",
"api_kechikish_soniya_count"))
or 'le="0.1"' in q or 'le="+Inf"' in q]
for q in tanlangan:
print(f" {q}")
print(f" (jami {len(matn)} qator, {len(matn) - len(tanlangan)} tasi "
f"ko'rsatilmadi)")
print("\n=== 2. Persentilni bucketlardan taxminlash ===")
moslangan = Histogram("k2", "", MOSLANGAN)
for v in kechikishlar:
moslangan.observe(float(v))
print(f" {'q':>5} {'haqiqiy':>9} {'standart':>9} {'xato':>7} "
f"{'moslangan':>9} {'xato':>7}")
for q in [0.5, 0.9, 0.95, 0.99]:
hq = float(np.quantile(kechikishlar, q))
a = histogram_quantile(q, kech)
b = histogram_quantile(q, moslangan)
print(f" {q:>5} {hq * 1000:>7.1f}ms {a * 1000:>7.1f}ms "
f"{(a - hq) / hq:>+7.1%} {b * 1000:>7.1f}ms {(b - hq) / hq:>+7.1%}")
print(f" bucketlar: standart {len(STANDART)}, moslangan {len(MOSLANGAN)}"
f" - aniqlik bucket kengligiga bog'liq")
print("\n=== 3. Counter dan tezlik: 15 soniyalik o'lchovlar, qayta ishga "
"tushish bilan ===")
haqiqiy_tezlik = 40.0 # so'rov/s
olchovlar, qiymat = [], 0.0
for i in range(12):
if i == 7:
qiymat = 0.0 # pod qayta ishga tushdi
qiymat += haqiqiy_tezlik * 15 + rng.normal(0, 20)
olchovlar.append(qiymat)
sodda, tuzatilgan = [], []
for a, b in zip(olchovlar, olchovlar[1:]):
sodda.append((b - a) / 15)
tuzatilgan.append((b - a if b >= a else b) / 15) # reset -> 0 dan
print(f" sodda farq: min {min(sodda):>8.1f}, o'rtacha "
f"{np.mean(sodda):>6.1f} so'rov/s")
print(f" reset hisobga: min {min(tuzatilgan):>8.1f}, o'rtacha "
f"{np.mean(tuzatilgan):>6.1f} so'rov/s (haqiqiy {haqiqiy_tezlik})")
print("\n=== 4. Replikalar bo'yicha birlashtirish ===")
replikalar = []
for i, (med, sig, n) in enumerate([(0.06, 0.4, 9000), (0.065, 0.4, 9000),
(0.3, 0.5, 2000)]):
h = Histogram("r", "", MOSLANGAN)
x = rng.lognormal(math.log(med), sig, n)
for v in x:
h.observe(float(v))
replikalar.append((h, x))
print(f" replika {i}: {n} so'rov, p99 {histogram_quantile(0.99, h) * 1000:.0f} ms")
hammasi = np.concatenate([x for _, x in replikalar])
haqiqiy = float(np.quantile(hammasi, 0.99))
ortacha_p99 = np.mean([histogram_quantile(0.99, h) for h, _ in replikalar])
birlashgan = replikalar[0][0].birlashtir(replikalar[1][0]) \
.birlashtir(replikalar[2][0])
b99 = histogram_quantile(0.99, birlashgan)
print(f" haqiqiy umumiy p99: {haqiqiy * 1000:>6.0f} ms")
print(f" p99 larning o'rtachasi (XATO): {ortacha_p99 * 1000:>6.0f} ms "
f"({(ortacha_p99 - haqiqiy) / haqiqiy:+.0%})")
print(f" birlashgan gistogramma p99: {b99 * 1000:>6.0f} ms "
f"({(b99 - haqiqiy) / haqiqiy:+.0%})")
print(" ⭐ Persentillarni o'rtachalamang - gistogrammalarni qo'shing")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yig'ish: 20000 so'rov (virtual kechikishlar) ===
api_sorovlar_jami{status="200",yol="/predict"} 17730
api_sorovlar_jami{status="422",yol="/predict"} 180
api_sorovlar_jami{status="500",yol="/predict"} 90
# HELP api_navbat_uzunligi Navbatdagi so'rovlar
# TYPE api_navbat_uzunligi gauge
api_navbat_uzunligi{} 3
api_kechikish_soniya_bucket{le="0.1"} 12815
api_kechikish_soniya_bucket{le="+Inf"} 20000
api_kechikish_soniya_sum 1916.705
api_kechikish_soniya_count 20000
(jami 27 qator, 17 tasi ko'rsatilmadi)
=== 2. Persentilni bucketlardan taxminlash ===
q haqiqiy standart xato moslangan xato
0.5 79.7ms 83.5ms +4.7% 79.8ms +0.1%
0.9 172.1ms 217.4ms +26.3% 178.0ms +3.5%
0.95 213.1ms 240.1ms +12.6% 217.9ms +2.2%
0.99 319.9ms 417.4ms +30.5% 333.7ms +4.3%
bucketlar: standart 11, moslangan 16 - aniqlik bucket kengligiga bog'liq
=== 3. Counter dan tezlik: 15 soniyalik o'lchovlar, qayta ishga tushish bilan ===
sodda farq: min -242.5, o'rtacha 14.5 so'rov/s
reset hisobga: min 38.2, o'rtacha 40.4 so'rov/s (haqiqiy 40.0)
=== 4. Replikalar bo'yicha birlashtirish ===
replika 0: 9000 so'rov, p99 158 ms
replika 1: 9000 so'rov, p99 180 ms
replika 2: 2000 so'rov, p99 1075 ms
haqiqiy umumiy p99: 569 ms
p99 larning o'rtachasi (XATO): 471 ms (-17%)
birlashgan gistogramma p99: 607 ms (+7%)
⭐ Persentillarni o'rtachalamang - gistogrammalarni qo'shingNatija tahlili.
- 1-bo'lim:
/metricssahifasi — oddiy matn. Counter yorliqlar bilan (yol,status), gistogramma kumulyativ bucketlar,_sumva_countbilan.le="0.1"qatori: 20000 so'rovdan 12815 tasi 100 ms gacha. Prometheus aynan shu matnni har 15-30 soniyada tortib oladi. - 2-bo'lim — persentil xatosi: standart bucketlar bilan p90 taxmini +26.3%, p99 — +30.5% xato (haqiqiy 319.9 ms o'rniga 417.4 ms). Sabab: 250-500 ms oralig'i bitta bucket va taxmin uning ichida chiziqli. Kechikish oralig'iga moslangan bucketlar bilan xato 0.1-4.3% ga tushdi. Xato har doim ijobiy chiqqani tasodif emas: lognormal dum bucket ichida chapga to'plangan, chiziqli interpolyatsiya esa massani tekis taqsimlaydi.
- 3-bo'lim — counter reset: 8-o'lchovda pod qayta ishga tushdi va counter 0 dan boshlandi. Sodda farq manfiy tezlik (-242.5 so'rov/s) va o'rtacha 14.5 berdi — haqiqiy 40 o'rniga. Reset ni hisobga olgan
rate40.4 berdi. Prometheus ningrate()funksiyasi buni avtomatik qiladi, lekin o'zingiz hisoblasangiz — unutmang. - 4-bo'lim — birlashtirish: uchinchi replika sekin (p99 1075 ms), lekin trafikning faqat 10% ini oladi. Uch p99 ning o'rtachasi 471 ms — haqiqiy umumiy p99 (569 ms) dan 17% past, ya'ni muammoni yashiradi. Bucketlarni qo'shib hisoblangan p99 — 607 ms (+7%, bucket kengligi xatosi). Persentilni o'rtachalashda xatoning ishorasi va kattaligi taqsimotga bog'liq — shuning uchun bu usul umuman ishonchsiz.
Misol 2 — Strukturali loglar, so'rov id va belgilar kechikishi
Mijoz ketishi modeli 45 virtual kun ishlaydi, kuniga 400 bashorat. Har so'rov uch JSON hodisa yozadi (qabul, belgilar, bashorat) — hammasida bitta sorov_id. 25-kuni ma'lumot quvurida xato paydo bo'ladi: to'lovlarning 60% i tiyinda (x100) kela boshlaydi, muddat_oy esa 20% hollarda kelmaydi. Ketish belgisi 14 kundan keyin ma'lum (faraziy). Barcha metrikalar faqat loglardan hisoblanadi.
"""Strukturali loglar, so'rov id, ma'lumot/model metrikalari va belgilar
kechikishi. 45 virtual kun; 25-kuni ma'lumot quvurida xato paydo bo'ladi."""
import hashlib
import json
from collections import defaultdict
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
KUNLAR = 45
KUNIGA = 400
BUZILISH_KUNI = 25
BELGI_KECHIKISHI = 14 # ketish belgisi 14 kundan keyin ma'lum (faraziy)
USTUNLAR = ["oylik_tolov", "muddat_oy", "murojaatlar"]
def mijozlar(rng, n):
tolov = rng.lognormal(np.log(120_000), 0.4, n) # so'm
muddat = rng.integers(1, 60, n).astype(float)
murojaat = rng.poisson(1.5, n).astype(float)
logit = (-1.0 + 0.9 * np.log(tolov / 120_000) - 0.05 * muddat
+ 0.45 * murojaat)
ketdi = rng.random(n) < 1 / (1 + np.exp(-logit))
return np.column_stack([tolov, muddat, murojaat]), ketdi.astype(int)
def belgilar(X):
"""Modelga kirish: log(to'lov), muddat, murojaatlar; null -> median."""
Z = X.copy()
Z[:, 0] = np.log(np.where(np.isnan(Z[:, 0]), 120_000, Z[:, 0]))
Z[:, 1] = np.where(np.isnan(Z[:, 1]), 30, Z[:, 1])
return Z
def sorov_id(kun, i):
return hashlib.sha256(f"{kun}-{i}".encode()).hexdigest()[:12]
def log(yozuvlar, **maydonlar):
"""Bitta hodisa = bitta JSON qator (vaqt belgisi o'rniga virtual kun)."""
yozuvlar.append(json.dumps(maydonlar, ensure_ascii=False))
def main() -> None:
rng = np.random.default_rng(3)
Xo, yo = mijozlar(rng, 5000)
model = LogisticRegression(max_iter=1000).fit(belgilar(Xo), yo)
loglar, belgi_jadvali = [], {}
for kun in range(KUNLAR):
X, y = mijozlar(rng, KUNIGA)
if kun >= BUZILISH_KUNI:
# quvur xatosi: to'lovning bir qismi tiyinda (x100),
# muddat esa ba'zan kelmaydi
tiyin = rng.random(KUNIGA) < 0.6
X[tiyin, 0] *= 100
X[rng.random(KUNIGA) < 0.2, 1] = np.nan
ball = model.predict_proba(belgilar(X))[:, 1]
belgi_ms = rng.lognormal(np.log(8), 0.3, KUNIGA)
kesh_otdi = rng.random(KUNIGA) < 0.03 # belgilar keshi o'tdi
belgi_ms[kesh_otdi] += rng.uniform(120, 250, kesh_otdi.sum())
model_ms = rng.lognormal(np.log(12), 0.3, KUNIGA)
for i in range(KUNIGA):
sid = sorov_id(kun, i)
kirish = {u: (None if np.isnan(v) else round(float(v), 2))
for u, v in zip(USTUNLAR, X[i])}
log(loglar, kun=kun, sorov_id=sid, hodisa="qabul", yol="/predict")
log(loglar, kun=kun, sorov_id=sid, hodisa="belgilar",
ms=round(float(belgi_ms[i]), 1), kesh=not kesh_otdi[i],
kirish=kirish)
log(loglar, kun=kun, sorov_id=sid, hodisa="bashorat",
model_versiya="churn-v3", ms=round(float(model_ms[i]), 1),
ball=round(float(ball[i]), 4))
belgi_jadvali[sid] = (kun + BELGI_KECHIKISHI, int(y[i]))
print("=== 1. Strukturali log qatorlari (JSON) ===")
for q in loglar[:3]:
print(f" {q[:78]}".rstrip())
print(f" jami {len(loglar)} qator: har so'rovga 3 hodisa, bitta sorov_id")
print("\n=== 2. So'rov id bo'yicha iz: eng sekin so'rov ===")
yozuvlar = [json.loads(q) for q in loglar]
izlar = defaultdict(list)
for r in yozuvlar:
izlar[r["sorov_id"]].append(r)
jami_ms = {sid: sum(r.get("ms", 0) for r in rr) for sid, rr in izlar.items()}
sekin = max(sorted(jami_ms), key=lambda s: jami_ms[s])
for r in izlar[sekin]:
qoshimcha = (f"{r['ms']:>6} ms" if "ms" in r else "")
if r["hodisa"] == "belgilar":
qoshimcha += f", kesh={r['kesh']}"
qator = f" {sekin} kun {r['kun']:>2} {r['hodisa']:<9} {qoshimcha}"
print(qator.rstrip())
otganlar = [r for r in yozuvlar if r["hodisa"] == "belgilar"]
p99_kesh = np.percentile([r["ms"] for r in otganlar if r["kesh"]], 99)
p99_yoq = np.percentile([r["ms"] for r in otganlar if not r["kesh"]], 99)
print(f" belgilar bosqichi p99: kesh bilan {p99_kesh:.0f} ms, "
f"keshsiz {p99_yoq:.0f} ms -> sekinlik sababi topildi")
print("\n=== 3. Loglardan kunlik metrikalar ===")
kun_boyicha = defaultdict(list)
for r in yozuvlar:
if r["hodisa"] != "qabul":
kun_boyicha[(r["kun"], r["hodisa"])].append(r)
kunlik = {}
for kun in range(KUNLAR):
kir = [r["kirish"] for r in kun_boyicha[(kun, "belgilar")]]
bash = kun_boyicha[(kun, "bashorat")]
b = np.array([r["ball"] for r in bash])
kunlik[kun] = {
"null": np.mean([k["muddat_oy"] is None for k in kir]),
"sxema": np.mean([(k["oylik_tolov"] or 0) > 2_000_000 for k in kir]),
"ball": b.mean(), "yuqori": (b > 0.5).mean(),
"sidlar": [r["sorov_id"] for r in bash], "ballar": b}
bugun = KUNLAR - 1
sarlavha = ["null muddat", "tolov>2mln", "o'rt ball", "ball>0.5",
f"AUC ({bugun}-kun bilgani)"]
print(f" {'kun':>3} {sarlavha[0]:>11} {sarlavha[1]:>10} {sarlavha[2]:>9} "
f"{sarlavha[3]:>8} {sarlavha[4]:>21}")
for kun in [5, 20, 24, 25, 30, 31, 44]:
m = kunlik[kun]
if kun + BELGI_KECHIKISHI <= bugun:
auc = roc_auc_score([belgi_jadvali[s][1] for s in m["sidlar"]],
m["ballar"])
auc_s = f"{auc:.3f}"
else:
auc_s = "hali noma'lum"
print(f" {kun:>3} {m['null']:>11.1%} {m['sxema']:>10.1%} "
f"{m['ball']:>9.3f} {m['yuqori']:>8.1%} {auc_s:>21}")
print("\n=== 4. Qachon SEZILADI: proksi vs sifat metrikasi ===")
ref = [kunlik[k]["ball"] for k in range(BUZILISH_KUNI)]
ort_ref, std_ref = np.mean(ref), np.std(ref, ddof=1)
birinchi = {}
for kun in range(KUNLAR):
m = kunlik[kun]
if "null ulushi > 5%" not in birinchi and m["null"] > 0.05:
birinchi["null ulushi > 5%"] = kun
if "sxema buzilishi > 1%" not in birinchi and m["sxema"] > 0.01:
birinchi["sxema buzilishi > 1%"] = kun
if ("o'rtacha ball > 4 std" not in birinchi
and abs(m["ball"] - ort_ref) > 4 * std_ref):
birinchi["o'rtacha ball > 4 std"] = kun
def oyna_auc(d): # [d-6, d] kunlar
sid = [s for k in range(d - 6, d + 1) for s in kunlik[k]["sidlar"]]
ball = np.concatenate([kunlik[k]["ballar"] for k in range(d - 6, d + 1)])
return roc_auc_score([belgi_jadvali[s][1] for s in sid], ball)
asos_auc = np.mean([oyna_auc(d) for d in range(6, BUZILISH_KUNI)])
for d in range(6, bugun - BELGI_KECHIKISHI + 1):
if oyna_auc(d) < asos_auc - 0.03:
birinchi["7 kunlik AUC -0.03"] = d + BELGI_KECHIKISHI
break
print(f" asos 7 kunlik AUC: {asos_auc:.3f}")
for nom, kun in sorted(birinchi.items(), key=lambda x: x[1]):
print(f" {nom:<22} birinchi signal: {kun}-kun "
f"(buzilishdan {kun - BUZILISH_KUNI} kun keyin)")
print(f" buzilish {BUZILISH_KUNI}-kuni; belgilar {BELGI_KECHIKISHI} kun "
f"kechikadi")
print(" ⭐ Belgilar kechiksa - ma'lumot va bashorat metrikalari (proksi) "
"birinchi signal")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Strukturali log qatorlari (JSON) ===
{"kun": 0, "sorov_id": "22841ea360fc", "hodisa": "qabul", "yol": "/predict"}
{"kun": 0, "sorov_id": "22841ea360fc", "hodisa": "belgilar", "ms": 8.5, "kesh"
{"kun": 0, "sorov_id": "22841ea360fc", "hodisa": "bashorat", "model_versiya":
jami 54000 qator: har so'rovga 3 hodisa, bitta sorov_id
=== 2. So'rov id bo'yicha iz: eng sekin so'rov ===
70fd086adf5b kun 32 qabul
70fd086adf5b kun 32 belgilar 260.8 ms, kesh=False
70fd086adf5b kun 32 bashorat 31.5 ms
belgilar bosqichi p99: kesh bilan 16 ms, keshsiz 256 ms -> sekinlik sababi topildi
=== 3. Loglardan kunlik metrikalar ===
kun null muddat tolov>2mln o'rt ball ball>0.5 AUC (44-kun bilgani)
5 0.0% 0.0% 0.180 3.8% 0.703
20 0.0% 0.0% 0.178 2.8% 0.763
24 0.0% 0.0% 0.177 3.8% 0.768
25 19.0% 59.8% 0.581 60.0% 0.537
30 19.2% 63.0% 0.611 64.8% 0.620
31 22.2% 58.2% 0.569 58.5% hali noma'lum
44 15.8% 60.5% 0.585 61.5% hali noma'lum
=== 4. Qachon SEZILADI: proksi vs sifat metrikasi ===
asos 7 kunlik AUC: 0.768
null ulushi > 5% birinchi signal: 25-kun (buzilishdan 0 kun keyin)
sxema buzilishi > 1% birinchi signal: 25-kun (buzilishdan 0 kun keyin)
o'rtacha ball > 4 std birinchi signal: 25-kun (buzilishdan 0 kun keyin)
7 kunlik AUC -0.03 birinchi signal: 39-kun (buzilishdan 14 kun keyin)
buzilish 25-kuni; belgilar 14 kun kechikadi
⭐ Belgilar kechiksa - ma'lumot va bashorat metrikalari (proksi) birinchi signalNatija tahlili.
- 1-bo'lim: 18000 so'rov — 54000 JSON qator. Har qator mustaqil lug'at: filtrlash va guruhlash uchun regex kerak emas.
- 2-bo'lim — so'rov id izi: eng sekin so'rov (32-kun) uchta yozuv bilan tiklandi:
belgilarbosqichi 260.8 ms vakesh=False, model esa atigi 31.5 ms. Keyin barcha loglar bo'yicha: belgilar bosqichining p99 i keshli so'rovlarda 16 ms, keshsiz — 256 ms. Sekinlik sababi — model emas, belgilar keshining o'tkazib yuborilishi. So'rov id siz bu bog'lanishni topish ancha qiyin bo'lardi. - 3-bo'lim — kunlik metrikalar: 25-kunda hamma proksi metrikalar birdaniga o'zgardi:
muddat_oynull ulushi 0% dan 19.0% ga, "to'lov > 2 mln" (sxema buzilishi) 59.8% ga, o'rtacha ball 0.177 dan 0.581 ga, 0.5 dan yuqori ballar 3.8% dan 60.0% ga. Lekin AUC ustuniga qarang: 44-kun holatida faqat 30-kungacha bo'lgan kunlarning AUC i ma'lum; 25-kun AUC i (0.537, odatiy ~0.77 o'rniga) aynan 39-kuni hisoblanishi mumkin bo'ldi. - 4-bo'lim — asosiy natija: uchta proksi signal buzilish kunining o'zida (0 kun kechikish) yondi, 7 kunlik AUC ning 0.03 ga tushishi esa 39-kuni — 14 kun keyin, ya'ni aynan belgi kechikishiga teng. Shu 14 kun davomida model har kuni yuzlab mijozga noto'g'ri ball berardi. Proksi metrikalar sababni ham ko'rsatadi (null va sxema — ma'lumot quvuri), AUC esa faqat "yomonlashdi" deydi.
- Bog'lanish: bashorat loglari (kirish + ball + versiya +
sorov_id) belgilar kelgach ular bilan birlashtirildi — xuddi shu mexanizm 27.13-darsda qayta o'qitish ma'lumoti manbai bo'ladi.
Misol 3 — Ogohlantirish qoidalari: precision, recall va alert fatigue
30 virtual kun, 5 daqiqalik oynalar (8640 ta). Trafik kunduzi ~2800, tunda ~110 so'rov/oyna; odatiy xato ulushi 0.5%. Tarixga kattaligi va davomiyligi turlicha 8 ta insident kiritilgan (haqiqat ma'lum). Yetti qoida taqqoslanadi: ikki statik chegara, "15 daqiqa davomida" sharti, ikki statistik chegara (oxirgi kun o'rtacha + k*std) va so'rovlar soniga moslangan binomial z (bir oynali va ikki oynali).
"""Ogohlantirish qoidalari: statik chegara, statistik chegara va binomial z.
30 virtual kun, 5 daqiqalik oynalar, 8 ta ma'lum insident. Precision, recall,
aniqlash kechikishi va haftasiga yolg'on ogohlantirishlar (alert fatigue)."""
import numpy as np
import pandas as pd
OYNA_DAQ = 5
KUNGA = 24 * 60 // OYNA_DAQ # 288 oyna
KUNLAR = 30
P0 = 0.005 # odatiy xato ulushi
# (kun, soat, davomiylik daqiqa, qo'shimcha xato ulushi) - "haqiqat"
INSIDENTLAR = [(2, 14, 30, 0.05), (5, 3, 60, 0.03), (8, 10, 15, 0.015),
(12, 20, 120, 0.008), (16, 12, 10, 0.10), (20, 4, 45, 0.02),
(24, 15, 240, 0.005), (28, 9, 20, 0.04)]
def malumot(rng):
t = np.arange(KUNLAR * KUNGA)
soat = (t % KUNGA) * OYNA_DAQ / 60
# tunda ~100, kunduzi ~3000 so'rov / 5 daqiqa
kutilgan = 100 + 2900 * np.exp(-((soat - 14) / 4.5) ** 2)
n = rng.poisson(kutilgan)
p = np.full(len(t), P0)
haqiqat = np.zeros(len(t), dtype=int) # insident raqami (1..8)
for i, (kun, s, davom, qosh) in enumerate(INSIDENTLAR, start=1):
bosh = kun * KUNGA + s * 60 // OYNA_DAQ
oxir = bosh + davom // OYNA_DAQ
p[bosh:oxir] += qosh
haqiqat[bosh:oxir] = i
xato = rng.binomial(n, p)
return pd.DataFrame({"n": n, "xato": xato, "insident": haqiqat})
def qoidalar(df):
r = df["xato"] / df["n"].clip(lower=1)
oldingi = r.shift(1)
ort = oldingi.rolling(KUNGA, min_periods=KUNGA // 2).mean()
std = oldingi.rolling(KUNGA, min_periods=KUNGA // 2).std()
# kunlik tarixiy ulush (xatolar / so'rovlar), joriy oynasiz
p_hat = (df["xato"].shift(1).rolling(KUNGA, min_periods=KUNGA // 2).sum()
/ df["n"].shift(1).rolling(KUNGA, min_periods=KUNGA // 2).sum())
z = (df["xato"] - df["n"] * p_hat) / np.sqrt(df["n"] * p_hat * (1 - p_hat))
def ketma_ket(shart, k):
return shart.astype(int).rolling(k).sum() == k
return {
"statik r > 2%": r > 0.02,
"statik r > 5%": r > 0.05,
"statik r > 2%, 15 daq": ketma_ket(r > 0.02, 3),
"o'rtacha + 3 std": r > ort + 3 * std,
"o'rtacha + 5 std": r > ort + 5 * std,
"binomial z > 4": z > 4,
"binomial z > 4, 10 daq": ketma_ket(z > 4, 2),
}
def baholash(signal, insident):
"""Ketma-ket yongan oynalar -> bitta ogohlantirish (hodisa)."""
s = signal.fillna(False).to_numpy()
boshlar = np.flatnonzero(s & ~np.r_[False, s[:-1]])
haqiqiy_alert, topilgan, kechikish = 0, {}, []
ins = insident.to_numpy()
for b in boshlar:
oxir = b
while oxir + 1 < len(s) and s[oxir + 1]:
oxir += 1
tegishli = set(ins[b:oxir + 1]) - {0}
if tegishli:
haqiqiy_alert += 1
for i in tegishli:
topilgan.setdefault(i, b)
for i, b in topilgan.items():
ins_bosh = int(np.flatnonzero(ins == i)[0])
kechikish.append((b - ins_bosh) * OYNA_DAQ)
jami = len(boshlar)
yolgon = jami - haqiqiy_alert
return {"jami": jami, "yolgon_haftasiga": yolgon / (KUNLAR / 7),
"precision": haqiqiy_alert / jami if jami else float("nan"),
"recall": len(topilgan) / len(INSIDENTLAR),
"kechikish": float(np.median(kechikish)) if kechikish
else float("nan"), "topilgan": sorted(topilgan)}
def main() -> None:
rng = np.random.default_rng(11)
df = malumot(rng)
print("=== 1. Ma'lumot: 30 kun x 288 oyna ===")
r = df["xato"] / df["n"]
kun = np.arange(len(df)) % KUNGA
tun = (kun < 5 * 12) & (df["insident"] == 0)
kunduz = (kun >= 12 * 12) & (kun < 16 * 12) & (df["insident"] == 0)
print(f" oynalar: {len(df)}, insidentli oynalar: "
f"{(df['insident'] > 0).sum()}")
print(f" tungi oyna: o'rtacha {df['n'][tun].mean():.0f} so'rov, xato "
f"ulushi std {r[tun].std():.4f}")
print(f" kunduzgi oyna: o'rtacha {df['n'][kunduz].mean():.0f} so'rov, "
f"xato ulushi std {r[kunduz].std():.4f}")
print(" bir xil P0 = 0.5%, lekin tunda shovqin ancha katta")
print("\n=== 2. Qoidalar taqqoslash ===")
ustun = "yolg'on/hafta"
print(f" {'qoida':<24} {'alert':>5} {ustun:>13} "
f"{'precision':>9} {'recall':>6} {'kechikish':>9}")
natija = {}
for nom, signal in qoidalar(df).items():
b = baholash(signal, df["insident"])
natija[nom] = b
kech = (f"{b['kechikish']:.0f} daq" if b["kechikish"] == b["kechikish"]
else "-")
print(f" {nom:<24} {b['jami']:>5} {b['yolgon_haftasiga']:>13.1f} "
f"{b['precision']:>9.2f} {b['recall']:>6.2f} {kech:>9}")
print("\n=== 3. Qaysi insidentlar topildi ===")
for nom in ["statik r > 5%", "statik r > 2%, 15 daq", "binomial z > 4, 10 daq"]:
belgilar = "".join("X" if i in natija[nom]["topilgan"] else "-"
for i in range(1, len(INSIDENTLAR) + 1))
print(f" {nom:<24} {belgilar} (1-8)")
tavsif = [f"{i}: +{q:.1%} {d} daq" for i, (_, _, d, q) in
enumerate(INSIDENTLAR, start=1)]
print(" " + ", ".join(tavsif[:4]))
print(" " + ", ".join(tavsif[4:]))
print("\n=== 4. Qaror: yolg'on <= 1/hafta, eng yuqori recall ===")
mos = {k: v for k, v in natija.items() if v["yolgon_haftasiga"] <= 1}
if mos:
eng = max(mos, key=lambda k: (mos[k]["recall"], -mos[k]["kechikish"]))
print(f" mos: {len(mos)} ta; tanlandi: {eng} (recall "
f"{mos[eng]['recall']:.2f}, kechikish "
f"{mos[eng]['kechikish']:.0f} daq)")
shovqinli = max(natija, key=lambda k: natija[k]["yolgon_haftasiga"])
print(f" eng shovqinli: {shovqinli} - haftasiga "
f"{natija[shovqinli]['yolgon_haftasiga']:.1f} yolg'on signal")
print(" ⭐ Chegara so'rovlar soniga moslashsin - tungi shovqin 'yong'in' emas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot: 30 kun x 288 oyna ===
oynalar: 8640, insidentli oynalar: 108
tungi oyna: o'rtacha 110 so'rov, xato ulushi std 0.0069
kunduzgi oyna: o'rtacha 2819 so'rov, xato ulushi std 0.0013
bir xil P0 = 0.5%, lekin tunda shovqin ancha katta
=== 2. Qoidalar taqqoslash ===
qoida alert yolg'on/hafta precision recall kechikish
statik r > 2% 94 19.6 0.11 0.88 0 daq
statik r > 5% 4 0.0 1.00 0.38 0 daq
statik r > 2%, 15 daq 6 0.0 1.00 0.62 10 daq
o'rtacha + 3 std 126 26.6 0.10 0.88 0 daq
o'rtacha + 5 std 31 4.7 0.35 0.75 0 daq
binomial z > 4 33 3.0 0.61 1.00 0 daq
binomial z > 4, 10 daq 10 0.0 1.00 0.88 5 daq
=== 3. Qaysi insidentlar topildi ===
statik r > 5% XX--X--- (1-8)
statik r > 2%, 15 daq XXX--X-X (1-8)
binomial z > 4, 10 daq XXXXXX-X (1-8)
1: +5.0% 30 daq, 2: +3.0% 60 daq, 3: +1.5% 15 daq, 4: +0.8% 120 daq
5: +10.0% 10 daq, 6: +2.0% 45 daq, 7: +0.5% 240 daq, 8: +4.0% 20 daq
=== 4. Qaror: yolg'on <= 1/hafta, eng yuqori recall ===
mos: 3 ta; tanlandi: binomial z > 4, 10 daq (recall 0.88, kechikish 5 daq)
eng shovqinli: o'rtacha + 3 std - haftasiga 26.6 yolg'on signal
⭐ Chegara so'rovlar soniga moslashsin - tungi shovqin 'yong'in' emasNatija tahlili.
- 1-bo'lim — muammoning ildizi: xato ulushi bir xil (0.5%), lekin tunda 110 so'rovlik oynada uning std si 0.0069, kunduzi 2819 so'rovda — 0.0013, besh barobar katta. Tunda 3 ta xato — 2.7% "xato ulushi". Har qanday qoida bu shovqinni hisobga olishi kerak.
- 2-bo'lim, statik chegaralar:
r > 2%8 insidentdan 7 tasini topdi (recall 0.88), lekin haftasiga 19.6 yolg'on signal berdi (precision 0.11) — deyarli hammasi tunda.r > 5%da yolg'on yo'q, lekin recall 0.38. "15 daqiqa" sharti yolg'onni nolga tushirdi, lekin recall 0.62 va mediana kechikish 10 daqiqa. - Statistik chegaralar eng yomon chiqdi (kutilgandan farqli, lekin tushunarli):
o'rtacha + 3 stdhaftasiga 26.6 yolg'on signal berdi. Sabab: kunlik std tun va kunduzni aralashtiradi, tungi oynalar esa o'rtacha + 3 std ni tez-tez oshadi. "Aqlli" dinamik chegara so'rovlar sonini bilmasa, statikdan yaxshi emas. - Binomial z: bir oynali
z > 4hamma 8 insidentni topdi (recall 1.00), lekin haftasiga 3.0 yolg'on — tunda n juda kichik (kutilgan xato ~0.55) va normal yaqinlashuv buziladi. Ikki ketma-ket oyna sharti bilan yolg'on 0.0 ga tushdi, recall 0.88, mediana kechikish 5 daqiqa. - 3-bo'lim: tanlangan qoida faqat 7-insidentni (+0.5%, 4 soat) o'tkazib yubordi — bu fon xato ulushining o'zi kattaligidagi siljish. Uni 5 daqiqalik oynada ajratish qiyin; uni sekin yonish ogohlantirishi ushlashi kerak (4-misol). "15 daqiqa" qoidasi esa 10 daqiqalik +10% insidentni (5) o'tkazib yubordi —
for:sharti insidentdan uzun bo'lsa, uni hech qachon ko'rmaydi. - 4-bo'lim — qaror: "haftasiga ko'pi bilan 1 yolg'on" talabiga uchta qoida mos keldi; recall bo'yicha eng yaxshisi —
binomial z > 4, 10 daq.
Misol 4 — SLO, xato byudjeti va burn-rate ogohlantirishlari
30 virtual kun, 5 daqiqalik oynalar. "Yomon" so'rov — 5xx yoki 300 ms dan sekin; odatiy yomon ulush 0.1%. SLO — 99.7%. Besh insident: katta xato (1 soat, +20%), tungi xato (3 soat, +3%), yarim uzilish (20 daqiqa, +50%), sekin regressiya (3 kun, +0.6%) va qisqa xato (30 daqiqa, +10%). Uch burn-rate qoidasi (2.7-bo'lim) sodda "5 daqiqada yomon ulush > byudjet" qoidasi bilan taqqoslanadi.
"""SLO/SLI, xato byudjeti va ko'p oynali burn-rate ogohlantirishlari.
30 virtual kun, 5 daqiqalik oynalar, 5 ta ma'lum insident."""
import numpy as np
import pandas as pd
OYNA = 5 # daqiqa
KUNGA = 288
KUNLAR = 30
SLO = 0.997 # 30 kunda so'rovlarning 99.7% "yaxshi"
BYUDJET = 1 - SLO
ASOS_YOMON = 0.001 # odatiy yomon ulush (5xx yoki > 300 ms)
# (nom, kun, soat, davomiylik daqiqa, qo'shimcha yomon ulush)
INSIDENTLAR = [("A katta xato", 3, 10, 60, 0.20),
("B tungi xato", 9, 2, 180, 0.03),
("C yarim uzilish", 14, 14, 20, 0.50),
("D sekin regressiya", 20, 0, 3 * 24 * 60, 0.006),
("E qisqa xato", 27, 13, 30, 0.10)]
# Google SRE Workbook g'oyasi: (nom, uzun oyna, qisqa oyna, burn chegarasi)
QOIDALAR = [("page: tez yonish", 12, 1, 14.4), # 1 soat / 5 daq
("page: sekin yonish", 72, 6, 6.0), # 6 soat / 30 daq
("ticket", 864, 72, 1.0)] # 3 kun / 6 soat
def malumot(rng):
t = np.arange(KUNLAR * KUNGA)
soat = (t % KUNGA) * OYNA / 60
n = rng.poisson(200 + 2800 * np.exp(-((soat - 14) / 4.5) ** 2))
p = np.full(len(t), ASOS_YOMON)
ins = np.full(len(t), -1)
for i, (_, kun, s, davom, q) in enumerate(INSIDENTLAR):
b = kun * KUNGA + s * 60 // OYNA
p[b:b + davom // OYNA] += q
ins[b:b + davom // OYNA] = i
return pd.DataFrame({"n": n, "yomon": rng.binomial(n, p), "ins": ins})
def burn(df, oyna):
y = df["yomon"].rolling(oyna, min_periods=1).sum()
n = df["n"].rolling(oyna, min_periods=1).sum()
return (y / n) / BYUDJET
def main() -> None:
rng = np.random.default_rng(5)
df = malumot(rng)
print("=== 1. SLI va xato byudjeti (30 kun) ===")
jami, yomon = df["n"].sum(), df["yomon"].sum()
sli = 1 - yomon / jami
byudjet = BYUDJET * jami
print(f" so'rovlar: {jami}, yomon: {yomon}")
print(f" SLI = {sli:.4%}, SLO = {SLO:.1%} -> "
f"{'bajarildi' if sli >= SLO else 'BUZILDI'}")
print(f" byudjet: {byudjet:.0f} yomon so'rov, sarflandi "
f"{yomon / byudjet:.1%}")
print(f" ruxsat etilgan to'liq uzilish: 30 kunda "
f"{BYUDJET * 30 * 24 * 60:.0f} daqiqa")
print("\n=== 2. Byudjet sarfi vaqt bo'yicha ===")
kunlik = df.groupby(np.arange(len(df)) // KUNGA)[["n", "yomon"]].sum()
kum = kunlik["yomon"].cumsum() / byudjet
muzlatish = next((k for k in kum.index if kum[k] >= 1.0), None)
for kun in [2, 3, 8, 9, 13, 14, 19, 22, 26, 27, 29]:
belgi = " <- byudjet tugadi" if muzlatish == kun else ""
print(f" {kun:>2}-kun oxiri: {kum[kun]:>7.1%}{belgi}")
if muzlatish is not None:
print(f" siyosat: {muzlatish}-kundan yangi funksiyalar chiqarish "
f"to'xtatiladi, faqat ishonchlilik ishlari")
else:
qoldi = 1 - kum.iloc[-1]
print(f" byudjet tugamadi, qoldi {qoldi:.1%}")
for i, (nom, *_) in enumerate(INSIDENTLAR):
m = df["ins"] == i
ulush = (df.loc[m, "yomon"].sum()
- ASOS_YOMON * df.loc[m, "n"].sum()) / byudjet
if ulush > qoldi:
print(f" yana bitta '{nom}' ({ulush:.1%}) SLO ni buzardi")
print("\n=== 3. Insidentlar: byudjetning qancha qismi ===")
for i, (nom, _, _, davom, q) in enumerate(INSIDENTLAR):
m = df["ins"] == i
qosh = df.loc[m, "yomon"].sum() - ASOS_YOMON * df.loc[m, "n"].sum()
print(f" {nom:<19} {davom:>5} daq, +{q:>5.1%}: byudjetning "
f"{qosh / byudjet:>6.1%} qismi")
print("\n=== 4. Burn-rate ogohlantirishlari ===")
signallar = {}
for nom, uzun, qisqa, chegara in QOIDALAR:
signallar[nom] = (burn(df, uzun) > chegara) & (burn(df, qisqa) > chegara)
signallar[f"sodda: 5 daq > {BYUDJET:.1%}"] = df["yomon"] / df["n"] > BYUDJET
ustun = "yolg'on"
print(f" {'qoida':<22} {'alertlar':>8} {ustun:>7} "
f"birinchi signal (daq, insident boshidan)")
for nom, s in signallar.items():
s = s.to_numpy()
boshlar = np.flatnonzero(s & ~np.r_[False, s[:-1]])
yaqin = []
for b in boshlar: # insident ichida yoki tugaganidan 6 soat ichida
oyna = df["ins"].to_numpy()[max(0, b - 72):b + 1]
yaqin.append(oyna.max() >= 0)
yolgon = len(boshlar) - sum(yaqin)
kech = []
for i, (qisqa_nom, kun, soat, davom, _) in enumerate(INSIDENTLAR):
b0 = kun * KUNGA + soat * 60 // OYNA
ichida = boshlar[(boshlar >= b0) & (boshlar < b0 + davom // OYNA)]
kech.append(f"{qisqa_nom[0]}:{(ichida[0] - b0) * OYNA}"
if len(ichida) else f"{qisqa_nom[0]}:-")
print(f" {nom:<22} {len(boshlar):>8} {yolgon:>7} {' '.join(kech)}")
print("\n=== 5. SLO tanlash: bir xil tizim, turli maqsad ===")
for slo in [0.995, 0.997, 0.999]:
b = 1 - slo
sarf = yomon / (b * jami)
holat = "bajarildi" if sli >= slo else "BUZILDI"
print(f" SLO {slo:.1%}: odatiy fon byudjetning "
f"{ASOS_YOMON / b:>4.0%} ini yeydi, jami sarf {sarf:>6.1%} "
f"-> {holat}")
print(" ⭐ Byudjet - ishonchlilik va tezlik orasidagi kelishuv; "
"ogohlantirish - byudjet yonish tezligiga")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. SLI va xato byudjeti (30 kun) ===
so'rovlar: 9762495, yomon: 27471
SLI = 99.7186%, SLO = 99.7% -> bajarildi
byudjet: 29287 yomon so'rov, sarflandi 93.8%
ruxsat etilgan to'liq uzilish: 30 kunda 130 daqiqa
=== 2. Byudjet sarfi vaqt bo'yicha ===
2-kun oxiri: 3.3%
3-kun oxiri: 18.4%
8-kun oxiri: 23.9%
9-kun oxiri: 25.8%
13-kun oxiri: 30.0%
14-kun oxiri: 51.5%
19-kun oxiri: 57.1%
22-kun oxiri: 80.3%
26-kun oxiri: 84.7%
27-kun oxiri: 91.5%
29-kun oxiri: 93.8%
byudjet tugamadi, qoldi 6.2%
yana bitta 'A katta xato' (14.0%) SLO ni buzardi
yana bitta 'C yarim uzilish' (20.4%) SLO ni buzardi
yana bitta 'D sekin regressiya' (19.8%) SLO ni buzardi
=== 3. Insidentlar: byudjetning qancha qismi ===
A katta xato 60 daq, +20.0%: byudjetning 14.0% qismi
B tungi xato 180 daq, + 3.0%: byudjetning 0.8% qismi
C yarim uzilish 20 daq, +50.0%: byudjetning 20.4% qismi
D sekin regressiya 4320 daq, + 0.6%: byudjetning 19.8% qismi
E qisqa xato 30 daq, +10.0%: byudjetning 5.8% qismi
=== 4. Burn-rate ogohlantirishlari ===
qoida alertlar yolg'on birinchi signal (daq, insident boshidan)
page: tez yonish 3 0 A:10 B:- C:5 D:- E:25
page: sekin yonish 2 0 A:10 B:- C:5 D:- E:-
ticket 3 0 A:30 B:- C:5 D:1285 E:-
sodda: 5 daq > 0.3% 664 559 A:0 B:- C:0 D:5 E:0
=== 5. SLO tanlash: bir xil tizim, turli maqsad ===
SLO 99.5%: odatiy fon byudjetning 20% ini yeydi, jami sarf 56.3% -> bajarildi
SLO 99.7%: odatiy fon byudjetning 33% ini yeydi, jami sarf 93.8% -> bajarildi
SLO 99.9%: odatiy fon byudjetning 100% ini yeydi, jami sarf 281.4% -> BUZILDI
⭐ Byudjet - ishonchlilik va tezlik orasidagi kelishuv; ogohlantirish - byudjet yonish tezligigaNatija tahlili.
- 1-bo'lim: SLI 99.7186% — SLO (99.7%) bajarildi, lekin byudjetning 93.8% i sarflandi. 99.7% SLO 30 kunda atigi 130 daqiqalik to'liq uzilishga teng.
- 2-bo'lim — byudjet vaqt bo'yicha: sarf zinapoya shaklida — har insident bir pog'ona: 3-kun (18.4%), 14-kun (51.5%), 20-22-kunlar (sekin regressiya, 80.3% gacha). Oy oxirida 6.2% qoldi: A, C yoki D kabi yana bitta hodisa SLO ni buzardi. Byudjet siyosati bo'yicha bu holatda jamoa xavfli deploy larni kechiktirishi kerak.
- 3-bo'lim — kichik lekin uzoq ham qimmat: 20 daqiqalik yarim uzilish (C) byudjetning 20.4% ini yedi, 3 kunlik +0.6% regressiya (D) esa 19.8% ini — deyarli bir xil. Tungi uch soatlik +3% (B) atigi 0.8% — tunda trafik kam. Byudjet foydalanuvchilar sonini sanaydi, daqiqalarni emas.
- 4-bo'lim — ogohlantirishlar: burn-rate qoidalarida yolg'on signal yo'q. Tez yonish page i A ni 10, C ni 5, E ni 25 daqiqada topdi; D (sekin regressiya) ni faqat ticket qoidasi topdi — 1285 daqiqada (taxminan 21 soat), bu to'g'ri: sekin yonish tunda odamni uyg'otishga arzimaydi, lekin ish vaqtida ko'rilishi kerak. B umuman ogohlantirish bermadi — u byudjetning 0.8% ini yedi, ya'ni harakat talab qilmaydi. Sodda qoida esa 664 ta ogohlantirish berdi, 559 tasi yolg'on — bu alert fatigue ning aniq ko'rinishi.
- Nozik joy (halol): E (30 daqiqa, +10%) byudjetning 5.8% ini yedi, lekin tez yonish page i uni faqat 25-daqiqada ushladi. Sabab: burn rate — ulush, kunduzgi cho'qqi soatida esa trafik o'rtachadan ko'p. "14.4 = 1 soatda 2%" formulasi o'rtacha trafik uchun; cho'qqida byudjet tezroq yonadi.
- 5-bo'lim — SLO tanlash: 99.9% SLO da odatiy fon (0.1% yomon) byudjetning 100% ini yeydi — insidentlarsiz ham SLO chegarada, amalda buzildi (sarf 281.4%). SLO tizimning haqiqiy fon darajasidan kelib chiqib tanlanadi: fon byudjetning kichik qismini (masalan, 20-35%) olishi kerak.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Xatolar va kechikish yaxshi — model yaxshi" | Ma'lumot va bashorat taqsimoti alohida kuzatiladi |
| "Sifatni belgilar kelganda ko'ramiz" | Belgi haftalar kechikadi; proksi — birinchi signal |
| "p99 larni o'rtachalash mumkin" | Bucketlarni qo'shib, keyin persentil |
| "Standart bucketlar yetarli" | SLA atrofida zich bucketlar kerak |
| "Dinamik chegara har doim aqlliroq" | So'rovlar sonini bilmasa, statikdan yomon |
| "Ko'proq ogohlantirish — xavfsizroq" | Alert fatigue: haqiqiysi ham e'tiborsiz qoladi |
| "for: 15m yolg'on signalni yo'qotadi — yaxshi" | Qisqa insidentni butunlay o'tkazib yuboradi |
| "SLO qancha yuqori bo'lsa, shuncha yaxshi" | Fon darajasi byudjetni yeb qo'yadi |
| "Har yomon daqiqa bir xil muhim" | Byudjet foydalanuvchilarni sanaydi: cho'qqi qimmat |
| "Loglar — faqat xatoni tuzatish uchun" | Bashorat logi — kelajakdagi baholash to'plami |
6. Keng tarqalgan xatolar va yechimlari
1. Faqat tizim metrikalari
dashboard: so'rov/s, xato %, p99, CPU # ⚠️
+ null ulushi, sxema xatolari, o'rtacha ball, ball > 0.5, # ✅
model_versiya bo'yicha trafik, biznes metrikasi2. Persentillarni o'rtachalash
p99 = np.mean([replika.p99 for replika in replikalar]) # ⚠️
p99 = histogram_quantile(0.99, sum(bucketlar)) # ✅3. Yuqori kardinallikli yorliq
SOROVLAR.labels(foydalanuvchi=uid).inc() # ⚠️ millionlab qator
SOROVLAR.labels(yol="/predict", status="200").inc() # ✅ id - logda4. Tuzilmasiz log
print(f"bashorat {ball} mijoz {mijoz_ismi} uchun") # ⚠️ matn + shaxsiy
log_json(sorov_id=sid, hodisa="bashorat", ball=ball, # ✅
model_versiya="churn-v3")5. Tungi shovqinga statik chegara
expr: xato_ulushi > 0.02 # ⚠️ tunda 3 xato = 2.7%
expr: binomial_z > 4 (ketma-ket 2 oyna, n >= minimal) # ✅6. Byudjetni emas, ulushni ogohlantirish
expr: yomon_ulush_5m > 0.003 # ⚠️ yuzlab alert
expr: burn_1h > 14.4 and burn_5m > 14.4 # ✅ page7. Counter reset
tezlik = (joriy - oldingi) / soniya # ⚠️ manfiy tezlik
tezlik = rate(oldingi, joriy, soniya) # ✅ reset hisobga7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 11-qism (o'tilgan): gipoteza testlari — binomial z va ko'p marta tekshirish muammosi ogohlantirishlarda ham bor
- 18-qism (o'tilgan): precision/recall — ogohlantirish qoidasini baholash xuddi klassifikatorni baholashdek
- 27.8-dars (o'tilgan): API xatolarini to'g'ri status kodlari bilan qaytarish — aks holda xato metrikasi yolg'on
- 27.10-dars (o'tilgan): canary va avtomasshtablash shu metrikalar asosida qaror qiladi
- 27.12-dars: drift aniqlash — 2-misoldagi "kirish taqsimoti o'zgardi" signalini statistik testlar bilan (PSI, KS) aniqlash
- 27.13-dars: qayta o'qitish — bashorat loglari va kechikkan belgilar qayta o'qitish ma'lumotiga aylanadi
8. Eng yaxshi amaliyotlar
To'rt qatlam: tizim, ma'lumot, model, biznes.
Belgi kechiksa — kirish va bashorat taqsimoti proksi sifatida.
Histogram bucketlari SLA atrofida zich; persentil — bucketlar yig'indisidan.
Yorliqlar past kardinallikli; id lar — logda.
Loglar JSON,
sorov_idvamodel_versiyabilan, shaxsiy ma'lumotsiz.Ogohlantirish = harakat; qolgani dashboard yoki ticket.
Qoidalar so'rovlar soniga mos (binomial) va tarixiy insidentlarda baholangan.
SLO fon darajasiga mos; ogohlantirish — burn rate bo'yicha, ko'p oynali.
Dashboard da deploy annotatsiyalari.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. ML monitoringining to'rt qatlami qaysilar?
2. Belgilar kechikishi nima va unga qarshi nima qilinadi?
3. Counter va gauge farqi?
4. Nima uchun persentillarni o'rtachalab bo'lmaydi?
5. histogram_quantile xatosi nimaga bog'liq?
6. Nima uchun foydalanuvchi id yorliq bo'lmasligi kerak?
7. So'rov id nima beradi?
8. Tunda statik chegara nima uchun ko'p yolg'on signal beradi?
9. "for: 15m" ning kamchiligi?
10. Xato byudjeti qanday hisoblanadi?
11. Burn rate = 1 nimani bildiradi?
12. Nima uchun ikki oyna (uzun va qisqa) ishlatiladi?Javoblar
- Tizim, ma'lumot, model, biznes
- To'g'ri javob haftalar keyin keladi; kirish va bashorat taqsimoti proksi sifatida kuzatiladi
- Counter faqat o'sadi (tezlik hisoblanadi), gauge istalgan tomonga
- Persentil chiziqli emas; trafik ulushlari va taqsimotlar farq qiladi
- Bucket kengligiga
- Har qiymat — alohida vaqt qatori, xotira portlaydi
- Bitta so'rovning barcha xizmatlardagi izi
- n kichik — ulushning tasodifiy tebranishi katta
- Kechikish qo'shadi, qisqa insidentni o'tkazib yuboradi
(1 - SLO) * jami so'rovlar- Byudjet aynan davr oxirida tugaydi
- Uzun — ahamiyatlilik, qisqa — hali davom etayotgani (tez tiklanish)
Vazifa 2: Xatolarni tuzating
1. p99 = (replika1_p99 + replika2_p99 + replika3_p99) / 3
2. KECHIKISH = Histogram(..., buckets=[0.005, 0.01, 0.1, 1, 10]) # SLA 300 ms
3. SOROVLAR.labels(mijoz_id=mijoz.id, yol=yol).inc()
4. log.info(f"Mijoz {ism} ({telefon}) uchun ball {ball}")
5. alert: xato_ulushi_5m > 0.01 (tunda 100 so'rov/oyna)
6. SLO = 99.99%, fon xato ulushi 0.05%Javoblar
- Replikalar bucketlarini qo'shing, keyin
histogram_quantile. - SLA atrofida zich:
[0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 1, 2.5]. mijoz_idni yorliqdan olib tashlang — u logga (JSON) yoziladi.log_json(sorov_id=sid, hodisa="bashorat", ball=ball)— ism va telefon yozilmaydi.- Binomial z yoki minimal so'rovlar soni + ketma-ket oynalar sharti.
- Byudjet 0.01%, fon 0.05% — fon byudjetdan 5 barobar katta: SLO insidentlarsiz ham buziladi. SLO ni fon darajasiga moslang (masalan, 99.8%).
Vazifa 3: Gistogramma
1-misoldagi Histogram ga birlashtir dan tashqari farq(boshqa) metodini qo'shing: ikki o'lchov orasidagi bucket farqlari (vaqt oynasi bo'yicha gistogramma — Prometheus dagi rate(..._bucket[5m]) g'oyasi). 10 ta 1 daqiqalik o'lchovdan oxirgi 5 daqiqaning p99 ini hisoblang va butun davr p99 i bilan taqqoslang.
Vazifa 4: Proksi metrikalar
2-misolga ikki proksi qo'shing: (a) 0.5 dan yuqori ballar ulushining kunlik z-skori (tarixiy o'rtacha va std bo'yicha); (b) murojaatlar ning kunlik o'rtachasi. Buzilishni boshqa turga almashtiring: murojaatlar hisoblagichi ikki barobar ko'p sanay boshlaydi (quvur xatosi). Qaysi proksi uni birinchi ko'radi, AUC qachon tushadi?
Vazifa 5: Ogohlantirishni sozlash
3-misolda binomial z > k, m oyna qoidasi uchun k (3, 4, 5) va m (1, 2, 3) to'rini ko'rib chiqing. Har kombinatsiya uchun recall, yolg'on/hafta va kechikishni jadvalga yozing. "Haftasiga <= 1 yolg'on" talabi bilan eng yaxshi kombinatsiyani tanlang. Keyin boshqa urug' bilan yangi 30 kunlik ma'lumot yaratib, tanlangan qoidani qayta baholang — natija saqlanadimi (ortiqcha moslash tekshiruvi)?
Vazifa 6: Xato byudjeti
4-misolga "haftalik hisobot" funksiyasini qo'shing: har hafta oxirida SLI, sarflangan byudjet, eng qimmat insident va "shu tezlikda byudjet qachon tugaydi" prognozi (oxirgi 7 kunlik burn rate bo'yicha). Hisobotni jadval sifatida chop eting.
Vazifa 7: O'ylash
Kredit skoring modeli uchun monitoring loyihalashtirishingiz kerak. Defolt belgisi 6-12 oydan keyin ma'lum bo'ladi. Rahbariyat "oyiga bir marta AUC hisobot qilamiz, shu yetarli" demoqda. Nima deysiz va qanday monitoring taklif qilasiz?
Javob
Qisqa javob: yetarli emas. Oyiga bir marta hisoblangan AUC 6-12 oy oldingi arizalar haqida gapiradi — muammo bugun boshlansa, u hisobotda yarim yildan keyin ko'rinadi. Belgi kechikishi uzun bo'lgani uchun asosiy og'irlik proksi metrikalarga tushadi.
1. Qatlamlar va chastota
| Qatlam | Metrika | Chastota | Ogohlantirish |
|---|---|---|---|
| Tizim | xato ulushi, p99, SLO burn | daqiqa | page / ticket |
| Ma'lumot | null, sxema, asosiy belgilar taqsimoti (daromad birligi!) | soat / kun | ticket |
| Model | ball taqsimoti, tasdiqlash ulushi, versiya bo'yicha | kun | ticket |
| Erta belgi | 1-to'lov o'tkazib yuborildi (30 kun) | hafta | hisobot + ticket |
| Yakuniy sifat | AUC, kalibrlash (6-12 oy keyin) | oy | hisobot |
| Biznes | tasdiqlangan kreditlar hajmi, zarar | hafta/oy | hisobot |
2. Erta belgi — eng qimmatli proksi
"Birinchi to'lov o'tkazib yuborildi" (first payment default) 30-60 kunda ma'lum va yakuniy defolt bilan kuchli bog'langan. Uning AUC ini haftalik hisoblash kechikishni 6-12 oydan 1-2 oyga qisqartiradi.
3. Ma'lumot tekshiruvlari — birinchi kundan
Darsning boshidagi "ming so'm va so'm" holati sxema va oraliq tekshiruvi bilan birinchi soatda ushlanardi: daromad taqsimotining medianasi 1000 barobar o'zgargan. Bu 2-misoldagi tolov>2mln ustuniga o'xshash oddiy qoida.
4. Model versiyasi va segmentlar
Ball taqsimoti umumiy emas, segmentlar bo'yicha (kanal: mobil/veb, ilova versiyasi, hudud) — o'zgarish ko'pincha bitta segmentda boshlanadi.
5. Ogohlantirish intizomi
Tunda uyg'otadigan (page) faqat tizim qatlami; ma'lumot va model — ish vaqtidagi ticket; har qoida tarixiy ma'lumotda (3-misol kabi) baholanadi.
Xulosa: oylik AUC — yakuniy tasdiq, monitoring emas. Monitoring — kirish va bashoratlarni har kuni kuzatish, erta belgilar va tez sxema tekshiruvlari; AUC esa ular ko'rsatgan signalni oylar o'tib tasdiqlaydi.
Nimani mustahkamlaydi: 2.2, 2.3, 2.6, 2.7-bo'limlar.
Xulosa
Bu darsda ML tizimini ishlab chiqarishda kuzatishni ko'rdik.
Eng muhim uch fikr:
Belgi kechikadi — proksi metrikalar birinchi. 2-misolda ma'lumot quvuridagi xato null ulushi, sxema buzilishi va bashorat taqsimotida buzilish kunining o'zida ko'rindi, 7 kunlik AUC esa 14 kundan keyin tushdi — aynan belgi kechikishiga teng. Strukturali loglar va
sorov_idbu metrikalarni ham, eng sekin so'rov sababini ham (belgilar keshi) loglardan hisoblash imkonini berdi.Metrikani to'g'ri yig'ish — o'zi alohida vazifa. 1-misolda standart bucketlar p99 ni 30.5% ga oshirib ko'rsatdi, moslangan bucketlar — 4.3% ga; replikalar p99 larini o'rtachalash sekin replikani yashirib, haqiqiy p99 ni 17% ga kamaytirdi; counter reset esa manfiy tezlik berdi.
Ogohlantirish — harakat, byudjet — kelishuv. 3-misolda statik va "o'rtacha + k*std" chegaralari tungi shovqin tufayli haftasiga 19.6 va 26.6 yolg'on signal berdi; so'rovlar soniga moslangan binomial z va ikki oynali shart yolg'onsiz 8 insidentdan 7 tasini topdi. 4-misolda burn-rate qoidalari yolg'onsiz ishladi, sodda ulush qoidasi esa 664 ta ogohlantirish berdi; 99.9% SLO esa fon xatolar tufayli insidentlarsiz ham bajarib bo'lmas bo'lib chiqdi.
Keyingi darsda Drift aniqlash: 2-misoldagi "kirish taqsimoti o'zgardi" signalini qanday qilib statistik jihatdan asosli aniqlash — PSI, KS testi, kategoriyalar uchun chi-kvadrat, ko'p o'lchovli drift va yolg'on signallar murosasi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!