Ezen az oldalon
Az AI Engineer által 2026. október 3-án közzétett előadásban a Crusoe egyik fejlesztői kapcsolattartója (developer advocate) elindított egy PyTorch tanítási feladatot két A100 csomóponton. Aztán közölte a demó saját monitorozó rendszerével, hogy az egyik GPU XID 79 hibát dobott. A csomópontot leállítottként jelölték meg, a feladatot pedig törölték. A folyamat SIGTERM jelet kapott, és „legfeljebb két percet arra, hogy kezelje ezt a SIGTERM-et, és elmentse az ellenőrzőpontjait vagy kiürítse a naplóit”. Beállt egy tartalék csomópont, és „a slurm feladat újraindul. Betölti az ellenőrzőpontot, és ott folytatja a tanítást, ahol abbahagyta”. Elejétől a végéig valamivel kevesebb mint tizenöt perc. Senkit sem kellett riasztani.
Egy szinkronizálás jóval kisebb vad, mint egy tanítás. A kérdés ugyanaz. Mire van szüksége egy futásnak ahhoz, hogy a feldolgozó leállítása perceket kerüljön, és egyetlen duplikátumot se?
Ez a válasz a Tajo saját szinkronizálási futtatójára, a kódból kiolvasva, nem egy prospektusból. A Reverse ETL a feladat alakját írja le. Ez itt arról szól, mi történik, amikor a feladat meghal.
Kövess végig egy kontaktot
Egy futás oldalanként olvassa a forrást, egy mentett kurzortól kezdve. Nevezzük az oldal kurzorát c3-nak, a kontaktunkat pedig az oldal 212. rekordjának. Mielőtt bármi beíródna, három dolog igaz erre a kontaktra.
Először: van kézbesítési kulcsa. A kulcs a szinkronizálás azonosítójának, a forrásnak, az oldal kurzorának, az oldalon belüli pozíciónak és magának a rekordnak a hash-e. A kurzor és a rekord bekerül a hash-be, de soha nem ágyazódik be a kulcsba, így a kulcs nem tartalmaz e-mail-címet.
Másodszor: az írás egy nyilvántartáson keresztül megy, nem egyenesen a célrendszerbe. Mielőtt a célrendszer hívása megtörténne, a kulcsot „függőben” állapotúként lefoglalja a rendszer. Ha az írás sikerül, a sort befejezettnek jelöli, és hét napig megőrzi.
Harmadszor: a kurzor nem mozdult. Csak egy köteg végén lép tovább, csak akkor, ha a köteg minden rekordja célba ért, és csak azután, hogy a futás bebizonyította, hogy még mindig övé a szinkronizálás.
Most állítsd le a feldolgozót az írás és az ellenőrzőpont között.
SIGTERM esetén minden várólista-feldolgozó egyetlen közös, tizenöt másodperces határidőn belül lezárja a folyamatban lévő munkáját, a folyamat pedig huszonöt másodperc után kilép, bármi fut is még; a systemd negyvenötöt ad neki, mielőtt kilövi. Az a futás, amely nem fejeződött be ezen az ablakon belül, továbbra is folyamatban lévőként van megjelölve, és a következő feldolgozó a várólista elakadt feladatokat helyreállító mechanizmusán keresztül veszi át. Kemény leállításnál egyáltalán nincs lezárás. Akárhogy is, ugyanaz a két tény igaz. A célrendszerben megvan a kontaktunk. A kurzorunk még mindig c3.
Az újrajátszás
A következő futás a c3-nál kezd, ugyanazt az oldalt olvassa, ugyanazt a kulcsot állítja elő a 212. rekordhoz, és megkérdezi a nyilvántartást. A nyilvántartás azt mondja: befejezve. A célrendszert nem hívja meg; a mentett eredményt visszajátssza, és a futás továbbmegy. A tömeges útvonalon, ahol a Brevo import végpontja egy teljes oldalt fogad egyetlen hívásban, ugyanez az ellenőrzés kulcsonként lefut, mielőtt a kérés összeállna. Az az oldal, amelyben minden kulcs már befejezett, semmit sem küld.
Ez az egész trükk, és érdemes elmondani, miért működik. A kulcs az oldal kurzorából származik a köteg elején, nem abból a futásból, amely épp végrehajtás alatt volt. Egy összeomlás, egy hatvan másodperccel későbbi újrapróbálkozás és egy holnapi ütemezett futás ugyanahhoz a rekordhoz ugyanazt a kulcsot állítja elő. A nyilvántartásnak mindegy, melyikük kérdez.
Most az ára. A nyilvántartás hét nap után felejt. A kód saját megjegyzése szerint a hét nap lefed egy késleltetett újrapróbálkozást és egy rövid kiesést, és ennél többet nem állít. Az ennél régebbi kurzorról induló újrajátszás újra ír. Egy keményen megszakított futás pedig várhat. A halott futás bérlete legfeljebb tizenöt percig él, és ütemezett szinkronizálásnál egy szabad bérletű futást a rendszer csak akkor söpör ki és ütemez újra, ha több mint egy órája indult. A Crusoe demója tizenöt percen belül visszaáll. A miénk nem mindig.
Kié a szinkronizálás, amíg újraindul
Egy szinkronizálás egyszerre egy futásé. A futás bérletet vesz fel a Redisben saját tokennel, tizenöt perces élettartammal és ötpercenkénti megújítással. Minden ellenőrzőpont előtt ellenőrzi, hogy még mindig nála van-e a bérlet. Ha nincs, leáll. A futást azzal az egyszerű mondattal rögzíti a rendszer, amelyet az ember olvasni fog: „The run lost its slot part-way through and stopped to avoid writing twice.” (a futás menet közben elvesztette a helyét, és leállt, hogy ne írjon kétszer). Egy második futást, amely egy élő tulajdonosba ütközik, nem próbál újra a rendszer. Ez a következő üzenettel ér véget: „Another run of this sync was already in progress” (ennek a szinkronizálásnak egy másik futása már folyamatban volt), a következő ütemezett futás pedig rendesen elindul.
A javítás felelőse is le van írva. Minden hibakódhoz tartozik egy mondat arról, mi történt, egy arról, mi a teendő, és egy felelős: te vagy a Tajo. Ha a célrendszer minden rekordot elutasított, mert rossz a mezőleképezés, az a te dolgod. Egy elveszett bérlet a miénk. A futás oldala megmutatja a következő lépést; a szinkronizálás-helyreállítási útmutató felsorolja, melyik.
Eddig ez a könnyebbik fele. Egy összeomlás egy újrajátszásba kerül, és semmi sem érkezik meg kétszer. A másik felét két incidens tanította meg.
Hiba hiba után
Egy szinkronizálás újra és újra elbukott. Nem szoros ciklusban: ütemezési intervallumonként egyszer, futásról futásra, minden alkalommal ugyanazzal az üzenettel, hogy egy egyéni azonosító nem tartalmazhat kettőspontot. A futás soha nem ért el a célrendszerig. Már a várólistára kerülés közben meghalt, mielőtt bármelyik futtató látta volna, így egy futtatóhoz kötött riasztásnak soha nem volt mire reagálnia.
Semmi sem figyelte a sorozatot. Ez az egész megállapítás.
A javítás egy megszakító. Minden rögzített hiba után beolvassa a szinkronizálás utolsó öt befejezett futását a szinkronizálás legutóbbi módosítása óta. Ha mind az öt hiba, szünetelteti a szinkronizálást, egy auditsort ír circuit_breaker_tripped okkal, és egyetlen kritikus riasztást küld. A sorozat a tárolt futásokból adódik, nem a feldolgozóban lévő számlálóból. Bármilyen úton rögzített hiba számít, beleértve a várólistára kerüléskor fellépő hibát is, amely az incidenst okozta. A kód megjegyzése szerint ez az ötödik futásnál megállította volna azt az incidenst. Egyetlen siker nullázza a sorozatot. A szüneteltetés feloldása újra élesíti a megszakítót, ahelyett hogy a régi sorok alapján újra kioldaná. A Shopify-összekötő útmutatója ugyanezt mondja egyetlen sorban: öt egymást követő sikertelen futás után a szinkronizálás magától szünetel, és az oldala az utolsó hibát mutatja.
Amit a megszakító nem tesz, az az, hogy bármit is megjavítana. A Crusoe kicseréli a csomópontot, és kérdezés nélkül újraindítja a feladatot. A Tajo megáll, és kérdez: a szüneteltetett szinkronizálás szüneteltetve marad, amíg egy ember el nem hárítja az okot, és újra közzé nem teszi. Az óvatos alapértelmezés szándékos: egy ügyfélrekordokba író szinkronizálás automatikus újrapróbálása maga is írás.
A csend, amit senki sem látott
Az ellenvetés jogos, és így hangzik: „Egy megszakító, amely öt hiba után szüneteltet, a második incidenst is elkapta volna. Miért kell egy második őr?” Mert a második incidens nem úgy nézett ki, mint egy hiba. Egy munkaterületen hosszú ideig volt egy aktív szinkronizálás, egyre nagyobb halom hibasor, és egyetlen siker sem, soha, és semmi nem riasztott. Minden létező jelzés aktivitáshoz volt kötve: egy futás elbukott, beérkezett egy webhook, túllépték a keretet. A nulla siker nem esemény. Egy esemény hiánya, a megszakító pedig csak akkor fut, ha hiba rögzül. Egy halott ütemező, egy beragadt várólista, egy soha el nem induló feldolgozó: ezek semmit sem rögzítenek, és a megszakító soha nem ébred fel.
A mozdony éberségi berendezése (dead man’s handle) fordítva működik. A mozdonyvezetőnek folyamatosan nyomnia kell, és a csend megállítja a vonatot. A szinkronizálási változat óránként egyszer fut, minden óra tizenharmadik percében. Minden aktív szinkronizálással rendelkező munkaterülettől egyetlen kérdést kérdez: sikerült-e bármelyik futás az elmúlt huszonnégy órában? Az a munkaterület, amelynek legrégebbi aktív szinkronizálását egy napnál rövidebb ideje kapcsolták be, békén marad; még nem volt egy napja arra, hogy sikerüljön. Az idősebb és néma munkaterület egyetlen kritikus riasztást kap. A riasztás megmondja, melyik csendről van szó: vannak rögzített futások, de egyik sem sikeres, vagy egyáltalán nincs rögzített futás. Incidensenként egyszer jelez. Egy jelölő a Redisben zárva tartja az első sikeres futásig, aztán törlődik, így a következő incidens ismét riaszt.
Mindkét őr egyetlen commitban érkezett, utólag. A megszakító megjegyzése szerint az ötödik futásnál megállította volna az első incidenst. Az éberségi riasztás megjegyzése szerint egy rég bekapcsolt, nulla futással rendelkező szinkronizálásnak riasztania kell. Egyik sem létezett, amikor szükség lett volna rá. Utólag írtuk meg őket, és mindkét incidenst rögzítettük a fájl fejlécében, hogy senki ne felejtse el, miért.
Mi készült el, és mi nem
Elkészült, az írás pillanatában:
- A kurzor minden hibátlan köteg végén mentésre kerül, és összeomláskor is.
- Minden rekord kézbesítési kulcsa az oldal kurzorából és a rekord pozíciójából származik, és minden célrendszer-hívás előtt egy hétnapos nyilvántartással van összevetve.
- Szinkronizálásonként egy bérlet, tizenöt perces élettartammal.
- Futásonként öt próbálkozás, hatvan másodpercről induló, növekvő várakozással.
- Egy megszakító, amely öt egymást követő hiba után szünetelteti a szinkronizálást, és rögzíti az okát.
- Óránkénti riasztás huszonnégy siker nélküli órára.
Nem készült el, és érdemes tudnod, mielőtt rá támaszkodsz:
- Az éberségi riasztás a munkaterületet figyeli, nem a szinkronizálást. Egy egészséges szinkronizálás elrejt egy néma testvért ugyanabban a munkaterületben.
- A riasztása a Tajo üzemeltetőihez jut el: mindig naplósorként, ahol be van állítva, ott Sentryn, webhookon vagy e-mailben is. A szinkronizálás oldalán nem jelenik meg. A megszakító szüneteltetése igen.
- Semmi nem indítja újra a szüneteltetett szinkronizálást. Egy ember teszi meg.
- A kemény leállítás utáni helyreállítás nem gyors. A bérlet tizenöt percig tarthat, a takarítás pedig egy órát vár.
- Mindkét küszöb, az öt hiba és a huszonnégy óra, telepítési beállítás. Nem tudod szinkronizálásonként beállítani őket.
Majdnem tökéletes lépések hosszú lánca is elbukik; a kiút az, hogy meghatározod a végállapotot, és azt ellenőrzöd. Ez a cikk ennek az üzemeltetési fele. Határozd meg, mit hagy maga után egy halott futás, és ellenőrizd azt a semmit, amit a csend hagy maga után.
A Crusoe záró gondolata az volt, hogy „a hibák elkerülhetetlenek”. A miénk egy lépéssel tovább megy. Az összeomlás volt a könnyebbik fele, mert sorokat hagyott maga után. A csend volt az a fele, amit meg kellett keresnünk.
Gyakran Ismételt Kérdések
Mi történik egy futó szinkronizálással, ha a feldolgozó egy köteg közepén meghal?
A kurzor továbbra is a megszakadt köteg elejére mutat, mert a Tajo csak akkor lépteti tovább, ha a köteg hibátlanul célba ért. A következő futás újra ugyanazt az oldalt olvassa. Minden rekord egy kézbesítési kulcsot visz magával, amely az oldal kurzorából és a rekord oldalon belüli pozíciójából származik, és egy nyilvántartás hét napig megőriz minden befejezett írást, így a már beírt rekordok a nyilvántartásból játszódnak vissza, ahelyett hogy másodszor is elküldené őket a rendszer a célrendszernek.
Miért nem írja be kétszer ugyanazt a kontaktot egy újrajátszott szinkronizálás?
Minden célrendszer-hívás előtt a futás összeveti a rekord kézbesítési kulcsát a nyilvántartással. A kulcs a szinkronizálás, a forrás, az oldal kurzora, az oldalon belüli pozíció és maga a rekord hash-e, így egy összeomlás, egy perccel későbbi újrapróbálkozás és egy másnapi ütemezett futás ugyanahhoz a rekordhoz ugyanazt a kulcsot állítja elő. A hétnapos ablakon belül már befejezettként megjelölt kulcsot a rendszer nem küldi el újra.
Mi az a dead man riasztás egy szinkronizálásnál?
Olyan ellenőrzés, amely a hiányra jelez, nem a hibára. A Tajo óránként egyszer átnéz minden munkaterületet, ahol aktív szinkronizálás van, és riaszt, ha az elmúlt huszonnégy órában egyetlen futás sem sikerült, beleértve azt az esetet is, amikor egyáltalán nem rögzült futás. Incidensenként egyszer jelez, és az első sikeres futás után újra élesedik. Azért létezik, mert egy hibariasztás csak a rögzített hibákat látja.