1 Punkte von GN⁺ 2024-02-14 | 1 Kommentare | Auf WhatsApp teilen
  • Antithesis will deterministisches autonomes Testen, das sich bei FoundationDB bewährt hat, auf allgemeine Software anwenden und schwer reproduzierbare Bugs in verteilten Systemen in wiederholbare Probleme verwandeln
  • FoundationDB entwickelte vor der eigentlichen Datenbankimplementierung eine Single-Thread-/Single-Process-Simulation und konnte seltene Fehlersituationen mit demselben Random Seed erneut ausführen
  • Dieser Ansatz machte nichtdeterministische Fehler wie Nebenläufigkeit, Netzwerkverzögerungen und -umordnungen, Festplattenprobleme und Maschinenausfälle testbar; bei FoundationDB soll es über den gesamten Zeitraum nur 1–2 von Kunden gemeldete Bugs gegeben haben
  • Antithesis entwickelte einen Hypervisor, der einen deterministischen Computer emuliert, damit bestehende Software nicht von Grund auf neu geschrieben werden muss; derzeit liegt der Fokus auf Zuverlässigkeits- und Fehlertoleranztests für verteilte Systeme
  • Antithesis hat mit MongoDB, der Ethereum Foundation und Palantir zusammengearbeitet und sich von einem Tool zum Finden seltener Bugs zu einem permanent laufenden Testservice entwickelt, der aktuelle Builds kontinuierlich validiert

Antithesis, entstanden aus den Erfahrungen mit FoundationDB

  • Antithesis wurde mehr als fünf Jahre lang im Stealth-Modus entwickelt und stellte dann eine Plattform vor, die auf den bei FoundationDB gewonnenen Erfahrungen mit deterministischem Testen basiert
  • Vor der Veröffentlichung arbeitete das Unternehmen an Recruiting, ersten Kunden und Investoren; der zuerst sichtbar gemachte Kern war eine Methode, komplexe Systeme wiederholbar zu testen

Das schwierigste Verifikationsproblem bei verteilten Datenbanken

  • FoundationDB begann 2010 damit, eine skalierbare und fehlertolerante verteilte Datenbank mit ACID-Transaktionen zu bauen
  • Damals war Spanner noch nicht veröffentlicht, und viele missverstanden das CAP-Theorem so, als seien starke Konsistenz und hohe Verfügbarkeit grundsätzlich nicht miteinander vereinbar
  • Die größte Schwierigkeit war weniger die Datenbank selbst als vielmehr die Frage, wie man ein solches System testet und Vertrauen in seine Korrektheit gewinnt

Die „unknown unknowns“, die herkömmliche Tests übersehen

  • Software muss auch Situationen bewältigen, an die Entwickler vorher nicht gedacht haben; klassische Tests sind jedoch vor allem stark darin, bereits erwartete Fälle zu prüfen
  • Wenn man einen bestimmten Fall so weit vorhergesehen hat, dass man ihn als Test formulieren kann, ist es wahrscheinlich, dass auch der Code für den Umgang mit dieser Situation geschrieben wurde
  • Deshalb sind klassische Tests für Regressionsschutz nützlich, aber schwach darin, unerwartete Fehler zu finden, die durch echte Nutzer und Betriebsumgebungen entstehen
  • In verteilten Storage-Systemen wird dieses Problem noch größer
    • Es gibt gleichzeitig Nebenläufigkeit innerhalb einzelner Maschinen und zwischen Maschinen
    • Netzwerke können Verzögerungen und Paketumordnungen verursachen
    • Festplatten, Maschinenausfälle, Stromausfälle, Brände in Rechenzentren und menschliche Fehler erweitern die Menge möglicher Fehlerursachen
    • Wenn ein kritischer Bug von der Reihenfolge von Ereignissen über mehrere Maschinen hinweg abhängt, ist er selbst nach dem ersten Fund schwer erneut zu reproduzieren

Die deterministische Simulation von FoundationDB

  • Das FoundationDB-Team baute noch vor der Datenbank eine vollständig deterministische, ereignisbasierte Netzwerksimulation
  • Der gesamte Cluster wurde innerhalb einer Single-Thread-/Single-Process-Anwendung simuliert, und die Ausführung wurde über denselben Zufallszahlengenerator gesteuert
  • Im virtuellen Cluster konnten Netzwerkfehler injiziert, Maschinen beendet und verschiedene anomale Situationen wiederholt erzeugt werden
  • Wenn eine bestimmte Ausführung einen Bug in der Anwendungslogik fand, konnte dieselbe Ereignisreihenfolge mit demselben Random Seed erneut ausgeführt werden
  • Dadurch ließen sich auch extrem seltene Bugs nachverfolgen, indem man Logs hinzufügte oder Debugging-Schritte wiederholte
  • Ein zugehöriger Vortrag fand 2014 auf der Strangeloop statt; das Video ist hier verfügbar

