Il nostro bot di Slack ha gestito 200 messaggi al giorno per tre mesi senza sudare. Poi un blogger tecnologico ne ha parlato in una newsletter e siamo passati da 200 a 12.000 messaggi in 48 ore.
Tutto si è bloccato. Non in modo drammatico — il server non ha preso fuoco o altro. Si è semplicemente… rallentato. E poi si è rallentato di più. E poi ha iniziato a perdere messaggi. E infine è diventato completamente silenzioso mentre 12.000 persone si chiedevano perché il bot AI di cui avevano appena sentito parlare non rispondeva.
Ecco cosa è successo, cosa abbiamo fatto e come siamo passati da “progetto collaterale divertente” a “cosa di cui le persone dipendono davvero” in una settimana.
Le Prime 6 Ore: Negazione e Panico
La newsletter è arrivata nelle caselle di posta alle 9 di un martedì. Alle 10, la nostra coda di messaggi aveva un arretrato di 400 messaggi. A mezzogiorno, la coda era a 2.000 e il tempo di risposta era di 45 secondi (normalmente sotto i 3 secondi).
La mia prima reazione: “Hmm, sono molti messaggi.” La mia seconda reazione, 20 minuti dopo: “Oh no.”
Il collo di bottiglia non era la CPU o la memoria — era l’API del modello AI. Ogni messaggio richiedeva una chiamata API e stavamo colpendo duramente i limiti di frequenza. Il nostro piano API gratuito permetteva 60 richieste al minuto. Ne avevamo bisogno di 200+ al minuto.
Soluzione veloce: aggiornare il piano API. Abbiamo portato il nostro limite di frequenza a 500 richieste al minuto entro 30 minuti passando a un piano a pagamento. La coda ha iniziato a drenarsi. Crisi parzialmente evitata.
Ma poi è arrivata la seconda ondata.
Ore 6-24: Tutto il Resto si Rompe
Aumentare il throughput dell’API ha rivelato tutti gli altri collo di bottiglia che non avevamo notato a basso volume.
Le connessioni al database erano esaurite. Ogni messaggio attivava una ricerca nel database per il contesto utente. A 200 messaggi/giorno, nessun problema. A 12.000, il nostro pool di connessioni era esaurito. Gli utenti ricevevano errori “servizio non disponibile”.
Soluzione: aumento delle dimensioni del pool di connessioni, aggiunta del pooling delle connessioni con PgBouncer e implementazione di repliche di lettura per le ricerche di contesto.
Memory leak nel gestore dei messaggi. Una variabile che memorizzava il contesto della conversazione cresceva senza essere ripulita. A basso volume, cresceva lentamente e veniva ripulita da riavvii occasionali. A alto volume, consumava tutta la memoria disponibile in circa 4 ore.
Soluzione: aggiunta di una corretta pulizia dopo che ogni messaggio è stato elaborato. Questo bug era presente fin dal primo giorno — semplicemente non era mai stato un problema fino a quando non lo è diventato.
Elaborazione in un thread singolo. I messaggi venivano elaborati sequenzialmente. Uno alla volta. A 200 messaggi/giorno, andava bene. A 12.000, significava che ogni messaggio aspettava dietro a ogni altro messaggio.
Soluzione: implementata l’elaborazione concorrente con una corretta coda di lavoro. I messaggi vengono distribuiti tra più lavoratori. Questo da solo ha ridotto il tempo medio di risposta da 45 secondi a meno di 5.
Il Momento “Oh, Abbiamo Bisogno di una Vera Infrastruttura”
Giunto all’ora 24, mi sono reso conto che la nostra architettura “funziona su un VPS da 10 dollari/mese” non avrebbe gestito una crescita sostenuta. Avevamo bisogno di:
Un bilanciatore di carico adeguato. Non perché ci servissero più server ancora, ma perché avevamo bisogno di controlli di integrità, riavvii automatici e la possibilità di deployare aggiornamenti senza tempo di inattività.
Una coda di messaggi. Coda di lavoro supportata da Redis che separa la ricezione dei messaggi dall’elaborazione dei messaggi. Se il modello AI è lento, i messaggi aspettano nella coda invece di andare in timeout. Se un lavoratore si guasta, il messaggio viene riprovato invece di perdersi.
Monitoraggio che avvisi realmente. Avevamo logging. Non avevamo avvisi. La differenza conta quando le cose si rompono alle 2 di notte e nessuno sta guardando i log.
Scalabilità orizzontale. La possibilità di aggiungere più lavoratori quando il carico aumenta. La nostra architettura ora si scala automaticamente: se la profondità della coda supera una soglia, nuovi lavoratori si attivano automaticamente.
Cosa Abbiamo Conseguito in una Settimana
Giorni 1-2: Aggiornamento dell limite di frequenza di emergenza, correzione del pool di connessioni, correzione del memory leak.
Giorni 3-4: Implementazione della coda di messaggi, elaborazione concorrente.
Giorni 5-6: Bilanciatore di carico, monitoraggio con avvisi, scalabilità orizzontale.
Giorno 7: Finalmente ho dormito.
Il costo totale dell’infrastruttura è passato da 10 dollari/mese a circa 120 dollari/mese. Ma siamo passati dal supportare 200 messaggi/giorno a gestirne comodamente 50.000. E l’architettura può scalare ulteriormente semplicemente aggiungendo lavoratori.
La Lista di Controllo per la Scalabilità che Avrei Voluto Avere
Se il tuo bot AI sta guadagnando trazione e vuoi essere pronto prima che arrivi il picco:
Configura ora il monitoraggio con avvisi. Tempo di risposta, tasso di errore, profondità della coda, utilizzo della memoria. Soglie di avviso a 2x i valori normali. Vuoi conoscere i problemi prima che siano gli utenti a dirti.
Implementa una coda di messaggi. Anche a basso volume. Separa la ricezione dall’elaborazione, abilita i retry e rende la scalabilità orizzontale banale in seguito.
Profilati l’uso delle risorse per messaggio. Quante query al database per messaggio? Quanta memoria? Quante chiamate API? Moltiplica questi numeri per il tuo obiettivo di crescita e vedi dove saranno i collo di bottiglia.
Testa a 10 volte il carico attuale. Usa uno strumento di test del carico per simulare un volume di messaggi 10 volte superiore per un’ora. Guarda cosa si rompe. Risolvilo prima che si rompa in produzione.
Documenta un piano di scalata. “Se il traffico raddoppia, fai queste tre cose.” Avere il piano scritto significa che puoi eseguirlo alle 2 di notte quando sei mezzo addormentato invece di cercare di architettare soluzioni sotto pressione.
Cosa Ho Imparato Sull’AI in Scala
Il modello AI non è di solito il collo di bottiglia — tutto intorno a esso lo è. Query al database, gestione del contesto, formattazione dell’output, instradamento dei messaggi — tutta l’infrastruttura “noiosa” che salti quando costruisci un prototipo. Su larga scala, le cose noiose contano più di quelle dell’AI.
Inoltre: i limiti di frequenza sono la restrizione di scalabilità meno apprezzata nelle applicazioni AI. La tua architettura brillante non conta se l’API del modello consente solo 60 richieste al minuto. Controlla i tuoi limiti prima di lanciare e abbi un piano per quando li supererai.
Il picco virale è stato stressante ma alla fine positivo. Ci ha costretto a costruire l’infrastruttura che avremmo dovuto costruire fin dall’inizio. E ora siamo pronti per il prossimo picco — quando arriverà.
🕒 Published: