Podcast

Schnell genug für GitHub

Aber nicht schnell genug für Dich?

GitHub läuft auf Rails, Instagram auf Django. Die Sache ist also klar: Rails und Django sind schnell genug für alle oder wie Lucas in der Ausgabe zu „Ruby on Rails“ sagte: „Wenn es für GitHub schnell genug ist, dann ist es auch schnell genug für Dich“. Aber ist es wirklich so einfach? In dieser Folge des INNOQ Podcasts diskutieren Christoph und Lucas über Web Performance. Der Fokus liegt auf Ruby- und Python-Anwendungen. Die beiden gehen aber auch auf Aspekte ein, die es in jeder Anwendung zu beachten gibt.
Weitere Episoden anhören

Shownotes & Links

Transkript

Transkript ausklappen / einklappen

Hallo und herzlich willkommen zu einer neuen Folge des INNOQ Podcasts. Heute habe ich mir den Christoph eingeladen. Hallo Christoph!

Christoph: Hallo Lucas!

Lucas: Wir wollen heute etwas besprechen, was der Christoph mir geschrieben hat, nachdem die Ruby on Rails Folge letztlich rausgekommen ist. Da habe ich so ganz platt gesagt: GitHub benutzt Rails, also wird Rails auch schnell genug für dich sein und das hat den Christoph gestört. Magst du erst mal sagen, warum dich das gestört hat?

Christoph: Ja, gerne. Das ist eine Aussage, die ich früher auch gerne mal getroffen habe, in ihrer ganzen schönen Einfachheit. Zwar nicht für Rails, sondern mehr für Django, das ist das Rails Äquivalent in der Python Welt. Aber das habe ich einfach auch empfohlen, weil es auch ein schönes Framework ist und es sich damit gut arbeiten lässt, aber das einfach so zu sagen, finde ich etwas blauäugig. Das ist einfach ein bisschen kurz gegriffen, weil wenn man das einsetzt, dann muss man eine ganze Reihe Dinge beachten, die nicht direkt implizit mitkommen, wenn man sich die Rails Doku oder Django Doku vielleicht durchliest. Und man muss auch viel Wissen haben, um das vernünftig zu betreiben. Und da ist das einfach zu kurz gegriffen, weil das einzige wo man sagen kann: Das kann man einfach so einsetzen, das ist schnell genug, ist bei der Hasenzüchtervereinsseite, wo man einmal pro Woche einen Zugriff hat. Aber wir wenden uns eigentlich in unserer Arbeit an IT Professionals und da wird schon etwas mehr Last drauf sein. Natürlich nicht so wie bei GitHub. In der Django Welt ist Instagram die größte Installation. Da kann man auch sagen, die haben ganz schön viel Traffic. Und die kriegen das auch irgendwie gewuppt. Aber da muss man viel machen. Vielleicht können wir es heute mal besprechen, was da alles nötig ist.

Lucas: Definitiv. Ich merke das auch, bei einem meiner Kunden mit einer Rails Anwendung, die relativ viel Last hat. Da war ich auch nach einer Zeit nochmal da und habe Performance Optimierung gemacht. Da würde ich auch gerne einfach heute in der Folge mal erzählen, was da die Fokus Themen waren, worauf ich gerade geschaut habe. Manche davon sind tatsächlich Sachen, die man in Ruby machen muss, die man in anderen Sprachen nicht machen muss. Andere Sachen sind aber auch ganz allgemein. Das muss man bei jeder Webanwendung optimieren. Ich glaube, das sollten wir auch in der Folge ein bisschen unterscheiden. Zwischen den Sachen, die in Ruby und Python sich in dieser Hinsicht sehr ähnlich sind, mit den Problemen, würde ich jetzt mal sagen auf der einen Seite aber auch die Sachen, die wirklich allgemein zu beachten sind. Das sind auch Sachen, die ich, wenn ich zum Beispiel die Webarchitektur-Schulung gebe, auch immer wieder mitbekomme, dass da die gleichen Fragen kommen. Und das ist auch unabhängig davon, ob die Sachen jetzt in Java geschrieben sind oder in Perl oder PHP, die einfach so Web Performance Sachen sind, die wir optimieren können. Ich glaube diese zwei Kategorien können wir uns auf jeden Fall mal anschauen.

Christoph: Das ist total wichtig. Da würde ich auch absolut zustimmen, dass wir das machen müssen und dass das auch allgemein für Webanwendungen gilt. Aber einen kleinen Einspruch habe ich da, gerade jetzt bei Rails und Django und vielleicht auch Frameworks in anderen Programmiersprachen, die nicht von Haus aus so viel Performance bieten. Da muss man viel früher mit manchen Themen anfangen, die andere einfach wegschlucken können, weil sie von Haus aus die Performance bieten, die dann vielleicht im Kundenumfeld reicht, weil sie doch nicht GitHub sind und zwar eine gewisse Last haben, aber die dann nicht so hoch ist. Aber das müssen wir auf jeden Fall besprechen. Ein ganz wichtiges Thema.

Lucas: Ja okay, dann lass uns doch erstmal mit dem Thema anfangen: Die Rolle der Datenbank. Das ist auch etwas, was in Anwendungen verschieden ist. Manche benutzen ihre Datenbank quasi nur als einen ganz einfachen Key Value Store und machen alle anderen Operationen quasi in ihrer Programmiersprache der Wahl. Andere machen sehr viel in der Datenbank. Wie schätzt du das denn ein? Was sind da deine Erfahrungen in verschiedenen Ökosystemen?

Christoph: Dass man eine Datenbank ganz verschieden einsetzen kann, das ist mir in den Projekten auch schon oft untergekommen. Einmal ist es wirklich ein Ersatz für das Filesystem, der transaktional ist, wo überhaupt keine Logik in der Datenbank benutzt wird. Das ist die eine Seite des Extrems. Die andere Seite ist, dass man alles in der Datenbank macht. Man kennt das vielleicht, dann hat man Stored Procedures, Trigger und sonstige Datenbankmechanismen drin und die ganze Logik eigentlich da und die Anwendung ist nur dafür da, die Daten daraus an den Webbrowser zu schaufeln und sonst nicht viel macht. Irgendwo dazwischen bewegt man sich immer. Ich bin eher auf der Seite, in meiner Komfortzone, dass man sagt, man macht die Logik mehr in der Anwendung selbst. Ich finde das irgendwie angenehmer. Das Programmierrmodell ist für mich da schöner und auch die Tools drumherum, da denkt man nur an Versionierung oder ähnliches, läuft irgendwie bei Programmiersprachen noch besser als meistens für Datenbanken. Ich will das jetzt nicht vorgeben, aber wenn wir mal auf unser eigentliches Thema zurückkommen: Was oft gesagt wird, ist, dass die Datenbank und der Netzwerk Traffic das eigentliche Bottleneck sind. Wir befinden uns in Rails wahrscheinlich im Anwendungs Code bei 1% der verbrauchten Zeit, 10% in der Datenbank und den Rest der Zeit befinden wir uns auf dem Netzwerk. Und ich glaube, das stimmt so erstmal nicht. Und ich glaube das nicht nur, sondern das hat man auch mal nachgemessen. Ich rede jetzt auch wieder über die Pythonwelt, aber das gilt für Rails oder für Ruby genauso. Es dauert relativ lange, wenn ich Daten aus der Datenbank gelesen habe, sozusagen diese Rohdaten, die dann in dem Datenbanktreiber ankommen, in Python oder in Ruby Objekte umzuwandeln. Also serialisieren in Objekte, die ich auch in meiner Anwendung benutzen kann. Und das fällt vielleicht oft gar nicht auf, weil ich eine Paginierung mache und zehn Ergebnisse pro Seite bekomme, dann ist die Datenmenge klein, aber dann habe ich vielleicht auch irgendwann mal größere Datenmengen. Dann habe ich 500 Objekte und dann geht das nicht so. Mir persönlich fällt das als User zum Beispiel auf, wenn ich lange Listen habe und dann kann ich immer durch 25 Ergebnisse blättern. Ich will aber irgendwas vergleichen und da hat man oben einen Dropdown, da kann ich dann auswählen: Wie viele will ich denn gleichzeitig anzeigen? 25, 50, 100. Dann stelle ich das mal auf 500, weil die Anwendung vielleicht keine vernünftige Suche hat und dann muss ich mit Steuerung F im Browser in der Anzeigeliste suchen und auf einmal bricht die Performance vollkommen zusammen. Und bei anderen nicht. Dann kann man das schon messen. Dann ist die CPU Zeit für die Konvertierung auf einmal dominierend und gar nicht mehr unbedingt das Netzwerk. Und von daher müsste man da wahrscheinlich auch schon mal drauf achten, wie man mit der Datenbank umgeht. Da sind wir bei einem Teilaspekt, wo ich sage, diese Aussagen sind manchmal einfach viel zu kurz gegriffen. Netzwerk und Datenbank sind nicht automatisch das Bottleneck.

