DIEGO CECATO / NOTE DAL CAMPO

Cloudflare Workers: il codice spento che consumava memoria

diego cecato//
Copertina dell'articolo sul profiling di Cloudflare Workers

Un servizio comincia a superare il limite di memoria. Gli errori aumentano, le metriche lo confermano e il codice sospetto, almeno sulla carta, è già stato disattivato. A questo punto si può continuare a ritoccare soglie e configurazioni, oppure verificare una domanda molto meno comoda: quel codice è davvero spento, o abbiamo semplicemente smesso di guardare quello che fa?

È il problema che Cloudflare racconta annunciando il profiling CPU e memoria su richiesta per Workers e Durable Objects, il 9 ottobre 2026. In un caso interno, un Worker arrivava a 133 MB al percentile P999, oltre il limite di 128 MB previsto per l’isolate. Il profilo delle allocazioni ha mostrato una presenza sorprendente: codice Prometheus considerato disabilitato rappresentava circa il 66,7% delle allocazioni osservate nel campione. Rimuovendo quel percorso, il P999 è sceso a 118 MB. Sono dati pubblicati da Cloudflare sul proprio sistema, non una promessa di miglioramento trasferibile a qualsiasi applicazione.

La notizia non è soltanto che sia comparso un altro flamegraph. È che diventa più semplice mettere in discussione un’ipotesi operativa direttamente dove il software lavora, con richieste e carichi che un ambiente di sviluppo spesso non riesce a riprodurre.

Il codice disattivato può continuare a presentare il conto

Chi ha mantenuto software abbastanza a lungo conosce questa categoria di problemi. Una funzionalità viene disabilitata, ma resta una parte della sua strumentazione: una chiamata che prepara dati, un oggetto che viene costruito, una serializzazione inutile, un contatore che non spedisce più nulla ma continua a essere aggiornato. Dall’esterno sembra che il servizio sia stato alleggerito. Nel percorso di esecuzione, invece, si paga ancora una parte del costo.

Nel caso descritto da Cloudflare, il codice di monitoraggio attraversava molti punti dell’applicazione. I dati raccolti non uscivano dal Worker, ma le allocazioni continuavano. La metrica di memoria indicava il problema; non spiegava perché si verificasse. Il profilo ha fornito un’indicazione sufficientemente precisa da giustificare una modifica mirata: eliminare completamente quel percorso, anziché inseguire un generico problema di consumo.

C’è una distinzione importante. Il 66,7% è la quota delle allocazioni nel profilo, non la prova che il 66,7% della memoria trattenuta appartenesse a Prometheus. Un allocation profile e un heap snapshot non misurano la stessa cosa. Scambiarli significa trasformare uno strumento diagnostico in una spiegazione sbagliata, per quanto rassicurante.

Un componente non è gratuito perché lo hai disabilitato nella configurazione. È gratuito soltanto quando il percorso che consumava risorse non viene più eseguito.

Le metriche indicano il sintomo. Il profilo mette alla prova la causa

Un grafico può dirti che il tempo CPU è cresciuto, che la memoria P99 si avvicina al limite o che gli errori si concentrano dopo un deploy. Sono informazioni necessarie, ma aggregate. Un log può registrare che una richiesta è fallita; non è detto che mostri quale funzione abbia assorbito il lavoro prima del fallimento. Il profiling aggiunge un’altra vista: campioni dello stack di esecuzione, raggruppati per funzione e per percorso di chiamata.

Nel flamegraph, la larghezza dei rettangoli rappresenta la quota di tempo CPU o di allocazioni attribuita ai frame campionati, a seconda del tipo di profilo. Non è una cronologia delle richieste: la posizione orizzontale non racconta quando sia successo qualcosa. Anche la distinzione fra tempo self e tempo total conta, perché una funzione può apparire costosa soltanto perché chiama lavoro costoso altrove. La documentazione di profiling in produzione di Cloudflare chiarisce questi limiti e descrive l’esportazione dei profili nel formato pprof.