Wie Tests die Entwicklungsgeschwindigkeit veränderten

  • Bei FoundationDB soll es in der gesamten Unternehmensgeschichte nur 1–2 von Kunden gemeldete Bugs gegeben haben
  • Kyle Kingsbury, auch bekannt als „aphyr“, testete FoundationDB nicht mit Jepsen, weil er davon ausging, nichts finden zu können
  • Als die Tests neue Bugs unmittelbar sichtbar machten, veränderte sich auch die Programmierweise des Teams
  • Compiler und starke Typsysteme geben Vertrauen gegenüber bestimmten Bugklassen, sind aber nicht dasselbe wie das Ausführen realer Software in Tausenden unerwarteter Situationen
  • Auf Basis dieses Vertrauens wagte das FoundationDB-Team große Änderungen
    • Es entfernte alle Abhängigkeiten einschließlich Zookeeper und schrieb in kurzer Zeit eine eigene Paxos-Implementierung, die im FoundationDB-Paper enthalten ist
    • Es schrieb das gesamte Subsystem zur Transaktionsverarbeitung neu, damit es schneller und besser skalierbar wurde
  • Der größte Effekt war nicht nur eine höhere Datenbankstabilität, sondern dass ein kleines Engineering-Team die Produktivität eines 50-mal größeren Teams erreichte

Die Lücke nach der Übernahme durch Apple

  • Apple übernahm FoundationDB 2015 und nutzte es als Grundlage für seine „cloud infrastructure“
  • Einige Jahre später wurde FoundationDB als Open Source veröffentlicht
  • Auch nachdem sich Mitglieder des FoundationDB-Teams auf andere große Tech-Unternehmen verteilt hatten, gab es in diesen Organisationen keine deterministischen Simulationstests nach FoundationDB-Art
  • Änderungen an Backend-Systemen wurden langsam umgesetzt, weil unbeabsichtigte Systemauswirkungen schwer vorherzusagen waren, und Diagnose sowie Behebung von Produktions-Bugs verschlangen monatelang Zeit erfahrener Engineers
  • 2018 gründeten sie zusammen mit Dave Scherer Antithesis; Ziel war es, deterministisches autonomes Testen nach FoundationDB-Art auch anderen Teams zugänglich zu machen

Wie man bestehende Software deterministisch macht

  • FoundationDB war ein Greenfield-Projekt, das von Anfang an für diese Testmethode konzipiert war, und Abhängigkeiten konnten entfernt werden
  • Herkömmliche Software erzeugt Threads, fragt die Zeit ab, fordert Zufallswerte vom Kernel an und kommuniziert über das Netzwerk mit anderer Software
  • Eine Entwicklungsmethodik, bei der sämtliche Software von Grund auf neu geschrieben werden muss, lässt sich kaum breit einsetzen; daher schrieb Antithesis einen Hypervisor, der einen deterministischen Computer emuliert
  • Dadurch kann Software, die innerhalb des Hypervisors läuft, in eine deterministische Ausführungsumgebung versetzt werden
  • Dazu gehört auch Arbeit an Low-Level-Verhalten wie den extended page tables von Intel-CPUs
  • Das Finden von Eigenschaftsverletzungen im Zustandsraum beliebiger Programme ist schwieriger als das Halteproblem, und selbst mit einem Halteorakel für alle Programme kann es nicht berechenbare Testeigenschaften geben

Aktuelle Plattform und Kundenbeispiele

  • Die Antithesis-Plattform soll die Software der Nutzer entgegennehmen, Bugs finden und gefundene Bugs jederzeit reproduzierbar machen
  • Auch in komplexen Fällen, in denen mehrere Services über das Netzwerk kommunizieren, soll die Reproduzierbarkeit erhalten bleiben
  • Nachdem ein Bug gefunden wurde, können leistungsfähige Debugging-Funktionen angewendet werden
  • Langfristig ist die Plattform darauf ausgelegt, viele Arten von Bugs in unterschiedlicher Software zu finden; derzeit konzentriert sie sich jedoch auf den Bereich, in dem bereits Erfahrung besteht: Zuverlässigkeits- und Fehlertoleranztests für verteilte Systeme
  • In den vergangenen Jahren arbeitete Antithesis mit Engineering-Teams zusammen, die große, komplexe Systeme betreiben, bei denen Zuverlässigkeit entscheidend ist
    • Mit MongoDB arbeitete Antithesis mehrere Jahre zusammen und half beim Testen der core server software und der WiredTiger storage engine
    • Mit der Ethereum Foundation arbeitete Antithesis etwa ein Jahr vor dem Merge zusammen und half beim Testen des Merge; die Zusammenarbeit läuft weiterhin
    • Auch mit Palantir besteht eine Zusammenarbeit

