CodeGym /Kurse /ChatGPT Apps /Zuverlässigkeit von Streams: Rate‑Limits, Backpressure un...

Zuverlässigkeit von Streams: Rate‑Limits, Backpressure und Ereignis‑Monitoring

ChatGPT Apps
Level 13 , Lektion 4
Verfügbar

1. Warum Streams besonders lastempfindlich sind

In den vorherigen Teilen haben wir bereits besprochen, wie MCP‑Events, die Stati job.progress/job.completed, Async‑Jobs und Streaming‑Kanäle (SSE/HTTP-stream) für GiftGenius aufgebaut sind. Jetzt ist wichtig zu betrachten, was mit dieser Architektur unter realer Last passiert.

Solange Sie einen einzigen Nutzer haben, der nur gelegentlich eine Geschenk‑Suche startet, wirkt alles wunderbar. Sobald GiftGenius jedoch in den Production‑Betrieb geht und gleichzeitig Hunderte Anfragen wie „allen Mitarbeitenden für das Firmen‑Event Geschenke auswählen“ reinkommen, stellen Sie plötzlich fest, dass:

  • auf dem Server Hunderte langlebiger SSE‑Verbindungen offen sind;
  • Worker fröhlich bei jedem kleinen Schritt job.progress schicken;
  • Logs um Gigabytes pro Tag wachsen;
  • das UI beim Nutzer ins Stottern gerät, obwohl „der Server scheinbar nicht abstürzt“.

Eine klassische HTTP‑Anfrage lebt Millisekunden oder Sekunden. Ein SSE‑ oder HTTP‑Stream kann Minuten oder sogar Stunden leben. Er hält eine Verbindung, Speicher und File‑Deskriptoren. Jedes gesendete Event bedeutet JSON‑Serialisierung, Kopieren übers Netz, Arbeit für den GC. Wenn man das als „nun ja, einfach noch ein weiterer console.log im Backend“ betrachtet, wird das System sehr schnell zur Heizung.

MCP‑Events haben noch eine Besonderheit: Sie werden für ein und dieselbe Aufgabe oft mehrfach erzeugt. Ein Worker, der den Fortschritt alle 0.1% aktualisiert, produziert beeindruckend viele Events pro Job. Am Ende erhalten Sie „Rauschen“: eine riesige Menge kleiner Nachrichten, die:

  • Netzwerk und CPU belasten;
  • Queues und Puffer verstopfen;
  • Debugging und Log‑Analyse schmerzhaft machen.

Deshalb sollte man Streams und MCP‑Events so ernst nehmen wie Datenbank‑Abfragen oder Modell‑Aufrufe: Es sind teure Ressourcen, die Normierung, Kontrolle und Monitoring benötigen.

Um damit klarzukommen, behalten wir drei große Themen im Kopf:

  1. Rate‑Limits — wir begrenzen, wie viele und wie oft wir überhaupt Events/Streams erzeugen und senden können.
  2. Backpressure — wir reagieren auf Situationen, in denen der Verbraucher nicht mit dem Produzenten Schritt hält.
  3. Monitoring und Metriken — wir messen, was passiert, und bemerken rechtzeitig, wenn alles zu kochen beginnt.

2. Rate‑Limiting für Streams und Events

Beginnen wir mit dem Naheliegendsten – den Limits.

Wichtig ist zu verstehen, dass in Streaming‑Szenarien der „gefährliche Übeltäter“ oft nicht der Client, sondern der Server ist. In gewöhnlichen REST‑APIs begrenzen Sie die Anzahl der Anfragen an den Server, damit ein Nutzer keinen DDoS verursacht. In der Welt von MCP und Streams ist es sehr einfach, einen umgekehrten DDoS zu erzeugen: Ein Worker oder ein MCP‑Server bombardiert den Client mit Tausenden Events pro Sekunde.

Welche Limits werden benötigt

Man denkt üblicherweise in drei Dimensionen.

