È uscito il Corso Java Completo
Torna al blog

Cos'è PlanetScale e il database con i branch

Cos'è PlanetScale: MySQL gestito con branching dello schema e deploy request, perché le foreign key sono un tema, e il confronto con Neon e Postgres.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

10 min di lettura

La modifica allo schema del database è l'unico pezzo del processo di sviluppo rimasto indietro. Il codice passa da una revisione, ha una cronologia e si può tornare indietro; una ALTER TABLE in produzione la lancia qualcuno con le mani sudate alle due di notte, e se va male si improvvisa. PlanetScale prende quel problema di petto. In questo articolo trovi come funziona il branching dello schema, cosa comporta davvero la questione delle foreign key e quando conviene rispetto alle alternative.

Cos'è

PlanetScale è un MySQL gestito costruito sopra Vitess, il sistema di sharding nato in Youtube, in cui le modifiche allo schema si fanno su un ramo separato e si portano in produzione tramite una richiesta di deploy, con revisione, senza bloccare le tabelle.

Sotto c'è MySQL, con il suo dialetto e i suoi driver: ti colleghi con il client di sempre, il tuo ORM funziona, le query sono quelle che conosci. Quello che cambia non è il linguaggio, è il processo con cui lo schema evolve. Se ti manca il quadro generale, parti da cos'è un database e da SQL o NoSQL.

Il branching dello schema

L'idea si spiega in una frase: fai con lo schema del database quello che fai con il codice.

Crei un ramo a partire dalla produzione: ha lo schema della produzione ma è un ambiente a sé, dove puoi aggiungere colonne, cambiare indici e rinominare tabelle senza che nessuno se ne accorga.

pscale branch create mia-app aggiungi-colonna-stato
pscale connect mia-app aggiungi-colonna-stato --port 3309
# l'app in locale punta a 127.0.0.1:3309 e vede il ramo

Sul ramo lanci le migrazioni come faresti ovunque:

ALTER TABLE ordini ADD COLUMN stato VARCHAR(32) NOT NULL DEFAULT 'nuovo';
CREATE INDEX idx_ordini_stato ON ordini (stato);

Fin qui è un ambiente di sviluppo isolato, che di per sé non è una rivoluzione. La parte che cambia il lavoro viene dopo.

La deploy request: la migrazione diventa una pull request

Quando il ramo è pronto, non lanci la migrazione in produzione. Apri una deploy request.

pscale deploy-request create mia-app aggiungi-colonna-stato

Il sistema confronta lo schema del ramo con quello della produzione e produce il diff: l'elenco esatto delle istruzioni DDL che verranno eseguite. Quel diff è un oggetto che si guarda, si commenta e si approva, esattamente come il diff di una pull request. È il concetto centrale del prodotto, ed è più di una comodità.

Cosa cambia concretamente:

  • Qualcuno rilegge lo schema prima della produzione. Non il codice della migrazione: il risultato. Una colonna senza indice, un tipo sbagliato, una rinomina che rompe l'applicazione vecchia si vedono in un diff e non in una riunione.
  • Il sistema rifiuta a monte i cambiamenti pericolosi. Se il ramo è fuori sincrono perché qualcun altro ha cambiato la stessa tabella, te lo dice invece di sovrascrivere.
  • L'applicazione avviene senza bloccare le tabelle. Vitess costruisce la nuova struttura di lato e commuta alla fine: è il problema per cui esistono gh-ost o pt-online-schema-change, qui integrato.
  • Esiste una finestra per tornare indietro. Dopo il deploy la modifica può essere revocata senza perdere i dati scritti nel frattempo.

Il flusso completo diventa questo, e si automatizza dentro la CI/CD:

PassaggioNel codiceNello schema
Inizio lavorogit checkout -bpscale branch create
ModificaScrivi il codiceLanci le ALTER sul ramo
RevisionePull request con diffDeploy request con diff DDL
ApprovazioneUn collega approvaUn collega approva
RilascioMerge sul principaleDeploy sul ramo di produzione
RipensamentoRevert del commitRevert entro la finestra

