Aufbau für große Systeme und lang laufende Hintergrundprozesse. Bildnachweis: Ilias Chebbi auf Unsplash Vor Monaten übernahm ich die Rolle, die den Aufbau von InfrastAufbau für große Systeme und lang laufende Hintergrundprozesse. Bildnachweis: Ilias Chebbi auf Unsplash Vor Monaten übernahm ich die Rolle, die den Aufbau von Infrast

Spotify für Predigten aufbauen.

2025/12/11 21:15

Aufbau für große Systeme und lang laufende Hintergrundaufgaben.

Bildnachweis: Ilias Chebbi auf Unsplash

Vor einigen Monaten übernahm ich die Rolle, die den Aufbau einer Infrastruktur für Medien(Audio)-Streaming erforderte. Aber über das Bereitstellen von Audio als streambare Chunks hinaus gab es lang laufende Medienverarbeitungsaufgaben und eine umfangreiche RAG-Pipeline, die Transkription, Transkodierung, Einbettung und sequentielle Medienaktualisierungen umfasste. Der Aufbau eines MVP mit einer Produktionsdenkweise ließ uns so lange wiederholen, bis wir ein nahtloses System erreicht hatten. Unser Ansatz war einer, bei dem wir Funktionen und den zugrunde liegenden Stack von Prioritäten integrierten.

Hauptanliegen:

Im Laufe des Aufbaus kam jede Iteration als Reaktion auf unmittelbare und oft "umfassende" Bedürfnisse. Das anfängliche Anliegen war die Warteschlange von Aufgaben, was mit Redis leicht ausreichte; wir haben einfach abgefeuert und vergessen. Bull MQ im NEST JS-Framework gab uns eine noch bessere Kontrolle über Wiederholungsversuche, Rückstände und die Dead-Letter-Warteschlange. Lokal und mit einigen Nutzlasten in der Produktion haben wir den Medienfluss richtig hinbekommen. Wir wurden bald durch das Gewicht der Beobachtbarkeit belastet:
Logs → Aufzeichnung von Aufgaben (Anfragen, Antworten, Fehler).
Metriken → Wie viel / wie oft diese Aufgaben ausgeführt werden, fehlschlagen, abgeschlossen werden usw.
Traces → Der Pfad, den eine Aufgabe über Dienste hinweg genommen hat (Funktionen/Methoden, die innerhalb des Flusspfades aufgerufen wurden).

Sie können einige dieser Probleme lösen, indem Sie APIs entwerfen und ein benutzerdefiniertes Dashboard erstellen, um sie einzubinden, aber das Problem der Skalierbarkeit wird ausreichen. Und tatsächlich haben wir die APIs entworfen.

Aufbau für Beobachtbarkeit

Die Herausforderung der Verwaltung komplexer, lang laufender Backend-Workflows, bei denen Fehler wiederherstellbar sein müssen und der Zustand dauerhaft sein muss, wurde Inngest zu unserer architektonischen Rettung. Es hat unseren Ansatz grundlegend neu gestaltet: jede lang laufende Hintergrundaufgabe wird zu einer Hintergrundfunktion, die durch ein bestimmtes Ereignis ausgelöst wird.

Zum Beispiel wird ein Transcription.request Ereignis eine TranscribeAudio Funktion auslösen. Diese Funktion könnte Step-Runs für Folgendes enthalten: fetch_audio_metadata, deepgram_transcribe, parse_save_trasncription und notify_user.

Dekonstruktion des Workflows: Die Inngest-Funktion und Step-Runs

