Papers we love: Crash-Only Software

 
15 maart 2017

Instabiele software is iets wat we allemaal kennen. Het blijft hangen, is ineens onverklaarbaar langzaam, geeft timouts of crasht. Het is op zijn best irritant, maar in het slechtste geval kost dit zelfs mensenlevens. Gelukkig is het meestal niet zo desastreus en is het resultaat vooral dataverlies of data-corruptie. Onze software schrijven we dan ook (meestal) op een dusdanige manier dat we data duurzaam opslaan in systemen die zijn ontworpen om met onverwachte omstandigheden om te gaan. Dit kan bijvoorbeeld door gebruik te maken van een database welke zelf met o.a. een transaction log werkt om duurzame schrijfactie te garanderen. Ook denken we na over de levensduur van onze applicatie en introduceren we mogelijkheden om software op een deterministische manier te starten en te stoppen.

Dit laatste kan echter alleen vanuit known states. Wat als er een NullPointerException optreed? Of een een deling door 0? Wat als we toch een free()‘d pointer dereferencen? Het zijn maar enkele veelvoorkomende voorbeelden, maar wel stuk voor stuk fouten die we allemaal wel een keer hebben gemaakt. De gemene deler is het resultaat van zo’n actie: de software crasht als we geluk hebben, of is in een ‘unknown state’ terecht gekomen — we kunnen geen garanties meer geven over het verdere verloop van de applicatie.

We zullen moeten recoveren, maar de manier hiervan verschilt per taal, per platform, per effect van de actie maar vooral ook van onze applicatie. Is het veilig om (een deel) opnieuw op te starten? Moet er iets naar disk worden weggeschreven? Moeten er locks worden vrijgegeven? Dit vereist hierdoor een intieme kennis en koppeling van onze “recovery logic” en de insides van ons systeem, iets wat we liever niet willen.

Omdat het compleet voorkomen van crashes onmogelijk is en de hierboven beschreven situaties ook op kunnen treden kunnen we ons ook afvragen of we het aantal fault states niet enorm kunnen reduceren. Wat als we alle mogelijke applicatie states reduceren naar de volgende ADT: data State = Healthy | Crashed? Dit is nu ook de centrale vraag van de paper van deze maand: Crash-Only Software. Ze beginnen met het uitgangspunt dat het voorkomen van crashen onmogelijk is. Daarnaast constateren ze van een paar bekende general purpose systemen dat de stop + start cyclus langer duurt dan de crash + start cyclus. Hieruit volgt dat je je af kunt vragen of je überhaupt een gewone afsluitprocedure moet hebben? Wat nu als je iedere foutieve state promoveert tot een crash en garandeerd dat de recovery hiervan (e.g. startup) wordt uitgevoerd en ook snel is? Hierdoor wordt het redeneren over de status van je systeem veel eenvoudiger en het voorkomt dat je heel veel tijd bent aan recovery scenarios. Het analyseren van foutieve states ontstaan door bugs is immers zeer arbeidsintensief.

De auteurs stellen voor om software componenten twee externe controls te geven: een “power off” switch en een “power on” switch. Door een stuk software op te knippen in kleine, op zichzelf staande elementen willen ze realiseren dat je per onderdeel deze switches kunt bedienen. Vraag jij aan een component of deze een bepaalde actie uitgevoerd kan worden en deze reageert niet binnen een verwacht tijdsbestek? Dan neem je aan dat deze kapot is en zet je hem “aan en uit”. Om dit te kunnen doen worden er wel enkele uitgangspunten gedefinieerd: a) er is sprake van seriele transacties tussen componenten, b) sessie data is maximaal enkele minuten oud en c) er is een persistent store buiten de “crashable” components die garandeerd dat de data duurzaam wordt weggeschreven.

Toegegeven, dit domein is relatief eenvoudig en leent zich dan ook erg voor deze opzet. Ze spreken over “Internet Systems” en hebben een prototype geschreven voor een J2EE webserver. De applicatie-architectuur van de software in kwestie heeft een standaard 3-tier architectuur [5] waarbij de de onderste laag dus de data verzorgd. Dit is, vanuit de “crash software” gezien, een service die geconsumeerd kan worden. Dit heeft mijns inziens ook wel weer wat weg van een microservices gedachte: iedereen heeft een duidelijk gedefinieerde bounded context en daarnaast is een systeem als geheel de compositie van de verschillende services. De werking van de applicatie zelf zoals deze wordt voorgesteld heeft dan weer wat weg van een smalltalk: componenten kunnen alleen met elkaar communiceren door middel van het versturen van berichten. Deze componenten zijn dan ook eigenlijk weer losse actoren a la een actor framework: ze zijn een eigen proces, hebben eigen (interne) state en kunnen daarnaast individueel geïnitieerd en getermineerd worden.

Een laatste verband kunnen we met Erlang [1], al dan niet met het OTP platform leggen. Individuele agents versturen en ontvangen messages, maar wanneer zij geen tijdige respons krijgen kan een watchdog ingrijpen door zijn subject af te schieten. Deze supervisor zal dus de levenscyclus van zijn child in de gaten houden, maar daar ook op reageren. Door op deze manier je software systeem te segmenteren kan je dus foutieve componenten individueel herstarten. Deze microreboots, zoals ze worden genoemd, zijn heel snel en maken het systeem als geheel zeer robuust. Wanneer er immers een fout optreedt resulteert dat in een complete herinitialisatie van het betreffende component, wat een correcte werking zou moeten garanderen. De paper sluit af met een korte sectie waarbij het hiervoor beschreven gedrag wordt gecombineerd met resource leases. Hierdoor worden resources als CPU-tijd, memory, maar ook (toegang tot) persistent data stores kortstondig geleend. Is de lease verlopen, dan verloopt het recht op het gebruiken van deze resource, waardoor het weer terugvalt aan het systeem. Exclusive locks zijn dan ook onmogelijk.

Al met al is het een paper die in zichzelf geen schokkende nieuwe dingen laat zien. Het zet je wel aan het denken over betrouwbaarheid. Waar haal je het vertrouwen in je software vandaan? Hoe kan je dit vergroten en kan je voorkomen dat je code heel complex en fragiel wordt wanneer het over datamanipulatie gaat. Moet je dit misschien lostrekken in individuele processen waar je gemakkelijker over kunt redeneren? Daarnaast zijn er een aantal parallellen getrokken met concepten die nu weer actueel zijn. Denk hierbij aan Akka, Vert.X, Quasar, Erlang/OTP en microservices. Het wordt weliswaar niet allemaal direct genoemd, maar de concepten zie je wel degelijk terug komen. In mijn ogen weer een prachtig voorbeeld over een wat oudere paper die letterlijk gelezen bijna irrelevant is, maar conceptueel nog zeer interessant en actueel is. Een aanrader!


Over deze papers we love: binnen Sogyo hebben we de traditie dat we maandelijks een nieuwsbrief rondsturen. De nieuwsbrief gaat veel over interne feitjes, echter sinds februari 2015 verzorgd Kevin van der Vlist het topic: papers we love. Hij deelt met ons papers die interessant zijn om te lezen en ons kunnen inspireren. De topic is uitgegroeid tot een mooie verzameling die we ook graag hier willen delen.

 


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Gave Technologie