sixty

für Node-Dienste

Welche Funktion, welche Abfrage, welches Deployment.

Ein Agent, mit einem Befehl installiert, misst jede exportierte async-Funktion und jede Abfrage, die deine App macht — und weiß, welche Funktion welche Abfrage abgesetzt hat. Jedes Release wird mit dem davor verglichen, der Befund benennt also dein Deployment und nicht die Stunde.

Was es misst

Jede exportierte async-Funktion
Wie lange sie gedauert hat, und wie viel davon ihr eigener Code war statt etwas, das sie aufgerufen hat. „Ist mein Code langsamer geworden oder das, was ich aufgerufen habe“ ist hier eine Zahl statt eines Nachmittags.
Jede Abfrage, auf fünf Clients
pg, mysql2, postgres.js, der MongoDB-Treiber und Prisma — automatisch gefunden und instrumentiert, auch durch Mongoose und durch einen Connection Pool unter Last. Zurückgegebene Zeilen, Spalten, Antwortgröße, und für MongoDB die Anzahl der Netzwerk-Roundtrips, die ein einzelner Lesevorgang tatsächlich gebraucht hat.
Welche Funktion welche Abfrage abgesetzt hat
Der Teil, der den Rest lesenswert macht. Eine Abfrage wird der Funktion über ihr gutgeschrieben, ein N+1 taucht also auf der Funktion auf, die die Schleife enthält, statt sich über eine Abfrage zu verteilen, die immer in Ordnung aussah.
Was die Datenbank getan hat, um zu antworten
Postgres wird nach dem Plan jedes Statements gefragt — ohne je einen Parameter zu binden, es ist also kein Wert von dir beteiligt. MySQL wird stattdessen aus performance_schema gelesen: geprüfte Zeilen pro zurückgegebener Zeile, und ob überhaupt ein Index benutzt wurde. Beides kommt als derselbe Befund an: das liest jene Tabelle ohne Index.
Deine HTTP-Routen, rein und raus
Bediente Anfragen, und Zeit, die auf die API von jemand anderem gewartet wurde — ein langsamer Dritter wird also nicht mehr als dein langsamer Code gemeldet.

Was es findet

Jedes davon wird gegen das Release davor verglichen, der Befund benennt also die Änderung und nicht den Tag.

Ein N+1, das im letzten Release nicht da war

Eine Funktion, die einen Datenbankaufruf pro Anfrage machte, macht jetzt vierzehn. An das Deployment verankert, sie benennt also das Release, das es eingeführt hat.

Eine Abfrage, die angefangen hat, die ganze Tabelle zu lesen

Zeilen pro Aufruf sind von 30 auf 30.000 gegangen. Auf einer warmen Datenbank mit Staging-großen Daten bewegt das die Uhr kaum — und eine Woche später legt es die Produktion lahm.

Eine Abfrage, die ihren Index nicht mehr benutzt

Der eine Befund hier, der eine Ursache statt eines Symptoms benennt. Er kommt oft an, bevor überhaupt etwas langsam ist: die Tabelle ist noch klein genug, dass ein Scan in Ordnung ist, und Wochen später wird daraus ein Ausfall, wenn sie es nicht mehr ist.

Dein eigener Code, der langsamer wird

Zeit in der Funktion selbst statt in dem, was sie aufgerufen hat — getrennt, damit du beim ersten Versuch in die richtige Datei schaust.

Ein Lesevorgang, aus dem hunderte Netzwerkwartezeiten wurden

MongoDB beantwortet einen Cursor in Batches. Wenn ein Ergebnis über einen Batch hinauswächst, werden aus einem Lesevorgang dutzende oder hunderte aufeinanderfolgende Roundtrips — die Dokumente sind identisch, es bewegt sich also nichts im System außer der Uhr.

Etwas, das jetzt in einer Schleife aufgerufen wird

Pro Aufruf unverändert — gleiche Geschwindigkeit, gleiche Daten — aber hunderte Male aufgerufen, wo es früher einmal aufgerufen wurde.

Eine Abfrage, die still nichts zurückgibt

Erfolgreich, kein Fehler, kein langsamer Aufruf, und leer zurückgekommen, wo früher Zeilen kamen. Die häufigste Art, wie einer dieser Dienste kaputtgeht, macht die Zahlen kleiner, nicht größer.

Was in deinem Verlauf landet

Kein Dashboard zum Lesen. Eine Karte pro Befund, mit den Zahlen, dem Release, das sie verändert hat, und den Belegen, die dein Coding-Agent braucht, um die Lösung zu schreiben.

Das sind die Zahlen, die `pnpm e2e` ausgibt, auf dem Klon dieses Repositories bei irgendwem. Es setzt einen Dienst auf, liefert ein Release mit zwei echten Regressionen aus und erkennt sie — nichts hier ist also eine Zahl, die wir gewählt haben.

Zeilenorders.getUserOrders
30 Zeilen30.000 Zeilen1000×

Bei einem Refactoring ist eine WHERE-Klausel verlorengegangen, die Funktion holt also die ganze Tabelle und filtert sie in JavaScript. Auf einer warmen Datenbank hat das die Uhr kaum bewegt.

seit Release v2, gegen v1

N+1orders.enrichOrders
1 Abfrage14 Abfragen14×

Eine Abfrage ist in eine Schleife gewandert. Jede einzelne ist schnell, keine einzelne Anfrage sieht also falsch aus — die Anzahl ist das Einzige, was sich geändert hat.

seit Release v2, gegen v1

Wie es installiert wird

Gib es dem Coding-Agenten, den du ohnehin offen hast. Er liest das Repository, findet heraus, ob das eine Next-Konfiguration, ein Vite-Plugin oder einen Startbefehl bedeutet, und hängt es ein — und behebt dann in derselben Unterhaltung, was gefunden wird.

claude mcp add sixty \
  -e SIXTY_API_KEY=sixty_sk_… \
  -e SIXTY_ENDPOINT=https://ingest.sixty.sh \
  -- npx -y @sixty-sh/mcp

# or by hand, in your server entry:
import { init } from '@sixty-sh/node'
init()

Der Release-Bezeichner wird von allein von Vercel, Render, Railway, Fly, Heroku oder GitHub Actions abgeholt. Der erste Vergleich kommt nach deinem nächsten Deployment, weil es nichts gibt, womit man ein Release vergleichen könnte.

Was es nicht tut

  • MySQL und MongoDB melden keinen Planbaum. MySQLs EXPLAIN braucht echte Parameterwerte und MongoDBs explain einen echten Filter, und dieser Agent verwirft Werte in dem Moment, in dem er sie sieht — für MySQL wird die Schlussfolgerung deshalb aus performance_schema gelesen, und für MongoDB gibt es noch nichts.
  • Async-Generatoren werden von der Build-Transformation übersprungen. Einen zu umschließen würde messen, wie lange es dauerte, bis der Aufrufer einen Iterator bekam, und das ist nicht die Zahl, die irgendjemand will.
  • Der Agent misst, was er von JavaScript aus erreichen kann. Zeit innerhalb eines nativen Moduls oder in einem anderen Prozess ist Zeit, die er nur als Warten sehen kann.

Falls du wegen einer dieser Sachen hier bist

Finde heraus, was deine letzte Änderung angerichtet hat.

Probier es aus!

Du baust etwas anderes? Sieh dir Python-Dienste, Lovable-Apps oder React-Native-Apps an — diese Seiten sind anders, und was wir sehen können, auch.

Performance-Monitoring für Node, Postgres, MySQL und MongoDB — Sixty