Das Kernhaltbarkeitsprimitive sind die Step-Runs. Eine Hintergrundfunktion wird intern in diese Step-Runs aufgeteilt, von denen jeder einen minimalen, atomaren Logikblock enthält.

  • Atomare Logik: Eine Funktion führt Ihre Geschäftslogik Schritt für Schritt aus. Wenn ein Schritt fehlschlägt, wird der Zustand des gesamten Laufs bewahrt, und der Lauf kann wiederholt werden. Dies startet die Funktion von Anfang an neu. Einzelne Schritte oder Step-Runs können nicht isoliert wiederholt werden.
  • Antwort-Serialisierung: Ein Step-Run wird durch seine Antwort definiert. Diese Antwort wird automatisch serialisiert, was für die Bewahrung komplexer oder stark typisierter Datenstrukturen über Ausführungsgrenzen hinweg wesentlich ist. Nachfolgende Step-Runs können diese serialisierte Antwort zuverlässig analysieren, oder die Logik kann für Effizienz in einem einzigen Schritt zusammengeführt werden.
  • Entkopplung und Planung: Innerhalb einer Funktion können wir bedingt neue, abhängige Ereignisse in die Warteschlange stellen oder planen, was komplexe Fan-out/Fan-in-Muster und langfristige Planung bis zu einem Jahr ermöglicht. Fehler und Erfolge an jedem Punkt können erfasst, verzweigt und weiter unten im Workflow behandelt werden.

Inngest-Funktionsabstrakt:

import { inngest } from 'inngest-client';

export const createMyFunction = (dependencies) => {
return inngest.createFunction(
{
id: 'my-function',
name: 'My Example Function',
retries: 3, // retry the entire run on failure
concurrency: { limit: 5 },
onFailure: async ({ event, error, step }) => {
// handle errors here
await step.run('handle-error', async () => {
console.error('Error processing event:', error);
});
},
},
{ event: 'my/event.triggered' },
async ({ event, step }) => {
const { payload } = event.data;

// Step 1: Define first step
const step1Result = await step.run('step-1', async () => {
// logic for step 1
return `Processed ${payload}`;
});

// Step 2: Define second step
const step2Result = await step.run('step-2', async () => {
// logic for step 2
return step1Result + ' -> step 2';
});

// Step N: Continue as needed
await step.run('final-step', async () => {
// finalization logic
console.log('Finished processing:', step2Result);
});

return { success: true };
},
);
};

Das ereignisgesteuerte Modell von Inngest bietet granulare Einblicke in jede Workflow-Ausführung:

  • Umfassende Ereignisverfolgung: Jede in die Warteschlange gestellte Funktionsausführung wird gegen ihr Ursprungsereignis protokolliert. Dies bietet eine klare, hochrangige Spur aller Aktivitäten im Zusammenhang mit einer einzelnen Benutzeraktion.
  • Detaillierte Laufeinblicke: Für jede Funktionsausführung (sowohl Erfolge als auch Fehler) bietet Inngest detaillierte Protokolle über seine ack (acknowledge) und nack (negative acknowledgment) Berichterstattung. Diese Protokolle enthalten Fehler-Stack-Traces, vollständige Anforderungsnutzlasten und die serialisierten Antwortnutzlasten für jeden einzelnen Step-Run.
  • Betriebsmetriken: Über Protokolle hinaus haben wir kritische Metriken zur Funktionsgesundheit gewonnen, einschließlich Erfolgsraten, Fehlerraten und Wiederholungszählung, die es uns ermöglichen, die Zuverlässigkeit und Latenz unserer verteilten Workflows kontinuierlich zu überwachen.

Aufbau für Widerstandsfähigkeit

Der Vorbehalt bei der Abhängigkeit von reiner Ereignisverarbeitung ist, dass, während Inngest Funktionsausführungen effizient in die Warteschlange stellt, die Ereignisse selbst nicht intern in die Warteschlange gestellt werden im Sinne eines traditionellen Messaging-Brokers. Dieses Fehlen einer expliziten Ereigniswarteschlange kann in Szenarien mit hohem Verkehrsaufkommen problematisch sein, aufgrund potenzieller Race-Conditions oder verworfener Ereignisse, wenn der Aufnahmeendpunkt überlastet ist.

Um dies zu adressieren und strikte Ereignishaltbarkeit durchzusetzen, haben wir ein dediziertes Warteschlangensystem als Puffer implementiert.

AWS Simple Queue System (SQS) war das System der Wahl (obwohl jedes robuste Warteschlangensystem machbar ist), angesichts unserer bestehenden Infrastruktur auf AWS. Wir haben ein Zwei-Warteschlangen-System entworfen: eine Hauptwarteschlange und eine Dead Letter Queue (DLQ).

