---
title: "Workeren døde. Synkroniseringen gjorde ikke."
description: "Dræb workeren midt i en batch, og følg én kontakt: hvilken cursor der blev gemt, hvorfor den gentagne skrivning ikke lander to gange, og hvem der ejer synkroniseringen, mens den genstarter. Derefter de to hændelser, der viste, at genopretning kun var det halve arbejde."
url: https://tajo.io/dk/blog/worker-died-sync-didnt/
markdown: https://tajo.io/dk/blog/worker-died-sync-didnt.md
lang: da-DK
author: "Set Noa"
author_url: https://tajo.io/dk/set-noa/
published: 2026-11-05
updated: 2026-11-05
alternates: {en-US: /blog/worker-died-sync-didnt.md, bg-BG: /bg/blog/worker-died-sync-didnt.md, cs-CZ: /cz/blog/worker-died-sync-didnt.md, de-DE: /de/blog/worker-died-sync-didnt.md, es-ES: /es/blog/worker-died-sync-didnt.md, fr-FR: /fr/blog/worker-died-sync-didnt.md, el-GR: /gr/blog/worker-died-sync-didnt.md, hi-IN: /hi/blog/worker-died-sync-didnt.md, id-ID: /in/blog/worker-died-sync-didnt.md, hr-HR: /hr/blog/worker-died-sync-didnt.md, hu-HU: /hu/blog/worker-died-sync-didnt.md, it-IT: /it/blog/worker-died-sync-didnt.md, ja-JP: /jp/blog/worker-died-sync-didnt.md, ko-KR: /kr/blog/worker-died-sync-didnt.md, ar-SA: /ar/blog/worker-died-sync-didnt.md, nl-NL: /nl/blog/worker-died-sync-didnt.md, no-NO: /no/blog/worker-died-sync-didnt.md, pl-PL: /pl/blog/worker-died-sync-didnt.md, pt-BR: /pt/blog/worker-died-sync-didnt.md, ro-RO: /ro/blog/worker-died-sync-didnt.md, ru-RU: /ru/blog/worker-died-sync-didnt.md, sv-SE: /se/blog/worker-died-sync-didnt.md, zh-CN: /zh-cn/blog/worker-died-sync-didnt.md, sl-SI: /sl/blog/worker-died-sync-didnt.md, sr-Latn-RS: /sr/blog/worker-died-sync-didnt.md, th-TH: /th/blog/worker-died-sync-didnt.md, tr-TR: /tr/blog/worker-died-sync-didnt.md, ur-PK: /ur/blog/worker-died-sync-didnt.md, vi-VN: /vi/blog/worker-died-sync-didnt.md, sk-SK: /sk/blog/worker-died-sync-didnt.md}
---