Lucas: Ja, definitiv. Bei dem einen Kunden, wo ich diese Performance Optimierung gemacht habe, da war es auch so. Da gibt es tatsächlich für das Sortieren von Suchergebnissen, da wird nicht einfach nach Datum sortiert, sondern die besten Suchergebnisse sollen als erstes angezeigt werden. Und das ist auch eine Stored procedure. Ich würde das auch jederzeit wieder als Stored procedures schreiben, weil es ist einfach etwas, was die gesamten Daten anschaut, ein Ranking darüber macht, irgendwelche Algorithmen, die sich dieser Kunde ausgedacht hat, sagt, das macht dieses Suchergebnis besser als das Suchergebnis, deswegen kommt das nach oben. Und das in der Datenbank zu machen bedeutet, dass wir weniger Daten über die Leitung schicken müssen, um da nachzusortieren. Es würde gar nicht so richtig funktionieren, das alles in der Anwendung zu machen. Da müsste man sehr viele Ergebnisse erstmal an die Anwendung schicken und dann sortieren. Und ich glaube, viele haben eine ganz große Abneigung gegen Stored procedures, weil wenn man sie schlecht macht, dann ist das irgendwie sowas, das in der Datenbank rumliegt, keiner weiß so richtig, wie man es ändert, wie es passiert ist und so weiter. Aber solche Stored procedures können auch mit in der Versionskontrolle der Anwendung drin sein. Da gibt es in Ruby auch ein Gem, das ich gerne verlinken kann, dass das automatisch verwaltet, wo man dann sagen kann: Hier ist die neue Version der Stored procedure und dann wird es als Migration mit eingespielt und dann kann man es auch sehen, wie sich dieses Stored procedure aber über die Zeit verändert hat. Und für mich sind solche Sachen schon eine wichtige Optimierung. Und gerade wenn man sowas wie Ruby und Python benutzt. Ruby und Python sind nicht so superschnelle Sprachen im Vergleich zu vielen anderen Programmiersprachen. Da lohnt sich das sogar noch früher, auf eine Optimierung zu gehen. Aber ich würde eigentlich allgemein sagen, ist das eine gute Optimierung, bestimmte Sachen in der Datenbank zu machen, wenn man eine mächtige Datenbank hat wie eine SQL Datenbank, aber auch in anderen Datenbanken, die dann andere Sprachen benutzen, weil man sich damit spart, sehr viele Daten erst mal übers Netzwerk zu schieben, dann da nochmal zu filtern und zu verarbeiten und dann erst anzuzeigen. Sondern die tatsächlich schon vorzufiltern. Ähnlich dazu, wie es Stored procedures machen wäre sowas wie eine Materialized View, die man auch großartig dafür benutzen kann, aus einer komplexen Abfrage etwas zu machen, was eigentlich nur so aussieht wie eine Tabelle für meinen Rails-Code oder für meinen Django-Code. Da sagt man dann einfach: Hier ist ein ganz einfaches Active Record Model oder wie das in Django dann auch heißt, obwohl in Wirklichkeit da unten drunter was viel komplexeres steckt, wo aber durch die Materialized View erstmal von meiner Anwendung versteckt wird. Das ist für mich auch eine Technik, die ich sehr gerne in der Optimierung von solchen Web Anwendungen einsetze. Ich glaube diese Angst vor SQL, die muss man ablegen. Man muss dann auch einfach mal SQL schreiben und dann kann man wirklich sehr große Performance Optimierung machen.

Christoph: Ja, da hätte ich einfach mal eine Frage an dich. Ich sehe das genauso. Aber mich würde interessieren. Eigentlich sind es zwei Fragen, ob du a) wenn es gehen würde, nicht lieber in dem Programmiermodell von Rails bleiben würdest. Weil ich finde das angenehmer. Wenn ich jetzt sage, auf der Django Seite oder in der Programmiersprache wie Python zu bleiben, anstatt mit Stored procedures zu arbeiten. Wenn du die Wahl hättest, wo du das lieber machen würdest. Bei mir wäre es ganz klar: ich würde nicht die Stored procedures nehmen oder ein anderes, sondern lieber mal im Modell bleiben. Und b) ob sowas auch in der Dokumentation von Rails steht? Bei Django gibt es Sachen, wie man auf der Django Seite eine Optimierung macht und da geht es hauptsächlich darum die Ergebnismenge zu minimieren, damit die Anzahl der Daten weniger ist, die verarbeitet werden müssen. Oder ob das nicht auch sowas ist, wo das schon greift: Ja, dann kannst du einfach Rails nehmen. Aber das musst du aber auch wissen. Also eigentlich musst du auch schon ganz gut in SQL sein.

Lucas: Also ich fange mit der zweiten Frage an, damit ich es nicht vergesse. Ich glaube, dass wenn man sich jetzt die Guides von Rails anschaut, dann hat man den Eindruck, sobald man irgendeine SQL von Hand schreibt, hat man verloren. Das ist falsch. Dass es grundsätzlich schlecht ist, SQL von Hand zu schreiben und sowas wie eine Stored procedures oder der Materialized View kommt in diesen grundlegenden Sachen überhaupt gar nicht vor. Da würde ich schon sagen, dass das ein Thema ist, was wenig beleuchtet wird, wo viele auch erst mal eine Abneigung dagegen haben, so etwas zu tun. Das würde ich jetzt erst mal so sagen. Habe ich etwas dagegen, SQL zu schreiben? Ich habe so ein, zwei Abneigungen gegen SQL. Nicht gegen das, was SQL kann, also SQL als relationale Algebra, das finde ich super. Das ist eigentlich auch ein gutes Modell, um gerade solche Operationen auszudrücken, sondern SQL, die Sprache selbst. Da habe ich schon lange etwas herumgeflucht, aber da möchte ich jetzt nicht zu tief reingehen. Deswegen bin ich jetzt aber auch kein Riesenfan davon, SQL zu schreiben. Und ich bin auch nicht der allerbeste SQL Optimierer der Welt. Definitiv nicht. Ich würde es vorziehen, eine andere Sprache zu nutzen, aber es muss nicht unbedingt die Sprache meiner Anwendung sein, weil eine Daten Transformation zu beschreiben ist eigentlich etwas, was man auch in einer deklarativen Sprache wie SQL sehr gut machen kann. Dafür muss ich nicht unbedingt sowas wie Ruby, JavaScript oder sonst irgendetwas haben. Ich kann aber auch ergänzen, dass ich bei meinem vorigen Arbeitgeber, ArangoDB, auch so eine Engine eingebaut habe, damit man in JavaScript in der Datenbank Abfragen schreiben kann. Aus genau solchen Gründen. Würde ich aber heute nicht mehr so machen. Da würde ich eher eine bessere relationale Algebrasprache für verwenden. Finde ich eigentlich ganz cool dafür. Wie siehst du das? Du würdest das lieber in Python schreiben?

Christoph: Ja, ich habe gar nichts gegen SQL. SQL finde ich auch so gesehen ganz cool, aber ich habe das Problem, wenn ich dann in einer Anwendung SQL schreiben muss, dann ist dieser Übergang schwierig. Da muss ich irgendwelche Prepared Statements machen. Damit bin ich aber in gewisser Weise beschränkt. Prepared Statements können nicht alles parameterisieren. Ich kann zum Beispiel in so einer Where-Klausel die Operatoren nicht parameterisieren, wenn ich sage: Wäre x > y, da kann ich das > nicht sagen, ich parameterisiere das und übersetze mal > oder ≥ oder < ein. Oder wenn ich vorne die Spaltennamen der Tabelle mache, kann ich die auch nicht parameterisieren, wenn ich sage: Hier brauche ich noch das. Und dann so einen Übergang zu finden und sagen, ich muss mir SQL auch noch programmatisch zusammenbauen, das finde ich dann besonders schlimm. Das hat auch noch Auswirkungen auf die Security. Davon wollte man nicht sprechen, dass es natürlich eine Gefahr für SQL Injections bewirkt. Aber ich bin immer froh, wenn man eine Anwendung schreibt, dann eigentlich damit nicht in Berührung komme und sage: Ich kann irgendwie ein ORM benutzen. Der nimmt mir das ab oder ich kann wirklich nur mit SQL Prepared Statements arbeiten. Aber das sind genau die einfachen Sachen, wo ich keine Stored procedures oder ähnliches brauche. Komplexes SQL zu schreiben ist halt schwierig, weil da gibt es keine vernünftige, jedenfalls ist mir das in der Python Welt nicht bekannt, sondern für einen vernünftigen Übergansgmodus eine andere relationale Beschreibungssprache. Also das fände ich cool, wenn man sozusagen das in Python ausdrücken kann. Es gibt so ein paar DSLs in verschiedenen Sprachen, das weiß ich auch, aber damit habe ich jetzt noch nicht so die wirklich guten Erfahrungen gemacht, dass die alles abdecken. Von daher finde ich es immer gut, wenn ich mir um die Performance keine Gedanken machen muss, sondern einfach in meiner schönen Python Welt bleiben darf.

Lucas: Hmmm, ja, das stimmt. Da finde ich eine Datenbank, die das ganz cool gelöst hat, die Datenbank selbst würde ich heutzutage nicht mehr empfehlen, aus verschiedenen Gründen, aber das Konzept davon ist etwas, was ich tatsächlich ganz cool finde. In RethinkDB gibt es eine als Abfragesprache ReQL und das ist tatsächlich einfach eine DSL in den jeweiligen Programmiersprachen, die sie unterstützen, wie zum Beispiel Ruby, JavaScript, Python. Da schreibt man diesen Code und der generiert daraus die Abfrage, die man aber selbst gar nicht sieht. Und die ist sehr nah an diesem Map/Reduce/… Schritten, die man so macht, dran und der übersetzt das aber in eine effiziente Datenbankabfrage. Das finde ich eigentlich eine ganz gute Lösung, weil ich würde behaupten, für viele ist es leichter, eine Daten Transformation mit diesen üblichen funktionalen programmierten Sachen wie jetzt Map, Reduce, Filter auszudrücken als mit SQL. Wenn man das, was man in dieser Transformations-Pipeline ausdrückt, übersetzen würde in etwas, was dann auf einer relationalen Algebra genau die Sachen in der Datenbank ausführen kann, finde ich das eine ganz gute Lösung. Aber ja, das stößt dann an die Grenzen und oft ist es dann auch schwierig, wenn man daraus ein Stored procedure machen möchte. Da müsste man quasi das SQL da rausholen. Ich bin da auch nicht zufrieden mit, aber in der Praxis ist es dann so, dass ich dann Stored procedures oder Materialized Views von Hand schreiben würde und die dann im Versionsmanagement der Anwendung mit verwalte und mit Migration in die Datenbank einspiele. Das wäre so das, was ich mache, auch wenn ich da ähnliche Bedenken habe wie du.

Christoph: Also für mich wäre so was ja sozusagen die richtige Lösung, aber die ist ja eigentlich an dem Framework vorbei. Das ist nicht so in dem Grundprinzip oder Grundsinn, wie die so ein Framework aufgebaut ist, dass man genau so was machen würde. Aber vielleicht kommen wir mal zum anderen Punkt oder einer anderen Annahme. Also eins ist ja auch, finde ich, dass wenn man sagt so coole Datenbanken, wie du das da gerade geschildert hast mit dieser, RethinkDB hieß die glaube ich. Oft ist es ja so, dass man sich das gar nicht unbedingt aussuchen kann. Wenn ich jetzt im Kundenprojekt bin, welche Datenbank nehme ich denn da? Dann steht da vielleicht Postgres oder eine MySQL, Oracle oder DB2 oder SQL Server, also die typischen. Oder vielleicht aber auch eine Dokumenten Datenbank wie MongoDB, CouchDB oder so und wenn, da würde ich jetzt gerne mal anknüpfen. Gerade bei diesen Dokumenten DBs ist es ja oft so, dass die nicht so mächtig sind in der Verarbeitungslogik, so wie SQL Datenbanken und dass ich dann doch relativ viel in der Anwendung mache, das Filtern und Rübergehen und dann kriege ich jetzt einen großen Datensatz, da müssen wir das auch irgendwie beschleunigen können, dass wir da was haben. Also ein Punkt, wie man dann in der Python Welt das so macht ist, dass man dann auf einmal auf C Extensions zurückgreifen muss. Also im Sinne von: Wir haben hier jetzt ja ein Modul, ein Python Modul, aber das ist in C geschrieben, weil das bringt die entsprechende Performance, um solche CPU intensiven Transformationen zum Beispiel zu machen. Und das ist nicht nur auf der Datenbank Seite so, wo ich das oft sehe. Also eine Empfehlung, die im Internet gegeben wird, wenn die Performance nicht stimmt. Auf der Frontend Seite nimm doch hier mal diese C JSON Library, weil heutzutage haben wir viele Sachen, die irgendwie eine HTTP JSON API bereitstellen. Und dann muss ich die Daten, die aus der Datenbank kommen, auch noch irgendwie oft in JSON transformieren. Und dann heißt es oft: Okay, dann ist die Serialisierung auf der Seite ziemlich schwierig und da gibt es in Python verschiedene, selbst im Standard gibt es einen in C geschriebene, die dann noch manchmal zu langsam ist, weil die mehr auf Korrektheit und Portabilität getrimmt ist und dann gibt es halt die Empfehlung: Installiere das. Und da kommen natürlich auch eine ganze Menge Probleme mit. Ich weiß nicht, wie sieht das aus in der Ruby Welt?

Lucas: Also in Ruby ist es auf jeden Fall auch so, dass man für bestimmte Sachen C Extensions benutzt. Und C Extensions haben halt immer das eine Problem und das ist halt C, weil C nun mal halt einfach nicht memory safe ist und dann kann es halt auch schon mal passieren, dass es die ganze Anwendung halt mit sich runterreißt. Das ist ein Problem, was glaube ich, einfach dann immer dann da ist, wenn man halt versucht mit C zu arbeiten und das dann einbettet in seine Anwendung. Es ist ja auch in Go beispielsweise, kann man ja auch in C Calls rüber machen, das ist ja auch etwas, wo die Autoren von cgo sagen, am besten sollst du das vermeiden, wenn das irgendwie geht, weil das könnte halt dafür sorgen, dass alles hier kaputt geht. Und in Ruby ist eine Sache, die glaube ich jeder benutzt, Nokogiri, was so ein XML Parser ist und der benutzt halt libxml untendrunter. Das ist ja auch so eine sehr verbreitete Library. Ich vermute, dass das in Python auch verwendet wird und das macht auch schon mal beim Installieren Probleme oder hat viele Jahre große Probleme gemacht. Mittlerweile haben die da schon eine gute Lösung für gefunden. Aber auch da sind dann halt solche Sachen, wie wenn du solche C Extensions hast, dann solltest du vielleicht schon darüber nachdenken, ob du das dann nicht als Docker Container verpacken möchtest, damit du schon mal sicherstellen kannst, dass die Umgebung drumherum die gleiche ist. Für alle das gleiche LibXML und so weiter, weil du dann halt oftmals die Abhängigkeitsverwaltung von deiner Programmiersprache verlässt, wenn du halt in so einer C Library dich einbindest. Wenn du jetzt den C Code einfach mit auslieferst und mit kompilierst, dann kann es wieder sein, dass der C Compiler auf deinem System das anders optimiert, als auf meinem System und so weiter und so fort. Aber meiner Erfahrung nach sind diese C Extensions quasi unumgehbar. Das was jetzt in letzter Zeit so ein bisschen als Trend aufgekommen ist, ist halt stattdessen die in Rust zu schreiben. Aber da gibt es natürlich einfach noch nicht so ausreichend viel Code für. Also wenn man es selber schreibt, ist es wahrscheinlich etwas, was man sich anschauen könnte. Habe ich aber überhaupt gar keine Praxiserfahrung mit. Ich habe auch noch nie selber eine C Extension geschrieben. Also wenn, dann habe ich mir halt eine Library rausgesucht, die eine C Extension ist, wo jemand solche Probleme gelöst hat. Weil C kann ich nur ganz rudimentär. Das würde ich mir nicht zutrauen damit irgendwelchen Produktionscode zu schreiben. Aber ja, es ist auf jeden Fall in der Ruby Welt weiterhin verbreitet, an bestimmten Stellen auf C Extensions zuzugreifen. Ob das jetzt der HTTP Parser im Webserver ist oder irgendein Serialisierungscode oder halt so was wie ein Parser für XML oder so etwas.

Christoph: Ein Aspekt, der bei mir dann immer untergekommen ist, dass oft schon die Entwicklungsumgebung nicht funktioniert hat, weil gerade in so einem größeren Enterprise Corporate Umfeld, da haben die Entwicklerinnen oftmals Windows Maschinen. Da ist überhaupt gar kein C Compiler erstmal dabei. Und dann können die lokal gar nicht arbeiten und da muss man halt dann auf Docker zurückgreifen. Docker gibt es für Windows jetzt auch noch nicht so ewig. Von daher hat das immer Schwierigkeiten gemacht und auch heutzutage haben noch nicht alle PCs auch eine Docker Installation unbedingt dabei. Und dieses Windows Subsystem für Linux, was man dafür braucht, um das dann vernünftig zu verwenden. Also alles was außerhalb der Python Standard Library ist, die auch einige Extensions in C bereitet, aber die leite ich sobald ich Python dann auf meiner Maschine habe, habe ich die dabei. Aber alles was aus dem Package Manager kommt, macht dann große Probleme. Und dann geht es darum: Okay, auch wenn ein Compiler vorhanden ist, zum Beispiel ist es nicht so, dass automatisch diese Extension kompiliert wird, weil die muss dann mit demselben Compiler kompiliert werden, mit dem die das Python Binary kompiliert wurde. Und wenn ich das zum Beispiel bei Windows runter lade, also von der Homepage und dann bei Windows einfach das Binary herunterladen und sowieso nicht von Source kompiliert habe, dann weiß ich das gar nicht und so. Und ich kann nur sagen, C Extensions sind super, um solche Bottlenecks zu umgehen. Aber ich würde dringend eigentlich davon abraten, die zu nutzen, wenn man es nicht unbedingt muss. Das finde ich genauso wie bei der Datenbank mit dem was du gesagt hast. Den ganzen Trick so stored procedures, materialized views und sonstiges kann man das alles umgehen. Aber eigentlich will man das nicht. Das ist so, man macht das, weil man es einfach muss und ist es auch wieder in so einem Framework irgendwie so ein bisschen vorbei. Nicht ganz so wie, dass man direkt auf eine andere Sprache wechselt, aber funktioniert dann halt nicht mehr so. Das ist ja gerade auch bei Rails ziemliche Werbung gewesen, also Out of The Box und dann dieses berühmte Video, wo wir machen dann unser Blog in ich weiß nicht, 10 Minuten oder 15 Minuten. Also so schnell kommt man ja noch nicht mal vom Münchner Hauptbahnhof zum Flughafen. Da ist ja ganz toll. Das verdirbt das so ein bisschen, wenn man dann die Out of The Box Erfahrung hat, ja ich installiere das mal, oh geht nicht. Irgendwelche kryptischen Fehlermeldungen, weil mir ein C Compiler fehlt. Ja, finde ich entsprechend schwierig.

Lucas: Also in Rails ist es so, dass über die Zeit einige C Extensions aus den Default Dependency rausgeflogen sind, weil sie ersetzt wurden durch Ruby Code. Und Nokogiri, also dieser LibXML2 Binding im Kern ist so ziemlich das Einzige. Also wenn ich mich nicht täusche, was jetzt noch da mit dabei ist, und die haben wirklich in den letzten Jahren sehr optimiert darauf, dass das auf jedem System einfach sofort, wo ein C Compiler ist, das sollte man natürlich voraussetzen, dass das dann tatsächlich auch baut und funktioniert. Aber trotzdem ist das Problem nicht unbekannt. Und gerade wenn man dann halt eine Library, die nicht Nokogiri ist, die nicht so oft benutzt wird, obwohl da nicht so viel Zeit reingesteckt wurde, um genau dieses Problem zu optimieren, kann das immer wieder schmerzhaft werden. Also definitiv.

Christoph: Also Quintessenz: keine Webservices SOAP XML in Django oder Rails machen. Sonst muss man sich mit C Extensions rumschlagen, weil bei Python ist es genauso. Die XML Parser sind auch… also es gibt welche in der Standard Library, aber die schnellen sind auch alle C Dinger und mit den entsprechenden Problemen. Vielleicht gehen wir mal einen Schritt weiter auch und solche C Extensions machen jetzt den einzelnen Request schnell. Sagen wir mal, wir sind jetzt schnell, weil wir haben irgendwie die eigentliche Datenverarbeitung findet in C statt oder einer C Extension. Und die Datenbank nimmt uns auch jede Menge Arbeit ab. Trotzdem haben wir noch ein Problem und ich weiß nicht, da musst du mir gleich helfen, wie es genau auf der Ruby VM ist. Aber in Python gibt es den Global Interpreter Lock und der erst mal vereinfacht sagt: Es kann nur ein Thread gleichzeitig laufen. Das heißt bei einem Multithreaded Server kann ich einen Request gleichzeitig bearbeiten. Es gibt da Ausnahmen, also wenn er auf IO wartet, dann geht das. Und wenn man auch im C Teil ist, dann kann er auch abgegeben werden, der Lock. Aber das ist ein bisschen tricky, weil solange er nur C macht ist das okay, aber wenn er den Interpreter State verändert, das heißt wenn er zum Beispiel ein Python Objekt erzeugt, dann muss er den wieder nehmen und so. Also sagen wir mal kurz zusammengefasst, darüber kann man bestimmt eine ganze Folge machen, wie das so auf Low Level alles funktioniert, ist es so, dass ich eigentlich nur ein Request gleichzeitig bearbeiten kann mit meiner CPU und ich meine zu wissen und da könntest du jetzt gut ergänzen, wahrscheinlich in dem Ruby Welt ist das genauso.

