Qualche anno fa avevo affrontato su questo blog il problema del superamento del tempo massimo di esecuzione di uno script, risolto con un pattern di checkpoint su cache e trigger di auto-riattivazione. Quella tecnica resta valida ancora oggi: risolve però un problema specifico, il tempo di esecuzione. Oggi ne affrontiamo uno diverso, altrettanto comune, ma slegato dal tempo: il limite di memoria e dimensione dei payload quando i dati da gestire sono semplicemente troppi per stare in un'unica esecuzione, indipendentemente da quanto sia veloce lo script.
Chiunque sviluppi su Google Apps Script (GAS) si è scontrato almeno una volta con i limiti della piattaforma: oltre al timeout di esecuzione, ci sono i vincoli di memoria dell'ambiente V8 e i tetti massimi per i payload in risposta. Quando il volume dei dati cresce (milioni di celle o centinaia di megabyte di JSON), la tentazione comune è quella di abbandonare l'ecosistema Workspace per migrare su backend serverless dedicati come AWS Lambda o Google Cloud Run.
Tuttavia, ripensando l'architettura del flusso dati, è possibile gestire dataset enormi direttamente in Apps Script senza spendere un centesimo in infrastrutture esterne.
Il principio architetturale: GAS come "Thin Proxy"
Il segreto per aggirare i limiti di runtime consiste nello spostare il carico computazionale pesante fuori dal runtime di Apps Script, delegando l'elaborazione al client (il browser dell'utente) e sfruttando Google Drive come storage intermedio.
Il flusso di una pipeline scalabile si articola in quattro fasi:
- Compressione e suddivisione (Chunking): il dataset di origine viene compresso (ad esempio tramite algoritmi DEFLATE/Gzip), codificato in Base64 e spezzettato in file di dimensioni contenute.
- Archiviazione su Drive: i chunk vengono caricati in una cartella dedicata su Google Drive.
- Distribuzione tramite GAS: Apps Script agisce da semplice intermediario (thin proxy), esponendo funzioni minime per recuperare i metadati e i singoli blocchi di dati.
- Riassemblaggio nel browser: l'interfaccia utente (costruita tramite
HtmlServiceo Web App) scarica i chunk in parallelo, li unisce, li decomprime ed esegue il parsing direttamente nella memoria del client.
Esempio pratico: il backend Apps Script
Il seguente codice implementa un endpoint e i metodi di supporto per servire dataset suddivisi a blocchi residenti su Google Drive.
/** * Restituisce l'elenco ordinato degli ID dei chunk presenti in una cartella di Drive. * @param {string} folderId - ID della cartella contenente i file di chunk. * @returns {Array<{id: string, name: string, size: number}>} Metadati dei chunk. */ function getDatasetManifest(folderId) { const folder = DriveApp.getFolderById(folderId); const files = folder.getFiles(); const manifest = []; while (files.hasNext()) { const file = files.next(); manifest.push({ id: file.getId(), name: file.getName(), size: file.getSize() }); } // Ordina i file in base al nome (es. chunk_001.txt, chunk_002.txt) return manifest.sort((a, b) => a.name.localeCompare(b.name)); } /** * Recupera il contenuto testuale (Base64 compresso) di uno specifico chunk. * @param {string} fileId - ID del file Drive da scaricare. * @returns {string} Contenuto del file. */ function fetchChunkContent(fileId) { try { const file = DriveApp.getFileById(fileId); return file.getBlob().getDataAsString(); } catch (error) { throw new Error(`Errore durante il recupero del chunk ${fileId}: ${error.message}`); } } /** * Punto di ingresso per servire l'interfaccia utente della Web App. */ function doGet() { return HtmlService.createTemplateFromFile('Index') .evaluate() .setTitle('Data Viewer ad Alte Prestazioni') .addMetaTag('viewport', 'width=device-width, initial-scale=1'); }
Riassemblaggio e decompressione client-side
Con questa struttura, Apps Script non deve mai caricare l'intero file in memoria, evitando l'errore di Exceeded memory limit.
Sul frontend (nel file Index.html), il client effettua chiamate asincrone tramite google.script.run per scaricare i chunk. Una volta ricevute le stringhe Base64:
- Le concatena nell'ordine corretto.
- Utilizza librerie JavaScript leggere come fflate o le API native del browser (
DecompressionStream) per decomprimere il payload binario. - Esegue
JSON.parse()sul testo decodificato.
In questo modo, il parsing di centinaia di megabyte avviene alla velocità della CPU locale dell'utente, svincolando completamente l'applicazione dai tempi di risposta e dalle quote di Apps Script.
Cosa è cambiato rispetto al mio articolo del 2018
Un aggiornamento doveroso su quel vecchio articolo, perché nel frattempo qualcosa è cambiato davvero: il limite esteso a 30 minuti per esecuzione, riservato allora agli account G Suite Business in Early Access, non esiste più. Oggi Google applica un tetto fisso di 6 minuti per esecuzione a tutti gli account, consumer e Workspace, senza eccezioni acquistabili o attivabili. Chi ancora oggi si affida a script che superano i 6 minuti deve necessariamente usare il pattern di checkpoint e trigger che avevo descritto allora, non ci sono più scorciatoie.
La buona notizia è che quel pattern resta lo standard raccomandato ancora oggi per il problema del tempo. Un paio di accorgimenti in più da tenere a mente se lo riapplichi ora:
- C'è un tetto massimo di 20 trigger basati sul tempo per singolo script (uguale per tutti gli account): se il tuo processo richiede molte riattivazioni concatenate, ripulisci sempre i trigger scaduti per non saturare quel limite.
- Per il valore di checkpoint, oggi conviene usare
PropertiesServiceinvece diCacheService: la cache scade al massimo dopo alcune ore, mentre le Properties persistono senza scadenza, quindi sono più robuste se il riavvio del trigger dovesse tardare per qualche motivo.
Quando servono entrambe le tecniche insieme
I due problemi, tempo e memoria, possono capitare insieme: un dataset enorme da processare (non solo distribuire) sul lato server, dove il solo chunking non basta perché anche l'elaborazione dei singoli blocchi richiede più di 6 minuti complessivi. In quel caso il pattern giusto è la combinazione di entrambi gli articoli: dividi i dati in chunk come descritto qui, ed elabora ogni chunk con il meccanismo di checkpoint e trigger di continuazione descritto nell'articolo del 2018, salvando in PropertiesService l'indice dell'ultimo chunk processato invece del singolo contatore di riga.
Conclusione
I vincoli di piattaforma non impongono necessariamente una migrazione verso architetture complesse e a pagamento. Utilizzando Google Apps Script come puro strato di distribuzione e delegando la manipolazione pesante dei dati al browser e alle API di compressione, è possibile processare dataset massivi rimanendo al 100% all'interno dell'infrastruttura gratuita di Google Workspace, magari combinando questa tecnica con quella per il tempo di esecuzione quando serve gestire sia volume che durata.

!
Non ci sono commenti
Nessuno ha ancora commentato questo articolo, fallo tu per primo!
scrivi un commento