Orkestratsiya: quvurni bosqichlarga bo'lish
Qirq daqiqa, keyin xato
Kechqurun o'qitish skriptini ishga tushirdingiz. U beshta ishni ketma-ket bajaradi:
1. ma'lumotni o'qish (2 daqiqa) 2. tozalash (5 daqiqa) 3. xususiyat hisoblash (30 daqiqa) 4. model o'qitish (15 daqiqa) 5. baholash va saqlash (1 daqiqa)
Qirq daqiqa kutdingiz. Ekranda xato chiqdi:
4-bosqich: ValueError
Endi nima qilasiz? Skriptni qaytadan ishga tushirasiz. U esa birinchi bosqichdan boshlaydi. Yana 37 daqiqa — allaqachon muvaffaqiyatli bajarilgan ishni qaytadan qilish uchun.
Xato beshinchi urinishda tuzalsa, siz uch soat yo'qotasiz. Ishning o'zi esa 15 daqiqalik.
Ovqat pishirish va idish yuvish
Muammoni boshqacha ko'ring. Oshpaz osh damlayapti:
- sabzi to'g'raladi
- go'sht qovuriladi
- guruch solinadi
Uchinchi qadamda guruch tugab qolsa, oshpaz sabzini qayta to'g'ramaydi. To'g'ralgan sabzi joyida turibdi. U faqat guruch olib keladi va o'sha joydan davom etadi.
Skriptingiz esa buni qila olmaydi, chunki u bosqichlarni ajratmagan — u bitta uzun ish.
Yechim: quvurni bosqichlarga bo'lish va har bosqichning natijasini saqlab qo'yish. Shunda qayta ishga tushirish faqat buzilgan joydan davom etadi.
Bu orkestratsiya deyiladi.
Bitta skript nima uchun yetarli emas
Hozirgacha o'qitish bitta skriptda edi: o'qi → tayyorla → o'qit → saqla. Ma'lumot o'sganda va bosqichlar ko'payganda bu tuzilish buziladi.
import pandas as pd show(pd.DataFrame([ {"muammo": "Oxirgi bosqichda yiqildi", "bitta skriptda": "Hammasi boshidan qayta ishlaydi", "orkestratsiyada": "Faqat yiqilgan bosqichdan davom etadi"}, {"muammo": "Faqat modelni almashtirmoqchisiz", "bitta skriptda": "Ma'lumot ham qayta tayyorlanadi", "orkestratsiyada": "Tayyorlash keshdan olinadi"}, {"muammo": "Ikki bosqich bir-biriga bog'liq emas", "bitta skriptda": "Ketma-ket bajariladi", "orkestratsiyada": "Parallel bajarilishi mumkin"}, {"muammo": "Kecha nima bo'lgan edi?", "bitta skriptda": "Jurnalda qidirasiz", "orkestratsiyada": "Har bosqichning holati saqlanadi"}, ]))
| muammo | bitta skriptda | orkestratsiyada |
|---|---|---|
| Oxirgi bosqichda yiqildi | Hammasi boshidan qayta ishlaydi | Faqat yiqilgan bosqichdan davom etadi |
| Faqat modelni almashtirmoqchisiz | Ma'lumot ham qayta tayyorlanadi | Tayyorlash keshdan olinadi |
| Ikki bosqich bir-biriga bog'liq emas | Ketma-ket bajariladi | Parallel bajarilishi mumkin |
| Kecha nima bo'lgan edi? | Jurnalda qidirasiz | Har bosqichning holati saqlanadi |
Quvur — bu graf
Endi eng muhim tushunchaga keldik, va uning nomi qo'rqinchli ko'rinadi: DAG.
Qo'rqmang — siz uni allaqachon bilasiz.
Osh damlashni eslang. Bosqichlar orasida bog'liqlik bor:
- Sabzini to'g'ramaguncha qozonga sololmaysiz.
- Go'shtni qovurmaguncha suv quyolmaysiz.
- Lekin sabzi to'g'rash va piyoz to'g'rash — bir-biriga bog'liq emas. Ikki kishi bir vaqtda qila oladi.
Mana shu ikki qoida DAG ni tashkil qiladi:
D — Directed (yo'naltirilgan). Bog'liqlikning yo'nalishi bor. Sabzi → qozon, teskarisi emas.
A — Acyclic (siklsiz). Aylana bo'lmaydi. "A B ni kutadi, B esa A ni kutadi" degan holat imkonsiz — aks holda ikkalasi ham abadiy kutadi.
G — Graph (graf). Bu shunchaki "nuqtalar va ularni bog'lovchi chiziqlar" degani.
Ya'ni DAG — bu ish rejasi: nima nimadan keyin kelishi va nima parallel bo'lishi mumkinligi yozilgan chizma. Boshqa hech narsa emas.
Nega bu foydali? Chunki reja yozib qo'yilgan bo'lsa, dastur uni o'qiy oladi:
- qaysi bosqichni qayta ishlatish kerakligini o'zi hisoblaydi;
- bog'liq bo'lmaganlarni parallel ishga tushiradi;
- yiqilganda qayerdan davom etishni biladi.
Bitta uzun skriptda bu reja hech qayerda yozilmagan — u faqat qatorlar tartibida yashiringan, va dastur uni ko'ra olmaydi.
diagramma( { "1. oqi\n(CSV -> parquet)": (0, 2.0, 2.5, 1.0, "manba"), "2. tekshir\n(sxema, sifat)": (3.2, 2.0, 2.5, 1.0, "qayta"), "3. tayyorla\n(xususiyatlar)": (6.4, 2.0, 2.5, 1.0, "qayta"), "4. oqit\n(model)": (9.6, 0.8, 2.5, 1.0, "qayta"), "5. baho\n(metrikalar)": (9.6, 3.2, 2.5, 1.0, "kuzatuv"), }, [ ("1. oqi\n(CSV -> parquet)", "2. tekshir\n(sxema, sifat)"), ("2. tekshir\n(sxema, sifat)", "3. tayyorla\n(xususiyatlar)"), ("3. tayyorla\n(xususiyatlar)", "4. oqit\n(model)"), ("3. tayyorla\n(xususiyatlar)", "5. baho\n(metrikalar)"), ], nom="quvur_dag.png", kenglik=13, izoh="Har bosqich o'z chiqishini yozadi. Keyingi bosqich o'sha faylni kirish sifatida oladi.", )

Har bosqich o'z chiqishini yozadi. Keyingi bosqich o'sha faylni kirish sifatida oladi.
Muhim qoida: har bosqich faylga yozadi. Shunda bosqich mustaqil bo'ladi — uni alohida ishga tushirish, keshlash va qayta bajarish mumkin.
Oddiy DAG ishga tushirgich
Airflow ni o'rnatishdan oldin g'oyani 80 qatorda yozamiz. Shunda keyin Airflow nimani avtomatlashtirayotgani aniq bo'ladi.
shu faylni yaratingsrc/quvur/dag.py
"""Oddiy DAG ishga tushirgich: bog'liqlik, keshlash, xato boshqaruvi. Airflow shuni katta miqyosda qiladi. Bu yerda g'oyani ko'rish uchun eng kichik variant. """ import json import time from dataclasses import dataclass, field from pathlib import Path from typing import Any, Callable @dataclass class Bosqich: nom: str ish: Callable[[], Any] kirish: tuple[str, ...] = () # kerak bo'ladigan fayllar chiqish: tuple[str, ...] = () # yasaydigan fayllar bogliq: tuple[str, ...] = () # oldin bajarilishi kerak bo'lgan bosqichlar @dataclass class Natija: holat: str # "bajarildi" | "keshdan" | "yiqildi" | "o'tkazildi" vaqt_s: float = 0.0 xato: str = "" def _yangimi(kirish: tuple[str, ...], chiqish: tuple[str, ...]) -> bool: """Chiqish fayllari kirishdan yangimi? Yangi bo'lsa qayta bajarish shart emas.""" if not chiqish: return False ch = [Path(c) for c in chiqish] if not all(c.exists() for c in ch): return False ki = [Path(k) for k in kirish if Path(k).exists()] if not ki: return True return min(c.stat().st_mtime for c in ch) >= max(k.stat().st_mtime for k in ki) def tartibla(bosqichlar: list[Bosqich]) -> list[Bosqich]: """Bog'liqlik bo'yicha tartiblaydi (topologik). Sikl bo'lsa xato beradi.""" qolgan = {b.nom: b for b in bosqichlar} tayyor: list[Bosqich] = [] korilgan: set[str] = set() while qolgan: ozod = [b for b in qolgan.values() if all(d in korilgan for d in b.bogliq)] if not ozod: raise ValueError(f"sikl yoki yetishmayotgan bog'liqlik: {sorted(qolgan)}") for b in sorted(ozod, key=lambda x: x.nom): tayyor.append(b) korilgan.add(b.nom) del qolgan[b.nom] return tayyor def ishga_tushir(bosqichlar: list[Bosqich], majburiy: bool = False, holat_yoli: str | Path = "quvur_holati.json") -> dict[str, Natija]: """DAG ni bajaradi. Bosqich yiqilsa, unga bog'liqlar O'TKAZIB YUBORILADI.""" natijalar: dict[str, Natija] = {} for b in tartibla(bosqichlar): yiqilgan = [d for d in b.bogliq if natijalar.get(d) and natijalar[d].holat in {"yiqildi", "o'tkazildi"}] if yiqilgan: natijalar[b.nom] = Natija("o'tkazildi", xato=f"bog'liq yiqildi: {yiqilgan[0]}") continue if not majburiy and _yangimi(b.kirish, b.chiqish): natijalar[b.nom] = Natija("keshdan") continue t0 = time.perf_counter() try: b.ish() natijalar[b.nom] = Natija("bajarildi", round(time.perf_counter() - t0, 3)) except Exception as e: natijalar[b.nom] = Natija("yiqildi", round(time.perf_counter() - t0, 3), f"{type(e).__name__}: {e}") Path(holat_yoli).write_text( json.dumps({k: v.__dict__ for k, v in natijalar.items()}, ensure_ascii=False, indent=2), encoding="utf-8") return natijalar def chop_et(natijalar: dict[str, Natija]) -> None: BELGI = {"bajarildi": "+", "keshdan": "=", "yiqildi": "X", "o'tkazildi": "-"} for nom, n in natijalar.items(): vaqt = f"{n.vaqt_s:6.2f}s" if n.vaqt_s else " -" print(f" [{BELGI[n.holat]}] {nom:12} {n.holat:12} {vaqt} {n.xato}")
Quvurni yozamiz
shu faylni yaratingquvur_ishga.py
"""hisobot quvuri: oqi -> tekshir -> tayyorla -> oqit -> baho.""" import json import sys from pathlib import Path sys.path.insert(0, "src") import joblib import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score, precision_score, recall_score from quvur.dag import Bosqich, chop_et, ishga_tushir from quvur.sxema import IJARA_SXEMASI, tekshir as sxema_tekshir from quvur.xususiyat import XUSUSIYATLAR, tayyorla XOM = "../datasets/ijara.csv" ORALIQ = Path("oraliq") ORALIQ.mkdir(exist_ok=True) P_XOM = ORALIQ / "xom.parquet" P_TOZA = ORALIQ / "toza.parquet" P_XUS = ORALIQ / "xususiyat.parquet" P_MODEL = ORALIQ / "model.joblib" P_BAHO = ORALIQ / "baho.json" def qadam_oqi(): pd.read_csv(XOM).to_parquet(P_XOM, index=False) def qadam_tekshir(): toza, karantin, _ = sxema_tekshir(pd.read_parquet(P_XOM), IJARA_SXEMASI) toza.to_parquet(P_TOZA, index=False) if len(karantin): karantin.to_parquet(ORALIQ / "karantin.parquet", index=False) def qadam_tayyorla(): d = pd.read_parquet(P_TOZA) x = tayyorla(d) x["broker"] = d["broker"].values x.to_parquet(P_XUS, index=False) def qadam_oqit(): d = pd.read_parquet(P_XUS) Xtr, _, ytr, _ = train_test_split(d[XUSUSIYATLAR], d["broker"], test_size=0.25, stratify=d["broker"], random_state=42) m = RandomForestClassifier(n_estimators=150, min_samples_leaf=3, random_state=42, n_jobs=1).fit(Xtr, ytr) joblib.dump(m, P_MODEL) def qadam_baho(): d = pd.read_parquet(P_XUS) _, Xte, _, yte = train_test_split(d[XUSUSIYATLAR], d["broker"], test_size=0.25, stratify=d["broker"], random_state=42) m = joblib.load(P_MODEL) p = m.predict(Xte) P_BAHO.write_text(json.dumps({ "f1": round(float(f1_score(yte, p)), 4), "aniqlik": round(float(precision_score(yte, p)), 4), "toliqlik": round(float(recall_score(yte, p)), 4), }, indent=2), encoding="utf-8") BOSQICHLAR = [ Bosqich("oqi", qadam_oqi, kirish=(XOM,), chiqish=(str(P_XOM),)), Bosqich("tekshir", qadam_tekshir, kirish=(str(P_XOM),), chiqish=(str(P_TOZA),), bogliq=("oqi",)), Bosqich("tayyorla", qadam_tayyorla, kirish=(str(P_TOZA),), chiqish=(str(P_XUS),), bogliq=("tekshir",)), Bosqich("oqit", qadam_oqit, kirish=(str(P_XUS),), chiqish=(str(P_MODEL),), bogliq=("tayyorla",)), Bosqich("baho", qadam_baho, kirish=(str(P_XUS), str(P_MODEL)), chiqish=(str(P_BAHO),), bogliq=("oqit",)), ] if __name__ == "__main__": majburiy = "--majburiy" in sys.argv chop_et(ishga_tushir(BOSQICHLAR, majburiy=majburiy)) if P_BAHO.exists(): print("\nmetrikalar:", P_BAHO.read_text(encoding="utf-8").replace("\n", " "))
Birinchi marta ishga tushiramiz — hamma bosqich bajarilishi kerak:
rm -rf oraliq python3 quvur_ishga.py
[+] oqi bajarildi 0.07s
[+] tekshir bajarildi 0.12s
[+] tayyorla bajarildi 0.15s
[+] oqit bajarildi 1.46s
[+] baho bajarildi 0.20s
metrikalar: { "f1": 0.8595, "aniqlik": 0.8929, "toliqlik": 0.8285 }
Keshlash: ikkinchi ishga tushirish
python3 quvur_ishga.py
[=] oqi keshdan -
[=] tekshir keshdan -
[=] tayyorla keshdan -
[=] oqit keshdan -
[=] baho keshdan -
metrikalar: { "f1": 0.8595, "aniqlik": 0.8929, "toliqlik": 0.8285 }
Hamma bosqich keshdan bo'ldi — chunki chiqish fayllari kirishdan yangi. Vaqt deyarli nolga tushdi.
Endi faqat bitta bosqichning kirishini o'zgartiramiz:
touch oraliq/xususiyat.parquet python3 quvur_ishga.py
[=] oqi keshdan -
[=] tekshir keshdan -
[=] tayyorla keshdan -
[+] oqit bajarildi 1.61s
[+] baho bajarildi 0.18s
metrikalar: { "f1": 0.8595, "aniqlik": 0.8929, "toliqlik": 0.8285 }
tayyorla ning chiqishi yangilandi, shuning uchun unga bog'liq oqit va baho qayta bajarildi.
oqi va tekshir esa keshda qoldi — ular bu o'zgarishdan ta'sirlanmaydi.
Qanday qilib dastur buni bildi? U DAG ni o'qidi.
Chizmada tayyorla dan oqit ga strelka bor, oqit dan baho ga ham. Demak tayyorla
o'zgarsa, o'sha strelkalar bo'ylab yuqoriga ta'sir tarqaladi. oqi va tekshir esa
tayyorla dan oldin turibdi — ularga hech narsa yetib bormaydi.
Bu qaror odam tomonidan emas, graf tomonidan qabul qilindi. Siz "endi nimani qayta ishlatish kerak?" deb o'ylab o'tirmadingiz.
Mana orkestratsiyaning asosiy foydasi: faqat kerakli qism qayta ishlaydi, va nima kerakli ekanini tizim o'zi hisoblaydi.
Bu g'oya sizga tanish
Agar make yoki Makefile bilan ishlagan bo'lsangiz — bu aynan o'sha mexanizm. 1976-yilda
yaratilgan.
Sabab bir xil: dasturchilar butun loyihani qayta kompilyatsiya qilishdan charchagan edi. Yechim ham bir xil: bog'liqlikni yozib qo'y, fayl vaqtiga qara, faqat kerakligini qayta qil.
Airflow, Dagster, Prefect — hammasi shu g'oyaning ML uchun moslashtirilgan ko'rinishi.
Xato bo'lganda nima bo'ladi
Bitta bosqichni ataylab buzamiz:
python3 -c " import sys; sys.path.insert(0, 'src') from quvur.dag import Bosqich, ishga_tushir, chop_et def yaxshi(): pass def yomon(): raise ValueError('ma\'lumot manbasi javob bermadi') b = [ Bosqich('oqi', yaxshi), Bosqich('tekshir', yomon, bogliq=('oqi',)), Bosqich('tayyorla', yaxshi, bogliq=('tekshir',)), Bosqich('oqit', yaxshi, bogliq=('tayyorla',)), Bosqich('hisobot', yaxshi), ] chop_et(ishga_tushir(b, holat_yoli='xato_holati.json')) "
[+] hisobot bajarildi - [+] oqi bajarildi - [X] tekshir yiqildi - ValueError: ma'lumot manbasi javob bermadi [-] tayyorla o'tkazildi - bog'liq yiqildi: tekshir [-] oqit o'tkazildi - bog'liq yiqildi: tayyorla
Uch narsaga e'tibor bering:
tekshiryiqildi va sababi yozildi- Unga bog'liq
tayyorlavaoqito'tkazib yuborildi — bajarilmadi, chunki ma'nosiz hisobotesa bajarildi — utekshirga bog'liq emas
Bitta bosqich yiqilganda butun quvur to'xtamaydi — faqat unga bog'liq tarmoq to'xtaydi.
Sikl tekshiruvi
DAG da sikl bo'lmasligi kerak. Bo'lsa — darhol xato:
python3 -c " import sys; sys.path.insert(0, 'src') from quvur.dag import Bosqich, tartibla b = [Bosqich('a', lambda: None, bogliq=('b',)), Bosqich('b', lambda: None, bogliq=('a',))] try: tartibla(b) except ValueError as e: print('XATO (yaxshi):', e) "
XATO (yaxshi): sikl yoki yetishmayotgan bog'liqlik: ['a', 'b']
Holat fayli
Har ishga tushirish holatni yozadi — keyin uni kuzatuv tizimi o'qiydi (34-bob):
python3 quvur_ishga.py > /dev/null 2>&1 cat quvur_holati.json
{
"oqi": {
"holat": "keshdan",
"vaqt_s": 0.0,
"xato": ""
},
"tekshir": {
"holat": "keshdan",
"vaqt_s": 0.0,
"xato": ""
},
"tayyorla": {
"holat": "keshdan",
"vaqt_s": 0.0,
"xato": ""
},
"oqit": {
"holat": "keshdan",
"vaqt_s": 0.0,
"xato": ""
},
"baho": {
"holat": "keshdan",
"vaqt_s": 0.0,
"xato": ""
}
}
Airflow va muqobillari
Yuqoridagi 80 qator g'oyani ko'rsatadi. Ishlab chiqarishda tayyor tizimlar ishlatiladi:
import pandas as pd show(pd.DataFrame([ {"asbob": "Airflow", "kuchli tomoni": "Sanoat standarti, boy ekotizim, jadval bo'yicha ishga tushirish", "kamchiligi": "Og'ir: bazasi, rejalashtiruvchi, veb-server kerak"}, {"asbob": "Prefect", "kuchli tomoni": "Python ga yaqin, sodda sintaksis", "kamchiligi": "Kichikroq hamjamiyat"}, {"asbob": "Dagster", "kuchli tomoni": "Ma'lumot aktivlari (assets) markazda, turlar tekshiriladi", "kamchiligi": "Boshqacha fikrlash talab qiladi"}, {"asbob": "Makefile", "kuchli tomoni": "Hech narsa o'rnatish shart emas, keshlash bepul", "kamchiligi": "Jadval, qayta urinish, kuzatuv yo'q"}, {"asbob": "DVC pipelines", "kuchli tomoni": "5-bobdagi ma'lumot versiyalash bilan birlashgan", "kamchiligi": "Faqat ML quvurlari uchun"}, ]))
| asbob | kuchli tomoni | kamchiligi |
|---|---|---|
| Airflow | Sanoat standarti, boy ekotizim, jadval bo'yicha ishga tushirish | Og'ir: bazasi, rejalashtiruvchi, veb-server kerak |
| Prefect | Python ga yaqin, sodda sintaksis | Kichikroq hamjamiyat |
| Dagster | Ma'lumot aktivlari (assets) markazda, turlar tekshiriladi | Boshqacha fikrlash talab qiladi |
| Makefile | Hech narsa o'rnatish shart emas, keshlash bepul | Jadval, qayta urinish, kuzatuv yo'q |
| DVC pipelines | 5-bobdagi ma'lumot versiyalash bilan birlashgan | Faqat ML quvurlari uchun |
Airflow da bizning quvur shunday ko'rinadi — tuzilma deyarli bir xil:
from airflow.decorators import dag, task from datetime import datetime @dag(schedule="0 3 * * *", start_date=datetime(2026, 9, 1), catchup=False) def hisobot_quvuri(): @task def oqi(): ... @task def tekshir(kirish): ... @task def tayyorla(kirish): ... @task def oqit(kirish): ... # bog'liqlik funksiya chaqiruvi orqali bildiriladi tayyorlangan = tayyorla(tekshir(oqi())) oqit(tayyorlangan) hisobot_quvuri()
schedule="0 3 * * *" — har kuni soat 3:00 da. Bu bizning versiyamizda yo'q va Airflow ni
tanlashning asosiy sabablaridan biri.
Kichik loyihaga Airflow kerak emas
Airflow ishga tushishi uchun PostgreSQL, rejalashtiruvchi va veb-server kerak — kamida uch konteyner, doimiy ishlab turadigan.
Bitta kunlik quvur uchun bu ortiqcha. Ketma-ketlik: skript → Makefile → DVC pipeline → Prefect/Dagster → Airflow. Har bosqichga o'tish uchun aniq sabab bo'lsin.
Idempotentlik: eng muhim xossa
Bosqich idempotent bo'lishi kerak: ikki marta bajarilsa, natija bir xil bo'lsin.
import pandas as pd show(pd.DataFrame([ {"yomon (idempotent emas)": "df.to_csv(yol, mode='a') # qo'shib yozadi", "yaxshi (idempotent)": "df.to_csv(yol) # ustidan yozadi"}, {"yomon (idempotent emas)": "INSERT INTO t VALUES (...)", "yaxshi (idempotent)": "INSERT ... ON CONFLICT DO UPDATE"}, {"yomon (idempotent emas)": "hisob += 1", "yaxshi (idempotent)": "hisob = manbadan_hisobla()"}, {"yomon (idempotent emas)": "model_v{n+1}.joblib # nomeri oshadi", "yaxshi (idempotent)": "model_{malumot_hash}.joblib"}, ]))
| yomon (idempotent emas) | yaxshi (idempotent) |
|---|---|
| df.to_csv(yol, mode='a') # qo'shib yozadi | df.to_csv(yol) # ustidan yozadi |
| INSERT INTO t VALUES (...) | INSERT ... ON CONFLICT DO UPDATE |
| hisob += 1 | hisob = manbadan_hisobla() |
| model_v{n+1}.joblib # nomeri oshadi | model_{malumot_hash}.joblib |
Nega bu muhim: qayta urinish (retry) idempotent bo'lmagan bosqichda ma'lumotni ikki marta qo'shib yuboradi. Va qayta urinish — orkestratsiyaning asosiy xususiyati.
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
| DAG | Bosqichlar + bog'liqliklar; sikl bo'lmasligi kerak |
| Har bosqich faylga yozadi | Shunda u mustaqil, keshlanadigan va qayta bajariladigan bo'ladi |
| Keshlash | Chiqish kirishdan yangi bo'lsa, qayta bajarilmaydi |
| Qisman qayta ishlash | Bitta bosqich o'zgarsa, faqat unga bog'liqlar qayta ishlaydi |
| Xato tarqalishi | Yiqilgan bosqichga bog'liqlar o'tkazib yuboriladi, qolganlari ishlaydi |
| Holat fayli | Har ishga tushirish natijasi yoziladi — kuzatuv uchun |
| Asbob tanlash | Skript → Makefile → DVC → Prefect → Airflow; sababsiz o'tmang |
| Idempotentlik | Ikki marta bajarilsa natija bir xil bo'lsin — retry shunga tayanadi |
Talaba ishi
dag.pygaqayta_urin=2parametrini qo'shing: bosqich yiqilsa, ikki marta qayta urinsin. Qaysi xatolarda qayta urinish mantiqiy, qaysilarida yo'q?- Bir-biriga bog'liq bo'lmagan bosqichlarni parallel bajaradigan variant yozing
(
ThreadPoolExecutor). Bizning quvurda qaysi bosqichlar parallel bo'la oladi? _yangimifaqat vaqtga qaraydi. Uni 5-bobdagi hash bilan almashtiring. Qaysi holatda bu muhim farq qiladi?quvur_ishga.pyga--faqat <bosqich>bayrog'ini qo'shing: faqat bitta bosqichni bajarsin.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.