Construirea pentru sisteme mari și joburi de fundal cu durată lungă. Credit: Ilias Chebbi pe Unsplash Cu luni în urmă, am preluat rolul care necesita construirea infrastConstruirea pentru sisteme mari și joburi de fundal cu durată lungă. Credit: Ilias Chebbi pe Unsplash Cu luni în urmă, am preluat rolul care necesita construirea infrast

Construirea Spotify pentru predici.

2025/12/11 21:15

Construirea pentru sisteme mari și joburi de fundal de lungă durată.

Credit: Ilias Chebbi pe Unsplash

Cu câteva luni în urmă, am preluat rolul care necesita construirea infrastructurii pentru streaming media (audio). Dar dincolo de servirea audio ca fragmente streamabile, existau joburi de procesare media de lungă durată și un pipeline RAG extins care se ocupa de transcriere, transcodare, încorporare și actualizări media secvențiale. Construirea unui MVP cu o mentalitate de producție ne-a făcut să reiterăm până am obținut un sistem fără probleme. Abordarea noastră a fost una în care am integrat funcționalități și stiva subiacentă de priorități.

Preocupare principală:

Pe parcursul construirii, fiecare iterație a venit ca răspuns la o nevoie imediată și adesea "cuprinzătoare". Preocuparea inițială a fost punerea în coadă a joburilor, care a fost suficientă cu Redis; am lansat pur și simplu și am uitat. Bull MQ în framework-ul NEST JS ne-a oferit un control și mai bun asupra reîncercărilor, backlog-urilor și cozii de scrisori moarte. Local și cu câteva încărcături în producție, am obținut fluxul media corect. Curând am fost împovărați de greutatea Observabilității:
Loguri → Înregistrarea joburilor (cereri, răspunsuri, erori).
Metrici → Cât de mult / cât de des aceste joburi rulează, eșuează, se finalizează etc.
Trasee → Calea pe care un job a luat-o prin servicii (funcții/metode apelate în calea fluxului).

Poți rezolva unele dintre acestea prin proiectarea API-urilor și construirea unui dashboard personalizat pentru a le conecta, dar problema scalabilității va fi suficientă. Și, de fapt, am proiectat API-urile.

Construirea pentru Observabilitate

Provocarea de a gestiona fluxuri de lucru backend complexe, de lungă durată, unde eșecurile trebuie să fie recuperabile, iar starea trebuie să fie durabilă, Inngest a devenit salvarea noastră arhitecturală. A reformat fundamental abordarea noastră: fiecare job de fundal de lungă durată devine o funcție de fundal, declanșată de un eveniment specific.

De exemplu, un eveniment Transcription.request va declanșa o funcție TranscribeAudio. Această funcție ar putea conține rulări de pași pentru: fetch_audio_metadata, deepgram_transcribe, parse_save_trasncription și notify_user.

Deconstruirea Fluxului de Lucru: Funcția Inngest și Rulările de Pași

Primitivul de durabilitate de bază sunt rulările de pași. O funcție de fundal este descompusă intern în aceste rulări de pași, fiecare conținând un bloc minimal, atomic de logică.

  • Logică Atomică: O funcție execută logica ta de afaceri pas cu pas. Dacă un pas eșuează, starea întregii rulări este păstrată, iar rularea poate fi reîncercată. Acest lucru repornește funcția de la început. Pașii individuali sau rulările de pași nu pot fi reîncercate izolat.
  • Serializarea Răspunsului: O rulare de pas este definită de răspunsul său. Acest răspuns este serializat automat, ceea ce este esențial pentru păstrarea structurilor de date complexe sau puternic tipizate peste granițele de execuție. Rulările de pași ulterioare pot analiza în mod fiabil acest răspuns serializat, sau logica poate fi îmbinată într-un singur pas pentru eficiență.
  • Decuplare și Programare: În cadrul unei funcții, putem pune în coadă sau programa condiționat evenimente noi, dependente, permițând modele complexe de fan-out/fan-in și programare pe termen lung de până la un an. Erorile și succesele în orice punct pot fi prinse, ramificate și gestionate mai departe în fluxul de lucru.

Abstractul funcției Inngest:

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 };
},
);
};

Modelul bazat pe evenimente al Inngest oferă o perspectivă granulară asupra fiecărei execuții a fluxului de lucru:

  • Urmărirea Completă a Evenimentelor: Fiecare execuție a funcției puse în coadă este înregistrată în raport cu evenimentul său de origine. Acest lucru oferă o urmă clară, de nivel înalt a tuturor activităților legate de o singură acțiune a utilizatorului.
  • Informații Detaliate despre Rulare: Pentru fiecare execuție a funcției (atât succese, cât și eșecuri), Inngest oferă loguri detaliate prin raportarea sa ack (confirmare) și nack (confirmare negativă). Aceste loguri includ urmele stivei de erori, încărcăturile complete ale cererilor și încărcăturile de răspuns serializate pentru fiecare rulare de pas individuală.
  • Metrici Operaționale: Dincolo de loguri, am obținut metrici critice privind sănătatea funcției, inclusiv rate de succes, rate de eșec și număr de reîncercări, permițându-ne să monitorizăm continuu fiabilitatea și latența fluxurilor noastre de lucru distribuite.

