Op deze pagina
In een talk die AI Engineer op 3 oktober 2026 publiceerde, startte een developer advocate van Crusoe een PyTorch-trainingsjob op twee A100-nodes. Daarna vertelde de spreker het eigen monitoringsysteem van de demo dat één GPU een XID 79-fout had gegeven. De node werd als uitgevallen gemarkeerd en de job geannuleerd. Het proces kreeg een SIGTERM en “tot twee minuten om die SIGTERM af te handelen en je checkpoints op te slaan of je logs weg te schrijven”. Een reservenode nam het over, en “de slurm-job herstart. Hij laadt het checkpoint en gaat verder met trainen waar hij gebleven was”. Van begin tot eind iets minder dan vijftien minuten. Niemand werd opgepiept.
Een synchronisatie is een veel kleiner beest dan een trainingsrun. De vraag is dezelfde. Wat heeft een run nodig zodat het uitvallen van de worker minuten kost en geen duplicaten oplevert?
Dit is het antwoord voor Tajo’s eigen sync-runner, gelezen uit de code en niet uit een brochure. Reverse ETL beschrijft de vorm van de job. Dit is wat er gebeurt als de job doodgaat.
Volg één contact
Een run leest de bron pagina voor pagina, vanaf een opgeslagen cursor. Noem de paginacursor c3 en ons contact het 212e record op die pagina. Drie dingen gelden voor dat contact voordat er iets wordt geschreven.
Ten eerste heeft het een leveringssleutel. De sleutel is een hash van de id van de synchronisatie, de bron, de paginacursor, de positie op de pagina en het record zelf. De cursor en het record gaan de hash in en worden nooit in de sleutel opgenomen, dus de sleutel bevat geen e-mailadres.
Ten tweede loopt de schrijfactie via een ledger, niet rechtstreeks naar de bestemming. Voordat de bestemming wordt aangeroepen, wordt de sleutel als in behandeling geclaimd. Slaagt de schrijfactie, dan wordt de rij als voltooid gemarkeerd en zeven dagen bewaard.
Ten derde is de cursor niet verschoven. Hij verschuift alleen aan het einde van een batch, alleen als elk record in die batch is aangekomen, en alleen nadat de run heeft bewezen dat hij de synchronisatie nog steeds bezit.
Stop nu de worker tussen de schrijfactie en het checkpoint.
Bij een SIGTERM rondt elke queue-worker zijn werk af binnen één gedeelde deadline van vijftien seconden, en het proces stopt na vijfentwintig seconden, wat er ook nog draait; systemd geeft het vijfenveertig seconden voordat het hard stopt. Een run die niet binnen dat venster klaar was, staat nog steeds als lopend gemarkeerd, en de volgende worker pakt hem op via het herstel van vastgelopen jobs in de queue. Bij een harde stop wordt er helemaal niets afgerond. Hoe dan ook gelden dezelfde twee feiten. De bestemming heeft ons contact. Onze cursor staat nog steeds op c3.
De herhaling
De volgende run begint bij c3, leest dezelfde pagina, leidt dezelfde sleutel af voor het 212e record en vraagt het aan de ledger. De ledger zegt: voltooid. De bestemming wordt niet aangeroepen; het opgeslagen resultaat wordt afgespeeld en de run gaat door. Op het bulkpad, waar het importendpoint van Brevo een hele pagina in één aanroep aanneemt, loopt dezelfde controle per sleutel voordat het verzoek wordt opgebouwd. Een pagina waarvan elke sleutel al voltooid is, verstuurt niets.
Dat is de hele truc, en het is de moeite waard om te zeggen waarom hij werkt. De sleutel wordt afgeleid van de paginacursor aan het begin van de batch, niet van de run die toevallig bezig was. Een crash, een nieuwe poging zestig seconden later en een geplande run morgen leiden allemaal voor hetzelfde record dezelfde sleutel af. Het maakt de ledger niet uit wie van hen het vraagt.
Nu de prijs. De ledger vergeet na zeven dagen. Het commentaar in de code zegt zelf dat zeven dagen een vertraagde nieuwe poging en een korte storing dekken, en meer beweert het niet. Een herhaling vanaf een cursor die ouder is, schrijft opnieuw. En een run die hard wordt onderbroken, kan blijven wachten. De lease van de dode run blijft tot vijftien minuten staan, en bij een geplande sync wordt een run waarvan de lease vrij is pas opgeruimd en opnieuw ingepland als hij meer dan een uur geleden is gestart. De demo van Crusoe is binnen vijftien minuten terug. Die van ons niet altijd.
Wie de synchronisatie bezit terwijl die herstart
Eén run bezit een synchronisatie tegelijk. De run neemt een lease in Redis met een eigen token, een levensduur van vijftien minuten en een verlenging elke vijf minuten. Voor elk checkpoint controleert hij dat hij die lease nog heeft. Zo niet, dan stopt hij. De run wordt vastgelegd met de eenvoudige zin die de persoon zal lezen: “The run lost its slot part-way through and stopped to avoid writing twice.” (De run verloor halverwege zijn plek en stopte om niet twee keer te schrijven.) Een tweede run die botst met een actieve eigenaar, wordt niet opnieuw geprobeerd. Hij eindigt met “Another run of this sync was already in progress” (Er liep al een andere run van deze synchronisatie), en de volgende geplande run start normaal.
Ook wie de oplossing bezit, is vastgelegd. Elke foutcode verwijst naar één zin over wat er gebeurde, één over wat je daarna moet doen, en een eigenaar: jij of Tajo. Een bestemming die elk record weigerde omdat de mapping verkeerd is, is van jou. Een verloren lease is van ons. De pagina van de run laat de volgende stap zien; de handleiding voor syncherstel vermeldt welke van de twee het is.
Tot hier is dit de makkelijke helft. Een crash kost één herhaling en niets komt twee keer aan. Twee incidenten leerden ons de andere helft.
Fout na fout
Eén synchronisatie faalde keer op keer. Niet in een strakke lus, maar één keer per schedulerinterval, run na run, elke keer met dezelfde melding: een aangepaste id mag geen dubbele punt bevatten. De run bereikte de bestemming nooit. Hij strandde al tijdens het in de wachtrij zetten, voordat een runner hem zag, dus een alert die op de runner was gebaseerd, had nooit iets om op te reageren.
Niets hield de reeks in de gaten. Dat is de hele bevinding.
De oplossing is een breaker. Na elke vastgelegde fout leest hij de laatste vijf afgeronde runs van de synchronisatie sinds die voor het laatst is gewijzigd. Zijn alle vijf fouten, dan pauzeert hij de synchronisatie, schrijft hij één auditrij met de reden circuit_breaker_tripped en geeft hij één kritieke alert. De reeks wordt afgeleid uit de opgeslagen runs, niet uit een teller in de worker. Een fout telt mee, langs welk pad hij ook is vastgelegd, inclusief de fout bij het in de wachtrij zetten die het incident veroorzaakte. Volgens het commentaar in de code had hij dat incident bij run vijf gestopt. Eén succes zet de reeks terug op nul. Het opheffen van de pauze schakelt de breaker opnieuw in, in plaats van hem opnieuw te laten afgaan op de oude rijen. De handleiding voor de Shopify-connector zegt hetzelfde in één regel: na vijf opeenvolgende mislukte runs pauzeert de synchronisatie zichzelf en toont de pagina de laatste fout.
Wat de breaker niet doet, is iets repareren. Crusoe vervangt de node en herstart de job zonder te vragen. Tajo stopt en vraagt: een gepauzeerde synchronisatie blijft gepauzeerd tot een persoon de oorzaak oplost en hem opnieuw publiceert. De voorzichtige standaard is bewust: een automatische nieuwe poging van een synchronisatie die naar klantrecords schrijft, is zelf een schrijfactie.
De stilte die niemand zag
Het bezwaar is terecht en gaat zo: “Een breaker die na vijf fouten pauzeert, had het tweede incident ook opgevangen. Waarom een tweede bewaker bouwen?” Omdat het tweede incident er niet uitzag als een fout. Lange tijd had een workspace een actieve synchronisatie, een groeiende stapel foutrijen en geen enkel succes, nooit, en niets gaf een alert. Elk signaal dat bestond, hing aan activiteit: een run mislukte, een webhook kwam binnen, een budget werd overschreden. Nul successen is geen gebeurtenis. Het is de afwezigheid van een gebeurtenis, en een breaker draait alleen als er een fout wordt vastgelegd. Een scheduler die dood is, een queue die vastzit, een worker die nooit opstart: die leggen niets vast, en een breaker wordt nooit wakker.
De dodemansknop van een locomotief werkt andersom. De machinist moet hem blijven indrukken, en stilte zet de trein stil. De synchronisatieversie draait één keer per uur, om dertien minuten over. Voor elke workspace met een actieve synchronisatie stelt hij één vraag: is er de afgelopen vierentwintig uur een run geslaagd? Een workspace waarvan de oudste actieve synchronisatie minder dan een dag geleden is ingeschakeld, blijft met rust; die heeft nog geen dag gehad om te slagen. Een workspace die ouder en stil is, krijgt één kritieke alert. De alert zegt welke stilte het is: runs vastgelegd maar geen enkele geslaagd, of helemaal geen runs vastgelegd. Hij gaat één keer per incident af. Een markering in Redis houdt hem dicht tot de eerste geslaagde run en wordt dan gewist, zodat het volgende incident opnieuw een alert geeft.
Beide bewakers kwamen in één commit, achteraf. Het commentaar van de breaker zegt dat hij het eerste incident bij run vijf had gestopt. Het commentaar van de dead-man-alert zegt dat een synchronisatie die lang geleden is ingeschakeld en nul runs heeft, een alert moet geven. Geen van beide bestond toen ze nodig waren. We schreven ze achteraf, met beide incidenten vastgelegd in de bestandsheader, zodat niemand vergeet waarom.
Wat er gebouwd is, en wat niet
Gebouwd, op het moment van schrijven:
- De cursor wordt opgeslagen aan het einde van elke foutloze batch, en nog een keer bij een crash.
- De leveringssleutel van elk record wordt afgeleid van de paginacursor en zijn positie, en voor elke aanroep van de bestemming gecontroleerd in een ledger van zeven dagen.
- Eén lease per synchronisatie, met een levensduur van vijftien minuten.
- Vijf pogingen per run, met een backoff die begint bij zestig seconden.
- Een breaker die de synchronisatie pauzeert na vijf opeenvolgende fouten en vastlegt waarom.
- Een alert per uur bij vierentwintig uur zonder succes.
Niet gebouwd, en goed om te weten voordat je erop vertrouwt:
- De dead-man-alert bewaakt de workspace, niet de synchronisatie. Eén gezonde synchronisatie verbergt een stille buur in dezelfde workspace.
- Zijn alert bereikt de operators van Tajo: altijd een logregel, en Sentry, een webhook of een e-mail waar dat is ingesteld. Hij verschijnt niet op de pagina van de synchronisatie. De pauze van de breaker wel.
- Niets herstart een gepauzeerde synchronisatie. Dat doet een persoon.
- Herstel na een harde stop is niet snel. De lease kan vijftien minuten blijven staan en de opruimronde wacht een uur.
- Beide drempels, vijf fouten en vierentwintig uur, zijn instellingen van de deployment. Je kunt ze niet per synchronisatie instellen.
Een lange keten van bijna perfecte stappen faalt toch; de uitweg is de eindtoestand definiëren en erop controleren. Dit artikel is de operationele helft daarvan. Definieer wat een dode run achterlaat, en controleer op het niets dat stilte achterlaat.
De slotzin van Crusoe was dat “storingen onvermijdelijk zijn”. De onze gaat een stap verder. De crash was de makkelijke helft, omdat hij rijen achterliet. De stilte was de helft waar we naar moesten zoeken.
Veelgestelde vragen
Wat gebeurt er met een lopende synchronisatie als de worker midden in een batch uitvalt?
De cursor wijst nog steeds naar het begin van de onderbroken batch, omdat Tajo hem pas verplaatst als een batch zonder fouten is aangekomen. De volgende run leest dezelfde pagina opnieuw. Elk record heeft een leveringssleutel die is afgeleid van de paginacursor en zijn positie, en een ledger bewaart elke voltooide schrijfactie zeven dagen. Records die al geschreven zijn, worden daarom uit de ledger afgespeeld in plaats van een tweede keer naar de bestemming te gaan.
Waarom schrijft een herhaalde synchronisatie hetzelfde contact niet twee keer?
Voor elke aanroep van de bestemming controleert de run de leveringssleutel van het record in een ledger. De sleutel is een hash van de synchronisatie, de bron, de paginacursor, de positie op de pagina en het record zelf. Een crash, een nieuwe poging een minuut later en een geplande run de volgende dag leiden dus voor hetzelfde record dezelfde sleutel af. Een sleutel die binnen het venster van zeven dagen al als voltooid is gemarkeerd, wordt niet opnieuw verzonden.
Wat is een dead-man-alert voor een synchronisatie?
Een controle die afgaat bij afwezigheid in plaats van bij een fout. Eén keer per uur bekijkt Tajo elke workspace met een actieve synchronisatie en geeft een alert als er de afgelopen vierentwintig uur geen run is geslaagd, ook als er helemaal geen run is vastgelegd. De alert gaat één keer per incident af en wordt na de eerste geslaagde run opnieuw ingeschakeld. Hij bestaat omdat een foutalert alleen fouten kan zien die zijn vastgelegd.