Vom Tool für seltene Bugs zum permanenten Testservice

  • Frühe Kunden nutzten Antithesis wie ein Spezialwerkzeug zum Finden und Reproduzieren der am schwersten aufzuspürenden und riskantesten Bugs
  • Als die Plattform reifer und interaktiver wurde, entwickelte sie sich zu einem permanent laufenden Service, der aktuelle Builds kontinuierlich testet
  • Ziel ist es, die Zeit zwischen dem Einführen eines Bugs und seiner Entdeckung zu verkürzen
  • Während der Entwicklung von FoundationDB machte diese Methode Diagnose und Behebung von Bugs deutlich einfacher und steigerte Effizienz sowie Softwarequalität
  • Antithesis möchte mit Organisationen sprechen, die verteilte Systeme betreiben und Wert auf Zuverlässigkeit und Engineering-Produktivität legen
  • Wer an schwierigen Problemen arbeiten möchte, wird auf die Stellenangebote verwiesen

1 Kommentare

 
GN⁺ 2024-02-14
Hacker-News-Kommentare
  • Der Ausdruck „legendärer 10x-Entwickler“ wirkt inzwischen so verzerrt, als meine er jemanden, der 15 Stunden am Tag an 6,5 Tagen pro Woche arbeitet und dann ausbrennt.
    Echte 10x- oder sogar 50x-Produktivität entsteht bei Menschen, die etwas umsetzen, das fast niemand für möglich hält oder überhaupt versteht, und dadurch funktionierende Software in deutlich kürzerer Zeit möglich machen.

    • Wenn die Branche solches 10x-Verhalten häufiger anerkannt und belohnt hätte, gäbe es davon vermutlich viel mehr.
      Manager schenken allzu oft der Person mehr Beachtung, die 8 Stunden Arbeit in 12 Stunden erledigt, als der Person, die dieselbe Arbeit in 8 Stunden schafft.
      Außerdem werden Versuche, von der „Norm“ abzuweichen, nicht gern gesehen, und im Zeitplan ist keine Zeit für Prozessverbesserungen vorgesehen. In einer Situation, in der man glaubt, man müsse die Eimer einfach schneller tragen, wird der Bau einer Schubkarre unterdrückt.
    • An der Uni habe ich ein paar Leute gesehen, die viel schneller programmieren konnten als ich; meistens hatten sie nicht wie ich mit 21 angefangen, sondern seit ihrem 12. Lebensjahr programmiert.
      Deshalb gibt es 10x-Engineers. Mit 30 haben sie nicht 10, sondern ungefähr 20 Jahre Programmiererfahrung.
      Auch beruflich haben sie viel mehr Erfahrung. Mit 15 fängt es vielleicht mit seltsamen kleinen Aufträgen an, bei denen man gelegentlich Verwandten hilft, aber mit etwa 18 arbeitet man in einer professionellen Firma parallel zum Informatikstudium.
      Zumindest war das früher so. Von 2004 bis etwa 2018 war das Realität, aber ich weiß nicht, ob es im heutigen Hiring-Umfeld noch möglich ist.
      Damit 10x-Engineers existieren, reichen ein paar Beispiele. Dass sie selten sind, darin scheinen sich die meisten im Großen und Ganzen einig zu sein; als öffentlich sichtbares Beispiel für einen 10x-Engineer könnte man diese Person nennen. Er selbst würde das sicher nie so sagen, aber meiner Einschätzung nach ist er ein 10x-Engineer: https://bellard.org/
      Wenn du nicht zustimmst, würde mich interessieren, worin du den Unterschied siehst. Ich bin nur jemand, der wie bei den blinden Männern und dem Elefanten nur einen Teil ertastet, und ich behaupte nicht, das ganze Bild zu sehen.
    • Ich bin die Diskussion über 10x-Engineers inzwischen leid und will einfach ein guter 1x-Engineer sein. Oder überhaupt irgendetwas im Leben gut können.
    • Auch diese Definition ist vage. Am Ende muss irgendjemand die Arbeit mit den eigenen Händen erledigen.
      Ein Ein-Mann-Armee-Entwickler, der alles allein schafft, passt nicht gut in Teams, in denen Arbeit standardisiert, kleinteilig zerlegt und verteilt wird.
      Solche Leute passen am besten zu eigenen Projekten ohne störende Kollegen oder Manager, aber die meisten Arbeitsplätze sind nicht so.
      Sobald man Teil eines Teams ist, kann man, egal wie herausragend man ist, allein nicht zu viel übernehmen; am Ende wird man dadurch gebremst, dass man Probleme beheben muss, die langsamere oder schwächere Teammitglieder verursacht haben, oder Managementprobleme ausbaden muss. Deshalb bewegt sich ein Team selbst mit einem Rockstar im Tempo des kleinsten gemeinsamen Nenners.
    • Einer der großen Multiplikatoreffekte in unserem Team besteht darin, das Bedürfnis hinter einer angeforderten Implementierung zu verstehen und alternative Lösungen vorzuschlagen, sodass Codeänderungen minimiert oder ganz vermieden werden.
      Es hilft auch, dass wir interne Tools bauen und sehr nah an Prozessen und Stakeholdern sind.
      „Hm, es gibt einen anderen Weg, das zu erreichen“ ist 10x — nicht einfach, etwas schneller zu machen.
  • Das ist vielleicht der beste Einführungstext, den ich je gelesen habe.
    Er legt gut die Grundlage dafür, wer die Leute sind und was sie gebaut haben, und erklärt, dass das, was sie jetzt bauen, eine Folge dessen ist, was sie zuvor gebaut haben.
    Man bekommt das Gefühl, dass sie dieses Problem für alle lösen wollen, weil sie selbst bereits erlebt haben, wie gut die Lösung ist.
    Danach zeigen sie auch Teams, die es bereits genutzt haben, darunter ziemlich große Namen mit komplexen Systemen.
    All das ist in einen guten Text verpackt, der bei Entwicklern und Gründern gut ankommt, und auch die Landingpage ist hervorragend.

    • Es wirkt eher wie Marketingtext als wie ein technischer Blogbeitrag.
      Ich hätte gern echte Anwendungsfälle und Beispiele gesehen.
      Stattdessen werden ein paar Namen großer Unternehmen aufgezählt, es wird behauptet, es sei ein innovatives Produkt, das wie Magie funktioniert, und dann kommen typische Buzzwords wie „10x-Programmierer“ und „Stealth Mode“ dazu. Wenn man Kundennamen veröffentlicht, passt „Stealth Mode“ nicht zusammen.
    • Es wird nicht erklärt, was es tatsächlich tut. Ich weiß nicht, ob es Fuzzing ist, ob man Testfälle selbst einspeist oder ob es Nichtdeterminismus in Hardware testet.
    • Das gesamte beschriebene Testsystem vermittelt mir das Gefühl, dass ich so etwas ebenfalls anstreben könnte.
      Es bietet eine Lebensweise, Denkweise und Arbeitsweise, die ich bisher nicht erlebt habe, und macht einen deshalb neugierig auf diese Lösung.
    • Ich frage mich, ob ich einen anderen Text gelesen habe.
      Der verlinkte Beitrag besteht zu drei Vierteln aus Geschichte und Begründungen, bevor er überhaupt sagt, was tatsächlich gebaut wurde.
      Es ist wie bei einem nervigen Rezeptblog, in dem man vegane Pfannkuchen machen will, aber erst die Kindheitsgeschichte des Autors ausgebreitet bekommt.
  • Ein starker Pitch, und ich will nicht negativ wirken, aber Formulierungen wie „alle Bugs gefunden“ können sich meiner Meinung nach nur dann als wahr erweisen, wenn man die Definition von Bug sehr eng fasst.
    Die bösartigsten und am schwersten zu findenden Bugs, die ich bisher erlebt habe, lagen weniger darin, dass ein Fehlerzustand erreicht wurde, sondern rund um die Business-Logik der Anwendung.
    Zum Beispiel: In der Datenbank ist eine abgeschlossene Transaktion eines Kunden verzeichnet, aber es gibt keinen abgeschlossenen gekauften Artikel – wie soll das auf der Seite mit den jüngsten Transaktionen des Kunden angezeigt werden?
    In solchen Fällen ist es ein großer Unterschied, ob man „irgendetwas wird angezeigt und es crasht nicht“ implementiert oder ob man über den gesamten Stack hinweg sicherstellt, dass die Entscheidung im Kontext der anderen Entscheidungen tatsächlich sinnvoll ist.
    Bei Datenbanken gibt es auch Probleme wie: „Der Query Planner erstellt in diesem Grenzfall einen sehr ineffizienten Plan.“
    Solche Dinge lassen sich nicht automatisch erkennen. Denn es geht nicht darum, dass das Programm einen Fehlerzustand erreicht, sondern darum, überhaupt herauszufinden, was in der Anwendung „korrekt“ bedeutet.
    Vielleicht setze ich die Messlatte für Bugs zu hoch, aber sich 0 Bugs vorzustellen ist etwas anderes, als Software in der realen Welt zu bauen. 0 Laufzeitfehler würde ich allerdings gelten lassen.

    • Ich denke, es war ein Fehler, mit dem Wort „all“ den Eindruck zu erwecken, FoundationDB habe absolut keine Bugs.
      Trotzdem ist FoundationDB wirklich dafür bekannt, den Stand der Technik bei Testpraktiken vorangetrieben zu haben: https://apple.github.io/foundationdb/testing.html
      Normalerweise würde das nach Arroganz oder Selbstüberschätzung riechen, aber hier ist man tatsächlich sehr nah an 0 Bugs herangekommen.
    • „Alle Bugs“ bedeutet hier eher, dass ein wahnsinnig gnadenloses deterministisches Testsystem Hunderttausende Male ausgeführt wurde und danach keine weiteren Bugs mehr gefunden hat.
      Natürlich kann man eine negative Aussage nicht beweisen, aber die Tatsache, diesen „alles grün“-Zustand erreicht zu haben, gab großes Vertrauen, dass man auf einem soliden Fundament aufbaut – und mit der Zeit zeigte sich, dass das tatsächlich so war.
    • Ein Bug ist meiner Ansicht nach „etwas, das etwas tun sollte und dabei gescheitert ist“.
      Probleme rund um Business-Logik sind kein Versagen des Systems; das System hat gemäß Spezifikation funktioniert, und die Spezifikation war nicht umfassend genug – also muss man nun iterativ verbessern.
    • Die beste Bug-Definition, die ich gehört habe, lautet: „Software, die sich nicht so verhält, wie dokumentiert.“
      Natürlich fehlt vielen Softwareprojekten Dokumentation, und das ist ein Dokumentations-Bug.
      Trotzdem finde ich diese Definition gut, weil sie einen auch bei unvollständiger Dokumentation zwingt zu fragen: „Wollen wir dieses Verhalten wirklich dokumentieren, oder wollen wir das Verhalten ändern und dann dokumentieren?“
      Zumindest mir macht sie es schwerer, merkwürdiges Verhalten einfach unter den Teppich zu kehren.
    • Fairerweise lautet der direkt nächste Satz: „Ich weiß, das klingt absurd.“
  • Seit ich durch den sled-Simulationsleitfaden https://sled.rs/simulation.html auf dieses Gebiet gestoßen bin, interessiert es mich enorm. Der Artikel zeigt grob, wie FoundationDB dabei vorgeht.
    Derzeit schreibe ich bei uns im Job daran, einen ähnlichen Testansatz einzuführen, indem unser Service so gebaut wird, dass er auf madsim https://github.com/madsim-rs/madsim?tab=readme-ov-file#madsim läuft.
    So kann man weiterhin Services im async/await-Stil mit tokio schreiben, sie in Tests aber durch einen deterministischen Executor ersetzen, der alle Quellen von Nichtdeterminismus patcht – einschließlich Abhängigkeiten, die das Betriebssystem aufrufen. Das funktioniert ziemlich nahtlos.
    Dass der Autor dieses Artikels sagt, die Anfangskosten seien enorm, ist keine Übertreibung. Es steckt viel Engineering-Aufwand darin, jede mögliche Quelle von Nichtdeterminismus zu behandeln und Services testbar und in einer sans-IO-Form https://sans-io.readthedocs.io/ neu zu schreiben.
    Aber wenn das System erst einmal steht, ist das Vertrauen, das man in den Code bekommt, schwer in Worte zu fassen. Kombiniert mit Tools wie quickcheck https://github.com/BurntSushi/quickcheck?tab=readme-ov-file#quickcheck kann man Hunderttausende subtile Fehlerszenarien testen: Ein-/Ausgabe, Reihenfolge von Events, Timeouts, Paketverluste, Dateisystemausfälle und mehr.
    Solche Tests sind ein extrem mächtiges Werkzeug im Werkzeugkasten, wenn man die Geduld und Ausdauer für die Investition mitbringt.
    Antithesis selbst sieht ebenfalls sehr cool aus. Deterministisches Testen bis unter die Betriebssystemebene zu ziehen, ist beeindruckend, und es dürfte ermöglichen, ganze Systeme zu testen, ohne jedes Mal manuell ein Harness zusammenzuflechten. Ich möchte es schnell ausprobieren.

    • All das ist zwar cool, aber ich muss ständig daran denken, wie oft die Kultur rund um Microservices und verteiltes Computing falsch empfohlen wird.
      Ein großer Teil der Komplexität, die ich in solchen Systemen gesehen habe, entsteht daraus, dass ein „Funktions“-Aufruf asynchron ist, vom Betriebssystem abhängt, irgendwann ausgeführt wird oder auch gar nicht, ein Bündel von Strings zurückgibt, das man parsen muss, um wieder in ein statisches Typsystem zu gelangen, und eigene Fehlermodi besitzt.
      Die scheinbar einfache Aufgabe, Logik in benannte Komponenten – also Funktionen – zu abstrahieren, wird extrem kompliziert.
      Wenn man die Logik im selben Prozess belässt und einfach Funktionen aufruft, muss man die genannten subtilen Fehler nicht testen.
      Ein Monolith ist nicht immer die bessere oder richtige Wahl, aber ich bin sehr skeptisch, ob der aktuelle Trend zu servicebasierten Softwarearchitekturen gerechtfertigt ist und sich wirklich auszahlt.
    • Ich frage mich, ob es öffentliche Beispiele dafür gibt, wie so eine Neufassung in der Praxis aussieht.
      Außerdem würde mich interessieren, ob es Unternehmen gibt, die Rust nutzen und auf diese Weise entwickeln.
      Nebenbei: TigerBeetle ist ebenfalls ein Produkt, das auf diese Weise geschrieben wurde.
    • Ich frage mich, ob es auch für Java-Anwendungen etwas wie madsim oder deterministische Simulationstests gibt.
  • Der Artikel ist wirklich unterhaltsam.
    Wenn man einen Satz wie „In diesem Zustand zu programmieren ist, als würde man in einem Kraftfeld leben, das einen vor allem Übel schützt … Wir haben alle Abhängigkeiten, einschließlich Zookeeper, entfernt, weil sie Bugs hatten, und in sehr kurzer Zeit unsere eigene Paxos-Implementierung geschrieben, und die hatte keine Bugs“ sagen und mit Belegen untermauern kann, wäre das wirklich beeindruckend.

    • Die früheste Haltung, die ich gesehen habe, dass Abhängigkeiten mehr Bugs enthalten als intern geschriebener Code und deshalb entfernt werden sollten, stammt aus einem Buch von 1995: https://store.doverpublications.com/products/9780486152936
      Darin heißt es, dass man wegen Bugs in numerischen Softwarepaketen beim Lösen des eigenen Problems am Ende fremde Software debuggen müsse, was so frustrierend sei, dass man, abgesehen von Linear-Algebra-Paketen, meist eigene Lösungen schreibe.
      Noch problematischer sei, dass Pakete Fehler in der Problemformulierung verdecken. Wenn man ein Gleichungssystem in einen Solver steckt, liefert dieser meist klaglos eine Lösung, selbst wenn das Problem schlecht konditioniert ist oder unerwartete Singularitäten auftreten und das Ergebnis von der physikalischen Realität abweicht; ist das Ganze in einem großen Programm vergraben, kann man diese Möglichkeit leicht ignorieren.
      Selbst wenn man verdächtiges Verhalten entdeckt, ist es schwierig, in das Paket hineinzusteigen und das Problem zu untersuchen, sodass man es am Ende doch selbst neu programmieren muss; hätte man das von Anfang an getan, wäre man wahrscheinlich tiefer in die Realität des Problems eingedrungen und hätte logische Verwirrungen frühzeitig beseitigt.
      Letztlich hängt die Entscheidung davon ab, wie rigoros man selbst ist, wie rigoros eine bestimmte Abhängigkeit ist und wie viel Zeit man hat. Eine eigene Datenbank würde ich nicht schreiben, weil es zu viele komplexe und gut getestete Optionen gibt. Wenn man dagegen nur einen Teil der Funktionalität eines kleinen Pakets mit schwachen Tests nutzt, kann es sinnvoll sein, es selbst zu bauen.
    • Ich habe bewiesen, dass mein Code gemäß der Spezifikation bugfrei ist.
      Ich behaupte allerdings nicht, dass meine Spezifikation keine Bugs enthält.
  • Mir kommen drei Gedanken.
    Erstens: eine großartige Idee zur richtigen Zeit. Wenn man die Stimmung unter Entwicklern zu Fuzzern, statischen Typen, Memory Safety, standardisierten Protokollen, Containern usw. betrachtet, wirkt es so, als hätten die Leute mit instabiler Software endlich die Geduld verloren.
    Zweitens: Es scheint auf einen Nischenmarkt abzuzielen. 2 Dollar pro CPU und Stunde, bei Reservierung 7000 Dollar pro CPU und Jahr, kein Free Tier für Hobby- oder Free-/Open-Source-Nutzung, und selbst Testzugang oder Kauf sind nur auf Anfrage möglich. Das ist ein schmerzhaftes, aber tragfähiges Geschäftsmodell. Schade ist nur, dass es nicht auf den größtmöglichen positiven Einfluss abzielt.
    Drittens: Die Qualität von Artikel und Dokumentation ist hoch, und mir gefällt wirklich, dass in der Dokumentation Formulierungen stehen wie: „Wenn Bugs in der Produktion oder bei Kunden gefunden werden, sollten Sie von uns eine Erklärung verlangen.“
    Genau so gewinnt man die Sympathie von Entwicklern. Das erinnert mich an Mullvad, das ich trotz einer früheren Enttäuschung immer noch Leuten empfehle.

    • Für die Zukunft planen sie Preise, die zu kleinen Teams passen, und vielleicht auch ein Free Tier für Free und Open Source.
      Das wurde im Zusammenhang damit unter https://news.ycombinator.com/item?id=39358526 erwähnt. Zur Einordnung: Ich bin Mitgründer von Antithesis.
    • Wenn das Erfolg hat, ist es die Art von Sache, die anfängt, die gesamte Softwarewelt zu beeinflussen.
      Auch Hardware könnte beginnen, Funktionen zur Unterstützung hinzuzufügen, und in 30 Jahren ist das vielleicht einfach die Art, wie Computing funktioniert.
      Aber bevor die Pioniere es wirklich verbreiten können, müssen sie erst die Kosten der ersten Pfeile wieder hereinholen. Man sollte das nicht als einzelnes Ereignis sehen, sondern als Beginn eines Prozesses.
    • Die Zielkunden sind meines Erachtens Teams, die bereits ausgereifte Software und umfassende Tests haben.
      Den Dokumenten nach sind die Bugs, für deren Finden diese Plattform entwickelt wurde, die groben, „nicht reproduzierbaren“ Arten, die in der Produktion nur selten auftreten.
      Die meisten Teams haben viel größere Probleme und offensichtlichere Bugs zu beheben. Tatsächlich hat der Großteil heutiger Produktionssoftware kaum mehr als rudimentäre Unit-Tests.
    • 2 Dollar pro CPU und Stunde können teuer oder billig sein, je nachdem, wie lange es dauert, ein Programm zu fuzzern.
      Mich würde interessieren, wie sich die Kosten in realen Anwendungsfällen multiplizieren.
  • Ich habe Antithesis dieses Jahr auf der Strangeloop getroffen und mit den Mitarbeitern gesprochen. Selbst im Vergleich zum aktuellen Stand der automatischen Fehlerinjektion, den ich aus meiner Zeit bei Amazon verfolgt habe, halte ich dieses Produkt für einen gewaltigen Sprung gegenüber vielen heute verwendeten formalen Verifikationssystemen.
    Tatsächlich konnte ich den Bug-Tracking-Prozess zu einem Problem nachvollziehen, das sie in Apache Spark Streaming gefunden hatten. Den Dokumenten nach handelte es sich um einen subtilen und bösartigen Korrektheitsfehler bei einer häufigen Operation, einen Grenzfall mit geringer Sichtbarkeit, der über Jahre hinweg Kopfschmerzen bereitet hätte.
    Am Ende stellte sich heraus, dass die Dokumentation falsch war, aber nachdem ich diesen Prozess gesehen habe, fällt es mir schwer, mir vorzustellen, wie wichtig ein Tool wie Antithesis in Unternehmen werden wird, die verteilte Systeme bauen.
    Ich hoffe, dass bald ein Blogpost erscheint, der tief in die technischen Details einsteigt. Ich würde gern hören, wie sie zu ihrem aktuellen Ansatz gelangt sind.

  • Ich will nicht sofort in einen überhitzten Hype-Zyklus einsteigen, aber das klingt wie der Heilige Gral. Man nimmt einfach eine bestehende Anwendung, geht davon aus, dass sie containerisiert ist, und muss darüber nur noch Eigenschaften prüfen, oder?
    Der Punkt, an dem es immer gehakt hat, war die Grundlage der Maschine, also nichtdeterministische CPUs und Betriebssysteme.
    Den gesamten vertikalen Computing-Stack neu zu bauen, ist praktisch unmöglich; sie haben das Problem also umgangen, indem sie einen hochgetreuen deterministischen Simulator gebaut haben.
    Mich würde allerdings interessieren, wie sie die Äquivalenz zwischen dem Simulator und bestehenden Betriebssystemen prüfen. Das klingt nicht trivial. Trotzdem überzeugt mich die Idee ziemlich.

    • Trotzdem muss man mit ihrem SDK viele Integrationstests schreiben. Sie nennen das Workloads.
      Danach führen sie die Tests aus, während sie alle möglichen Fehler injizieren: Betriebssystemausfälle, Netzwerkprobleme, Race Conditions und Timing-Bedingungen, Probleme mit Zufallszahlengeneratoren usw.
      Wahrscheinlich ist das heute die einzige praktische Möglichkeit, solche Dinge zuverlässig zu testen, aber man muss weiterhin alle Tests schreiben und den Anwendungszustand definieren.
    • Ich bin mir auch nicht sicher, ob Containerisierung wirklich erforderlich ist. Dem Artikel zufolge klingt Antithesis eher nach einer Lösung auf der Hypervisor-Schicht.
  • Es heißt, es sei eine „Plattform, die Software entgegennimmt und darin Bugs jagt“ – aber was ist es also konkret?
    Es sieht aus wie ein Cloud-Service, der Integrationstests ausführt. Man muss herausfinden, wie man in diese spezielle Umgebung deployt, und offenbar weiterhin Integrationstests mit speziellen Libraries schreiben.
    Aber selbst wenn man das ganze Integrations-Refactoring erledigt: Mir ist nicht klar, wie das echte Bugs finden soll, die ich in meiner Umgebung mit meinen Integrationstests nicht ohnehin schon gefunden hätte.

    • Ich empfehle, die Dokumentation gründlich zu lesen. Dort gibt es ziemlich viele Inhalte, die solche Fragen beantworten.
      Allerdings verlangt Antithesis keine manuellen Tests und auch nicht das Schreiben von Integrationstests.
      Man muss das Softwaresystem als Container paketieren, was vergleichsweise einfach ist, und danach eine Workload schreiben, die den normalen Betrieb des Systems nachahmt. Bei einer E-Commerce-Website wären das zum Beispiel Produkte ansehen, in den Warenkorb legen, bezahlen usw.
      Auf dieser Grundlage führt Antithesis die Workload aus, variiert Eingaben und injiziert Fehler, um die Software zu testen und Verstöße gegen Testeigenschaften zu finden.
      Mehr als 60 Testeigenschaften wie Crashes, Speichermangel usw. sind eingebaut. Um systemspezifische Probleme stärker offenzulegen, kann – und sollte – man auch benutzerdefinierte Eigenschaften definieren.
      Während die Tests laufen, werden Eigenschaftsverletzungen gemeldet, mit vielen nützlichen Debug-Informationen. Besonders interessante Testläufe lassen sich zurückspulen, Eingaben ändern, Artefakte sichern, Logging hinzufügen usw., sodass umfangreiche weitere Analysen möglich sind.
    • Im Artikel heißt es, sie hätten einen deterministischen Hypervisor gebaut, der alle pseudozufälligen Aktionen aus einem Start-Seed ausführt und damit perfekte Reproduzierbarkeit ermöglicht.
      Mehr weiß ich bisher nicht. Ich vermute, dass es irgendeine Form von Fuzzing und statischer Analyse gibt oder eine Definition der Aktionen, die die Software ausführen kann.
      Ehrlich gesagt wirkt es so, als gäbe es viele Überschneidungen mit dem, was die Sprache Vale lösen will: https://vale.dev/
      Allerdings scheint der Fokus nicht darauf zu liegen, eine neue Sprache zu entwickeln, mit der neue Software von Grund auf in diesem Zustand ist, sondern bestehende Software näher an diesen Zustand heranzubringen.
    • Wenn man die Dokumentation liest, scheint dein Verständnis im Großen und Ganzen zu stimmen. Man schreibt Integrationstests, also „Workloads“, und sie führen diese in verschiedenen Szenarien aus.
      Mithilfe des Hypervisors ändern sie Random-Seeds, lassen HTTP-Requests fehlschlagen oder lange dauern, trennen Verbindungen zwischen Servern, ändern die Reihenfolge von Serverantworten und erzeugen allerlei Dinge, die man normalerweise nicht kontrolliert, die in der Realität aber vorkommen.
      Anschließend vergleichen sie das mit der erwarteten Workload-Antwort, um herauszufinden, welche Bedingungen das System kaputtmachen.
      Deshalb verkaufen sie es als Jahresvertrag: Man bezahlt dafür, dass die Workloads das ganze Jahr über kontinuierlich laufen und alle möglichen Kombinationen von Fehlern ausprobieren.
  • Ich war sehr gespannt und habe die Dokumentation kurz überflogen, aber ich verstehe nicht so recht, wie sich das von randomisierten Unit-Tests unterscheidet.
    Wenn man bereits eine Sammlung von Unit-Tests hat, ist das nicht 99 % der Arbeit? Verstehe ich da etwas falsch?
    Zu diesem Schluss bin ich nach dem Lesen der Getting-Started-Reihe in der Dokumentation gekommen, insbesondere des Abschnitts Workloads: https://antithesis.com/docs/getting_started/workload.html

    • Ich bin von Antithesis und wäre neugierig, welcher Teil der Getting-Started-Dokumentation dir diesen Eindruck vermittelt hat.
      Die Seite How Antithesis Works könnte beantworten, worin der Unterschied zum bloßen Bündeln von Unit-Tests besteht: https://antithesis.com/docs/introduction/how_antithesis_works.html
      Kurz gesagt: Unit-Tests können beim Aufbau einer Workload helfen, sind aber nicht erforderlich.
      Wir erkunden autonom Ausführungspfade eines Softwaresystems, indem wir unterschiedliche Eingaben, Fehler usw. einführen, und finden Verhalten, das Autorinnen und Autoren von Unit-Tests möglicherweise nicht vorhergesehen haben.
    • Das stimmt, und es fühlt sich auch fast genauso an; der Unterschied liegt aber darin, dass es verspricht, auch dann noch so einfach zu bleiben, nachdem Threads, Locks, Netzwerkaufrufe, Festplattenzugriffe usw. hinzukommen.
      Wenn du einen Test für eine Funktion geschrieben hast, die einen Netzwerkaufruf macht und das Ergebnis auf die Festplatte schreibt, dann schlägt der Test fehl, wenn dein Code Fälle wie einen fehlschlagenden oder auf unbestimmte Zeit hängenden Netzwerkaufruf, zu wenig Speicherplatz auf der Festplatte oder einen Stromausfall kurz vor dem Schließen der Datei nicht korrekt behandelt.
      Insofern ja, es stimmt – aber es erweitert den Bereich, der sich so leicht wie mit Unit-Tests testen lässt, auf ein deutlich interessanteres Komplexitätsniveau.