Erstens: Limits pro Benutzer oder Session. Man darf einem Benutzer nicht erlauben, zwanzig parallele GiftGenius‑Master‑Widgets zu öffnen, jedes mit seinem eigenen SSE‑Stream. Eine sinnvolle Begrenzung sind einige aktive Streams pro Session sowie ein Limit der Anzahl von Jobs im Status running für einen Benutzer oder einen Tenant.

Zweitens: Limits pro Job. Hier interessiert uns die Ereignisfrequenz. Es genügt völlig, job.progress nicht häufiger als einmal in N Millisekunden zu senden oder nur bei merklicher Änderung, zum Beispiel alle 5% Fortschritt. Man muss keine Nachricht für jedes verarbeitete Katalogprodukt schicken. Außerdem sollte die Payload‑Größe begrenzt werden: Ein Progress‑Event sollte keine Megabytes an Text enthalten.

Drittens: Limits pro IP oder Organisation. Das ist bereits Schutz vor Missbrauch, wenn jemand ein Skript startet, das Aufgaben spammt, oder wenn Ihre App unerwartet populär wird. Hier kommen die bekannten Mechanismen von API‑Gateways und Proxys ins Spiel.

Einfache Umsetzung eines Frequenzlimits für Events

Betrachten wir einen GiftGenius‑Worker, der im Hintergrund Geschenke für eine lange Empfängerliste sucht und periodisch den Fortschritt per MCP‑Notification event/progress sendet. Wir möchten, dass Events nicht häufiger als einmal in 500 Millisekunden gesendet werden und nur, wenn sich der Prozentsatz um mindestens 5 Punkte ändert.

Ein hypothetischer TS‑Pseudocode für den Worker:

// nehmen wir an, es gibt mcpClient.sendNotification(...)
let lastSentPercent = 0;
let lastSentAt = 0;

function reportProgress(jobId: string, percent: number, message: string) {
  const now = Date.now();
  const percentDelta = percent - lastSentPercent;
  const timeDelta = now - lastSentAt;

  // senden nur, wenn >= 500 ms vergangen sind ODER der Wert um >= 5% gestiegen ist
  if (percentDelta >= 5 || timeDelta >= 500) {
    mcpClient.sendNotification("event/progress", {
      jobId,
      percent,
      message,
    });
    lastSentPercent = percent;
    lastSentAt = now;
  }
}

Dieser Ansatz heißt Throttling: Wir „lichten“ den Ereignisstrom nach Zeit und nach Wertänderung aus.

Wenn Sie in Phasen aufteilen („Phase 1 von 3“, „Phase 2 von 3“), ist die Logik noch einfacher: Events nur bei Phasenwechsel senden.

Limit für die Anzahl gleichzeitig geöffneter Streams

Auf der MCP‑Serverseite haben Sie höchstwahrscheinlich einen HTTP‑Handler für SSE:

// app/api/events/[userId]/route.ts (Next.js 16 App Router)
export async function GET(
  req: Request,
  { params }: { params: { userId: string } },
) {
  const userId = params.userId;

  if (!canOpenMoreStreams(userId)) {
    return new Response("Too many streams", { status: 429 });
  }

  const stream = new ReadableStream({
    start(controller) {
      registerSseClient(userId, controller);
    },
    cancel() {
      unregisterSseClient(userId);
    },
  });

  return new Response(stream, {
    headers: { "Content-Type": "text/event-stream" },
  });
}

Die Funktion canOpenMoreStreams kann die aktuelle Anzahl geöffneter Verbindungen pro Nutzer prüfen und mit einem Schwellenwert vergleichen (z. B. nicht mehr als drei parallele Streams). Wenn das Limit überschritten ist, geben wir 429 zurück und erklären in den GPT‑Anweisungen dem Modell, dass es in so einer Situation besser keinen weiteren langen Master zu starten versucht, sondern dem Nutzer signalisiert: „Es läuft bereits eine aktive Suche, warten wir diese zuerst ab.“

In kleinen Systemen lassen sich solche Prüfungen im Prozessspeicher umsetzen. In einer seriöseren Infrastruktur wandert das in ein MCP‑Gateway oder einen eigenen Rate‑Limit‑Service.

3. Backpressure: Was tun, wenn der Verbraucher nicht hinterherkommt

Rate‑Limits begrenzen, wie viel wir produzieren wollen. Aber selbst bei vorsichtigen Limits ist eine Situation möglich, in der der Verbraucher „ins Stocken gerät“: Der Nutzer hat schlechtes mobiles Internet, der Browser‑Tab hängt, ChatGPT ist gerade stark ausgelastet.

Backpressure ist die Reaktion des Systems darauf, dass der Verbraucher nicht nachkommt. Anstatt Daten endlos zu akkumulieren und irgendwann mit OOM abzustürzen, tun wir bewusst Folgendes:

  • wir verlangsamen;
  • wir aggregieren Events;
  • wir verwerfen weniger wichtige.

Wo der Druck entsteht

Ein typisches Szenario für GiftGenius kann so aussehen: Ein Worker schreibt Events in eine Queue (z. B. Redis Streams oder einfach eine Tabelle in der DB), der MCP‑Server liest sie und schiebt sie in den SSE‑Kanal. Wenn der Client langsam ist (3G, alter Laptop, viele weitere Tabs), füllt sich der TCP‑Puffer, der Node‑Prozess schafft es nicht, die Queue leerzuziehen, und akkumuliert am Ende Events im Speicher. Dann sieht man etwas Vertrautes:

FATAL ERROR: Ineffective mark-compacts near heap limit

Backpressure auf Netzwerkebene (TCP) haben Sie bereits, aber diese weiß nichts über Ihre Domänenobjekte. Sie sagt nur: „Hey, bremse, der Puffer ist voll.“ Unsere Aufgabe ist es, das auf Ebene der MCP‑Events zu interpretieren.

Pufferung mit Limit und Verwerfen von Events

Für Progress und Status haben wir eine angenehme Eigenschaft: Nicht alle Events sind gleich wertvoll. Für den Nutzer ist der letzte aktuelle Prozentsatz wichtig, nicht die Historie aller Zwischenwerte „51%, 52%, 53%, 54%“. Das heißt, wir können einen Teil der Events problemlos droppen und nur das letzte senden.

Angenommen, wir haben eine Schicht, die Progress‑Events von Workern entgegennimmt und sie pro jobId in einen Puffer legt:

type ProgressEvent = { jobId: string; percent: number; message: string };

const progressBuffers = new Map<string, ProgressEvent[]>();
const MAX_BUFFER = 10;

function bufferProgress(event: ProgressEvent) {
  const buffer = progressBuffers.get(event.jobId) ?? [];
  buffer.push(event);

  // Puffergröße begrenzen
  if (buffer.length > MAX_BUFFER) {
    // nur die letzten wenigen Events behalten
    progressBuffers.set(event.jobId, buffer.slice(-MAX_BUFFER));
  } else {
    progressBuffers.set(event.jobId, buffer);
  }
}

Ein separater Timer, z. B. alle 500 ms, schaut in den Puffer und sendet nur das letzte Event, ignoriert die übrigen:

setInterval(() => {
  for (const [jobId, buffer] of progressBuffers.entries()) {
    if (!buffer.length) continue;

    const last = buffer[buffer.length - 1];
    sendProgressToClient(last); // SSE/MCP notification

    progressBuffers.set(jobId, []); // leeren
  }
}, 500);

Das ist ein Beispiel für Conflation: Mehrere Updates zu einem aktuellen zusammenfassen. Für Progress ein Gold‑Pattern.

