KINDLE HOME

ABChat — Fix Notturno: public e2e, timeout SSE, bug button

Resoconto di una sessione di manutenzione sulla flotta ABChat: pagine pubbliche servite a tutti, fine dei timeout fantasma nello stream, e un pulsante di segnalazione bug. Tre sintomi, tre catene di cause di sistema.

Questo è il resoconto di una sessione di manutenzione sull'infrastruttura ABChat — la flotta di brain che gira su emibrain, grbrain e avocado. Tre fronti, tutti partiti da sintomi concreti riportati durante la giornata, tutti chiusi con il codice in repository e il deploy sui server reali. Niente patch al volo sulla produzione: ogni modifica al codice è passata da un commit, un push, e un pull coordinato sui tre server.

Le pagine pubbliche che non si vedevano

Il primo sintomo era netto: una pagina generata da un brain — l'analisi di un verbale per un cliente — restituiva un redirect al login invece del contenuto. La causa non era una, ma una catena. Il symlink che nginx usa per servire la cartella pubblica di un brain non esisteva: solo un paio di brain, sistemati a mano in passato, ce l'avevano. Creato quello, il redirect spariva ma lasciava il posto a un 403: il blocco nginx che serve le pagine pubbliche non dichiarava index index.html, quindi una richiesta di directory cercava un index.php inesistente e si fermava. Aggiunta quella riga, la pagina finalmente rispondeva 200.

Ma il problema vero era più a monte. Indagando perché i brain nuovi nascessero senza quel symlink, è emerso che il provisioning gira dentro il container PHP, mentre la cartella pubblica dell'host non era montata nel container. Il codice creava diligentemente il symlink — solo che lo creava nel filesystem effimero del container, non sull'host dove nginx serve davvero. Lo stesso valeva per il file di dominio. La correzione è stata montare /var/abchat/public e /var/abchat/shared nei container, così che quei symlink atterrino dove devono. Validato creando un brain di prova: il symlink e il file di dominio sono comparsi sull'host, esattamente dove servivano.

Un sintomo singolo — "non vedo questa pagina" — che si è srotolato in tre cause stratificate: il symlink mancante, l'index nginx, e il mount del container. Sistemarle tutte ha reso la pubblicazione affidabile per chiunque, non solo per chi viene aggiustato a mano.

I messaggi che restavano appesi

Il secondo fronte era il più insidioso, perché colpiva ogni utente. Mandavi un messaggio, partiva lo spinner, e lo spinner non si fermava più — salvo poi scoprire, al refresh della pagina, che la risposta era già lì, salvata. Non era un problema di modello né un timeout vero: il backend rispondeva, il lavoro finiva, ma l'evento di completamento non arrivava mai al browser.

La radice era un dettaglio chirurgico. Il controller che streamava la risposta in tempo reale chiamava una funzione di flush del buffer di output senza che un buffer fosse mai stato aperto. Ogni pezzo di risposta generava un'eccezione, lo stream moriva a metà, e l'evento "fatto" non veniva consegnato. Sullo stesso meccanismo, un job in background entrava in loop di tentativi falliti: nel log di un server si contavano quasi duemila occorrenze dello stesso errore. La diagnosi, fatta indipendentemente da due sessioni, convergeva sulla stessa riga di codice.

La correzione è doppia. Lato server, una guardia che esegue il flush solo se un buffer esiste davvero, così l'eccezione non può più nascere. Lato browser, una rete di sicurezza: se lo stream muore senza consegnare il completamento, un controllo periodico verifica se il job è ormai terminato e, in quel caso, recupera la risposta e chiude lo spinner. Difensiva, avvolta in try-catch, incapace di rompere la chat esistente. Deployata sui tre server, con il restart dei processi PHP per ricaricare il codice.

Il pulsante per segnalare i problemi

Il terzo fronte era una richiesta diretta: un pulsante per segnalare un bug, in cima alla chat. La logica è semplice ma decisa nel disaccoppiamento. Il pulsante apre una finestrella dove l'utente scrive cosa non va; alla conferma, parte una chiamata che raccoglie automaticamente il contesto — quale brain, quale sessione, l'ultimo messaggio, l'utente, l'orario — e lo impacchetta in una mail strutturata verso un indirizzo dedicato. Da lì, un sistema di triage può leggere il soggetto in modo deterministico e andare dritto alla conversazione giusta, senza interpretare linguaggio naturale.

Il controller è isolato in un file a sé, così non può rompere nient'altro. La mail è stata testata end-to-end: una segnalazione di prova è partita e arrivata davvero. Il pulsante è ora sui tre server, accanto al badge che ricorda a tutti che siamo in beta.

Il metodo, e cosa resta

Tutto il lavoro è stato sviluppato e testato su un server di sviluppo, committato da una macchina con i permessi di scrittura, e poi distribuito agli altri server tramite pull — mai un commit originato su un server di produzione del cliente. Le prove sono state fatte sul brain demo e su brain usa-e-getta, archiviati a gioco fatto, e mai accendendo sessioni di test sui brain di clienti reali.

Sul versante tracciabilità, due issue sono stati chiusi perché risolti — il timeout dello stream e il pulsante di segnalazione — e un terzo, l'incidente sui permessi delle cartelle, è stato chiuso rimandando al suo fix permanente. Altri tre restano aperti con commenti di stato: il percorso dei brain che a volte viene ricostruito dallo slug invece che dal database, il chown delle cartelle al provisioning, e un'etichetta di modello fuorviante nell'interfaccia. Ognuno con la sua diagnosi scritta, pronta per chi lo prenderà in mano.

La lezione che torna, sessione dopo sessione, è la stessa: i sintomi sono quasi sempre la punta di una catena. La pagina che non si vede, lo spinner che non si ferma, il messaggio che sembra perso — ciascuno ha portato, scavando, a una causa di sistema che valeva la pena sistemare una volta per tutte, invece di tamponare il singolo caso.

- FINE -
1