Lucas: Genau. Also in Ruby ist das genauso, da hieß das früher auch Global Interpreter Lock. Das wurde dann irgendwann in Global VM Lock umbenannt, weil es dann halt kein Interpreter mehr war, sondern eine VM. Aber im Kern ist es genau das gleiche. Ich würde es so sagen, dass man quasi zu jedem Zeitpunkt, kann nur ein Thread Ruby Code ausführen und plus diese Einschränkung, dass bestimmte C Extensions, wenn Sie auf bestimmte APIs von Ruby zugreifen, gelten für sie die gleichen Einschränkungen. Und sobald wir halt IO Operationen machen, dann fällt diese Einschränkung weg. Also mehr so, es können halt quasi mehrere Threads gleichzeitig auf IO warten und dann weckt der halt den Thread auf, der als nächstes dran wäre. Und im Normalfall sind das halt Operation System Threads. Ich glaube auch in Ruby und in Python, oder?

Christoph: In Python sind das auch OS Threads und ja, die scheinen sich sehr zu ähneln von dem, was sie so können und machen. Aber wir haben ja auch gerade gesagt, es ist ja vielleicht ein Trugschluss, dass dieses auf IO warten, dazu gehört ja auch Netzwerk, auch immer das Bottleneck ist. Eigentlich wollen wir CPU sozusagen frei machen, um damit Daten zu bearbeiten und genau das geht ja dann nicht. Also sozusagen bzw. das geht nur in einem Thread dabei. Und wenn ich als fünf anfragen habe mit einer hohen Menge an Daten, die aus der Datenbank kommen, dann habe ich da ja wahrscheinlich auch noch ein Problem. Ja, da gibt es erst mal Out of The Box von Python keine Lösung. Und ich weiß zum Beispiel, dass ich ja, nehme jetzt mal an, ich bin in der Java Welt unterwegs, da habe ich irgendwie einen Server, der komplett in Java geschrieben ist. Nehmen wir mal einen Klassiker, den Tomcat. Da weiß ich okay, der kann halt x Requests gleichzeitig bearbeiten, weil er sozusagen, ich sage mal, für die Zuschauer in Anführungszeichen echtes Multithreading Server ist. Und genauso wie vielleicht in Go, eine Sprache, wo ich mich ganz gut auskenne, ist es so, dass ich da in der Standard Library einfach ein http 2 Server habe, der einfach auch für Produktionen eingesetzt werden kann. Also nicht irgendwie noch mal so die Basics. Und da brauche ich auch gar kein weiteres Wissen zu haben. So, wie komme ich jetzt irgendwie an eine Lösung dafür, dass ich mehrere Requests gleichzeitig abarbeiten will?

Lucas: Genau das ist in Ruby ähnlich. Wir kommen gleich noch einmal auf diese Co-Routinen zurück, da gibt es erst mal auch Support in Ruby. Aber ich glaube, wir sollten erst noch mal über den anderen Lösungsansatz, der auch ganz lange auch von GitHub beispielsweise gefahren wurde. Das sind diese Forking Webserver. Also dadurch, dass wir sagen wir haben einen Global Interpreter oder VM Lock, wollen wir irgendwie dafür sorgen, dass mehrere Sachen gleichzeitig passieren. Also benutzen wir keine Threads, sondern wir benutzen Prozesse, also starten quasi einen Prozess, also benutzen pro Request einen Prozess. Und ich glaube so das erste, dass das so populär gemacht hat, war halt der Unicorn Server. Der wurde zum Beispiel auch von GitHub in Produktion eingesetzt. Ich weiß nicht was die heute einsetzen, das konnte ich jetzt auch nicht mehr rausfinden, aber die haben es auf jeden Fall sehr lange in Produktion benutzt. Wer sich vielleicht noch daran erinnert, dass man manchmal so eine Fehler Seite bekommen hat, wo so ein wütendes Einhorn einen angeguckt hat, das angry Unicorn, das war ein Witz bezogen auf diesen Unicorn Webserver. Und wenn ich mich nicht täusche, haben die Python Leute den dann später quasi auch auf Python portiert, oder?

Christoph: Ja, es gibt den Green Unicorn oder gUnicorn Server, war eine Portierung. Hat sich jetzt auch selbstständig weiterentwickelt. Den aktuellen Status weiß ich gar nicht, wie viel er noch gemeinsam hat, außer dem größten Teil des Namens. Aber das Prinzip ist halt dasselbe. Wir haben irgendwie einen Server, der HTTP entgegennimmt, Prozesse aufspannt, wo dann ein Python Interpreter drinnen läuft bzw. dann bei Ruby jawohl eine Ruby VM. Und die Daten dann an den Prozess gibt und das derjenige Prozess dann halt den Request auch verarbeiten kann, damit wir irgendwie eine Parallelisierung kriegen, damit wir unsere 187 Trilliarden Cores, die wir heutzutage in so einem Server drinnen haben, auch auslasten können und auch mehr als einen Prozess gleichzeitig nutzen können.

Lucas: Und das ist dazu die Lösung gewesen. In Ruby ist es so, dass Unicorn jetzt seit einiger Zeit, würde ich sagen, von Puma abgelöst wurde, das halt zum einen ein Forking Server ist, aber zum anderen auch pro Prozess gibt es da noch mal einen Thread Pool. Also es gibt einen Prozess Pool und einen Thread Pool pro Prozess und in dieser Kombination kann man dann von beidem profitieren. Und das ist der Webserver, den ich heute empfehle. Also zum heutigen Zeitpunkt empfehlen würde als Produktions Webserver für eine Rails Anwendung und das ist mittlerweile auch das, was das Rails Team als den Default empfiehlt. Für Produktion ist Puma zu verwenden. Grundsätzlich also dieses Problem angeht plus, dass auch trotzdem noch Threading unterstützt für IO Bound Tasks, wie jetzt auf die Datenbank warten. Genau.

Christoph: In der Python Welt ist es ähnlich, da gibt es uWSGI heißt das. WSGI ist das Web-Server Gateway Interface, das ist so ein Standard, wie Webserver halt mit Python Prozessen sprechen können, damit nicht alle irgendwie für alles einen Adapter schreiben müssen, für jedes Framework, für jeden Webserver. Und uWSGI ist glaube ich, der Python Standard aktuell, den man so benutzt, der macht das ganz ähnlich. Da gibt es auch Prozesse, man kann aber auch Threading einschalten und dann halt noch so ein paar Extras in Sachen Monitoring und Restarts und solche Sachen. Der übernimmt jetzt solche Aufgaben. Ich glaube, da sind die sich ganz ähnlich. Aber was ja interessant ist, ist ja, was ich ja jetzt auch öfter bemerkt habe. Das wissen die Leute gar nicht so unbedingt, dass ich ja so ein Rails oder auch so ein Django Framework einfach nicht so out of the box starten kann. Doch ich kann starten, aber dass ich dann irgendwie diese eingebauten Webserver habe, die dann einen Request gleichzeitig bearbeiten können und auch noch sonst total langsam sind und es ja auch gar nicht so einfach ist, so ein Setup zu machen, wo dann auf einmal so ein uWSGI oder so einen Puma Server einfach davor stehen. Das ist ja nicht so einfach so. Wir schreiben mal hier uWSGI Start und alles ist gut. Das muss man irgendwie konfigurieren, auf welchen Ports hört wer wie was, wo werden Daten übergeben? Und so weiter und so fort. Also das ist geht glaube ich weit über das Framework hinaus.

Lucas: Wo ich sagen würde, dass das tatsächlich in Rails anders ist. Seit Rails 5 ist Puma der Standard Webserver, der ist auch fertig konfiguriert. Das heißt, wenn ich Rails Server starte, dann startet da Puma und macht auch direkt Fork und Multithreading und so weiter. Macht er alles in der Default Konfiguration. Das heißt, also das was mittlerweile da eingebaut ist, ist ein produktionsreifer Webserver. Das war lange nicht so definitiv. Hat sich aber in den letzten Jahren geändert und funktioniert meiner Erfahrung nach tatsächlich schon in der Grundkonfiguration sehr gut. Schaut sich an wie viele CPU Kerne hast du usw. und konfiguriert sich dann entsprechend. Also da muss man gar nicht mehr so viel Mühe reinstecken. Das ist schon eigentlich mittlerweile ganz cool geworden.

Christoph: Das finde ich erfreulich zu hören, da ist die Python Welt noch nicht ganz so weit. Also ein Grund, warum das jetzt auch nicht so out of the box überall ist, ist weil uWSGI ist natürlich auch ein C Projekt. Wie ist das denn mit Puma? Also, wir hatten gerade über die ganzen Probleme von C Sachen gesprochen, wenn wir die da kombinieren wollen. Und deshalb ist glaube ich, der Default in Sachen, jetzt nicht nur bei Django auch bei Flask oder, also bei Web Frameworks in der Python Welt, dass man eigentlich keinen Webserver vorgegeben hat, damit erst mal out of the box läuft und ich auf meinem lokalen Rechner ein schönes Hello World hinkriege. Und in Produktion muss ich mich dann halt doch um was anderes kümmern. Ist in Puma dann rein Ruby oder haben wir wieder die C Probleme?

Lucas: Du hast recht, das ist tatsächlich so, dass da C drin ist. Damit ist das, was ich eben gesagt habe, dass Nokogiri das Einzige ist, was C benutzt in dem Standard Rails Stack falsch, weil das ist da auch so. Der benutzt auf jeden Fall HTTP Parser, der in C geschrieben ist und ich weiß nicht, ob noch weitere Teile. Aber auf jeden Fall der HTTP Parser ist in C geschrieben. Aber damit hatte ich tatsächlich jetzt auch noch nie Probleme. Das ist immer problemlos installiert worden und hat funktioniert, sowohl auf Linux als auch auf MacOS, mit Windows habe ich aber auch nicht so viel Erfahrung, muss ich sagen. Also weiß nicht, ob das da anders ist. Aber grundsätzlich auf Linux und auf MacOS hat das immer funktioniert und auch in Produktion hat es dann auch funktioniert. Ja.