Für Events vom Typ „Log“ oder partial_result kann die Strategie anders aussehen. Dort ist der Verlust von Events oft nicht akzeptabel: Log‑Text ist wichtig, und ein fehlender JSON‑Chunk kann die Datenstruktur beschädigen. In diesen Fällen können Sie:

  • Nachrichten aggregieren (mehrere Log‑Zeilen zu einem Paket zusammenfassen);
  • oder dem Worker ein Steuersignal schicken: „drossle die Log‑Erzeugung“.

In asynchronen Systemen ist die zweite Variante schwieriger, man sollte sie aber zumindest in Betracht ziehen.

Begrenzung der Queue‑Tiefe

Backpressure ist nicht auf den Event‑Puffer direkt vor dem Versand beschränkt. Man muss alle Queues im System betrachten:

  • die Warteschlange der Aufgaben, die auf einen Worker warten;
  • die Ereignis‑Queue zwischen Worker und MCP‑Server;
  • Puffer innerhalb der Streaming‑Bibliotheken auf Serverseite.

Für jede Queue ist es wichtig, ein sinnvolles Tiefenlimit zu definieren. Wenn die Queue überläuft, antworten Sie entweder den Clients mit „System überlastet, bitte später erneut versuchen“, verwerfen weniger wichtige Jobs oder verlagern einen Teil der Szenarien in einen „Offline‑Modus“ (z. B. einen Bericht generieren und den Link später senden).

Ein weiterer interessanter Kniff ist die Priorisierung von Ereignistypen. Bei Überlastung können Sie beginnen, nur job.completed und job.failed zu senden und job.progress herabzustufen oder ganz abzuschalten.

4. Monitoring für Streams und Events

Ohne Messungen wird die ganze Schönheit mit Rate‑Limits und Backpressure zur Scharlatanerie. Man muss sehen, dass Streams verdächtig zahlreich werden, Events mit Lag kommen und Clients massenhaft abfallen.

Streams verhalten sich anders als gewöhnliche HTTP‑Anfragen: Ihre Dauer kann sich in Minuten und Stunden messen, daher liefern klassische Metriken wie „Requests pro Sekunde“ und „durchschnittliche Latenz“ kein vollständiges Bild.

Zentrale Metriken

Für SSE‑ oder HTTP/stream‑Verbindungen ist es sinnvoll, mehrere Messgrößen zu beobachten.

  1. Verbindungsmetriken. Wie viele aktive SSE‑Streams gibt es gerade? Wie lange lebt eine Verbindung im Schnitt? Welcher Prozentsatz der Streams endet mit Fehler oder Timeout? Ein plötzlicher Anstieg aktiver Verbindungen spricht für einen potenziellen Traffic‑Sturm oder ein Ressourcen‑Leak (Clients schließen Verbindungen nicht). Ein plötzlicher Einbruch deutet auf massenhaft Abbrüche hin (z. B. Netzprobleme oder einen kritischen Bug auf dem Server).
  2. Ereignismetriken. Wie viele Events senden Sie pro Sekunde über alle Streams (EPS – events per second, im Grunde Anzahl der Events pro Sekunde)? Wie groß ist ein Event im Durchschnitt? Wie viele Deserialisierungs‑ oder Payload‑Validierungsfehler beobachten Sie? Wenn die Event‑Größe plötzlich wächst, hat vielleicht jemand angefangen, in job.progress statt eines kurzen Strings den gesamten Berichtstext zu schicken.
  3. Job‑Metriken. Verteilung nach Stati (pending, running, completed, failed, canceled), durchschnittliche Laufzeit nach Aufgabentyp, Anteil der Jobs, die in Retry oder Dead‑Letter landen. Das hilft zu verstehen, dass Probleme nicht nur auf Netzwerkebene, sondern auch in den Workern liegen können: Externes API wurde langsamer, massenhafte Fehler sind aufgetreten.
  4. Backpressure‑ und Systemmetriken. In Streaming‑Systemen betrachtet man oft die Tiefe von Puffern und Queues zwischen Komponenten sowie den Prozentsatz der Zeit, in der der Stream blockiert ist und darauf wartet, dass der Verbraucher Platz schafft. Wenn Ihre Queues fast immer bis zum Anschlag gefüllt sind, ist das ein klarer Hinweis, dass das System am Limit arbeitet. Wichtig sind auch Systemkennzahlen: CPU und Speicher auf den Streaming‑Servern sowie Fehler/Timeouts auf Netzwerkebene. Manchmal ist die Netzwerkbandbreite zwischen MCP‑Server und ChatGPT der Flaschenhals.

