Wir bei codecentric haben hunderte von automatischen Builds die täglich laufen und.. manchmal auch fehlschlagen. Doch soll es hier nicht um faule Ausreden wie „ach das war doch nur ne kleine Änderung das sollte nicht schiefgehen“ gehen, sondern um die Fälle in denen der Build bricht weil.. nunja.. man weiß es halt nicht so richtig.
Klar sagt man schnell gern: Oh das muss kosmische Strahlung sein. Ich werfe den Build nochmal an. Aber wir wissen alle daß das nicht stimmen kann. Um wirklich effizient ein CI System nutzen zu können im Folgenden eine Liste von Ursachen von solch spontanen Buildfehlschlägen und wie man sie beheben kann.
- Ein Test führt Zeitberechnung durch, und sowohl im Testcode als auch im Produktionscode wird die Zeit genommen. Während das fast immer ohne erkennbare Unterschiede funktioniert, kann es ab und zu Abweichungen geben. Die Meldungen sehen dann merkwürdigerweise so aus: Time was 23:30:00 but expected 23:30:00 (die Milli- und Mikrosekunden werden von den üblichen toString Methoden nicht angezeigt.)
- Code, Testcode oder das CI lassen nach Ausführungen Dateien liegen. Oft handelt es sich um Logdateien, manchmal Ergebnisse von Tests. Dies kann Ergebnisse verfälschen, weshalb unbedingt diese Dateien zu entfernen sind. Auch sollte unbedingt Festplatten Monitoring eingesetzt werden, da auf Buildservern die Platten schnell sehr voll werden (Hudson bietet Optionen zur Überwachung und zum Aufräumen)
- Benutzer die sich auf dem CI Server einloggen können so versehentlich Ressourcen sperren, z.B. Dateien durch Öffnen, oder Ports durch manuelles Serverstarten. Oder sonstwas, womit keiner gerechnet hat. Deshalb sollte es auf Buildservern keine Benutzerlogins geben. Sämtliche Analysen sollten wenn überhaupt nur lesend erfolgen. Dennoch sollte den Entwicklern bewusst sein, daß je nach Tool selbst lesender Zugriff schon Schreibzugriff verhindert.
- Es handelt sich nicht zwingend um einen Bug im Code oder Test wenn das Systemdatum den Ersten Januar 1970 anzeigt. Natürlich könnte es ein Fehler sein, aber eventuell geht die Uhr des Buildservers auch einfach falsch. Buildserver sollten immer die aktuelle Uhrzeit verwenden. Dies kann ganz einfach mit einem ntp daemon sichergestellt werden. Falls ein Test eine bestimmte Uhrzeit testen möchte, so sollte nicht die Systemzeit verändert werden, sondern stattdessen der Code so umgebaut werden, daß er einen TimeProvider verwendet welcher im Test die gewünschte Uhrzeit liefert, sich ansonsten wie die normale Systemzeit verhält.
- Falls es doch schmutziger Tricks bedarf um gewisse Teile des Codes zu testen, so ist es in der Regel sicherer die Testausführung zu forken, dadurch können dann selbst umgesetzte Systemvariablen keinen Einfluss auf die Tests in anderen JVMs haben. Code Coverage Tools und andere Bytecodemanipulierer zählen mitunter auch als Hack. Geforkte Ausführung ist oft sicherer.
- Falls Ihr mehrere Buildserver habt, stellt sicher, daß diese sich so ähnlich wie nur möglich sind. Nur wenn man es sich wirklich leisten kann und will sollte man mehrere verschiedene Server mit fest definierten Unterschieden als Farm aufsetzen. So kann dann auf Systemspezifika getestet werden. Allerdings sollte es keine überraschenden Unterschiede geben.
- Man sollte in Erwägung ziehen das zu testende System für die nächtlichen Integrationstest (automatisch) komplett neu zu installieren. Diese Testserver werden mit der Zeit instabil, insbesondere dann wenn viele explorative Tests und ad-hoc Demonstrationen auf dem System gemacht werden. Komplette Neuinstallation klingt abschreckend, ist aber oft einfacher als gedacht. Allerdings dauert die Installation jede Nacht dann etwas mehr Zeit.
- Wenn man Software für automatische Tests deployed sollte man unbedingt zuerst den Server stoppen, die neue Software an die richtige Stelle kopieren und dann den Server starten. Jegliche Art von „hot deployment“ ist leider nicht stabil genug um verlässlichen Ergebnisse zu liefern.
- Nach jeglicher Art von Konfigurationsänderung oder Veränderungen an der erweiterten Testinfrastruktur sollte ein manueller Testlauf gestartet werden. Tut man dies nicht vermischt sich diese Änderung mit dem nächsten Entwicklercommit und dieser sucht dann vergebens in seinem Code nach der Änderung die den Test brach, obwohl es sich um die Konfiguration handelt.
- Falls sich Tests aufhängen oder blockieren, insbesondere wenn mehrere Tests zur gleichen Zeit (nicht zwingend in dem gleichen Prozess) laufen, so sollte man sich darüber freuen, einen Heap und Threaddump machen und erst dann die JVM neustarten. Es kann sein, daß die Tests zufällig eines dieser unheimlich schwer zu findenden Nebenläufigkeitsproblemen gefunden haben. Man sollte dafür dankbar sein, da man den Fehler sonst nie gefunden hätte. Leider wird zu oft einfach nur neugestartet.
Natürlich ist es möglich, daß weiterhin der Build bricht ohne daß die Software Fehler enthält. Aber genau dies verschwendet viel Zeit auf der Suche nach Phantomfehlern. Deshalb muss man die Schwachstellen seines Builds kennen und daran arbeiten diese zu beheben.
Wir hatten zum Beispiel ein Problem mit einer unserer Integrationtest Suiten. Die Tests verbinden sich mit einem externen Dienst, welcher manchmal nicht antwortet oder nur hängt. Bis vor kurzem brach dies unseren Build. Das Ergebnis war, daß jeder Entwickler sich den roten Build ansah, die Logs studierte und schlussendlich zur Feststellung gelangt, daß mal wieder der Dienst nicht verfügbar war. Nach einer Diskussion über dieses Problem beschlossen wir diese Zeitverschwendung nicht mehr zu machen. Also fügten wir eine kleine Mechanik in die Tests ein, welche die Timeouts erkennt und den Build nicht brechen lässt. Dafür markiert es den Test mit dem nichtkritischem Tag „timeouted“. Natürlich können wir keine Aussage über den Test treffen, aber er ist ebenso wenig Grün wie Rot. Da aber Rot das Anzeichen für die Entwickler ist tätig zu werden konnten wir ihn nicht Rot machen. Leider führt dies zu der Einschränkung, daß wirkliche Grüne Builds keine Tests mit dem Tag „timeouted“ haben dürfen. Im RobotFramework kann man einen dritten Zustand für „nichtkritische Fehlschläge“ definieren. Vielleicht ist dies ja auch eine Option für Euch?
Die wichtigste Botschaft ist: Wenn ein gebrochener Build nicht durch Test- oder Produktionscode verursacht wurde, so muss man unbedingt die Ursache finden und beheben. Man kann nicht einfach auf kosmische Strahlen verweisen, da sonst irgendwann jeder einfach nur „kosmische Strahlen“ sagt und der Build keine Aussagekraft mehr hat. Ein roter Build sollte immer bedeuten: Leute macht was!
Weitere Beiträge
von Fabian Lange
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Fabian Lange
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.