Christoph: Das ist natürlich gut, dass das soweit ist. Aber wie gesagt, da muss Python vielleicht noch ein bisschen aufholen. Vielleicht wollen sie das, aber wie gesagt gar nicht, weil es halt C ist. Mal schauen, wie sich das entwickelt. Aber manchmal reicht ja auch so ein Forking Server gar nicht. Also ein Use Case könnte es ja auch sein, dass wir jetzt gleichzeitig gar nicht doch so viel CPU brauchen. Oder doch? Kann beides sein. Aber dass wir ganz viele Requests gleichzeitig haben und da ist das Prozessmodell natürlich ein bisschen schwierig. Entweder hast du einen Pool, dann bist du halt begrenzt an deinem Durchsatz, weil sind halt nur so und so viel Prozesse. Oder du machst halt sehr viele Prozesse auf, die auch einen gewissen Overhead haben. Also das ist jetzt kein Rails oder Django Problem. Das war ja schon früher bei Apache Webserver so, dass war mit der erste Bekannte der so, man nennt die ja Forking Server, der sozusagen Prozesse startet für die Request, dass man dann irgendwie irgendwann mal begrenzt ist. Also andere C Webserver haben dann Threads genommen, mehr Performance erreichen, weil da der overhead nicht ganz so groß ist, wie beim Prozess. Aber das geht ja jetzt in unserer schönen Rails und Django Welt nicht, was machen wir denn dann?

Lucas: Also eine Möglichkeit, die zumindest in der Rails Welt oder in der Ruby Welt noch relativ neu ist, ist auf Non Blocking IO zu setzen bzw. auf kooperatives Multitasking. Also man nennt das Coroutines so allgemein, aber in Ruby heißt es aus irgendeinem Grund Fibers. Und Fibers gibt es jetzt halt auch ein paar Jahre schon und mittlerweile gibt es einen Webserver, der darauf aufsetzt, der heißt Falcon. Wie man sieht, sind das alles Tiernamen. Keine Ahnung, hat sich so ergeben, als Tradition vielleicht. Und das gehört allgemein gesprochen zum async Project, also async Ruby Project, was tatsächlich Fiber benutzt, um Non Blocking IO in Ruby zu machen. Das heißt also, was ich jetzt machen könnte, ist, ich könnte meine Rails Anwendung statt mit einem Puma mit diesem async Ruby bzw. Falcon starten und dann würde der Non Blocking IO benutzen für die Kommunikation nach draußen. Das muss man aber direkt einschränken, weil beispielsweise Active Record, das ist ja das Datenbank Layer von Rails, damit nicht kompatibel ist. Das heißt also, Rails kann das aktuell nicht. Also mit Rails können wir keine async Ruby nutzen, solange wir Active Record als Datenbank dazu benutzen. Wenn wir jetzt einen anderen benutzen, würde das vielleicht gehen, aber für diesen Standard Rails Stack wird das nicht gehen. Daran erkennt man auch schon so ein bisschen, dass das immer noch sehr in den Kinderschuhen steckt, in der Rails Welt. Das ist vielleicht, weiß ich nicht, in Python vielleicht anders.

Christoph: Ein bisschen. Ich muss ein bisschen schmunzeln, wenn das Falcon heißt, weil es gibt in Python ein Web Framework, das heißt Falcon und wir hatten in der Vorbereitung auch darüber gesprochen, dass wir irgendwie in der IT das Problem haben, dass für die gleichen Dinge verschiedene Namen verwendet werden. Und du hast gerade das Beispiel Fibers, die ja sonst in anderen Sprachen Coroutinen heißen. Und jetzt gibt es auch natürlich den umgekehrten Fall. Falcon gibt es im Web Framework in Ruby und in Python. In Ruby ist es asynchron und in Python ist es natürlich synchron. Aber jetzt zur Frage zurück. Die Python Welt ist etwas weiter. Da gibt es auch Coroutinen. Die heißen auch so, nicht immer, also es gab so welche, die waren da nicht so Coroutinen, die Generators, die waren aber in der Semantik ziemlich ähnlich. Jetzt gibt es auch, ich weiß nicht welche, 3.x Versionen gibt es die auch wirklich als Coroutine, es gibt sogar Bytecode Instructions dafür. Und die erlauben also a) erst mal auf der Sprachebene, so eine Async Await Syntax, wie man das vielleicht kennt aus C# oder auch aus neueren JavaScript Version. Und mit solchen Coroutines zu arbeiten in so einer Event Loop und für diese Event Loop nutzt man das, was eben auch in Node benutzt wird, die libuv. Da gibt es einen Webserver, der heißt uvicorn, also von dem Unicorn sind wir beim uvicorn gelandet. Die macht dieses low level Zeug und die kann man dann mit verschiedenen Frameworks einsetzen, sogar auch mit Django. Seit der neuesten 3.2er Version geht das auch in Django, dass man libuv einsetzt und unten drunter halt dann asynchron ist. Und ein Schritt sind sie da auch noch weiter. Ich hatte ja vorhin gesagt, es gibt WSGI, also Web Server Gateway Interface und es gibt noch ASGI, das ist das Asynchronous Server Gateway Interface, das versucht, genau das zu machen, was WSGI für synchrone Aufrufe gemacht hat, was lange Zeit ein Problem war, dass man, wenn man WSGI benutzt hat, egal was man als Low Level und Implementierung hatte, konnte man nichts asynchrones machen, weil das gab dieses Interface halt nicht her. Und jetzt gibt es halt das dieses ASGI, dass das macht. Aber es hat dieselben Kinderkrankheiten, die du gerade genannt hast. So die Datenbank Treiber und so, die sind alle noch synchron oder ganz viele. Und das heißt, oft kann ich damit zwar superschnell an eine Million Clients Hello World ausliefern, aber ich kann das Hello World nicht aus der Datenbank ziehen. Also es gibt schon ein, zwei Treiber dafür und diese haben natürlich noch bei weitem nicht den Reifegrad, wie die alten synchronen Treiber. Und ja, da muss man halt gucken. Es bietet sich so ein bisschen an, so im Machine Learning Bereich, wo Python relativ stark vertreten ist, obwohl das ja auch nur Glue Code ist. Das ist ja alles kein Python, sondern das ist ja alles C, C++ und irgendwelche Sachen, die auf der Grafikkarte, auf der GPU laufen. Und mit Python wird das ja zusammengeklebt. Und dass man da viele Anfragen annimmt, die asynchron bearbeitet, die irgendwie in so Queue stellt und dann relativ gut viele Clients gleichzeitig bedienen kann, weil die dann diese Machine Learning Aufgaben auf einem anderen Server laufen oder wirklich in anderen Prozessen, also wo so ein Mixed Modell ist. Man hat asynchron und man hat auch noch mehrere Prozesse dabei, was aber das Programmiermodell jetzt nicht wirklich einfach macht. Das ist relativ fehleranfällig, weil du liest halt den Code nicht mehr so von oben nach unten und weiß was passiert, sondern du musst in deinem Kopf irgendwie konstruieren, wie denn jetzt die Sprünge da so sind. Und zwar Sprünge, die nicht irgendwie durch Anweisung wie Break Continue oder sonst wie, wo man einen Sprung sehen könnte im Source Code, sondern einfach nur durch die Event Loop, wo der Scheduler/Dispatcher oder wie man den auch nennt in der jeweiligen Event Loop halt viel hin und her schaltet. Das ist echt nicht einfach. Die await/async Syntax hat es ein bisschen einfacher gemacht, weil da kann man solche Sprung Punkte deutlich besser erkennen, aber der Weisheit letzter Schluss ist das auch noch nicht. Und also Django unterstützt das zwar, aber alle Standard, alle Extensions, die es noch so gibt, oder Plugins, sowas gibt es ja auch in der Rails Welt glaube ich ganz viel. Ich weiß nicht, oder Middlewares oder wie immer man das auch alles nennt. Die sind dafür auch alle nicht wirklich ausgerichtet. Das heißt, da muss ich auf der grünen Wiese was machen und alles selber machen. Und da verderbe ich mir das auch so ein bisschen, wo ich aber sag: Nimm doch Django, nimm doch Rails, da gibt es ja alles für. Ja aber es gibt alles nicht in asynchron, also jedenfalls in der Python Welt, über die Rails Welt kannst du ja vielleicht noch mal berichten, wie es da so aussieht mit solchen Extensions oder Middlewares oder wie die überhaupt heißen, kenne ich mich auch nicht ganz genau aus.

Lucas: Also grundsätzlich Libraries heißen Gems, aber ja, an der Stelle geht es halt auch um ein Rails-spezifische Gems oder datenbankspezifische Gems. Und da ist es definitiv so, dass ganz viele Sachen eben nicht in async da sind. Da finde ich, kann man dann auch noch mal kurz auf die Node Seite gucken in Node.js ist es ja so, dass grundsätzlich erst mal alles async verfügbar ist, alle Datenbank Treiber, alles was hat, HTTP-Calls machen möchte oder so was ist immer asynchron. Was womit man eigentlich nie schon mal das Problem hat erst mal eine Library zu finden, weil die sind halt da. Auf der anderen Seite muss man auch ganz klar sagen jemand wie ich, der auch schon Node-Projekte gemacht hat, da fehlen einem ja auch immer noch ganz viele Libraries, die man halt aus einer Ruby-Welt einfach gewohnt ist. Und da weiß man halt ja okay, hier das und das, das sind super stabile Ruby-Libraries, die ich benutzen kann und die fehlen mir dann oft in Node auch. Aber das ist dann halt eher nicht in dem IO-Bereich, sondern eher in diesem Businesslogik Bereich, wo man halt einfach super coole fertige Frameworks findet, auf die man sich verlassen kann, wo man halt weiß hier, wenn ich File-Uploads brauche, dann nehme ich diese Library, die funktioniert in Produktion, die ist klasse, wo ich dann in Node dann erst mal ein bisschen rumsuchen muss, bis ich irgendwas finde, was einigermaßen okay ist und dann muss ich das noch zusammenstöpseln mit allen möglichen Sachen. Ja, das ist da aber für mich so als zwei Sprachen, die ich halt sehr, sehr gut beherrsche, JavaScript und Ruby ist, dass da immer eine sehr schwere Abwägung da zu finden. Und dann ist es für mich halt so, dass wäre auch das, was ich heute empfehlen würde, wenn man Rails macht, tatsächlich nicht auf dieses Non Blocking IO zu setzen. Dafür gibt es einfach noch nicht genug Sachen, sondern eher auf Puma zu setzen. Auch wenn das halt an bestimmten Stellen dann nicht die Vorteile hat, die man in so einem Non Blocking IO hat. Eine andere Entwicklung, die es in Ruby noch gibt seit Ruby 3.0, ist halt, dass es noch Ractor gibt. Das ist eine Actor Abstraktion, um mit diesen ganzen Problemen umzugehen. Aber da kenne ich überhaupt gar keine produktionsreifen Sachen, die da drin geschrieben sind. Das ist erst mal in Ruby mit dabei, könnte auch noch interessante Entwicklungen geben. Das ist dann eher so dieses Modell, was man vielleicht aus Erlang und Konsorten kennt. Könnte aber auch noch interessant werden, was da kommt. Also ich persönlich beobachte sowohl diesen Bereich von diesem async-Ruby als auch den Bereich von Ractor, um zu sehen, ob da was passiert. Aber in Produktion würde ich das Stand heute noch nicht benutzen, um diese Optimierung durchzuführen. Ich weiß nicht. Ich glaube auch wenn man jetzt nochmal auf unsere ursprüngliche Sache guckt GitHub. Ich glaube auch GitHub wird nicht diese zwei Sachen in Produktion benutzen, sondern andere Tricks verwenden, um damit umzugehen.