Questa differenza è la ragione per cui una dashboard ricca di grafici non garantisce, da sola, una diagnosi migliore. Si possono misurare cento indicatori e continuare a non avere la prova che serve per scegliere quale riga di codice modificare. Il profilo è utile quando restringe le ipotesi, non quando aggiunge un pannello alla collezione.

Anche un innocuo JSON può sprecare più CPU di quanto immagini

Cloudflare porta un secondo esempio, questa volta legato al Worker del binding R2. In un profilo CPU, una funzione chiamata genericR2JsonReplacer assorbiva oltre il 5% del tempo campionato. La particolarità era nel modo in cui lavorava: veniva richiamata dal meccanismo di JSON.stringify per ogni nodo, ma percorreva a sua volta l’albero. Un valore annidato veniva quindi elaborato più volte, in funzione della profondità.

Correggendo il lavoro duplicato, Cloudflare riferisce che quella funzione è diventata 2,7 volte più veloce. Non il Worker intero: è una differenza che merita di essere scritta, perché altrimenti un miglioramento locale diventa facilmente un numero pubblicitario. Nello stesso profilo compariva anche una doppia chiamata a metrics(); memorizzare il risultato della prima ha eliminato una seconda elaborazione, che secondo l’azienda valeva circa l’1% del tempo CPU osservato.

Sono difetti molto ordinari. Non richiedono algoritmi esotici né un nuovo framework. Richiedono di vedere il lavoro effettivo, compreso quello che la struttura del codice rende difficile notare. È lo stesso principio che emerge quando si ragiona sul costo delle risorse dal codice ai processi: prima di ottimizzare bisogna capire dove si accumula il costo, non dove immaginiamo che si trovi.

Profilare in produzione non significa fotografare tutta la produzione

La nuova funzionalità permette di richiedere profili CPU o delle allocazioni dalla dashboard Workers Observability, dalla CLI cf o dall’API. Si sceglie la versione, il tipo di profilo e una durata; la documentazione indica una finestra configurabile da 1 a 50 secondi. Per i Worker distribuiti viene individuato un isolate già caricato e recentemente attivo. Non viene creato un nuovo isolate soltanto per fare la misura: sarebbe un test su un’esecuzione artificiale, non l’osservazione di quella reale.

Qui compare un vincolo spesso sottovalutato: serve traffico rappresentativo mentre il profiler sta registrando. Se la versione scelta non riceve richieste, non c’è abbastanza lavoro da osservare. Inoltre latest nella CLI può indicare la versione creata più recentemente, non necessariamente quella effettivamente distribuita. Per i Durable Objects la selezione può invece puntare a una specifica istanza identificata, utile quando il comportamento anomalo riguarda uno stato particolare e non un Worker stateless qualunque.

Per la CPU, Cloudflare descrive un campionamento V8 con intervallo di un millisecondo. Il runtime acquisisce il lock dell’isolate per avviare il profiler, lo rilascia durante la raccolta e lo riprende soltanto per interromperla. Tenere il lock per tutta la durata impedirebbe al JavaScript di lavorare e renderebbe il profilo una misura di un’applicazione bloccata. È un dettaglio architetturale più interessante della semplice presenza del pulsante “Capture profile”.

Tre limiti che possono farti leggere male il flamegraph

Il primo è temporale. Un profilo on-demand osserva una finestra selezionata, non ciò che è accaduto mezz’ora prima. Se l’errore è raro e imprevedibile, si può catturare un periodo perfettamente normale e concludere, erroneamente, che il problema non esista. Cloudflare dichiara di lavorare al profiling continuo proprio per ridurre questo limite; al momento dell’annuncio è un progetto futuro, non una capacità già disponibile.

Il secondo riguarda la memoria. Il profilo heap registra campioni delle allocazioni avvenute durante la cattura, non gli oggetti rimasti vivi nel processo e non necessariamente le allocazioni dello startup. La documentazione indica un campione dello stack ogni 512 kB allocati. Se il consumo problematico nasce all’avvio oppure dipende da oggetti trattenuti a lungo, serviranno altre misure e forse un heap snapshot locale. Un grafico delle allocazioni non è una fotografia della memoria residente.