[Datasynkronisering](https://tajo.io/dk/blog/?category=data-sync)

# Workeren døde. Synkroniseringen gjorde ikke.

Dræb workeren midt i en batch, og følg én kontakt: hvilken cursor der blev gemt, hvorfor den gentagne skrivning ikke lander to gange, og hvem der ejer synkroniseringen, mens den genstarter. Derefter de to hændelser, der viste, at genopretning kun var det halve arbejde.

genopretning efter fejl i synkronisering

Hvad sker der med en kørende synkronisering, når workeren dør midt i en batch?

Cursoren flytter sig først ved slutningen af en fejlfri batch. En worker, der dræbes midt i en batch, efterlader den altså ved starten af den batch, og den næste kørsel læser den samme side. Hver posts leveringsnøgle afledes af sidens cursor og postens position, og en ledger over fuldførte skrivninger i syv dage forhindrer, at gentagelsen lander to gange. Én lease pr. synkronisering forhindrer, at to kørsler arbejder samtidig. Det var den lette halvdel. Én synkronisering blev ved med at fejle, kørsel efter kørsel, før noget satte den på pause, og et workspace stod stille med fejl og uden succes, før noget opdagede det. Derfor er der nu en breaker, der sætter på pause efter fem fejl i træk, og en alarm hver time ved fireogtyve timer uden succes.

[Læs mere](https://tajo.io/dk/blog/worker-died-sync-didnt/#f%C3%B8lg-%C3%A9n-kontakt)

I en [talk, som AI Engineer offentliggjorde den 3. oktober 2026](https://www.youtube.com/watch?v=bRGyYaE0lxI&t=701s), startede en developer advocate fra Crusoe et PyTorch-træningsjob på to A100-noder. Derefter fortalte taleren demoens eget overvågningssystem, at én GPU havde kastet en XID 79-fejl. Noden blev markeret som nede, og jobbet blev annulleret. Processen fik et SIGTERM og »[op til to minutter til at håndtere det SIGTERM og enten gemme dine checkpoints eller skrive dine logs ud](https://www.youtube.com/watch?v=bRGyYaE0lxI&t=633s)«. En reservenode trådte til, og »[slurm-jobbet genstarter. Det indlæser checkpointet og fortsætter træningen, hvor det slap](https://www.youtube.com/watch?v=bRGyYaE0lxI&t=769s)«. Lidt under femten minutter fra start til slut. Ingen blev kaldt ud.

En synkronisering er et meget mindre dyr end en træningskørsel. Spørgsmålet er det samme. Hvad har en kørsel brug for, så det koster minutter og ingen dubletter at dræbe workeren?

Her er svaret for [Tajos egen synkroniseringsrunner](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/), læst ud af koden og ikke ud af en brochure. [Reverse ETL](https://tajo.io/dk/blog/what-is-reverse-etl/) beskriver jobbets form. Her handler det om, hvad der sker, når jobbet dør.

## Følg én kontakt

En kørsel læser kilden én side ad gangen, ud fra en gemt cursor. Kald sidens cursor `c3` og vores kontakt den 212. post på den side. Tre ting gælder for den kontakt, før noget bliver skrevet.

For det første har den en leveringsnøgle. Nøglen er en hash af synkroniseringens id, kilden, sidens cursor, positionen på siden og selve posten. Cursoren og posten indgår i hashen og indlejres aldrig i nøglen, så nøglen indeholder [ingen e-mailadresse](https://tajo.io/dk/resources/help/getting-started/understanding-data-sync/).

For det andet går skrivningen gennem en [ledger](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#ledger) og ikke direkte til destinationen. Før destinationen kaldes, reserveres nøglen som afventende. Når skrivningen lykkes, markeres rækken som fuldført og gemmes i syv dage.

For det tredje har cursoren ikke flyttet sig. Den flytter sig kun ved slutningen af en batch, kun hvis hver post i den batch er landet, og kun efter at kørslen har bevist, at den stadig ejer synkroniseringen.

Dræb nu workeren mellem skrivningen og checkpointet.

Ved et SIGTERM afslutter hver køworker sit arbejde inden for én fælles frist på femten sekunder, og processen afsluttes efter femogtyve sekunder, uanset hvad der stadig kører; systemd giver den femogfyrre, før den dræber. En kørsel, der ikke blev færdig inden for det vindue, står stadig som i gang, og den næste worker samler den op via køens genopretning af hængende job. Ved et hårdt kill bliver der slet ikke afsluttet noget. Uanset hvad gælder de samme to fakta. Destinationen har vores kontakt. Vores [cursor](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#cursor) står stadig på `c3`.

## Gentagelsen

Den næste kørsel starter ved `c3`, læser den samme side, afleder den samme nøgle for den 212. post og spørger ledgeren. Ledgeren siger fuldført. Destinationen kaldes ikke; det gemte resultat afspilles, og kørslen fortsætter. På bulk-vejen, hvor Brevos import-endpoint tager en hel side i ét kald, kører det samme tjek pr. nøgle, før forespørgslen bygges. En side, hvor alle nøgler allerede er fuldført, sender ingenting.

Det er hele tricket, og det er værd at sige, hvorfor det virker. Nøglen afledes af sidens cursor ved starten af batchen, ikke af den kørsel, der tilfældigvis var i gang. Et nedbrud, et nyt forsøg tres sekunder senere og en planlagt kørsel i morgen [afleder alle den samme nøgle](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#ledger) for den samme post. Ledgeren er ligeglad med, hvem af dem der spørger.

Nu prisen. [Ledgeren glemmer efter syv dage](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#ledger). Kodens egen kommentar siger, at syv dage dækker et forsinket nyt forsøg og et kort nedbrud, og mere påstår den ikke. En gentagelse fra en cursor, der er ældre end det, skriver igen. Og en kørsel, der afbrydes hårdt, kan komme til at vente. Den døde kørsels lease holder i op til femten minutter, og på en planlagt synkronisering bliver en kørsel, hvis lease er ledig, først [ryddet op og planlagt igen, når den startede for over en time siden](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#stuck-runs). Crusoes demo er [tilbage på under femten minutter](https://www.youtube.com/watch?v=bRGyYaE0lxI&t=753s). Det er vores ikke altid.

## Hvem ejer synkroniseringen, mens den genstarter

Én kørsel ejer en synkronisering ad gangen. Kørslen tager en [lease](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#lease) i Redis med sit eget token, en levetid på femten minutter og en fornyelse hvert femte minut. Før hvert checkpoint bekræfter den, at den stadig har den lease. Hvis ikke, stopper den. Kørslen registreres med den [enkle sætning, som personen vil læse](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#failures): »The run lost its slot part-way through and stopped to avoid writing twice.« (Kørslen mistede sin plads undervejs og stoppede for ikke at skrive to gange.) En anden kørsel, der støder ind i en aktiv ejer, forsøges ikke igen. Den [slutter med](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#failures) »Another run of this sync was already in progress« (En anden kørsel af denne synkronisering var allerede i gang), og den næste planlagte kørsel starter normalt.

Ejerskabet af rettelsen er også skrevet ned. Hver [fejlkode](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#failures) peger på én sætning om, hvad der skete, én om, hvad der skal gøres nu, og en ejer: dig eller Tajo. En destination, der afviste hver post, fordi mappingen er forkert, er din. [En mistet lease er vores](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#failures). Kørslens side viser det næste skridt; [guiden til genopretning af synkroniseringer](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#failures) viser hvilken.

Indtil nu er dette den lette halvdel. Et nedbrud koster én gentagelse, og intet lander to gange. To hændelser lærte os den anden halvdel.

## Fejl efter fejl

Én synkronisering fejlede igen og igen. Ikke i en tæt løkke, men én gang pr. scheduler-interval, kørsel efter kørsel, med den samme besked hver gang: at et brugerdefineret id ikke må indeholde et kolon. Kørslen nåede aldrig destinationen. Den døde, mens den blev sat i kø, før nogen runner så den, så en alarm, der var knyttet til runneren, aldrig havde noget at reagere på.

Intet holdt øje med fejlserien. Det er hele konklusionen.

Løsningen er en breaker. Efter hver registreret fejl læser den synkroniseringens seneste fem afsluttede kørsler, siden synkroniseringen sidst blev ændret. Hvis alle fem er fejl, sætter den synkroniseringen på pause, skriver én auditrække med årsagen `circuit_breaker_tripped` og udløser én kritisk alarm. Fejlserien afledes af de gemte kørsler, ikke af en tæller i workeren. En fejl tæller, uanset hvilken vej den er registreret, også den fejl ved køsætningen, der forårsagede hændelsen. Ifølge kodens kommentar ville den have stoppet den hændelse ved kørsel fem. Én succes nulstiller serien. Når pausen ophæves, gøres breakeren klar igen i stedet for at blive udløst på ny af de gamle rækker. [Guiden til Shopify-connectoren](https://tajo.io/dk/docs/ecommerce-platforms/shopify/) siger det samme på én linje: efter fem mislykkede kørsler i træk sætter synkroniseringen sig selv på pause, og dens side viser den seneste fejl.

Hvad breakeren ikke gør, er at reparere noget. Crusoe udskifter noden og genstarter jobbet uden at spørge. Tajo stopper og spørger: en synkronisering på pause forbliver på pause, indtil en person retter årsagen og [udgiver den igen](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#pause). Den forsigtige standard er bevidst: et automatisk nyt forsøg på en synkronisering, der skriver til kundeposter, er i sig selv en skrivning.

## Stilheden, som ingen så

Indvendingen er rimelig, og den lyder sådan: »En breaker, der sætter på pause efter fem fejl, ville også have fanget den anden hændelse. Hvorfor bygge endnu en vagt?« Fordi den anden hændelse ikke lignede en fejl. I lang tid havde et workspace en aktiv synkronisering, en voksende bunke fejlrækker og ikke én eneste succes, nogensinde, og intet slog alarm. Alle eksisterende signaler var knyttet til aktivitet: en kørsel fejlede, en webhook ankom, et budget blev overskredet. Nul succeser er ikke en hændelse. Det er fraværet af en, og en breaker kører kun, når en fejl registreres. En scheduler, der er død, en kø, der sidder fast, en worker, der aldrig starter op: de registrerer intet, og en breaker vågner aldrig.

Et lokomotivs dødmandsknap virker omvendt. Lokomotivføreren skal blive ved med at trykke på den, og stilhed stopper toget. Synkroniseringsudgaven [kører én gang i timen](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#hourly-check), tretten minutter over hel. For hvert workspace med en aktiv synkronisering stiller den ét spørgsmål: er nogen kørsel lykkedes inden for de seneste fireogtyve timer? Et workspace, hvis ældste aktive synkronisering blev slået til for under et døgn siden, får lov at være; det har ikke haft et døgn til at lykkes endnu. Et workspace, der er ældre og tavst, får én kritisk alarm. Alarmen siger, hvilken stilhed det er: kørsler registreret, men ingen lykkedes, eller slet ingen kørsler registreret. Den udløses én gang pr. hændelse. En markør i Redis holder den lukket indtil den første vellykkede kørsel og ryddes derefter, så den næste hændelse giver alarm igen.

Begge vagter kom i ét commit, bagefter. Breakerens kommentar siger, at den ville have stoppet den første hændelse ved kørsel fem. Dødmandsalarmens kommentar siger, at en synkronisering, der blev slået til for længe siden og har nul kørsler, skal give alarm. Ingen af dem fandtes, da der var brug for dem. Vi skrev dem bagefter, med begge hændelser noteret i filens header, så ingen glemmer hvorfor.

## Hvad er bygget, og hvad er ikke

Bygget i skrivende stund:

- Cursoren gemmes ved slutningen af hver fejlfri batch og igen ved et nedbrud.
- Hver posts leveringsnøgle afledes af sidens cursor og postens position og tjekkes mod en [ledger på syv dage](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#ledger) før hvert kald til destinationen.
- Én [lease](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#lease) pr. synkronisering med en levetid på femten minutter.
- Fem [forsøg](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#retries) pr. kørsel med backoff fra tres sekunder.
- En [breaker](https://tajo.io/dk/resources/help/getting-started/understanding-data-sync/), der sætter synkroniseringen på pause efter fem fejl i træk og registrerer hvorfor.
- En [alarm hver time](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#hourly-check) ved fireogtyve timer uden succes.

Ikke bygget, og værd at vide, før du stoler på det:

- Dødmandsalarmen overvåger workspacet, ikke synkroniseringen. Én sund synkronisering skjuler en tavs søskende i samme workspace.
- Dens alarm [når Tajos driftsfolk](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#hourly-check): altid en loglinje og Sentry, en webhook eller en e-mail, hvor det er konfigureret. Den vises ikke på synkroniseringens side. Det gør breakerens pause.
- Intet genstarter en synkronisering på pause. Det gør en person.
- Genopretning efter et hårdt kill er ikke hurtig. Leasen kan holde i femten minutter, og [oprydningen venter en time](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#stuck-runs).
- Begge tærskler, fem fejl og fireogtyve timer, er [indstillinger for deploymentet](https://tajo.io/dk/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#limits). Du kan ikke sætte dem pr. synkronisering.

[En lang kæde af næsten perfekte trin fejler stadig](https://tajo.io/dk/blog/almost-perfect-steps-unreliable-chain/); vejen ud er at definere sluttilstanden og tjekke for den. Denne artikel er den operationelle halvdel af det. Definer, hvad en død kørsel efterlader, og tjek for det ingenting, som stilhed efterlader.

Crusoes afsluttende linje var, at »[fejl er uundgåelige](https://www.youtube.com/watch?v=bRGyYaE0lxI&t=955s)«. Vores går et skridt videre. Nedbruddet var den lette halvdel, fordi det efterlod rækker. Stilheden var den halvdel, vi måtte lede efter.

## Ofte Stillede Spørgsmål

**Hvad sker der med en kørende synkronisering, når workeren dør midt i en batch?**

Cursoren peger stadig på starten af den afbrudte batch, fordi Tajo først flytter den, når en batch er landet uden fejl. Den næste kørsel læser den samme side igen. Hver post har en leveringsnøgle, der er afledt af sidens cursor og postens position, og en ledger gemmer hver fuldført skrivning i syv dage. Poster, der allerede er skrevet, afspilles derfor fra ledgeren i stedet for at blive sendt til destinationen en gang til.

**Hvorfor skriver en gentaget synkronisering ikke den samme kontakt to gange?**

Før hvert kald til destinationen tjekker kørslen postens leveringsnøgle mod en ledger. Nøglen er en hash af synkroniseringen, kilden, sidens cursor, positionen på siden og selve posten. Et nedbrud, et nyt forsøg et minut senere og en planlagt kørsel dagen efter afleder derfor den samme nøgle for den samme post. En nøgle, der allerede er markeret som fuldført inden for vinduet på syv dage, sendes ikke igen.

**Hvad er en dødmandsalarm for en synkronisering?**

Et tjek, der udløses af fravær i stedet for af fejl. Én gang i timen ser Tajo på hvert workspace med en aktiv synkronisering og slår alarm, hvis ingen kørsel er lykkedes inden for de seneste fireogtyve timer, også når der slet ikke er registreret nogen kørsel. Alarmen udløses én gang pr. hændelse og gøres klar igen efter den første vellykkede kørsel. Den findes, fordi en fejlalarm kun kan se fejl, der er blevet registreret.

## Relaterede artikler

### [Seks regler for en synkronisering, der kører, mens ingen kigger](https://tajo.io/dk/blog/six-rules-unattended-sync/)

6\. nov. 2026 · 8 min læsetid

7 afsnit

### [Arbejdsgangen mellem boksene](https://tajo.io/dk/blog/the-workflow-between-the-boxes/)

8\. okt. 2026 · 4 min læsetid

Butikspost → Medarbejderens rettelse → E-maildestination

### [Brevo-integrationer: vælg en forbindelse, du kan verificere](https://tajo.io/dk/blog/brevo-connector-guide/)

19\. aug. 2026 · 3 min læsetid

6 afsnit