In Summe liefern diese vier Gruppen Antworten auf drei Fragen: Wie viele Streams leben gerade, wie viele Daten bewegen Sie, wie verhalten sich die Jobs und wo genau beginnt das System zu ersticken.

Was soll geloggt werden

Logs sind die zweite Säule der Observability. Es ist wichtig, Events und Verbindungen so zu loggen, dass man später die Historie für einen konkreten Job rekonstruieren kann.

Typischerweise fügt man in Logs für jedes Event und jeden Stream hinzu:

  • jobId und/oder eventId;
  • userId und sessionId (bei Mandantenfähigkeit);
  • Event‑Typ (progress, completed, failed, resource.updated);
  • Kanaltyp (SSE oder HTTP/stream);
  • Sendungs‑Zeitstempel und, wenn möglich, den Erzeugungs‑Zeitstempel beim Worker.

Damit kann man den Lag berechnen: die Differenz zwischen dem Zeitpunkt, an dem der Worker das Event erzeugt hat, und dem Zeitpunkt, an dem es in den Socket gesendet wurde. Das Wachstum dieser Lag‑Zeit ist ein guter Indikator für Backpressure‑Probleme.

Man muss aufpassen, dass Logs nicht selbst zur Lastquelle werden. Für hochfrequente Events wie job.progress ist es nicht immer sinnvoll, jedes Event zu loggen; man kann Sampling einschalten – z. B. jedes N‑te Event loggen – oder Statistiken aggregieren.

Im Code kann das wie ein einfacher Helper aussehen:

function logEvent(event: {
  type: string;
  jobId: string;
  userId?: string;
  channel: "sse" | "http-stream";
  payload: unknown;
}) {
  console.info({
    ...event,
    timestamp: new Date().toISOString(),
  });
}

Im realen Projekt kapseln Sie das in eine Library für strukturiertes Logging, aber die Idee bleibt: maximal hilfreicher Kontext in jedem Eintrag.

5. Alerts und Degradationspolitik

Wenn Sie Metriken und Logs haben, ist der nächste Schritt, Alerts einzurichten und zu durchdenken, wie das System „degradiert“, wenn es ihm schlecht geht. Die Idee: Lieber ehrlich schlechter arbeiten als plötzlich abstürzen.

Beispiele für Alerts

Für GiftGenius ist es sinnvoll, einige typische Situationen zu beobachten.

Erstens: eine ungewöhnlich hohe Anzahl aktiver Streams. Wenn Sie normalerweise Dutzende aktiver SSE‑Verbindungen haben und es plötzlich Tausende sind, sollten Sie herausfinden, was passiert. Vielleicht sind Sie beliebt geworden, vielleicht gibt es aber auch einen Bug und Verbindungen werden nicht geschlossen.

Zweitens: Verzögerung zwischen dem faktischen Abschluss eines Jobs und dem Eintreffen von job.completed beim Client. Wenn diese Verzögerung einen Schwellenwert überschreitet (sagen wir 510 Sekunden), stauen sich irgendwo zwischen Worker und Client Events oder Verbindungen geraten ins Stocken.

Drittens: hoher Anteil von job.failed oder job.canceled im Vergleich zu erfolgreichen. Die Ursache kann sowohl im Worker liegen (defektes externes API, neuer Bug) als auch in der erhöhten Sensibilität der Nutzer gegenüber Verzögerungen (sie brechen Aufgaben häufiger ab).