Il terzo limite è il contesto. Un flamegraph può puntare a nomi minificati se non sono disponibili le source map. Può fotografare una versione sbagliata, un isolate poco rappresentativo o un carico diverso da quello che genera gli errori. E una funzione larga non è automaticamente un difetto: cifrare dati o elaborare una risposta può essere il lavoro necessario del servizio. La diagnosi comincia quando si distingue il costo inevitabile dal costo eliminabile.

Un metodo utile: ipotesi, campione, modifica, nuova misura

Un buon ciclo di ottimizzazione parte dalla metrica che giustifica l’indagine: latenza, errori, CPU, memoria e distribuzione dei percentili. Poi si formula un’ipotesi falsificabile. Non “il Worker è lento”, ma “stiamo serializzando più volte lo stesso oggetto” oppure “una strumentazione disabilitata continua ad allocare”. Si cattura un profilo sulla versione giusta e con traffico adeguato, si legge la catena di chiamate, si cambia una causa per volta e si confrontano le metriche prima e dopo.

Il confronto deve guardare anche la coda lunga. Una media più bassa può convivere con peggioramenti ai percentili alti, proprio quelli che producono timeout e incidenti. Nel caso Cloudflare, il passaggio del P999 da 133 a 118 MB ha senso perché porta un percentile prima oltre soglia al di sotto del limite dichiarato. Non dimostra che tutti i Worker guadagneranno 15 MB, né che ogni errore di memoria dipenda da Prometheus.

Per chi lavora con strumenti di sviluppo e agenti AI, il punto si collega anche al profiling integrato nel flusso degli agenti: una proposta di modifica vale poco senza un criterio osservabile che dimostri il miglioramento. Che la diagnosi sia fatta da un programmatore o suggerita da un agente, la prova resta nel comportamento del software e nel confronto delle misure.

L’osservabilità serve a correggere decisioni, non a produrre grafici

Il nuovo profiler di Cloudflare è una buona notizia per chi usa Workers e Durable Objects perché avvicina il debugging al carico effettivo, senza pretendere che un test locale replichi tutta la complessità della produzione. Ma il valore vero non è il flamegraph colorato. È poter smettere di difendere un’ipotesi quando i campioni la smentiscono.

Quel codice doveva essere spento. Le metriche dicevano che la memoria era un problema. Il profilo ha mostrato dove guardare. È una sequenza meno spettacolare di molte promesse sulla performance, ma molto più utile: misurare, capire, intervenire e misurare di nuovo. Tutto il resto, compreso il pannello più bello del mondo, è arredamento.

Domande frequenti

Il profiling in produzione sostituisce log e metriche?

No. Log e metriche segnalano errori, andamento e percentili; un profilo CPU o delle allocazioni aiuta a individuare le funzioni e i percorsi che consumano risorse. Per una diagnosi affidabile servono entrambe le viste.

Come si avvia un profilo CPU o memoria su Cloudflare Workers?

Dalla sezione Observability del Worker nella dashboard, tramite CLI cf o API. Occorre scegliere la versione e la durata della cattura e assicurarsi che l’isolate riceva traffico rappresentativo mentre il profilo viene registrato.

Un profilo delle allocazioni equivale a uno snapshot della memoria occupata?

No. Il profilo registra campioni delle allocazioni avvenute durante la finestra di cattura, non necessariamente gli oggetti ancora residenti né quelli allocati all’avvio. Per distinguere allocazioni e memoria trattenuta possono servire misure aggiuntive.

Perché i dati Cloudflare non dimostrano che ogni Worker migliorerà allo stesso modo?

I numeri pubblicati descrivono casi interni specifici: una funzione resa 2,7 volte più veloce e un Worker il cui P999 di memoria è sceso da 133 a 118 MB dopo una correzione. Non sono benchmark generali né garanzie di risultato per altri servizi.

È possibile profilare uno specifico Durable Object?

Sì. A differenza di un Worker stateless, un Durable Object può essere selezionato per nome o identità, così il profiling viene indirizzato all’istanza che mantiene lo stato da investigare.