Chi ha vissuto una migrazione andata male in produzione capisce subito quanto valga. Chi lavora da solo su un progetto piccolo, onestamente, molto meno: il branching dello schema è una funzione da team, e su un progetto personale è cerimoniale in più senza beneficio.

La questione delle foreign key

Va affrontata perché è la prima cosa che si sente dire di PlanetScale, ed è raccontata quasi sempre male.

Storicamente PlanetScale non ha supportato i vincoli di chiave esterna, per un motivo architetturale: Vitess distribuisce le tabelle su più shard, e un vincolo da verificare in modo atomico tra due tabelle che potrebbero stare su macchine diverse è un problema difficile. Il supporto è arrivato in seguito con dei limiti, ma il presupposto di progettazione resta quello, e conviene ragionarci sopra come se il vincolo non ci fosse.

Cosa comporta davvero

Non comporta "niente relazioni". Le relazioni restano: la colonna ordini.utente_id continua a contenere l'id di un utente e le JOIN funzionano esattamente come prima. Quello che manca è il controllo automatico che il database fa al posto tuo.

Le tre conseguenze pratiche:

1. L'integrità referenziale la garantisce l'applicazione. Se cancelli un utente, il database non cancella né blocca i suoi ordini: restano lì, orfani, e a nessuno importa finché una query non li trova. Il ON DELETE CASCADE che avevi scritto e dimenticato va riscritto come logica esplicita nel codice, dentro una transazione.

2. Gli ORM vanno configurati. Prisma, per dirne uno, ha un'impostazione apposita per emulare le relazioni a livello applicativo invece di creare vincoli nel database:

datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

Con quella impostazione l'ORM smette di generare le foreign key e si prende carico lui dei controlli. Importante: senza vincoli il database non crea più gli indici impliciti che alcuni motori aggiungono sulle colonne di relazione, quindi gli indici sulle colonne usate nelle JOIN vanno dichiarati a mano — dimenticarlo è la causa più comune di query improvvisamente lentissime.

3. Cambia il modo di pensare allo schema. Meno cancellazioni fisiche a catena, più cancellazioni logiche con una colonna di stato; più attenzione a scrivere le operazioni correlate dentro una transazione; qualche processo di pulizia periodico per le righe rimaste scollegate.

È un problema?

Dipende da dove vieni. Se progetti schemi relazionali stretti, con vincoli e cascate come rete di sicurezza, è una rinuncia vera e va messa nel conto prima, non dopo. Se ragioni già così — molti team che lavorano con ORM moderni hanno smesso da tempo di affidarsi ai vincoli del database — cambia poco.

Quello che non va fatto è liquidarla come dettaglio. L'integrità che il database non impone qualcuno deve imporla, e quel qualcuno diventa il tuo codice, per sempre, in ogni punto che scrive.

PlanetScale o Neon?

È il confronto più utile perché i due prodotti si assomigliano nel racconto e sono diversi nella sostanza.

Neon è Postgres con il branching dei dati. PlanetScale è MySQL con il branching dello schema.

Su Neon il ramo è una copia istantanea del database compresi i dati, ottenuta separando calcolo e storage: serve ad avere un ambiente realistico per ogni pull request. Su PlanetScale il ramo nasce per far evolvere la struttura, e il pezzo forte è il processo di revisione e applicazione del cambiamento in produzione.

PlanetScaleNeon
MotoreMySQL (su Vitess)PostgreSQL
Oggetto del branchingLo schemaI dati
Punto di forzaDeploy request con diff e revisioneRamo completo con i dati, istantaneo
Migrazioni in produzioneNon bloccanti, con revertLe lanci tu come su Postgres
Foreign keyStoricamente assenti, supporto limitatoComplete
Scalabilità in scritturaSharding nativo di VitessVerticale sul calcolo
Sospensione a riposoDipende dal pianoSì, con ritardo alla riaccensione
Adatto aTeam con molte migrazioni, carichi grandiTeam che vogliono un ambiente per PR

Non sono alternative sullo stesso asse. La domanda giusta non è "quale dei due è meglio", è: il tuo problema è provare le modifiche con dati veri oppure portarle in produzione senza fermare niente? Nel primo caso Neon, nel secondo PlanetScale. E se non hai né l'uno né l'altro problema, un MySQL o un Postgres gestito normale va benissimo — il criterio generale è in quale database scegliere.

Il confronto con un database su una macchina tua resta quello di sempre: costa meno, e in cambio l'aggiornamento, i backup e le migrazioni non bloccanti sono affari tuoi. Il punto di partenza è configurare un VPS da zero.

Cosa fa perdere ore

Migrare un progetto senza controllare le foreign key. Lo schema si importa, l'applicazione parte, e i problemi di integrità arrivano settimane dopo sotto forma di dati incoerenti. Va verificato prima.

Dimenticare gli indici sulle colonne di relazione. Senza vincoli non li crea nessuno. Una JOIN su una colonna non indicizzata funziona benissimo su mille righe e affonda su un milione.

Trattare un ramo di schema come un ambiente con dati veri. Se ti serve provare una migrazione su un volume realistico, è un requisito da verificare esplicitamente.

Fare ALTER direttamente sul ramo di produzione. Si può, e annulla il motivo per cui sei qui: va tenuto protetto e modificabile solo tramite deploy request.

Ignorare che le connessioni vanno governate. Con le funzioni serverless in particolare: ogni invocazione che apre la sua connessione moltiplica il carico. PlanetScale offre un driver HTTP che aggira il problema, comodo se l'applicazione gira su Vercel.

Restare legati senza rendersene conto. Il branching e le deploy request sono funzioni della piattaforma, non di MySQL: il database si porta via, il processo di lavoro no.

Quando conviene

Conviene se: siete un team che tocca lo schema spesso e le migrazioni vi fanno paura — è il caso d'uso per cui il prodotto esiste; hai tabelle grandi dove una ALTER bloccante significa fermo servizio; vuoi che le modifiche allo schema passino da una revisione come il codice; stai già su MySQL e non hai intenzione di cambiare motore; prevedi una crescita in scrittura che a un certo punto richiederà sharding, e preferisci l'infrastruttura che lo fa già.

Non conviene se: lavori da solo o su un progetto piccolo, dove la cerimonia della deploy request è puro attrito; il tuo schema si regge sui vincoli di integrità del database e riscriverli nell'applicazione è un lavoro che non vuoi fare; preferisci Postgres — e allora la conversazione si sposta su Neon o su un Postgres gestito qualsiasi; hai già un database che funziona e nessuno dei problemi che PlanetScale risolve.

In sintesi

PlanetScale è MySQL gestito in cui lo schema si modifica su un ramo e si porta in produzione con una richiesta di deploy, revisionabile e reversibile.

La deploy request è il vero prodotto: vedi il diff DDL prima che tocchi la produzione, qualcuno lo approva, l'applicazione avviene senza bloccare le tabelle e c'è una finestra per annullare.

Le foreign key sono il compromesso da conoscere in anticipo. Le relazioni restano, i controlli automatici no: l'integrità passa nel codice e gli indici vanno dichiarati a mano.

Rispetto a Neon la differenza è netta: Neon rami i dati, PlanetScale rami lo schema. Scegli in base a quale dei due problemi hai davvero.

È una funzione da team. Su un progetto personale, un MySQL gestito normale fa lo stesso lavoro con meno passaggi.

Per l'alternativa Postgres leggi cos'è Neon; per il ragionamento a monte su cosa scegliere, quale database scegliere.