Christoph: Ein Problem, was wir auch haben von der ursprünglichen Fragestellung, warum man das eigentlich einsetzt, ist ja, wir wollen ja CPU intensive Sachen vielleicht machen und haben jetzt schon mehrfach gesehen Okay, eigentlich programmieren wir drum herum, weil wir sagen Na ja, das eine schieben wir nach C, das andere in die Datenbank und machen doch relativ wenig Anwendungs-Code sozusagen, oder oder sozusagen trivialen Anwendungs-Code. Und dieses async-Modell unterstützt ja eigentlich so was nicht. Also eigentlich darf ich ja nicht viel Zeit in so einem async Task heißt das in manchen Frameworks halt verbrauchen, weil da wird ja nur einer dieser Task gleichzeitig abgearbeitet, so ne Event Loop kann zwar eine Million Tasks verwalten und ganz schnell durch switchen, schneller als das Betriebssystem, aber CPU Zeit kann ich da halt auch nicht machen. Und ich hatte es vorhin bei dem Machine Learning angedeutet, da muss man halt auch schon wieder drumherum programmieren, also die eine Performance Optimierung torpediert sozusagen die anderen beiden, die wir uns jetzt mal so überlegt hatten, was denn so passiert. Das finde ich das Traurige oder das Spannende, wie sich das entwickeln wird, sozusagen, wie man das irgendwie unter einen Hut kriegt. Und andere Sprachen muss man einfach auch wieder eingestehen, die in vielen Bereichen nicht so komfortabel sind. Nehmen wir mal Go, die können das halt, die haben mit ihren Goroutinen. Da kannste halt CPU Zeit verballern. Die sind aber sehr leichtgewichtig, weil die im User-Space gescheduled werden, also keine Betriebssysteme, Prozessoren oder so dahinter sind. Und die können dann sozusagen das aus beiden Welten ganz gut verbinden. Also ganz viele Anfragen gleichzeitig und auch noch kann ich da CPU Zeit verballern und es sowieso viel schneller. Also von daher ja, ich bin da gespannt, wie sich das so ganz entwickeln wird dabei. Auf jeden Fall sind für mich da aber noch nicht so die Probleme eigentlich wirklich alle gelöst. Es geht in viele Richtungen gerade. Aber so dieses es am Anfang ja dann ist das schnell genug für GitHub, dann ist das schnell genug für dich, will ich jetzt sagen. Ja, das stimmt so erstmal nicht. Da muss man schon mal genau drauf schauen. Was will ich denn machen später? Welche Kompromisse gehe ich ein? Also werde ich doch doch noch zum SQL Profi. Also ich bin Full-Stack ich kann Rails und SQL.

Lucas: Ja, also das sollte man auf jeden Fall im Hinterkopf behalten. Ich merke einfach, wenn ich Anwendungen schreibe, dass viel von dem, was ich schreibe ist entweder SQL oder Frontend und das die Mitte ist dann doch sehr dünn, weil sie eigentlich hauptsächlich Sachen durchreicht. Und da ist für mich immer noch und da bleibe ich auch bei dem, was ich gesagt habe, in der Rails-Folge. Ich nehme nichts zurück von dem, was ich in der Rails-Folge gesagt habe. Hat halt Rails immer noch - und das sehe ich, also Django habe ich nicht so viel Erfahrung mit wie du, aber ich habe schon mal ein bisschen was mit Django gemacht. Es hat einfach viele von diesen Sachen, wenn man Server-Side gerenderte Anwendungen schreibt, die einfach mit dabei sind, die sehr komfortabel sind. Die ich sowohl in Go als auch in Node als auch in anderen Ökosystemen dann doch schnell vermisse an Grundfunktionalitäten und guten Libraries, die da sind. Aber ja, ich tausche das ein gegen eine Sprache, die halt ein Global Interpreter Lock hat und die damit diesen Trade off hat zwischen wie gehe ich damit um, wenn ich dann CPU intensive Tasks mache? Und wie gehe ich aber auch mit IO-bound Problemen um?

Christoph: Ja, oder du musst einfach halt noch mal drei Monate ins Projekt, Performance Optimierung machen, wie du gerade gesagt hast, da gibt es vielleicht auch Kompromisse, wo man sagt, vielleicht könne ich mir schöneres vorstellen. Also ich meine, ich tüftelt gerne an so Sachen und freue mich dann. Ich meine klar, ich will das auch gar nicht in Abrede stellen. Ich würde Django auch empfehlen und ich würde auch Rails empfehlen, aber immer zu sagen, so macht euch mal vorher so ein paar Gedanken über diese Dinge, über die wir jetzt alle gesprochen haben. Welche Daten wollt ihr verarbeiten? Wie ihr wollt ihr gleichzeitig verarbeiten? Was ist denn so euer Aufkommen? Ihr seid zwar bestimmt nicht GitHub, aber ihr seid auch nicht der Hasenzüchterverein. Also von daher guckt da mal. Noch eine Sache, die ich interessant fand, so bei der Recherche, wenn man das mal so langsam an der Stelle abschließen wollen. Von diesem Problem. Fand ich ja das hier mit GitHub und auch bei Instagram, also den größten Usern von Rails und Django. Beide Schwierigkeiten hatten auch zum Beispiel mit der mit der Start-Up Zeit. Das kann man in den Engineering Blogs der beiden Unternehmen nachlesen. Und da bin ich fast vom Stuhl gekippt, dass es da hieß die brauchen eine Minute zum Hochstarten. Da dachte ich ja, was machen die denn da, haben die einen JEE-Server noch mal in Ruby nachimplementiert oder was ist da los? Also von daher weiß ich nicht. Also für mich passt das halt nicht so in die Zeit, wo beide so Sachen machen wie oder sich schmücken damit. Und was ich auch grundsätzlich befürworte, dass man sagt, wir machen jetzt Continuous Deployment. Und dann muss ich eine Minute warten, bis die Anwendungen da oben sind. Das passt für mich gar nicht so gut zusammen. Also weiß ich nicht irgendwie. Ich meine, das liegt wahrscheinlich mit daran, weil die relativ monolithische Anwendungen haben und dann irgendwie ein paar hunderttausend Zeilen Code parsen. Und es sind halt gar keine kompilierten Sprachen, die werden eingelesen, geparsed und vielleicht in Bytecode verwandelt, jedenfalls in Python. Ich weiß nicht, wie es bei Ruby ist, wahrscheinlich auch wenn es eine VM ist, also wird dann verwandelt und so. Und bis es dann hochkommt. Das ist natürlich auch eine Sache, die man sich vielleicht auch überlegen muss. Ich gehe jetzt gar nicht davon aus, dass unsere Kunden vielleicht so was hätten wie: Wir haben jetzt ein Riesending und starten dann so lange. Aber ich kann mir vorstellen, wenn ich zum Beispiel so als on demand gibt es ja auch so oder Function-as-a-Service. Da sind die ja schon mal fast raus, wenn sogar relativ lange Start up Zeiten haben. Und wie gesagt, Function-as-a-Service ist wahrscheinlich das Extrem. Aber es gibt ja auch diese, man horcht auf einem Port und wartet, bis dann eine Request reinkommt und fährt dann eine Anwendung hoch. Aber das kann ich mir da vorstellen, dass man relativ schnell in solche Probleme geht. Also wenn es nicht der Microservices mit 500 Zeilen ist, sondern ich komme dann auf einmal auf 50.000 Zeilen. Das finde ich schon noch Es gehört zu einer normalen Anwendung vielleicht dazu. Dann will ich glatt behaupten, macht sich das schon bemerkbar. Und da sind wir vielleicht jetzt, müssen wir mal sehen. Ich weiß, dass, wenn wir jetzt noch mal da doch auf die Ursprungsthese geht, das ist alles nicht so einfach, dass ja Instagram und GitHub auch an solchen Dingen einfach auch noch mal mit ganz speziellen Tricks vorbei arbeiten oder anderen Sachen. Also ich weiß, jetzt bei Instagram gibt es einen schönen Blogpost, den verlinken wir dann auch, wo die einfach mal den Garbage Collector abschalten, um Performance zu kriegen. Hört sich ein bisschen komisch an. Vielleicht zur Erklärung Python hat eigentlich ein Reference Accounting. Das heißt, immer wenn ein Objekt irgendwo hingegeben wird, wird dieser Zähler, wie viele Referenzen auf das Objekt gibt es erhöht und wenn es wieder freigegeben wird ein und wie wird es wieder verringert um einen wenn er Null ist, wird das einfach gelöscht. Und das ist problematisch, wenn man zyklische Referenzen hat. Wenn das eine auf das andere Objekt verweist, aber sonst keins, dann ist dann Referenz Count eins. Aber sie sind eigentlich gar nicht mehr erreichbar. Deshalb gibt es auch normalen Garbage Collector, der sozusagen alles abgeht an Objekten und dann die Sachen auch noch rauswirft. Und ja, dann haben sie gemerkt, das macht Schwierigkeiten, wenn Sie Prozesse forken… wollen wir gar nicht in die technischen Details eingehen. Aber es ist halt relativ coole Performance Gewinne und Speicher Gewinne vor allem diese da rausgeholt haben. Und das sind jetzt auch so Sachen, die wir doch keinem normalen Kunden erst mal empfehlen. Du kannst super Rails nehmen, du musst halt nur irgendwie mal da eingreifen und den Garbage-Collector abschalten oder XY in den inneren Eingeweiden machen. Das kann man ja keinem Kunden guten Gewissens verkaufen. Würde ich auch nicht machen wollen. Instagram und GitHub können sich so was leisten. Die haben genügend EntwicklerInnen, wo man das betreuen kann. Aber ich sag mal für so typische Kunden von uns, so Mittelstand oder auch von mir aus auch Konzerne, die gar nicht in IT tätig sind, sondern einfach Konzerne sind und IT-Abteilung haben, die auch groß sind, können so was nicht leisten.