Und schließlich: erhöhte Rate an Verbindungsfehlern und Stream‑Abbrüchen. Wenn die Zahl unerwarteter Disconnects steigt, gibt es möglicherweise Netzwerkprobleme oder Client‑Probleme, und man sollte über Fallback‑Szenarien nachdenken.

Degradationsmuster

Wenn das System überlastet ist, kann man in einen „Ressourcen‑Sparmodus“ wechseln. Das ist besser, als einfach überall 500 zu antworten.

Das häufigste Muster ist eine adaptive Ereignisfrequenz. Wenn Sie sehen, dass die Event‑Rate (Anzahl der Events pro Sekunde) zehnmal über das Übliche hinausgeschossen ist und der Lag in den Queues wächst, reduzieren Sie die Frequenz der Progress‑Events. War es alle 1%, machen Sie alle 10%. War es alle 500 ms, machen Sie alle 23 Sekunden. Der Nutzer kommt gut ohne ultrapräzisen Fortschritt aus – mit komplett eingefrorenem UI hingegen weniger.

Für weniger wichtige Events – etwa resource.updated bei einem Hintergrund‑Feed‑Update – kann man die Sendung vorübergehend ganz deaktivieren, solange das System unter Last steht.

Ein weiterer Ansatz ist, einen Teil der Szenarien von Streams auf periodisches Polling umzustellen. Wenn SSE‑Kanäle zusammenbrechen, kann der MCP‑Server dem Widget ein System‑Event wie system.overloaded schicken, und das Widget wechselt zur Strategie „alle N Sekunden frage ich den REST‑Endpoint nach dem Job‑Status ab“.

6. Ein kleines Praxisfragment für GiftGenius

Um alles zu verbinden, nehmen wir an, dass wir bereits Folgendes haben:

  • ein MCP‑Tool startGiftSearch, das einen Job erstellt und jobId zurückgibt;
  • einen Worker, der die Suche ausführt und event/progress und event/completed sendet;
  • einen SSE‑Endpoint /api/events/[userId], mit dem sich das Widget in Next.js verbindet.

Fügen wir eine einfache Schutzschicht gegen den „Ereignissturm“ hinzu und ein minimales Monitoring.

Fortschritt nach Schritten und Zeit begrenzen

Im Worker fügen wir Throttling und Conflation hinzu, wie oben besprochen. Jetzt werden Events höchstens alle halbe Sekunde und bei einer Änderung von mindestens 5% gesendet.

Erfassung aktiver Streams

Im SSE‑Endpoint halten wir einen Zähler pro Nutzer vor:

const activeStreams = new Map<string, number>();
const STREAM_LIMIT = 3;

function canOpenMoreStreams(userId: string) {
  const current = activeStreams.get(userId) ?? 0;
  return current < STREAM_LIMIT;
}

function registerSseClient(userId: string, controller: ReadableStreamDefaultController) {
  const current = activeStreams.get(userId) ?? 0;
  activeStreams.set(userId, current + 1);

  // hier den Controller in einer Struktur speichern,
  // um später in diesen Stream Ereignisse zu schreiben
}

function unregisterSseClient(userId: string) {
  const current = activeStreams.get(userId) ?? 1;
  activeStreams.set(userId, Math.max(0, current - 1));
}

Metriken zu activeStreams.size kann der Server zusätzlich an Prometheus/Grafana oder ein anderes Monitoring‑System senden.

Einfachste Metrik für die Event‑Rate

Für den Anfang kann man wenigstens grob zählen, wie viele Events wir senden:

let eventsSentLastMinute = 0;

function sendProgressToClient(ev: ProgressEvent) {
  // ... Serialisierung und Schreiben in den SSE‑Stream
  eventsSentLastMinute++;
}

setInterval(() => {
  console.info({
    metric: "events_per_minute",
    value: eventsSentLastMinute,
    timestamp: new Date().toISOString(),
  });
  eventsSentLastMinute = 0;
}, 60_000);

Mit der Zeit kann man das durch richtige Counter und Alerts ersetzen, aber als Ausgangspunkt ist es bereits hilfreich.

