Ein Go-Bidder, der Auktionen im Timeout verliert, während sein Durchschnitt gesund aussieht, hat zwei Probleme unter einem Symptom: eine Deadline, die der Exchange gehört und den Round Trip umfasst, und einen Collector, der mark assist der Goroutine berechnet, die das Gebot bewertet. Hier steht, wie die Deadline aufgeteilt wird, welche Go-Stellschrauben sich auszahlen und was ein Rust-Hot-Path nicht behebt.
Ein Bidder, der Auktionen im Timeout verliert, während sein Durchschnitt gesund aussieht, wird von der falschen Zahl beschrieben. Die Deadline gehört der Exchange und deckt das Netzwerk in beide Richtungen ab. Ein Collector ist eine Eigenschaft des Prozesses, seine Pause wird also jeder Verbindung zugleich berechnet, und eine Spitze auf nur einer Verbindung braucht eine andere Erklärung.
In Rust neu schreiben oder die Runtime tunen ist eine Wahl zwischen zwei Antworten auf eine Frage, die niemand gestellt hat: Welcher Teil der Deadline wird ausgegeben, und wofür. Was folgt, trennt das Budget vom Collector, den Collector vom Scheduler und den Rewrite von der Komponente, die ihn verdient.
Die kurze Antwort ist strukturell. Die Exchange setzt die Deadline, und sie schließt das Netzwerk ein, die erste Reparatur teilt also das p99 einer Verbindung in Handler-Zeit, Wartezeit auf einen Prozessor und Zeit auf der Leitung. Was amBrain öffentlich belegen kann: Wir haben RTBBidder gebaut, eine von Grund auf gelieferte Demand-Side-Platform, in der jede Gebotsentscheidung Dutzende Targeting-Bedingungen pro Impression auswertet. Die Latenzwerte, die wir als gemessen veröffentlichen, stammen aus Trading-Pfaden, nicht aus einem Ad-Bidder, und keine Zahl unten ist an einem Bidder von uns gemessen.
Die OpenRTB-Spezifikation definiert tmax als die maximale Zeit in Millisekunden, die die Exchange für den Eingang von Geboten erlaubt, inklusive Internet-Latenz, und sagt, dass der Wert jede frühere Vorgabe außer Kraft setzt. Das Budget ist ein Round Trip, der in jeder Request mitkommt.
Der Schwellenwert, an dem Sie gemessen werden, ist nicht der auf Ihrem Dashboard. Die Authorized-Buyers-Dokumentation von Google verlangt, dass 85 Prozent der Antworten innerhalb der Deadline eintreffen, gemessen an der Trading Location, und drosselt Bidder, die das verfehlen. Zwischen ihrer Uhr und Ihrer sitzt alles, was keine Berechnung ist:
Die interne Deadline liegt also um den Betrag unter tmax, den Ihr eigenes Histogramm für eine Antwort auf dieser Verbindung ausweist, je Exchange neu abgeleitet und nicht einmal für die ganze Flotte gesetzt. Eine Deadline ist kein Kapazitätsplan: Arbeit, die an der Deadline abgebrochen wird, hat ihre CPU bereits verbraucht.
Unter Überlast heißt das, den vollen Preis für Antworten zu zahlen, die niemand zählt, die fehlende Reparatur ist also Admission Control: tmax lesen, gegen die gemessene Queue-Verzögerung halten und mit no-bid antworten, wenn die Rechnung nicht aufgeht. Ein schnelles no-bid zählt auf die 85 Prozent ein, ein spätes Gebot nicht.
Ein Garbage Collector ist eine Eigenschaft des Prozesses, ein Zyklus, der auf irgendeiner Verbindung ausgelöst wird, belastet also jede Verbindung. Zuerst verfehlt die Verbindung mit dem knappsten tmax und der schwersten Request. Schließen Sie aus, was dasselbe Bild ohne Collector erzeugt:
Keines der vier wird durch eine Collector-Einstellung repariert, und für die Aufteilung gibt es Instrumente. Ein CPU-Profil trennt die Collector-Rechnungen nach Symbol: runtime.gcAssistAlloc für die Belastung des Handlers und runtime.gcBgMarkWorker für das Hintergrund-Marking. Stop-the-world-Zeit ist /sched/pauses/total/gc:seconds, die Runnable-Wartezeit ist /sched/latencies:seconds, und die Accept-Queue wird außerhalb des Prozesses über ListenOverflows gelesen.
Eine Unterscheidung entscheidet, welche Go-Reparatur Sie brauchen. Eine stop-the-world-Pause wird jeder Goroutine gleichzeitig berechnet, sie erscheint also als flache Spitze auf allen Verbindungen. Ein mark assist wird der Goroutine berechnet, die allokiert hat, er landet also auf den Requests, die am meisten allokiert haben. Zwei Dinge senken einen Assist: weniger Bytes pro Bid Request oder ein längerer Zyklus, in dem Hintergrund-Worker mehr vom Marking übernehmen. Nur das Erste übersteht eine Änderung im Traffic-Mix.
Der Go-Collector ist nebenläufig, und der offizielle Guide sagt ausdrücklich, dass die Pausenlänge nicht mit der Heap-Größe skaliert, stop-the-world-Übergänge sind also kurz. Die Quelle, auf die es ankommt, sind die Assists: Goroutinen assistieren dem Collector, wenn schnell allokiert wird, denn das Hintergrund-Marking bekommt ein festes Viertel der Prozessoren, und der Fehlbetrag wird dem Allokierenden berechnet.
Die Rate macht daraus eine Schwelle statt einer Steigung. Die Allokationsrate ist QPS mal Bytes pro Bid Request gegen einen festen Hintergrundanteil, also kann Code, der bei einem Fünftel Ihres Traffics nie assistiert, bei 100K QPS auf fast jeder Request assistieren.
Die Marking-Kosten sind proportional zum lebenden Zeigergraphen, nicht zum Müll, und ein Bidder hält genau die falsche Form dafür: Kampagnen-Indizes, Audience-Segmente, Frequency-Caches. Discord veröffentlichte 2020 denselben Befund, mit einem Collector, der einen ganzen LRU-Cache scannte, um zu entscheiden, ob der Speicher frei war. Fünf Mechanismen verstecken sich hinter einem Begriff:
Lesen Sie das als fünf getrennte Rechnungen. Genau eine wird durch eine Collector-Einstellung beglichen und keine durch einen Sprachwechsel, bevor die Aufteilung gemessen ist.
Gil Tene hat diesen Fehler coordinated omission genannt: Das messende System stimmt sich so mit dem gemessenen System ab, dass Ausreißer gar nicht erst gemessen werden, denn ein Closed Loop wartet auf eine Antwort und hört während eines Stillstands auf zu senden. ScyllaDB veröffentlichte 2021 einen Vergleich, in dem ein Workload closed-loop ein p99 von 249 Mikrosekunden meldete und unter offener Last mit Korrektur 665 ms - ein Unterschied um etwa das 2,700-Fache.
Ein Lastgenerator, der auf eine Antwort wartet, hört genau während des Stillstands auf zu senden, den er finden sollte, und mittelt die Stille anschließend ins Ergebnis ein. Das Perzentil, das er danach ausgibt, beschreibt den Generator, nicht Ihren Bidder.
Benennen Sie die Abbruchzahl, bevor Sie tunen, denn ein Rewrite aus Erschöpfung ist keine Entscheidung. Zwei Kennzahlen, nicht eine: Heap-Allokationen pro Request, die den Assist treiben, und der lebende Heap, der das Marking treibt.
Die Reihenfolge ist Quelle vor Obergrenze, nicht größter Gewinn zuerst. Beginnen Sie dort, wo der Assist entsteht: Escape-Analyse auf dem Bid-Pfad, Puffer wiederverwenden statt allokieren, und ein Codec, der die Felder liest, die Sie brauchen, statt einen frischen Objektgraphen zu materialisieren. Halten Sie dessen Slices kurzlebig: Ein Slice in den Request-Puffer hält diesen ganzen Puffer für die Lebensdauer des Gebots fest.
Der Ansatz hat eine Obergrenze: Uber berichtete 2021, dass das Tunen von GOGC gegen das Container-Speicherlimit rund 70,000 Cores über seine geschäftskritischen Services hinweg zurückgewonnen hat. Das ist ein Kostenergebnis, kein Perzentil.
RTB House beschrieb im Juni 2025 einen JVM-Bidding-Service, bei dem eine Aufteilung in Microservices ein hohes Volumen kleiner Requests erzeugte: Die zusätzliche Latenz musste innerhalb von 7 ms bleiben, bei einer durchschnittlichen Request von etwa 2.5 ms, und das 98. und 99. Perzentil brachen unter häufigen G1-Pausen ein. Sie wechselten auf generational ZGC und zahlten mit Speicher.
Beachten Sie, was diese Reparatur verlangte: einen zweiten Collector, auf den man umschalten kann. Go liefert einen, und er ist nicht austauschbar, also sind die Go-Hebel die Allokationsrate, die Form des lebenden Sets und GOGC gegen GOMEMLIMIT. Was ein Rust-Hot-Path stattdessen entfernt, ist genau benennbar: kein Assist, kein Hintergrund-Marking, kein erzwungener Zyklus. Was nicht verschwindet, ist länger, als die meisten Teams erwarten:
Wenn der Tail im Parsen der Request steckt, in der Verbindung zur Exchange oder im Scheduler-Queueing, gibt Rust keine dieser Millisekunden zurück, und die falsche Komponente neu zu schreiben kostet ein Quartal und hält dieselbe Timeout-Rate.
Verschieben Sie also das kleinste Stück, dem die Allokationen gehören, nicht den Service: die Auswertungsschleife für die Impression und ihre Indizes, Kandidatenauswahl, Targeting, Frequency- und Budget-Lookups, Scoring. Bepreisen Sie die Grenze pro Übergang: einmal pro Bid Request mit einem flachen Puffer, nie einmal pro Targeting-Regel. Validieren Sie auf separaten Instanzen, die einen gespiegelten Stream bekommen, nie innerhalb des getesteten Prozesses, wo ein Schattenpfad genau die beiden Größen verdoppelt, die Sie messen.
Ein Akzeptanztest, der auf beiden Wegen funktioniert: ein kurzes Fenster mit abgeschaltetem Collector fahren, unter einer Speicherobergrenze, die Sie kontrollieren, und p99.9 innerhalb des Handlers aufzeichnen. Fahren Sie ihn auf einer Instanz hinter einem Bruchteil des Traffics und mit automatischem Rollback, denn mit abgeschaltetem GOGC treibt eine Heap-Spitze gegen die Obergrenze die Runtime in unmittelbar aufeinanderfolgende Zyklen, und der Guide sagt, dieser Stillstand kann unbegrenzt dauern. Lesen Sie das Ergebnis nur, wenn der GC-CPU-Limiter nie eingegriffen hat: Sobald er das tut, beschreibt das Perzentil den Limiter.
Die zweite Hälfte der Frage, wer solche Arbeit macht, hat einen Test, der ohne Anbieterliste auskommt. Eine Firma, die GC-getriebene Latenz repariert, verhält sich anders als eine, die einen Rewrite verkauft:
Keines davon verlangt Vertrauen: Jedes ist ein Dokument, das Sie im ersten Gespräch anfordern können, und kommt eines vage zurück, wird die Diagnose übersprungen.
Die erste Frage ist also nicht, welche Sprache. Sie lautet, welcher der drei Summanden - Handler, Scheduler oder Wire - die fehlenden Millisekunden auf der Verbindung besitzt, die ins Timeout läuft, und ob sich die pro Bid Request allokierten Bytes bewegen, wenn Sie sie angehen.
Was amBrain öffentlich belegen kann: amBrain ist ein Softwareentwicklungsunternehmen mit Fokus auf Trading-Plattformen, Matching-Engines, Real-Time-Bidding-Systeme und Casino-Plattform-Engineering, wir bauen seit 2019 Software, und in AdTech bauen wir DSP-Entwicklung, Real-Time-Bidding-Plattformen und Ad-Exchange-Engineering. Wir arbeiten in drei Formaten: vollständige Umsetzung, dediziertes Team oder Engineers, die in Ihrem Team mitarbeiten. Wenn Sie einen Rewrite gegen einen Tuning-Durchgang abwägen, führt das Gespräch, das sich lohnt, erst die Aufteilung durch und wählt dann eine Sprache.
Bringen Sie Ihre aktuelle Architektur und den Fehlerfall mit, der Sie beunruhigt - wir gehen ihn in einer halben Stunde gemeinsam durch.