Lucas: Ja, also in Ruby hatte es ganz lange Tradition, dass größere Unternehmen den Garbage Collector und andere Teile optimiert haben. Also, dass sie halt ein Ruby kompiliert haben, mit einem eigenen Garbage Collector oder mit einem gepatchten Garbage Collector und so weiter. Eine ganze Weile gab es zum Beispiel diese Ruby Enterprise Edition. Das war ein Ruby, was halt eine gepatchte Version von Ruby ist, wo halt ein anderer Garbage Collector und so weiter drin war. Also auch andere Memory Allocator beispielsweise drin war und so weiter und so fort. Das ist heute tatsächlich nicht mehr so verbreitet. Da hat sich glücklicherweise so ergeben, dass zum Beispiel auch GitHub in Ruby, also in den Haupt-Ruby Interpreter Pull-Requests gestellt haben, um da die Garbage Collection schneller zu machen und so weiter. Das ist also nicht mehr so verbreitet. Aber ich vermute trotzdem, dass GitHub auch heute noch nicht, das Standard-Ruby laufen lässt, sondern ein selbst kompiliertes Ruby mit irgendwelchen Optimierungen und so weiter. Und das war aber tatsächlich auch bei meinen Projekten bisher noch nie nötig, auf so etwas zurückzugreifen. Aber ich hatte auch noch nicht Instagram oder GitHub als Kunde. Aber so auf diesem etwas größeren Scale, aber nicht riesigen Scale hat das auf jeden Fall gereicht. Und da merkt man tatsächlich auch in einigen Ruby Versionssprüngen hat man gemerkt, dass da was eingeflossen ist, was es schneller gemacht hat. Was ich sehr gut finde, weil dann halt solche… Also das war früher wirklich vor sechs, sieben Jahren, da war es wirklich noch üblich, dass man irgendwie ein Ruby hatte und dann hat man sich da noch irgendein Patch mit reingezogen oder so was. Das ist Gott sei Dank heute nicht mehr so!

Christoph: Ja, das ist gut.

Lucas: Aber wir haben ja auch schon über dieses ganze Thema C-Extensions gesprochen. Und das C-Extensions dieses unangenehme Problem haben, dass sie nicht mehr memory-safe sind und dann vielleicht den Ruby Prozess oder den Python Prozess mit runterreißen. Eine Alternative, die man dann natürlich auch immer hat, ist tatsächlich einzelne Services in einer anderen Sprache zu schreiben. Also wenn die Architektur das hergibt, dass man sagt diese spezielle Sache keine Ahnung, das ist jetzt das Ranking Algorithmus, der ist super komplex und den möchte ich nicht in Ruby schreiben, sondern den schreibe ich jetzt in weiß ich nicht, in Rust ja, dann wäre ja eine Alternative, anstatt den dann einzubetten in meine Anwendung tatsächlich einen eigenen Service draus zu machen. Also das ist auch etwas, was GitHub definitiv an einigen Stellen macht einzelne Funktionalitäten in anderen Programmiersprachen zu schreiben. Und ich glaube, das ist auch etwas, was in der Python Welt wahrscheinlich auch verbreitet ist zu überlegen, an welchen Stellen gehe ich vielleicht aus meinem Monolithen raus oder vielleicht habe ich gar keinen Monolithen, sondern ich habe sowieso so eine Self-Contained-System-Architektur oder so etwas und entscheide mich, einzelne Services in einer anderen Sprache zu schreiben, die bestimmte Vorteile hat, weil sie zum Beispiel… vielleicht schreibe ich ein Service in Note, weil dieser Service ist total IO-bound und macht gar keine CPU Sachen, sondern der ist eigentlich nur so eine Durchleitungskomponente. Dann schreibe ich die in Node oder ich habe irgendeine super komplexe CPU intensive Tasks und die mache ich dann in Go oder in Rust oder was auch immer. Ich glaube, das ist auf jeden Fall auch eine Sache, die man auf dem Scale machen muss.

Christoph: Ich glaube, das fängt viel früher an als auf deren Scale. Das kennt man, das neue Services, die dann Performance-Kritisch sind, in einer anderen Sprache gemacht werden. Java, Go, Rust what ever, vielleicht sogar in C oder C++. Das passiert, ist natürlich schwierig, wenn man jetzt die Ausgangsthese die ich ein bisschen angezweifelt hatte. Wenn das für dich reicht, dann reicht für dich dann Rails auch, oder Django wird für dich auch reichen. Und diese Firmen, ich nehme jetzt mal Beispiel GitHub, weil das können wir verlinken, da schreiben Sie auch über den Service, den sie dann einfach auch in Node geschrieben haben. Finde ich auch gut, ist auch ein total valides Pattern, das so zu machen. Diese Aussage ist dann einfach wie gesagt so einfach zu sagen, es reicht für die, ne für die reicht es nicht, weil die machen ja auch so alles, was wir heute so besprochen haben, allein auf der Framework und auf der Interpreter Ebene und auf der Datenbank Ebene. Und sie macht auch noch ganz viel Architektur Arbeit um so was zu ermöglichen, das ich überhaupt einen Performance kritisches Teil meiner Anwendung überhaupt rausziehen kann. Also Monolithen hast du gerade gesagt, geht das ja gar nicht. Also ich muss ja schon irgendwie was machen und ich glaube, was ganz viel ist und was wir auch schon wahrscheinlich unseren Kunden relativ frühzeitig empfehlen würden, ist natürlich viel Sachen, die mit der Architektur zu tun haben. Du machst ja auch Schulungen zur Web Architektur und da zählt auch so was in Caching, CDNs, Komprimierung oder so. Vielleicht kannst du darüber noch mal ein bisschen erzählen, was da noch so alles ist, was sozusagen… Also wo ich jetzt keine komplette Web Architektur Schulung, wo man sagt, da entlasten wir eigentlich die eigentliche Anwendung. Das wäre das Ziel.

Lucas: Also das ist für mich tatsächlich etwas, wo wir, habe ich am Anfang gesagt, bestimmte Sachen sind tatsächlich etwas, wo es nicht mehr über Ruby oder Python geht, sondern was ich immer machen würde, egal ob das eine Java Anwendung, eine Go Anwendung oder sonst was ist. Wo wir immer drauf achten müssen wir um eine performante Webanwendungen zu schreiben und die leider oft übersehen werden, weil sie nicht einfach irgendwie out of the box aus irgendeinem Framework rausfallen können, sondern weil wir uns dafür mit HTTP, mit Web Architektur usw. beschäftigen müssen. Ganz klassisches Beispiel dafür wäre Caching. Wie setze ich meine Anwendung so auf, dass sie möglichst oft nichts tun muss, weil der Client sie einfach aus sich selbst heraus beantworten kann oder weil das irgendein CDN beantworten kann. Das wäre ein Beispiel, wo das helfen kann. Genauso sich mit CDNs zu beschäftigen, ist das für meinen Kunden eine Option, ein CDN vor meine Anwendung zu setzen? Oder ist es für meinen Kunden Option, ein CDN neben eine Anwendung zu setzen, dass nur bestimmte Sachen ausliefert, um einfach bestimmte Sachen schneller auszuliefern, um meine Anwendung zu entlasten, die dann solche Sachen nicht ausliefern muss. Auch das würde ich ganz früh schon anfangen, das würde ich nicht erst machen, wenn wir skaliert haben, sondern das würde ich direkt von Anfang an in Betracht ziehen, was sollten wir da tun in diesen Bereichen. Ein weiteres Thema wäre: Welche Sachen können wir vielleicht in dem Reverse Proxy elegant lösen? Wo gibt es Reverse Proxy, der für vielleicht bestimmte Optimierungen für dich durchführen kann, Reverse Proxy der Build Optimierungen durchführen kann, oder vielleicht die ganzen Sachen, die ich in der HTTP-Folge gesagt habe - HTTP 2 aktivieren, vielleicht sogar mal HTTP 3 ausprobieren. Komprimieren der Antworten mit GZIP oder Brotli oder so etwas. Das sind Sachen, die sollten wir auf jeden Fall frühzeitig machen. Wir sollten frühzeitig darauf schauen, dass wir die Anzahl der Requests einfach vielleicht irgendwie reduziert bekommen, weil wir nicht eine riesen Kaskade an Request aufbauen, die dieser Request dann erst ausgeführt werden, wenn wir diese Antwort bekommen haben und so weiter. Diese Sachen sehe ich einfach in jedem Projekt immer wieder, dass die vorkommen, weil die Leute darauf nicht so achten. Da können uns auch gerade die Browser-Tools bei helfen, da einfach nochmal in den Network-Tab reinzuschauen, was machen wir da eigentlich da hintereinander welche Request warten da vielleicht auf andere Requests, die das gar nicht müssten. So etwas finde ich grundsätzlich immer wichtig und sollten wir immer frühzeitig drauf schauen, wenn wir eine Webanwendung bauen und vollständig unabhängig von der Backend-Technologie. Und dann gibt es natürlich noch das ganze Thema: Welche Sachen kann ich vielleicht auf dem Client lösen und nicht auf dem Server? Das ist ja auch eine Möglichkeit meinen Server zu entlasten, indem ich einfach bestimmte Sachen gar nicht auf den Server mache, sondern auf dem Client. Auch das eine ganz klare Architekturentscheidung mit Vor- und Nachteilen, die wir treffen sollten, wo wir uns einfach mit beschäftigen sollten, wenn wir unsere Anwendung planen, sag ich mal.