Wenn man alles oben Genannte zusammenführt – Limits, Backpressure, Metriken/Alerts und einen angemessenen UX‑Fallback –, dann hört Ihr GiftGenius auf, „eine Demo für Demos“ zu sein, und übersteht echte Traffic‑Stürme. In den nächsten Modulen, in denen wir über Gateway, Produktionsarchitektur und vollwertige Observability sprechen, werden uns diese Patterns weiterhin nützlich sein.

7. Typische Fehler im Umgang mit Streams, Rate‑Limits und Monitoring

Fehler Nr. 1: Keine Limits für die Anzahl der Streams und die Event‑Frequenz.
Entwickler fügen SSE hinzu „weil es hübsch ist“, Worker senden brav Progress für jedes verarbeitete Objekt, und alles scheint in der Demo zu funktionieren. Doch beim ersten Ansturm realer Nutzer verbringt der Server den Großteil seiner Ressourcen mit der Serialisierung und dem Versand Tausender winziger Events, und das UI in ChatGPT wird zur Diashow.

Fehler Nr. 2: Der Versuch, „alles und sofort“ ohne Begrenzungen zu puffern.
Im Code entsteht ein unbeschränktes Array „nicht gesendeter Events“, das wächst, bis sich der Client erholt. Spoiler: Er erholt sich nicht, der Server stirbt vorher. Jeder Puffer braucht ein hartes Maximum, und die Logik zur Behandlung von Überläufen muss explizit sein.

Fehler Nr. 3: Alle Ereignistypen gleich behandeln.
Progress kann aggregiert und gedroppt werden (der letzte Prozentsatz ist wichtiger als die Bewegungshistorie). Bei Logs und Partial‑Ergebnissen geht das nicht – der Verlust eines Chunks kann Daten beschädigen. Entwerfen Sie das System so, dass Sie Ereignisse im Voraus nach Wichtigkeit gruppieren und für jede Gruppe eine Überlastungsstrategie haben.

Fehler Nr. 4: Fehlende Observability.
Keine Metriken zu aktiven Streams, keine Erfassung der Event‑Rate, in den Logs nur „etwas ist schiefgelaufen“. In so einer Situation erfahren Sie von Problemen nur durch Nutzerfeedback und CPU‑Lastkurven. Wenigstens grundlegende Metriken und Logs nach jobId und eventId einzurichten, ist keine Kür, sondern Pflicht.

Fehler Nr. 5: Harte UX, die keine Degradation berücksichtigt.
Widget und GPT‑Anweisungen gehen davon aus, dass der Stream immer verfügbar ist, der Fortschritt „in Echtzeit“ aktualisiert wird und Partial‑Results streng nach Drehbuch eintreffen. Bei den ersten Netzproblemen sieht der Nutzer eine „hängende“ Fortschrittsanzeige und keinerlei Erklärung. Viel besser ist ein ehrlicher Fallback im UX: „Aktuell gibt es Probleme mit Live‑Updates, ich setze die Auswahl dennoch fort und melde mich, sobald ich fertig bin“ – und dann auf selteneres Updating oder Polling umschalten.

Fehler Nr. 6: Das Vertrauen darauf, dass „unsere Nutzer nicht viele gleichzeitige Aufgaben erzeugen“.
Die Praxis zeigt: Wenn Sie die Zahl paralleler Jobs und Streams nicht begrenzen, wird irgendjemand fünf Tabs öffnen, in jedem die Geschenksuche „auf Maximum“ starten und Kaffee trinken gehen. Die Idee „wird schon gutgehen“ endet in der Produktion fast immer mit einer intensiven Bekanntschaft mit dem Monitoring unter dem Getöse von Alerts.

1
Umfrage/Quiz
Benachrichtigungen, Level 13, Lektion 4
Nicht verfügbar
Benachrichtigungen
Benachrichtigungen und Streaming‑Szenarien (MCP‑Ereignisse)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION