Vai al contenuto

Aggiornare senza tempi di fermo

Aggiorna un Quire autonomo senza tempi di fermo.

Le regole sono nella sezione 7 di docs/architecture/23-ops.md e nella sezione 4.1 di docs/architecture/07-data.md. Questa è la procedura.

La garanzia che rende tutto sicuro

La release R funziona correttamente con lo schema R e lo schema R meno uno. Ogni modifica di schema è divisa in espansione, transizione e consolidamento:

  1. Espansione: aggiungi la nuova colonna, tabella o indice. Il vecchio codice la ignora.
  2. Transizione, per almeno una release: il nuovo codice scrive in entrambe le forme e legge quella nuova; un job ripristinabile riempie le vecchie righe.
  3. Consolidamento: elimina la vecchia forma, in una release successiva, da sola.

Così in ogni momento di un aggiornamento progressivo, processi vecchi e nuovi possono condividere un unico database. Non esistono migrazioni a ritroso: una migrazione che ha eliminato una colonna un’ora fa non può restituire le righe scritte in quell’ora.

Il job CI schema-compat verifica la garanzia a ogni release eseguendo i test della release precedente contro il nuovo schema.

Prima di iniziare

  1. Leggi le note di release. Una release che richiede una finestra di manutenzione lo dice, con la stima; al massimo una per release.
  2. Esegui la prova di ripristino, oppure verifica che sia risultata verde per questa release (backup-restore.md). Una prova fallita blocca l’aggiornamento.
  3. Fai un backup di base: docker compose -f docker/compose.yaml --profile backup run --rm backup.

Docker Compose, un solo host

export QUIRE_RELEASE=2026.10.0            # or set it in docker/.env
docker compose -f docker/compose.yaml pull   # or build
docker compose -f docker/compose.yaml run --rm migrate
docker compose -f docker/compose.yaml up -d --no-deps web content collab
docker compose -f docker/compose.yaml up -d --no-deps worker scheduler

L’ordine è intenzionale:

  1. Prima la migrazione, mentre la vecchia release serve traffico. Le migrazioni di espansione sono invisibili per lei.
  2. Poi il livello web. A SIGTERM ciascun processo web commuta /readyz su draining, completa le richieste in corso entro 30 secondi, chiude gli stream con un suggerimento di riconnessione ed esce. stop_grace_period è 40 secondi così Compose non interrompe mai un drenaggio sano.
  3. Per ultimi i worker, così la forma evento più recente viene prodotta prima che il consumatore più recente la richieda. I worker smettono subito di prelevare e hanno 120 secondi; un job che non riesce a finire viene prelevato altrove, il che è sicuro perché ogni job è idempotente. Lo scheduler cede la leadership al suo prossimo tick.

Su un solo host, Compose sostituisce ciascun contenitore a turno, quindi c’è una breve pausa per servizio. Per non avere alcuna pausa, esegui il livello web come due contenitori dietro il tuo proxy (un file di override che aggiunge un secondo servizio web senza porta pubblicata), e ricreali uno alla volta, attendendo che ciascuno risulti sano prima del successivo.

Più host o un orchestratore

Usa lo stesso ordine: migra una volta da un singolo job, poi fai rolling del livello web con picco uno e non disponibili zero, poi i worker. Punta le sonde di readiness a /readyz e quelle di liveness a /healthz.

Con database tenant dedicati, il passaggio migrate fa entrambe le cose: migra prima il database di controllo, poi ogni database elencato in ops.tenant_database, uno alla volta, ciascuno sotto il proprio lock. Un fallimento in un database tenant non ferma gli altri. Quando ogni database ha finito, confronta i registri delle migrazioni ed esce non-zero a meno che ogni database abbia applicato esattamente le migrazioni del database di controllo, nominando ciascuno in ritardo o in avanti. Lo stesso comando installa le tabelle di coda in ciascun database, perché il worker consuma i job di un tenant fissato dove sono stati scritti.

bun apps/worker/src/migrate.ts   # what the Compose step runs
bun run db:migrate:all                            # the same, from a checkout

Ciascun database dedicato è raggiunto tramite il nome con cui è registrato. Un database registrato come env:QUIRE_DB_NORTHWIND_URL richiede:

Variabile Usata per
QUIRE_DB_NORTHWIND_URL Il ruolo applicativo, per il livello web e il worker
QUIRE_DB_NORTHWIND_URL_MIGRATOR Il ruolo migratore, per questo comando e per gli spostamenti
QUIRE_DB_NORTHWIND_URL_SUPERUSER Facoltativo: riapplica il bootstrap (ruoli, schemi, helper) prima di migrare

Un database registrato senza connessione _MIGRATOR viene segnalato come fallimento, mai saltato. Il livello web può procedere col rolling una volta finito il database di controllo. Un database tenant indietro di un’ora avvisa; di un giorno chiama.

pgvector

Dalla migrazione 0264 il corpus di grounding usa un indice HNSW pgvector dove il server ha l’estensione; il servizio postgres di Compose è compilato con essa (docker/postgres.Dockerfile). Il primo migrate dopo il cambio di immagini crea l’estensione tramite il bootstrap superuser, e la 0264 poi aggiunge una colonna vettoriale generata e costruisce l’indice. Aggiungere la colonna riscrive app.ai_chunk una volta sotto lock esclusivo, quindi le richieste di grounding attendono; nient’altro tocca quella tabella.

Su un server senza pgvector, la 0264 registra un avviso e non cambia nulla, e il recupero resta esatto. Con un pgvector precedente alla 0.8 la colonna e l’indice vengono costruiti ma il recupero resta esatto finché l’estensione non viene aggiornata (alter extension vector update), perché le scansioni HNSW filtrate richiedono le scansioni iterative della 0.8. Per abilitarlo in seguito su un server che ne è privo, installa l’estensione, esegui di nuovo il bootstrap (oppure create extension vector come superuser), poi come quire_migrator:

set maintenance_work_mem = '1GB';  -- the HNSW build is much faster in memory
select ops.ai_chunk_enable_vector_index();

È idempotente e restituisce enabled oppure unavailable. Eseguilo anche su ciascun database tenant dedicato.

Rollback

Il rollback del codice è sempre disponibile: imposta QUIRE_RELEASE al tag precedente e di nuovo up -d. Funziona perché lo schema è compatibile in entrambe le direzioni all’interno di una release.

Il rollback dello schema non è offerto. Ciò che non può essere annullato, e come recuperare:

Non reversibile Recupero
Una migrazione di consolidamento che ha eliminato una colonna Ripristino point-in-time a prima dell’eliminazione in un nuovo database, estrazione, fusione
Una modifica dati sul posto Come sopra, poi riconcilia le scritture successive
Webhook ed eventi inviati Eventi compensativi, mai eliminazione
Email inviate Una persona scrive il follow-up
La catena hash di audit Mai riscritta; accoda una voce di correzione

Per questo una migrazione di consolidamento viaggia da sola: un ripristino ha così un confine pulito.

Verifica dell’aggiornamento

docker compose -f docker/compose.yaml ps           # every service healthy
curl -fsS http://localhost:8080/readyz             # ready, and what is configured
docker compose -f docker/compose.yaml logs migrate # the migrations applied
Navigazione

Digita per cercare…

↑↓ per spostarti↵ per selezionareEsc per chiudere