Christoph: Absolut. Dem muss ich hundertprozentig zustimmen. Das ist ja so ähnlich wie bei sonst einer Programm Optimierung. Ich sollte immer auch gucken auf der algorithmischen Seite ob da alles in Ordnung ist, bevor ich Takt Zyklen zähle und dann irgendwelche low level Optimierung mache. Das ist klar und deshalb ist Architekturarbeit natürlich total wichtig und gerade dabei wichtig. Aber ich würde vielleicht zum Abschluss eine provokante These in den Raum stellen: Bei Rails und Django musst du dich sofort drum kümmern. Am besten so Tag 1 des Projekts kümmerst du dich drum. Jetzt mache ich aber meine Anwendung in Go und dann brauche ich das nicht, weil na ja, was du so erzählt, das kann man HTTP 2 Server und der kann komprimieren und so was kann man machen. Dann sag ich: Ja das hat Go jetzt aber out of the box in seiner Standard-Library. Ein HTTP 2 Server löse viele Probleme mit. Der hat auch die CPU Power um diese Komprimierung und so zu machen. Also in der Rails Welt und in der Django Welt machen das ja die Server die davor stehen, haben wir jetzt heute gehört. Unicorn, Puma, uwsgi, da wird das ja an anderer Stelle gemacht. Und das heißt ich habe einen ganzen Wust dadrum, so an Infrastruktur, mit der ich mich auch auskennen muss, die ich manchmal gar nicht brauche und wo es vielleicht den Unterschied zu macht zwischen GitHub - die brauchen CDN, die brauchen eine große verteilte Sache über mehrere Datenzentren vielleicht, weil ich sagen kann: Ja, mein Mittelständler, der kommt aber mit einer Instanz oder zwei Instanzen in Go aus, so zur Redundanz oder zur Ausfallsicherheit und kriegt so was mitgeliefert. Das heißt nicht, dass ich mir da jetzt um Architektur keine Gedanken machen muss, aber die Implikationen, die sich daraus ergeben, sind halt ganz anders - die technischen Implikationen. Sagen wir mal wie gesagt, in Architektur haben wir festgestellt, wir wollen schon auf der HTTP Ebene ganz viel Optimierung machen. Da muss ich halt wissen okay, welcher Server kann denn zum Beispiel auch schon HTTP 2, den ich da mache und der kann aber auch zum Beispiel das WSGI Protokoll oder ich weiß nicht, kann der Puma HTTP 2?

Lucas: Nein.

Christoph: Nein, kann er nicht, siehst du. Dann bräuchte man wahrscheinlich davor wieder noch mal einen Proxy. Und das hat also eine große Anzahl an Implikationen, die ich dann irgendwie anders lösen muss. Am Framework vorbei wieder, die ich woanders dann zum Beispiel mitbekomme auch in der Java Welt. Der Tomcat kann schon einiges, obwohl er meistens nicht direkt ans Internet gesetzt wird, sondern auch hinter irgendeinem Proxy steht. Und deshalb. Architektur kann man nicht auslassen, auch wenn ich weiß ja, wahrscheinlich komme ich damit klar, aber wie viel mehr muss ich dann noch tun? Und wo finde ich das und wie viel muss ich zusammenstöpseln, und wie viel technische Ahnung muss ich davon haben für Konfiguration, Deployment und so weiter. Das explodiert dann bei unser beiden schönen Rails und Django-Frameworks, die wir beide auch total gut finden. Das sollte man noch beachten. Würde ich mal so in den Raum stellen, dass das dann an anderer Stelle doch noch deutlich leichter ist.

Lucas:
Das ist finde ich eigentlich auch eine ganz gutes Wrap-Up, ich glaube, dass es tatsächlich so ist: Also ich kenne jetzt auch Go seit einiger Zeit aus dem Projekt und habe damit meine Praxiserfahrung gemacht, ich würde dir zustimmen. Bestimmte Sachen in Go muss ich mich nicht drum kümmern, wo ich mich in Ruby frühzeitig drum kümmern muss, weil aus Performance Gründen. Aber dafür ist es dann halt so, wenn ich mir mein Rails anschaue, dann habe ich bestimmte Sachen gelöst, wo ich in Go entweder mühsam eine Library für finden muss, die dieses Problem löst, da geht es dann eher um solche Web Sachen sage ich mal. Meine File-Uploades mit Bild Optimierung und leg es bitte in S3 ab und so weiter und so fort. Ist in Rails mit eingebaut. In Go muss ich es entweder selber schreiben oder muss es mir halt irgendwo zusammensuchen und finde dann nichts in der Qualität wie das, was ich schon aus der Railswelt kenne. Das heißt also, verschiedene Sachen sind in diesen Ökosystemen quasi for free dabei, aber die anderen Sachen fehlen dann genau an der Stelle. Also das wäre dann meiner Meinung nach so ein bisschen so der Trade off zwischen diesen beiden Ökosystemen. Also ich sage jetzt mal Go/Node auf der einen Seite und Ruby/Python auf der anderen Seite.

Christoph: Sehe ich genauso. Das ist ja auch gerade weil das alles so dabei ist und das Programmier-Modell einfacher ist und sagen wir mal, höhere oder bessere, leichtere Abstraktionen bestehen, empfehle ich auch so was wie Django oder Rails. Da stimme ich zu. Aber das Problem ist ja halt. Also in der idealen Welt würde ich mir wünschen, du kannst das nehmen und du musst aber den anderen Kram dann nicht auch noch alles dir aufschaufeln. Jedenfalls nicht im ersten Schritt. Ist natürlich ok, wenn man das macht, aber du kannst damit starten und du wirst damit erstmal nicht so belästigt. Die Befürchtung ist bei mir das sozusagen der Kern, der Rails/Django Kern meiner Anwendung, eigentlich nur noch den kleinsten Teil ausmacht und ich viele andere Arbeiten drumherum machen muss, um mit den ganzen anderen Unzulänglichkeiten umzugehen. Wobei ich dann manchmal sagen muss, das ist nur meine persönliche Meinung dazu, für mich. Das kann man auch anders sehen. Da gibt es überhaupt keine Wertung. Ich habe es da manchmal lieber, mich nicht mit Infrastruktur rumschlagen zu müssen, sondern dann lieber irgendwas in Go zu programmieren, wo es in Rails, schon eine fertige Library für gibt. Dann kann ich halt in einer schönen Programmiersprache programmieren und mach nicht irgendwelche YAML Konfigurationen damit mein Caching und mein CDN funktionieren. Aber das ist reine Geschmackssache. Aber zu welcher Conclusio kommen wir denn jetzt mit der Aussage: wenn es schnell genug für GitHub ist, dann ist das schnell genug für dich? Können wir wahrscheinlich so gar nicht mehr stehen lassen in der einfachen Form, oder?

Lucas: Genau, ich würde ich auf jeden Fall sagen, also da muss man mindestens mal eine bis drei Fußnoten dranhängen an dieser Aussage, weil man zumindest mal darauf hinweisen muss, dass man an bestimmten Stellen Optimierungen durchführen muss, die einem keiner jetzt erst mal so einfach so sagt. Da steht jetzt nicht einfach eine Liste: Folge Dinge musst du auf jeden Fall tun oder die nimmt dir das Framework einfach magisch ab. Das ist das eine und das andere, aber da würde ich auch behaupten, da befreit einen auch Go nicht von, ist halt Architekturarbeit, die halt zu leisten ist, die man auch vielleicht früher leisten muss, als man erstmal denkt. Zum einen halt der Schnitt des Systems in verschiedene Subsysteme, damit man auch mal die Möglichkeit hat, sie auszutauschen durch irgendwas anderes oder so was wie Caching, CDNs, Aufteilung zwischen Client und Servern. Das muss ich auf jeden Fall machen. Da hilft mir kein Web Framework der Welt und kein Ökosystem der Welt, das einfach magisch zu lösen.

Christoph: Gut zusammengefasst! Uns fehlt nur noch der knackige Slogan, also: Rails, aber mit Architektur und Infrastruktur.

Lucas: Das stimmt. Nur brauchen wir da vielleicht was knackigeres.

Christoph: Ganz richtig. Und der letzte Tipp ist natürlich, wo du gerade sagtest: Wir müssen so viel wie möglich machen, damit die Anwendung nichts zu tun hat. Da machen wir es einfach unattraktiv. Dann hat es auch nichts zu tun. Da sind wir wieder beim Hasenzüchterverein.

Lucas: Sehr guter Trick. Cool. Super Christoph, dann danke ich dir für die viele Zeit, für die Hörerinnen und Hörer, die bis zu diesem Zeitpunkt durchgehalten haben. Noch mal die Erinnerung daran. Wenn ihr noch Fragen habt, die ja gerne in der QA-Folge, die wir in der Folge 100 veröffentlichen, dann schickt sie uns gerne. Wir freuen uns. Wir haben schon einiges erhalten, aber wir haben auf jeden Fall noch ein bisschen Platz. Wenn ihr noch welche habt, dann schickt sie uns gerne. Ja und sonst würde ich mal sagen, dir noch einen schönen Tag und dann bis zum nächsten Mal.

Christoph: Danke dir auch, Ciao.

Alumnus

Lucas war bis August 2023 Senior Consultant bei INNOQ. Er beschäftigt sich mit der Architektur, Konzeption und Umsetzung von Web Anwendungen in Front- und Backend. Er programmiert in Ruby und JavaScript und hilft bei der Entscheidung und Einführung verschiedener NoSQL Lösungen. Lucas ist Autor des Buchs “The Rails 7 Way”. Seine Stimme ist regelmäßig im INNOQ Podcast zu hören. Außerhalb seiner Arbeit beschäftigt er sich mit Open Source und Community Arbeit (wie die Organisation und das Coaching beim lokalen CoderDojo).

Senior Consultant

Christoph Iserlohn ist Senior Consultant bei INNOQ. Er hat langjährige Erfahrung mit der Entwicklung und Architektur von verteilten Systemen. Sein Hauptaugenmerk liegt dabei auf den Themen Skalierbarkeit, Verfügbarkeit und Sicherheit.