Build, code e concorrenza
Cosa fa una build
Sezione intitolata “Cosa fa una build”npm run build esegue, nell’ordine:
- sync del Content Layer: per ogni collezione chiama il loader;
- fetch REST: una richiesta per CPT, più una batch per le immagini di ogni collezione che ne ha;
- validazione Zod + mapping e scrittura nello store;
- rendering delle pagine (
getStaticPathsper i dettagli); - asset: CSS, JS delle isole, sitemap, file pubblici.
Ordine di grandezza: ~14 richieste HTTP per una build completa (10 collezioni + 4–5 batch immagini + impostazioni). I tempi dipendono da WordPress e dalla rete, non dal numero di pagine.
I loader sono indipendenti
Sezione intitolata “I loader sono indipendenti”Ogni collezione ha il suo loader. Astro li esegue durante il sync e ognuno gestisce i propri errori:
- un loader che fallisce non “avvelena” gli altri;
- con
ENABLE_HTML_FALLBACK=falseil primo errore fa fallire la build (voluto); - con
trueil loader logga un warning e lascia la collezione vuota.
Non c’è stato condiviso tra i loader, nessuna coda applicativa, nessun lock: ogni loader fa le sue richieste. L’unica ottimizzazione è il batch dei media, che evita N richieste per N immagini.
Perché serve un rebuild
Sezione intitolata “Perché serve un rebuild”Il sito è statico: i contenuti sono scritti dentro l’HTML. Non esiste un database da interrogare a runtime. Quindi:
- modifica su WordPress → il sito pubblico non cambia;
- serve rifare la build e ripubblicare i file.
È il prezzo dello statico. Per un sito vetrina va bene; per contenuti che cambiano ogni minuto no (vedi Perché questo template).
Il webhook: perché parte una Action
Sezione intitolata “Il webhook: perché parte una Action”headless-webhook.php intercetta:
wp_after_insert_postper i CPT del sito e per le pagine;updated_optionper le opzioniheadless_*(contatti, social, legali, toggle).
Al salvataggio:
- imposta un transient di 60 secondi (debounce);
- su
shutdownchiama GitHubrepository_dispatchconevent_type: wp-content-update; - il workflow
deploy.ymlascolta quell’evento e parte.
Perché il debounce: un editor che salva cinque volte in un minuto deve produrre una
build, non cinque. Perché blocking: false: l’admin non deve aspettare la rete.
Perché il webhook non contiene logica di build: WordPress notifica, GitHub costruisce.
La concorrenza, in pratica
Sezione intitolata “La concorrenza, in pratica”Ci sono tre livelli distinti. Non sono la stessa cosa.
1. Dentro la build
Sezione intitolata “1. Dentro la build”Astro sincronizza le collezioni; i loader non condividono risorse. Le richieste HTTP possono essere concorrenti, ma non c’è una coda nostra da gestire. Il risultato è uno snapshot coerente dei contenuti al momento della build.
2. Tra una modifica e l’altra (webhook)
Sezione intitolata “2. Tra una modifica e l’altra (webhook)”Il debounce da 60 secondi collassa le raffiche. Se due modifiche avvengono a più di 60 secondi di distanza, partono due dispatch.
3. Sulle GitHub Actions
Sezione intitolata “3. Sulle GitHub Actions”concurrency: group: deploy cancel-in-progress: falsegroup: deploy: al massimo un deploy alla volta per repository.cancel-in-progress: false: i run in coda non vengono cancellati.
Cosa succede con tre dispatch ravvicinati ma fuori dal debounce:
- il primo run parte;
- gli altri restano in coda;
- ogni run rifà la build leggendo lo stato attuale di WordPress.
Quindi l’ultimo run pubblica lo stato più recente. I run intermedi sono ridondanti ma
innocui. Cancellare quelli in coda (cancel-in-progress: true) ridurrebbe i minuti,
ma rischierebbe di interrompere un sync FTPS a metà. Per un sito vetrina la scelta
conservativa è corretta.
Cosa non è
Sezione intitolata “Cosa non è”- Non è una coda di messaggi: nessun worker, nessun retry automatico.
- Non è un lock sui contenuti: WordPress può cambiare durante la build.
- Non è un sistema di preview: non c’è modo di vedere la modifica prima del deploy.
Se i contenuti sono tanti
Sezione intitolata “Se i contenuti sono tanti”Il loader legge per_page=100. Oltre 100 elementi per CPT servono pagine:
const all: MyItem[] = [];let page = 1;while (true) { const batch = schema.parse( await fetchWordPressJson({ endpoint, searchParams: { per_page: 100, page, _fields: "..." }, }), ) as MyItem[]; all.push(...batch); if (batch.length < 100) break; page++;}Aggiungilo quando serve davvero, non prima: il limite è documentato e la build fallisce in modo silenzioso solo se superi i 100 senza accorgertene. Se prevedi cataloghi grandi, mettilo subito e aggiungi un test.
Ottimizzazioni possibili (non ancora necessarie)
Sezione intitolata “Ottimizzazioni possibili (non ancora necessarie)”- cache delle risposte REST tra build (token, ETag);
- richieste parallele esplicite per collezione;
- build incrementali (Astro non le fa per i loader custom);
- preview separata con
ENABLE_HTML_FALLBACK=truee un sottoinsieme di contenuti.
Ognuna aggiunge complessità. Il template preferisce una build semplice e prevedibile.