für Ruby- und Rails-Apps
Einen zweiten Schritt gibt es nicht.
Rails hat einen dokumentierten Boot-Hook und einen Event-Bus, der ohnehin jede Abfrage bekannt gibt, dieser Agent braucht also weder einen Initializer noch einen Wrapper: Gem hinzufügen, zwei Umgebungsvariablen setzen, und Controller werden zu Operationen, denen ihre Abfragen zugeordnet sind. Alles unten passiert, ohne dass du eine Zeile Instrumentierung schreibst.
Was es misst
- Jede Controller-Action
- Benannt nach controller#action, der Verlauf liest sich also so wie deine Routendatei. Das Railtie umschließt sie beim Start — es gibt nichts einzubinden und nichts, woran man beim nächsten Controller denken müsste.
- Jede Abfrage, die ActiveRecord absetzt
- Abonniert über sql.active_record, den Bus, auf dem Rails ohnehin veröffentlicht. Zeilen, das normalisierte Statement, die Antwortgröße, und welche Action sie abgesetzt hat.
- Drei Datenbanken, nach ihren eigenen Regeln
- pg mit Abfrageplänen über EXPLAIN; mysql2 und trilogy nach MySQLs Quoting-Regeln gelesen statt nach denen von Postgres, weil eine Zeichenkette in doppelten Anführungszeichen im einen ein Literal und im anderen ein Bezeichner ist; und mongo, was Mongoid benutzt, mit der Befehlsform als Identität.
- Was die Datenbank getan hat, um zu antworten
- Postgres wird nach dem Plan jedes Statements gefragt — ein EXPLAIN mit generischem Plan, pro Statement gecacht, außerhalb des Spans des Aufrufers abgesetzt, damit es nie als dessen Arbeit gemessen wird, und ohne je einen Parameter zu binden, es ist also kein Wert von dir beteiligt.
- Deine eigenen Klassen, wenn du willst
- include Sixty::Instrumented macht jede öffentliche Instanzmethode zu einer Operation und lässt private Methoden und Accessoren in Ruhe. Oder Sixty.instrument(Stripe::Charge, :create) für eine Methode in der Klasse eines anderen.
Was es findet
Jedes davon wird gegen das Release davor verglichen, der Befund benennt also die Änderung und nicht den Tag.
Eine Abfrage, die ihren Index nicht mehr benutzt
Der eine Befund hier, der eine Ursache statt eines Symptoms benennt, und 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.
Ein N+1, das im letzten Release nicht da war
Der Defekt, dessen Rails am häufigsten beschuldigt wird, gefangen über die Anzahl statt über die Uhr: aus einer Abfrage pro Action wurden zwanzig, jede von ihnen schnell, und includes ist vor drei Wochen bei einem Refactoring herausgefallen.
Eine Abfrage, die angefangen hat, die ganze Tabelle zu lesen
Zeilen pro Aufruf sind von 30 auf 30.000 gegangen — ein Scope, dessen where nach Ruby gewandert ist, das dieselbe Antwort liefert und sich völlig vernünftig liest.
Dein eigener Code, der langsamer wird
Zeit in der Methode selbst statt in dem, was sie aufgerufen hat, „ist mein Code langsamer geworden oder das, was ich aufgerufen habe“ ist also eine Zahl statt eines Nachmittags.
Eine Abfrage, die still nichts zurückgibt
Erfolgreich, nichts geworfen, und leer zurückgekommen, wo früher Zeilen kamen. ActiveRecord ist vollkommen zufrieden; die Seite ist leer.
Etwas, das jetzt in einer Schleife aufgerufen wird
Pro Aufruf unverändert und hunderte Male aufgerufen, wo es früher einmal aufgerufen wurde — auch durch einen Hintergrundjob, der als eigener Prozess meldet.
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 zwei Regressionen, die apps/demo-rails absichtlich ausliefert, gegen den Datensatz, den bin/rails db:prepare anlegt — etwa 30.000 Bestellungen. Du kannst es laufen lassen und ihnen beim Ankommen zusehen.
OrdersQuery#for_userJemand hat den Scope refactored und das where ist nach Ruby gewandert, jeder Aufruf lädt also die ganze Tabelle und filtert im Speicher. Es liefert dieselbe Antwort, und auf einem Laptop kostet es ein paar Millisekunden.
seit Release v2, gegen v1
OrdersQuery#enrichAus einer gebündelten Abfrage wurde eine pro Bestellung. Jede Abfrage ist für sich schnell und korrekt indiziert, und das lässt diese Art von Bug Code-Review und Staging überleben.
seit Release v2, gegen v1
Wie es installiert wird
Auf Rails ist das die ganze Installation: das Gem und zwei Umgebungsvariablen. Das Railtie fügt die Middleware hinzu, abonniert den Abfrage-Bus und umschließt Controller-Actions beim Start, es gibt also keinen Initializer zu schreiben und nichts aufzurufen.
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: gem 'sixty' # Gemfile export SIXTY_API_KEY=sixty_sk_… export SIXTY_SERVICE=checkout-api # On Rails, that is the install. Outside it: require 'sixty' Sixty.init use Sixty::Instrument::Rack # config.ru — Sinatra, Hanami, a bare app # Your own classes, if you want them measured: class OrdersQuery include Sixty::Instrumented end
Der Release-Bezeichner wird von allein von Vercel, Render, Railway, Fly, Heroku oder GitHub Actions abgeholt, und SIXTY_RELEASE setzt ihn überall sonst. Der erste Vergleich kommt nach deinem nächsten Deployment, weil es nichts gibt, womit man ein Release vergleichen könnte.
Was es nicht tut
- Abfragepläne gibt es nur für Postgres. MySQL hat kein EXPLAIN mit generischem Plan und MongoDB hat kein Statement zum Erklären — und eines von beiden zu erklären hieße, die Abfragewerte von jemandem aufzubewahren, um sie in einem von uns zusammengesetzten Befehl an dessen eigene Datenbank zurückzuschicken, und das wird dieser Agent nicht tun.
- CPU und Warten werden nicht getrennt. Das braucht eine Uhr pro Thread und einen Span, der seinen Thread besitzt, und das hat nur der Python-Agent.
- Unter Puma mit Workern oder unter Sidekiq meldet jeder Prozess eigenständig und der Collector führt sie zusammen. Das ist korrekt, und man sollte es wissen, wenn man eine Zahl pro Prozess liest.
- include Sixty::Instrumented misst öffentliche Instanzmethoden. Eine private Methode, die die eigentliche Arbeit macht, ist unsichtbar, bis du sie auf anderem Weg zu einer Operation machst — der Preis dafür, nicht zu raten, was messenswert ist.
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 Node-Dienste, PHP- und Laravel-Apps oder Python-Dienste an — diese Seiten sind anders, und was wir sehen können, auch.