Skip to content
ZERONE
Natrag na uvide
Distribuirani sustavi2026-04-18 · 4 min čitanjaIz Case 01

Batch finalizacija na razini kontejnera: zašto monitor 83 minute nije vidio ništa

Pipeline s N paralelnih sub-jobova finalizira status na razini batcha — svi workeri rade, ali monitor javlja zastoj dok zadnji kontejner ne završi. Rješenje: finalizirati po kontejneru, ne po batchu.

Bug nije bio da je nešto pokvareno. Bug je bio da je monitor tvrdio kako ništa ne radi — dok je sve radilo.

Setup: discovery daemon svakih 30 minuta razdjeljuje 3 000 upita na 8 paralelnih Docker kontejnera. Svaki kontejner obrađuje ~375 upita po 9/min = 41 minuta po kontejneru. Kako duljine redova variraju, vrijeme završetka varira između 8 i 17 minuta po kontejneru — ali zadnji ide do 83 minute.

Monitor je pollao svakih 5 minuta: "koliko je batch završio u zadnjem satu?". Dok batch nije bio u potpunosti gotov, javljao je 0 done/h. Operatorski dashboardi su pokazivali "Discovery pipeline inactive". U stvarnosti je osam kontejnera radilo punim kapacitetom.

Arhitektonski grijeh

Originalni kod je imao jedan finalize() poziv na kraju batch wrappera:

def run_batch(queries):
    assign_to_containers(queries)
    wait_for_all_containers()
    finalize(batch_id)  # ← tek se ovdje status propagira

Znači: dok zadnji kontejner ne završi zadnji upit, signal uspjeha ne postoji ni u jednoj tablici koju monitor čita.

Popravak

Svaki kontejner javlja vlastiti završetak:

def run_container(container_id, queries):
    for q in queries:
        process(q)
        write_result(q, container_id)
    # Svaki finish event se odmah propagira
    finalize_container(container_id, batch_id, count=len(queries))

Uz to: batch wrapper na kraju radi samo zaključni finalize_batch(batch_id) poziv za batch-level statistiku (ukupno trajanje itd.), ne više za row-level progress.

Monitor sada vidi nove brojeve pri svakom završetku kontejnera. Iz "0 done/h" postaje "37, 284, 531, …" unutar prvih 20 minuta.

Pravilo kalibracije

Iz incidenta smo izveli numeričko pravilo koje od tada primjenjujemo na svakom batch pipelineu:

Veličina batcha = Workeri × Throughput/min × Ciljanih minuta

Za ciljano vrijeme završetka od 15 minuta pri 9 upita/min s 4 workera: 4 × 9 × 15 = 540 upita/batch (zaokruženo na 600). S 8 workera: 8 × 9 × 15 = 1 080 (zaokruženo na 1 200).

Ranija veličina batcha od 3 000 bila je palac-pravilo bez obzira na granularnost monitora. S batchevima od 600, svaka iteracija sada traje ispod 20 minuta — monitor vidi nove finish evente svakih 8 minuta.

Prenosivi pattern

Anti-pattern nije ograničen na web crawlere. Pronašli smo ga u tri druga setupa:

  • ETL pipelineovi koji pune staging tablice po batchu i tek na kraju guraju u produkciju kroz INSERT ... SELECT.
  • ML trening koji checkpoint piše tek na kraju svake epohe — monitoring pokazuje "stale" 40+ minuta na velikim epohama.
  • Backup jobovi koji status postavljaju na ✅ tek nakon što su svi chunkovi gotovi — 6 h status-sljepoće dok backup traje.

Operativni protulijek je uvijek isti: finalizirati što granularnije. Per-container, per-shard, per-epoch, per-chunk. Sve što čini granularnost monitoringa znatno kraćom od ukupnog runtimea je ispravna odluka.

Sličan izazov i kod vas?

Vjerojatno smo već vidjeli nešto slično. Razgovarajmo.

Započnite razgovor