Construirea pentru Reziliență

Avertismentul pentru a te baza pe procesarea pură a evenimentelor este că, în timp ce Inngest pune eficient în coadă execuțiile funcțiilor, evenimentele în sine nu sunt puse în coadă intern în sensul tradițional al unui broker de mesaje. Această absență a unei cozi explicite de evenimente poate fi problematică în scenarii cu trafic ridicat din cauza potențialelor condiții de cursă sau a evenimentelor pierdute dacă punctul final de ingestie este copleșit.

Pentru a aborda acest lucru și a impune durabilitatea strictă a evenimentelor, am implementat un sistem dedicat de cozi ca tampon.

AWS Simple Queue System (SQS) a fost sistemul ales (deși orice sistem robust de cozi este realizabil), având în vedere infrastructura noastră existentă pe AWS. Am proiectat un sistem cu două cozi: o Coadă Principală și o Coadă de Scrisori Moarte (DLQ).

Am stabilit un Mediu de Lucru Elastic Beanstalk (EB) configurat special pentru a consuma mesaje direct din Coada Principală. Dacă un mesaj din Coada Principală nu poate fi procesat de Lucrătorul EB de un număr stabilit de ori, Coada Principală mută automat mesajul eșuat în DLQ dedicată. Acest lucru asigură că niciun eveniment nu este pierdut permanent dacă nu reușește să declanșeze sau să fie preluat de Inngest. Acest mediu de lucru diferă de un mediu standard de server web EB, deoarece responsabilitatea sa unică este consumul și procesarea mesajelor (în acest caz, redirecționarea mesajului consumat către punctul final API Inngest).

ÎNȚELEGEREA LIMITELOR ȘI SPECIFICAȚIILOR

O parte subestimată și destul de pertinentă a construirii infrastructurii la scară de întreprindere este că aceasta consumă resurse, și acestea sunt de lungă durată. Arhitectura microserviciilor oferă scalabilitate per serviciu. Stocarea, RAM-ul și timeout-urile resurselor vor intra în joc. Specificația noastră pentru tipul de instanță AWS, de exemplu, s-a mutat rapid de la t3.micro la t3.small și acum este fixată la t3.medium. Pentru joburile de fundal de lungă durată, intensive în CPU, scalarea orizontală cu instanțe mici eșuează deoarece blocajul este timpul necesar pentru a procesa un singur job, nu volumul de joburi noi care intră în coadă.

Joburile sau funcțiile precum transcodarea, încorporarea sunt de obicei limitate de CPU și limitate de memorie. Limitate de CPU deoarece necesită o utilizare susținută, intensă a CPU-ului, și limitate de memorie deoarece adesea necesită RAM substanțial pentru a încărca modele mari sau pentru a gestiona eficient fișiere sau încărcături mari.

În cele din urmă, această arhitectură augmentată, plasând durabilitatea SQS și execuția controlată a unui mediu de lucru EB direct în amonte de API-ul Inngest, a oferit reziliență esențială. Am obținut proprietatea strictă a evenimentelor, am eliminat condițiile de cursă în timpul vârfurilor de trafic și am câștigat un mecanism de scrisori moarte non-volatil. Am folosit Inngest pentru capacitățile sale de orchestrare a fluxului de lucru și de depanare, bazându-ne în același timp pe primitivele AWS pentru un debit maxim de mesaje și durabilitate. Sistemul rezultat nu este doar scalabil, ci și foarte auditabil, traducând cu succes joburile de fundal complexe, de lungă durată în micro-pași siguri, observabili și toleranți la eșec.


Building Spotify for Sermons a fost publicat inițial în Coinmonks pe Medium, unde oamenii continuă conversația prin evidențierea și răspunsul la această poveste.

Declinarea responsabilității: Articolele publicate pe această platformă provin de pe platforme publice și sunt furnizate doar în scop informativ. Acestea nu reflectă în mod necesar punctele de vedere ale MEXC. Toate drepturile rămân la autorii originali. Dacă consideri că orice conținut încalcă drepturile terților, contactează [email protected] pentru eliminare. MEXC nu oferă nicio garanție cu privire la acuratețea, exhaustivitatea sau actualitatea conținutului și nu răspunde pentru nicio acțiune întreprinsă pe baza informațiilor furnizate. Conținutul nu constituie consiliere financiară, juridică sau profesională și nici nu trebuie considerat o recomandare sau o aprobare din partea MEXC.