Datenbanken aufwecken, ohne tote Verbindungen
Veröffentlichte Apps auf Mythex und ihre Datenbanken gehen schlafen, wenn sie niemand nutzt. Wenn eine App aufwachte, konnte sie die erste Anfrage 10 bis 12 Sekunden lang festhalten, weil sie versuchte, Datenbankverbindungen wiederzuverwenden, die während des allgemeinen Schlafs gestorben waren. Jetzt geben wir Spring-Boot-Apps Datenbankeinstellungen, die den Schlaf überstehen, und weisen den Agenten an, Apps jedes Stacks genauso zu bauen. Auf der Hosting-Plattform sank die Zeit bis zur ersten Antwort nach dem Aufwachen von 12,7 Sekunden auf 2,3 Sekunden.
Warum Apps und Datenbanken schlafen
Eine veröffentlichte Mythex-App läuft auf einer Maschine, die nach einigen Minuten Leerlauf pausiert, und ihre Datenbank ist serverloses Postgres, das nach fünf Minuten Leerlauf schlafen geht und bei der nächsten Anfrage aufwacht. Das hält eine ruhige App günstig: Sie zahlen für das, was die Datenbank tatsächlich genutzt hat, und eine schlafende App verbraucht keine Rechenleistung. Über die Abrechnungsseite haben wir in Datenbanken nach tatsächlicher Nutzung abrechnen geschrieben.
Der Haken: Die beiden schlafen getrennt, und sie sind sich nicht einig, was eine Verbindung ist.
Das Symptom: ein langsamer erster Besuch
Die meisten Server-Apps halten einen Verbindungspool: eine Handvoll offener Datenbankverbindungen, die bereitstehen, damit jede Anfrage nicht die Kosten für das Öffnen einer neuen tragen muss. Das ist das richtige Design für eine App, die immer läuft.
So sieht der Ablauf mit Schlaf aus:
- Die App läuft, mit einigen ungenutzten Verbindungen in ihrem Pool.
- Niemand besucht sie. Die Maschine der App pausiert, eingefroren, mit intaktem Pool.
- Die Datenbank geht schlafen und schließt ihr Ende jeder Verbindung.
- Ein Besucher kommt. Die App erwacht genau so, wie sie war, und hält noch Verbindungen, die die Datenbank nicht mehr kennt.
- Der Pool gibt eine dieser Verbindungen heraus. Sie muss erst herausfinden, dass sie tot ist, indem sie es versucht und wartet, bevor sie sie aufgibt.
In einer Spring-Boot-App blockierte das Prüfen dieser toten Verbindungen die erste Anfrage rund 10 Sekunden, bevor sie mit „Connection is not available“ fehlschlug. Für den Besucher hing die Seite einfach und zeigte dann vielleicht einen Fehler.
Warum es Spring-Apps am schlimmsten traf
Spring Boot ist ein beliebtes Java-Framework, und es nutzt standardmäßig einen Verbindungspool namens HikariCP. Spring kann diesen Pool über Einstellungen konfigurieren, auch wie viele ungenutzte Verbindungen gehalten werden.
Mythex gibt jeder App ihre Datenbank aber als einzelne DATABASE_URL im Format postgresql://user:pass@host/db. Spring kann dieses Format nicht direkt lesen. Also haben Spring-Apps auf Mythex ihren eigenen Pool im Code aufgebaut, indem sie diese URL zerlegt haben, und ein von Hand gebauter Pool ignoriert alle spring.datasource.*-Einstellungen von Spring. Selbst wenn jemand die richtigen Poolwerte gekannt hätte, hätten sie nirgends gewirkt.
Die Korrektur, Teil 1: Einstellungen, die Spring nutzen kann
Wenn ein Projekt eine Datenbank hat, bekommt es jetzt außerdem Umgebungsvariablen, die Spring nativ liest:
SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAMEundSPRING_DATASOURCE_PASSWORD, ausDATABASE_URLumgewandelt (mit erforderlichem SSL, außer die URL sagt etwas anderes).- Poolwerte, die den Schlaf überstehen:
| Einstellung | Wert | Warum |
|---|---|---|
| Mindestanzahl ungenutzter Verbindungen | 0 | Zwischen den Anfragen bleibt nichts offen, also kann nichts veralten |
| Leerlauf-Timeout | 30 Sekunden | Ungenutzte Verbindungen bald schließen |
| Maximale Lebensdauer | 2 Minuten | Jede Verbindung ausmustern, bevor sie alt werden kann |
| Verbindungs-Timeout | 5 Sekunden | Eine Anfrage nicht lange hängen lassen, wenn die Datenbank langsam antwortet |
Da keine ungenutzten Verbindungen gehalten werden, öffnet eine aufgewachte App eine einzige frische Verbindung, statt sich durch tote zu kämpfen.
Sind diese Variablen vorhanden, konfiguriert Spring den Pool selbst, und die Einstellungen greifen. Was das Projekt selbst gesetzt hat, gewinnt immer. Wir füllen nur fehlende Werte aus. Apps, die nicht Spring sind, ignorieren diese Variablen.
Das Detail, das es fast wirkungslos gemacht hätte
Spring ordnet Umgebungsvariablen seinen Einstellungen nach einer Regel zu, die leicht falsch zu machen ist. Die Einstellung minimum-idle muss als SPRING_DATASOURCE_HIKARI_MINIMUMIDLE geschrieben werden: Bindestriche entfallen, Wörter werden zusammengezogen. Schreibt man sie als SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE, was natürlicher aussieht, wird daraus eine andere, nicht existierende Einstellung (minimum.idle), und sie wird still ignoriert.
So einen Fehler übersteht jede Prüfung und bewirkt in der Produktion nichts. Deshalb prüft ein Test sowohl, dass die Werte gesetzt sind, als auch, dass keine Variable die Form mit Unterstrichen verwendet.
Die Korrektur, Teil 2: jeder andere Stack
Spring ist nicht das einzige Framework mit einem Pool, und wir können nicht jede Bibliothek in jeder Sprache vorkonfigurieren. Deshalb bekommt der Mythex-Agent, die KI, die Ihre App schreibt, jetzt für jeden Stack eine dauerhafte Anweisung: Veröffentlichte Apps und ihre Datenbanken schlafen, wenn sie untätig sind, deshalb kann eine offen gehaltene Verbindung bei der nächsten Anfrage tot sein. Er sollte:
- keine ungenutzten Datenbankverbindungen offen halten oder jede vor der Nutzung prüfen,
- bei einem Verbindungsfehler einmal erneut versuchen und
- die Datenbankeinstellungen verwenden, die die Plattform in der Umgebung bereitstellt, statt einen eigenen Pool aufzubauen, indem er
DATABASE_URLzerlegt.
Der letzte Punkt schließt den Kreis mit Teil 1: Eine App, die die bereitgestellten Einstellungen nutzt, übernimmt die schlafsichere Konfiguration automatisch.
Das Ergebnis
Gemessen auf der Hosting-Plattform, auf der wir veröffentlichte Apps betreiben, sank die erste Antwort nach dem Aufwachen von 12,7 Sekunden auf 2,3 Sekunden.
Was wir daraus mitnehmen
- Jede Schicht, die schlafen geht, muss sich darüber einig sein, was den Schlaf überlebt. Eine eingefrorene App und eine schlafende Datenbank haben jeweils eine vernünftige Entscheidung getroffen. Zusammen erwachte die App mit einem Zustand, der nicht mehr stimmte.
- Standards für Dauerbetrieb passen nicht zu Apps, die auf null skalieren. Verbindungen warm zu halten ist eine gute Idee, bis alles drumherum kalt wird.
- Geben Sie Frameworks ihre Daten in ihrem eigenen Format. Eine URL war für uns einfach und zwang jede Spring-App, einen Pool von Hand zu bauen, der die eigene Konfiguration ignorierte.
- Prüfen Sie, dass eine Einstellung so geschrieben ist, wie das Framework sie liest. Ein korrekter Wert unter dem falschen Namen bewirkt still nichts.
Wenn Sie neu bei der Funktionsweise von Datenbanken auf Mythex sind, lesen Sie die Datenbank-Dokumentation und unseren Erklärtext zu was Postgres ist.