Persistenza, concorrenza e limiti del file JSON
Prerequisiti: lezioni 07, 19 e 29. Comprendi cosa significa conservare dati oltre la vita del processo.
Definizioni e scelte
- Memoria volatile e persistenza
- Una variabile vive nel processo; un file può essere letto dopo il riavvio. Persistenza non significa automaticamente backup, disponibilità o integrità: un file può essere sovrascritto, corrotto o perso.
- Read-modify-write
- È la sequenza leggi, modifica e riscrivi. Tra lettura e scrittura un altro attore può modificare gli stessi dati. L’ultima scrittura può cancellare l’effetto della precedente, anche se entrambe le richieste ricevono successo.
- Transazione
- Raggruppa operazioni con garanzie definite di atomicità e isolamento. Un database può offrire strumenti per gestire aggiornamenti concorrenti; leggere e riscrivere JSON non crea una transazione.
Archivio minimo per un processo CLI
Salva come archivio.mjs. Usa un file dedicato al laboratorio; questo esempio è per esecuzioni sequenziali e non va usato come archivio concorrente di un server.
import { readFile, writeFile } from 'node:fs/promises';
const file = new URL('./persone-lab.json', import.meta.url);
async function carica() {
let testo;
try { testo = await readFile(file, 'utf8'); }
catch (err) {
if (err.code === 'ENOENT') return [];
throw err;
}
const dati = JSON.parse(testo);
if (!Array.isArray(dati) || !dati.every(p =>
p && typeof p.nome === 'string')) {
throw new Error('Archivio non valido');
}
return dati;
}
const persone = await carica();
persone.push({ nome: 'Ada' });
await writeFile(file, JSON.stringify(persone, null, 2), 'utf8');
console.log('Persone salvate:', persone.length);Verifica guidata
- Su una cartella nuova esegui
node archivio.mjs: stampa 1 e crea il file. - Eseguilo nuovamente: stampa 2. Il dato è sopravvissuto al primo processo.
- In una copia di prova rendi il JSON invalido: l’esecuzione deve fallire senza sostituirlo con una lista vuota.
Perché non basta per un server
La scrittura diretta può lasciare un file parziale in caso di interruzione. Scrivere un file temporaneo e rinominarlo può ridurre alcuni rischi, ma non risolve aggiornamenti persi, accesso multiprocesso o tutte le garanzie di durabilità. Una coda interna serializza soltanto chi passa per quella stessa coda; un secondo processo la ignora.
Prova tu: traccia il conflitto
A e B leggono entrambi una lista di 2 persone. A aggiunge Lia e salva 3 elementi. B aggiunge Leo alla propria copia e salva. Quante persone restano?
Spiegazione
Restano 3 elementi e Lia è persa: B ha salvato una copia vecchia. Per ottenere 4 serve coordinare lettura e scrittura, oppure usare operazioni e transazioni adeguate in un database.
Passo successivo: definisci un’interfaccia di repository, separala dalle route e prova lo stesso contratto con memoria e archivio persistente. Riferimento alle API del filesystem.