När automatisk omstart gör mer skada än kraschen

En Raspberry Pi med AI-accelerator skulle räkna bilar som passerar. Efter några dagar började den bete sig obegripligt: Python segfaultade vid import, pakethanteraren kraschade, och till slut vägrade systemfiler som ingen hade rört att tolkas alls.

Segmentation fault
*** stack smashing detected ***
SyntaxError: unterminated triple-quoted string

Det sista felet är det som borde få håret att resa sig. En syntaxfel i en fil som ingår i operativsystemets egen Python-installation betyder att filen på disk inte längre är den som paketet en gång levererade.

Kraschen var inte problemet. Det som gjorde skada var reparationen.

Kedjan

Acceleratorns drivrutin är känslig för hur processer avslutas. Dör programmet på fel sätt lämnas trasigt tillstånd kvar i systemet — och nästa process som försöker prata med hårdvaran ärver eländet och kraschar den också.

Tjänsten var satt att starta om automatiskt vid fel. Utan fördröjning. Alltså:

  1. Programmet kraschar och lämnar trasigt tillstånd efter sig.
  2. Tjänstehanteraren startar det igen omedelbart.
  3. Det nya programmet möter samma trasiga tillstånd och kraschar direkt.
  4. Tillbaka till steg två — hundratals gånger i timmen.

Varje varv skrev minnesöverskrivningar över det som råkade ligga bredvid. Med tillräckligt många varv nådde skadan bibliotek och filer som inte hade med projektet att göra. Det som började som ett programfel slutade som ett skadat operativsystem.

Reparationen

En omstart av maskinen räckte inte, vilket är själva poängen: trasigt minne försvinner vid omstart, trasiga filer gör det inte.

Systemets Python-baspaket fick installeras om. En detalj som kostade tid: paketverktyget hade en hjälpkomponent som i sig kraschade på korruptionen och blockerade hela reparationen. Den fick flyttas undan tillfälligt, paketen installeras om, och komponenten sedan sättas tillbaka. Klassisk moment 22 — verktyget du behöver för att laga systemet är en del av det som är trasigt.

Fixen som betyder något

Tre rader i tjänstedefinitionen, och de förtjänar att förklaras var för sig:

  • Vänta en halv minut före omstart. Det ger drivrutinen tid att städa upp efter sig innan nästa process kommer och knackar på.
  • Ge upp efter ett par försök inom ett givet tidsfönster. En tjänst som misslyckas tre gånger på fem minuter kommer inte att lyckas på det fjärde. Den ska stanna nere och larma.
  • Starta om drivrutinen först när tjänsten väl ska igång igen, så att den börjar från ett känt tillstånd.

Det första man tänker på när något kraschar är hur man får igång det igen. Det man borde tänka på är hur man begränsar antalet försök.

De tre fällorna på vägen dit

Innan det ens gick att köra stabilt fanns tre problem som är värda att spara för den som håller på med samma sorts hårdvara.

Importordningen spelar roll. Acceleratorns bibliotek och bildbehandlingsbiblioteket drog in olika versioner av samma kryptoberoende. Importerades de i fel ordning blev resultatet segfault eller en odefinierad symbol — inte ett felmeddelande som pekar på orsaken. Lösningen var att alltid importera hårdvarubiblioteket först, i varenda fil.

Städa inte upp för hand efter ett bibliotek som gör det åt dig. Aktiveringen av nätverksgruppen returnerar ett objekt som ska hanteras med with. Anropar man avaktiveringen manuellt får man en segfault i stället för ett felmeddelande.

Modellens utdata var inte det jag antog. Nätverket hade efterbehandlingen inbyggd och returnerade en lista med en array per klass, med varierande antal rader. Att försöka göra det till en rektangulär matris fungerar ibland — nämligen precis så länge alla klasser råkar ha lika många träffar.

Så vet du att det funkar

Att tjänsten körde i tio minuter bevisar ingenting — det gjorde den innan också.

  1. Framkalla en krasch med flit och se vad som händer. Startar tjänsten om direkt, eller väntar den? Ger den upp efter några försök?
  2. Titta i loggen efter omstartsmönster, inte bara efter felmeddelanden. Många snabba omstarter är i sig ett larm, även om varje enskild ser oskyldig ut.
  3. Kontrollera systemets hälsa efteråt. Går pakethanteraren att köra? Går det att importera standardbiblioteken? Skadan visar sig ofta någon annanstans än där den orsakades.

Vad jag tar med mig

Automatisk omstart är en reparationsmetod som vilar på ett antagande: att felet var tillfälligt. När felet i stället lämnar skadat tillstånd efter sig blir omstarten inte en räddning utan en förstärkare — och ju snabbare den sker, desto större blir skadan.

Det gäller precis lika mycket när det är jag eller en agent som gör omstarten. Att svara på en krasch med att försöka igen är samma beteende som tjänstehanteraren har, bara långsammare. Frågan som är värd något är inte ”hur får jag igång den” utan ”varför dog den, och vad lämnade den efter sig”.

Och så det som gäller all hårdvara nära metallen: det finns inget sådant som en oskyldig krasch när koden pratar direkt med en accelerator. Fördröjning och en gräns för antalet försök borde vara standard från början, inte något man lägger till efter att ha installerat om operativsystemet.

Skrivet av Daniel Azzarri · Om · Alla inlägg