Finalizacija na nivou kontejnera: zašto je monitor 83 minuta pokazivao ništa
Pipeline sa N paralelnih pod-poslova finalizuje status na nivou batch-a — svi worker-i rade, ali monitor javlja mirovanje dok poslednji kontejner ne završi. Popravka: finalizacija po kontejneru, ne po batch-u.
Bag nije bio da nešto ne radi. Bag je bio da monitor tvrdi da ništa ne radi, dok je sve radilo.
Podešavanje: discovery demon svakih 30 minuta razdeli batch od 3 000 query-ja na 8 paralelnih Docker kontejnera. Svaki kontejner obrađuje ~375 query-ja po 9 u minuti = 41 minut po kontejneru. Pošto kontejneri imaju različite queue dužine, finish vreme varira 8–17 min po kontejneru — ali poslednji uzme do 83 minuta.
Monitor je pollovao svakih 5 min: „Koliko je batch završio u poslednjem satu?". Dok batch nije bio ceo gotov, javljao je 0 done/h. Operator dashboard-i su pokazivali: „Discovery pipeline inactive". U stvarnosti je osam kontejnera radilo punim kapacitetom.
Arhitektonski greh
Originalni kod je imao finalize() na kraju batch wrapper-a:
def run_batch(queries):
assign_to_containers(queries)
wait_for_all_containers()
finalize(batch_id) # ← tek ovde se status propagira
Znači: dok poslednji kontejner ne završi poslednji query, uspešan status ne postoji ni u jednoj tabeli koju monitor čita.
Popravka
Svaki kontejner javlja svoj kraj:
def run_container(container_id, queries):
for q in queries:
process(q)
write_result(q, container_id)
# Svaki finishing event se odmah propagira
finalize_container(container_id, batch_id, count=len(queries))
Plus: batch wrapper na kraju radi samo finalize_batch(batch_id) za batch-level statistiku (ukupno vreme itd.), ne više za row-level progress.
Monitor sada vidi nove brojeve pri svakom container finish-u. Iz „0 done/h" postaje „37, 284, 531, …" u prvih 20 minuta.
Pravilo kalibracije
Iz incidenta smo izvukli numeričko pravilo:
Batch-size = Workers × Throughput/min × Target-Minutes
Za target od 15 minuta pri 9 query/min i 4 worker-a: 4 × 9 × 15 = 540 query/batch (zaokruženo 600). Sa 8 worker-a: 8 × 9 × 15 = 1 080 (zaokruženo 1 200).
Prethodna 3 000 batch-size je bila rule-of-thumb bez obzira na granularnost monitor-a. Sa 600 batch-om svaka iteracija traje ispod 20 minuta — monitor vidi nove finish događaje svakih 8 minuta.
Prenosiv obrazac
Anti-pattern nije ograničen na web crawlere. Našli smo ga u tri druga setup-a:
- ETL pipeline-ovi koji puni staging tabele po batch-u i tek na kraju gurnu u produkciju preko
INSERT ... SELECT. - ML trening koji piše checkpoint tek na kraju svake epohe — monitor pokazuje „stale" 40+ min kod velikih epoha.
- Backup job-ovi koji status postavljaju na ✅ tek kad su svi chunk-ovi gotovi — 6h status slepila tokom backup-a.
Operativni protivlek je uvek isti: finalizacija što granularnije. Per-container, per-shard, per-epoch, per-chunk. Sve što pravi monitor granularnost znatno kraćom od ukupnog runtime-a je prava odluka.