Wir haben eine Elastic Beanstalk (EB) Worker-Umgebung eingerichtet, die speziell konfiguriert ist, um Nachrichten direkt aus der Hauptwarteschlange zu konsumieren. Wenn eine Nachricht in der Hauptwarteschlange vom EB-Worker eine festgelegte Anzahl von Malen nicht verarbeitet werden kann, verschiebt die Hauptwarteschlange automatisch die fehlgeschlagene Nachricht in die dedizierte DLQ. Dies stellt sicher, dass kein Ereignis dauerhaft verloren geht, wenn es nicht ausgelöst oder von Inngest aufgenommen werden kann. Diese Worker-Umgebung unterscheidet sich von einer Standard-EB-Webserver-Umgebung, da ihre einzige Verantwortung der Nachrichtenkonsum und die Verarbeitung ist (in diesem Fall die Weiterleitung der konsumierten Nachricht an den Inngest-API-Endpunkt).

VERSTÄNDNIS VON GRENZEN UND SPEZIFIKATIONEN

Ein unterschätzter und eher relevanter Teil des Aufbaus von Infrastruktur im Unternehmensmaßstab ist, dass sie Ressourcen verbraucht und sie lang laufend sind. Die Microservices-Architektur bietet Skalierbarkeit pro Dienst. Speicher, RAM und Timeouts von Ressourcen werden ins Spiel kommen. Unsere Spezifikation für den AWS-Instanztyp beispielsweise wechselte schnell von t3.micro zu t3.small und ist jetzt bei t3.medium festgelegt. Für lang laufende, CPU-intensive Hintergrundaufgaben scheitert die horizontale Skalierung mit winzigen Instanzen, weil der Engpass die Zeit ist, die es braucht, um eine einzelne Aufgabe zu verarbeiten, nicht das Volumen neuer Aufgaben, die in die Warteschlange eintreten.

Aufgaben oder Funktionen wie Transkodierung, Einbettung sind typischerweise CPU-gebunden und Speicher-gebunden. CPU-gebunden, weil sie anhaltende, intensive CPU-Nutzung erfordern, und Speicher-gebunden, weil sie oft erheblichen RAM benötigen, um große Modelle zu laden oder große Dateien oder Nutzlasten effizient zu handhaben.

Letztendlich bot diese erweiterte Architektur, die die Haltbarkeit von SQS und die kontrollierte Ausführung einer EB-Worker-Umgebung direkt stromaufwärts der Inngest-API platziert, wesentliche Widerstandsfähigkeit. Wir haben strikte Ereigniseigentümerschaft erreicht, Race-Conditions während Verkehrsspitzen eliminiert und einen nicht-flüchtigen Dead-Letter-Mechanismus gewonnen. Wir nutzten Inngest für seine Workflow-Orchestrierungs- und Debugging-Fähigkeiten, während wir uns auf AWS-Primitive für maximalen Nachrichtendurchsatz und Haltbarkeit verließen. Das resultierende System ist nicht nur skalierbar, sondern auch hochgradig prüfbar und übersetzt erfolgreich komplexe, lang laufende Backend-Aufgaben in sichere, beobachtbare und fehlertolerante Mikroschritte.


Building Spotify for Sermons. wurde ursprünglich in Coinmonks auf Medium veröffentlicht, wo Menschen das Gespräch fortsetzen, indem sie diese Geschichte hervorheben und darauf reagieren.

Haftungsausschluss: Die auf dieser Website veröffentlichten Artikel stammen von öffentlichen Plattformen und dienen ausschließlich zu Informationszwecken. Sie spiegeln nicht unbedingt die Ansichten von MEXC wider. Alle Rechte verbleiben bei den ursprünglichen Autoren. Sollten Sie der Meinung sein, dass Inhalte die Rechte Dritter verletzen, wenden Sie sich bitte an service@support.mexc.com um die Inhalte entfernen zu lassen. MEXC übernimmt keine Garantie für die Richtigkeit, Vollständigkeit oder Aktualität der Inhalte und ist nicht verantwortlich für Maßnahmen, die aufgrund der bereitgestellten Informationen ergriffen werden. Die Inhalte stellen keine finanzielle, rechtliche oder sonstige professionelle Beratung dar und sind auch nicht als Empfehlung oder Billigung von MEXC zu verstehen.