Replica in PostgreSQL 12+

PostgreSQL e' un potente DBMS relazionale Open Source noto per la robustezza e la ricchezza di funzionalita' anche sull'alta affidabilita'. In questo documento descriveremo la configurazione della replica con una versione 12 o successiva di PostgreSQL perche' e' cambiata in modo significativo rispetto alle precedenti versioni.

Questo documento non e' introduttivo ed e' consigliato ad un pubblico informaticamente adulto...
Un documento introduttivo su PostgreSQL e' Introduzione a PostgreSQL, un documento piu' completo e' Qualcosa in piu' su PostgreSQL la replica e la sua configurazione nelle precedenti versioni e' descritta infine descritta in Replica in PostgreSQL. Infine alcuni importanti elementi sulla continuita' operativa per le basi dati sono riportati in Architetture di Database per l'HA.

Introduzione

In questo breve documento una base dati PostgreSQL attiva su un nodo Primary viene configurata per essere replicata su uno o piu' nodi secondari in Standby. Architettura Streaming Replication PostgreSQL Vengono analizzate sia le configurazioni per fornire l'alta affidabilita' (HA: High Availability) con server posti nella stessa rete locale ed RPO e RTO molto bassi; sia configurazioni addatte a garantire la continuita' operativa in caso di disastro (DR: Disaster Recovery) con server posti su reti differenti a distanza geografica. Naturalmente le configurazioni per DR hanno un RTO piu' elevato.
Le configurazioni presentate sono relative a PostgreSQL in versione 12.0 o successiva, poiche' sono cambiate rispetto alle versioni precedenti. Se siete interessati ad una versione precedente di PostgreSQL leggete Replica in PostgreSQL.
I server utilizzati per la replica debbono avere la stessa versione di PostgreSQL e, possibilimente, la stessa versione di sistema operativo. E' anche fortemente consigliato che utilizzino gli stessi path, stessi utenti/gruppi di sistema, stesse minor version, ...

PostgreSQL

Un breve accenno all'architettura di PostgreSQL puo' essere utile...
Architettura PostgreSQL Il processo postmaster e' il processo principale che si occupa della gestione delle connessioni (e' in LISTEN sulla porta socket 5432) ed e' il "padre" di tutti i processi, sia di sistema (eg. wal writer) sia quelli relativi alle connessione utente. Tutti i processi girano come utente postgres ed eseguono un attach al segmento di shared memory su cui vengono mantenuti buffer e lock.

La gestione delle transazioni in PostgreSQL avviene con la tecnica del MVCC (Multiversion Concurrency Control) e la consistenza dei dati su disco e' assicurata con il logging (Write-Ahead Logging). L'isolation level di default e' Read Committed. Ogni transazione al commit effettua una scrittura sul WAL (Write-Ahead Log): in questo modo e' certo che le attivita' confermate alla transazione sono state scritte su disco. La scrittura sul WAL e' sequenziale e molto veloce. La scrittura effettiva sui file delle tabelle e degli indici viene effettuata successivamente ottimizzando le scritture sul disco. I WAL possono essere archiviati e questo consente l'effettuazione di backup a caldo e il PITR (Point In Time Recovery).

Il Clustered Database e' la struttura del file system che contiene i dati. All'interno vengono mantenuti i file di configurazione ($PGDATA/*.conf), i log delle transazioni (nella directory $PGDATA/pg_wal) e le strutture interne della base dati che contengono tabelle ed indici (suddivise per database nella directory $PGDATA/base). Il blocco ha dimensione 8KB mentre i wal log hanno come dimensione 16MB.

La replica di una base dati PostgreSQL e' basata sulla ricezione e l'applicazione dei WAL sui sistemi secondari o Standby. Nelle prime versioni di PostgreSQL era presente la sola modalita' di log shipping ma le versioni piu' recenti implementano la streaming replication che ha una latenza molto bassa.

Dal punto di vista architetturale la replica della versione 12 non ha differenze significative rispetto alle versioni precedenti pero' la configurazione e' completamente diversa. Non e' piu' presente il file recovery.conf ma va utilizzato il file standby.signal per indicare che il database e' in stato di standby e tutte le configurazioni sono contenute nel file postgresql.conf come vedremo in dettaglio nel seguito.

Configurazione Primary

Il sistema Primary o Master e' quello che ospita la base dati in condizioni di normale operativita'.

Oltre alla normale configurazione che si effettua con il tuning dei parametri per ottenere prestazioni ottimali con le applicazioni ospitate, vanno impostati parametri specifici per la replica.

  1. Configurazione postgresql.conf
    Rispetto alla configurazione di default Postgres va configurato in modo da accettare connessioni dalla rete [NdE questa configurazione richiede un riavvio per essere attivata ma viene generalmente eseguita nei primi passi di setup per consentire l'accesso al DB via rete]:
    $ vi postgres.conf listen_addresses = '*'

    In pratica con la versione 12 e successive non e' quasi mai necessaria una configurazione specifica ed il riavvio dell'istanza Primary perche' i valori di default dei parametri sono gia' adatti alla replica.

  2. Utenza di replica
    In una configurazione di produzione e' opportuno creare un'utenza dedicata alla replica e non utilizzare l'utenza superuser [NdE questa configurazione e' immediatamente attiva]:
    $ psql CREATE ROLE repl_usr REPLICATION LOGIN PASSWORD 'XXX'; ^D
    NB Non utilizzate XXX come password!
  3. Configurazione pg_hba.conf
    Il file di configurazione degli accessi a Postgres deve consentire la replica [NdE questa configurazione richiede un reload per essere attivata]:
    $ vi pg_hba.conf # HA dedicated network host replication repl_usr 10.0.1.0/24 md5 # DR dedicated network host replication repl_usr 192.168.0.0/24 md5
  4. Copia/backup
    Il database Master e' ora pronto per essere replicato. La modalita' consigliata e':
     pg_basebackup -h master_host -U repl_usr -p 5432 -D $PGDATA -Fp -Xs -P -R

Configurazione Standby in alta affidabilita'

La configurazione di default eseguita pg_basebackup e' gia' sufficiente: gia fatto!

Con l'opzione -R del pg_basebackup vengono creati anche tutti i file di supporto per la replica. In particolare viene creato il file standby.signal che indica che il database e' in stato di standby e nel file postgresql.auto.conf viene inserito il parametro primary_conninfo gia' correttamente configurato.
Se si vuole utilizzare il nodo slave solo per l'HA (nessun accesso in lettura) basta rinominare il file standby.signal in recovery.signal. Puo' essere impostato il parametro promote_trigger_file nel file postgresql.conf [NdA sostituisce il parametro trigger_file del file recovery.conf].

  1. Avvio Standby
    Va avviato il Postgres di Standby cosi' si pone in attesa dei dati.

Ora sono disponibili ed utilizzabili due istanze PostgreSQL allineate tra loro (la seconda in sola lettura).

Variazioni sul tema

Sono possibili diverse configurazioni ulteriori... nel seguito ne vedremo alcune.

La configurazione in cascading replication e' adatta per repliche geografiche multiple.
In una configurazione di DR un requisito e' la distanza e la separazione tra l'ambiente di produzione ed i server in Standby. Saranno quindi molto piu' alte le latenze di rete ed l'RPO ottenibile. La configurazione deve essere ottimizzata per ridurre il traffico di rete.
Un server di Standby non deve necessariamente connettersi direttamente al Primary ma puo' raccogliere le modifiche da un altro Standby formando cosi' una catena. Il server piu' vicino al Primary viene chiamato di Upstream mentre quello piu' lontano viene chiamato Downstream. Questa configurazione consente di diminuire le connessioni dirette verso il Primary e di ottimizzare il traffico di rete se piu' server si trovano in una rete diversa da quella del Primary (situazione tipica per un sito di DR).

PostgreSQL Cascading Replication
Nella figura di esempio si utilizza la replica in cascata per mantenere allineati due CED in una tipica configurazione di Disaster Recovery.
Il traffico di rete e' ottimizzato poiche' e' attiva una sola replica in geografico tra il Primary e l'Upstream Secondary2 mentre la replica verso il Downstream Secondary3 avviene su rete locale.

La configurazione sincrona minimizza le perdite di dati ma ha un impatto significativo sulle performances.
La replica su PostgreSQL per default e' asincrona. E' possibile configurare la replica sincrona (2-safe replication) impostando i parametri synchronous_standby_names e synchronous_commit. Nella configurazione sincrona le transazioni non vengono chiuse fino a che non siano state trasferite ad almeno uno standby. La configurazione si effettua con il parametro synchronous_commit impostandolo a remote_write. La replica sincrona puo' indurre rallentamenti alle transazioni [NdA la replica sincrona rallenta sempre tutte le transazioni poiche' richiede un round trip di conferma, solo in alcuni casi il rallentamento non e' sensibile]: pertanto e' da utilizzare solo in casi specifici quando la latenza e' minimale e sono disponibili piu' Slave.

Tuning

Anche se i valori di default dei parametri di configurazione relativi alla replica presenti nelle versioni 12+ sono gia' adatti per l'utilizzo della streaming replication e' possibile effettuare un tuning specifico quando si rende necessario. Nel seguito vediamo i parametri piu' utili.

Se la replica viene interrotta per qualche ragione poi PostgreSQL recupera automaticamente, purche' siano ancora disponibili i WAL da applicare. I WAL mantenuti sul Master sono definiti dal parametro wal_keep_segments il cui valore di default e'... 0 ovvero nessun WAL in piu' rispetto a quelli strettamente necessari. Se si utilizza la replica e' opportuno impostare il parametro in modo da mantenere un numero adeguato di WAL segments che hanno una dimensione tipica di 16MB.
Attenzione: dalla versione 13+ il parametro da utilizzare e' wal_keep_size. Il default e' 0, il mio suggerimento e' quello di configurare questo parametro: una dimensione consigliabile e' 1GB.
Per mantenere la disponibilita' dei WAL necessari alle repliche e' anche possibile utilizzare l'archiving o i replication slot... ma la descrizione di queste alternative supera le possibilita' di questa paginetta introduttiva [NdA KISS: keep it simple! Ma se volete utilizzare i replication slot con il parametro primary_slot_name sul secondario allora non dimenticate di impostare il parametro max_slot_wal_keep_size sul primario disponibile dalla 13+].

Sullo Slave quando l’applicazione di un WAL va in conflitto con una query quest'ultima viene abortita. Questo puo' avvenire con query di lunga durata (eg. pg_dump) eseguite su tabelle che vengono modificate e sui viene applicato il vacuum sul primario. E' un comportamento voluto perche' la precedenza viene data alla sicurezza dei dati e non alle query eseguite sullo standby.
Questo fenomeno puo’ essere ridotto agendo sui parametri max_standby_archive_delay e max_standby_streaming_delay che concedono piu’ tempo allo Slave per terminare le query. Altro parametro utilizzabile e’ l'hot_standby_feedback che notifica la presenza di query sullo Slave ma influenza il primary perche' puo' impedire il vacuuming in alcune condizioni di carico. Infine e’ possibile interrompere l’applicazione dei WAL con pg_wal_replay_pause() e riprenderlo al termine delle query pesanti.

Quando il numero di standby server e' elevato puo' essere necessario aumentare il parametro max_wal_senders.

Con connessioni geografiche puo' essere utile aumentare rispetto al valore di default (60 secondi) i parametri wal_sender_timeout sul primary e wal_receiver_timeout sul secondary.

Come gia' riportato la replica sincrona va utilizzata in casi specifici perche' rallenta, anche in modo significativo, il commit delle transazioni. Se gli slave hanno la stessa priorita' e' consigliabile utilizzare la modalita' ANY con un numero inferiore al totale per minimizzare le attese (eg. synchronous_standby_names=ANY 1 (s1, s2, s3) conferma il commit dopo aver ricevuto la conferma da solo uno dei tre slave configurati). Anche se la configurazione generale prevede la replica sincrona e' possibile derogare a questa con il parametro synchronous_commit che puo' assumere i valori: remote_apply, on, remote_write, local, off. Il significato delle opzioni e' abbastanza logico: se synchronous_standby_names non e' impostato le due modalita' sono on ed off ed indicano l'attesa o meno del flush del WAL su disco; altrimenti le possibilita' ulteriori sono attendere: l'applicazione della transazione sugli slave, il flush del WAL su disco sugli slave, la scrittura del WAL su disco sugli slave, il flush su disco locale, oppure nessuna attesa. Il parametro synchronous_commit ha come context il livello user, quindi puo' essere configurato all'occorrenza.

I parametri riportati sono utili sia con la streaming replication, descritta in questa pagina, che con la logical replication. L'unica avvertenza ulteriore con la logical replication e' che il numero di sottoscrizioni e' tipicamente piu' elevato del numero di secondari e quindi e' opportuno aumentare adeguatamente il parametro max_wal_senders. Inoltre va impostato il parametro wal_level = logical: il default del parametro e' "replica" e non e' sufficiente per la replica logica, la modifica richiede un riavvio.

Principali operative

La documentazione della configurazione e delle operative da utilizzare per una corretta gestione degli ambienti ha un'importanza forse superiore alla parte tecnica di implementazione!
Aspetto importante nella continuita' operativa e' che vi sono eventi che non possono essere gestiti da automatismi ma richiedono interventi manuali ed autorizzazioni: anche questi processi debbono essere descritti. Altrimenti il rischio e' quello di avere una macchina perfetta... ma senza il pilota!

Promote

L'operazione piu' critica dal punto di vista del RTO (Recovery Time Objective) e' quando lo Standby deve sostituire il Primary che e' andato in fault.
L'operazione e' semplice, veloce ed eseguibile in modi diversi:

Il Failover di PostgreSQL tipicamente richiede un tempo molto limitato (RTO < pochi secondi): e' sicuramente maggiore il tempo che occorre per prendere la decisione giusta. Con il failover vengono recuperate tutte le transazioni ricevute dallo Standby, tuttavia non vi e' certezza che lo Standby abbia ricevuto tutte le transazioni eseguite sul Primary (RPO > 0).
Per effetturare uno Switchover e' necessario terminare in modo pulito il primario, attendere l'applicazione di tutte le transazioni raccolte dal secondario (generalmente immediata ma possono esserci eccezioni) ed infine effettuare il promote. Con questa operativa non si ha perdita di trasazioni e si evita il resilvering del vecchio Primary se deve essere nuovamente messo in linea.

Monitoraggio

Oltre a verificare il corretto funzionamento dei sistemi, per il momento dato per scontato, e' necessario controllare il corretto allineamento delle repliche.

Con la configurazione in Hot Standby la verifica funzionale e' semplice: basta collegarsi e lanciare una query!

Per controllare lo stato delle repliche e' possibile utilizzare la seguente query sul Primary che restituisce una riga per ogni processo WAL Sender e quindi per ogni Standby connesso:

select client_addr, state, sync_state, txid_current_snapshot(),
       sent_lsn, write_lsn, flush_lsn, replay_lsn,
       backend_start 
  from pg_stat_replication;

Sono informazioni analoghe rispetto a queste due query da lanciare rispettivamente sul Master e sullo Slave:

select pg_current_wal_lsn();
select pg_last_wal_receive_lsn();

Una metrica fondamentale per la replica e' il lag ovvero il ritardo nell'applicazione delle modifiche sullo slave. Un modo per valutarlo e':

select case when pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn()
            then 0
            else extract(epoch from now() - pg_last_xact_replay_timestamp())::INTEGER
        end as LAG_seconds;

Sul primary molto utili sono le informazioni delle colonne *_lag sempre della vista pg_stat_replication anche se a volte =null [NdA dipende se la replica e' sincrona o asincrona e se sono occorse transazioni di recente].
Altra query utile sul secondary e': select * from pg_stat_wal_receiver;
Informazioni banali, ma spesso utili, si ottengono com: select now(), inet_server_addr()||':'||inet_server_port() as pg_host, pg_is_in_recovery();

Un report completo sulla configurazione di PostgreSQL, con una sezione dedicata alla replica, si ottiene con questo script.

Anche a livello di sistema operativo si possono eseguire alcuni controlli utili per la replica. In particolare e' possibile valutare la latenza (importante per valutare il lag delle repliche sincrone) e la banda:

Prove RTT (ping -c8 primary_host) rtt min/avg/max/mdev = 0.179/0.235/0.377/0.062 ms (P - S1) rtt min/avg/max/mdev = 42.436/43.067/44.349/0.623 ms (P -S 2) Prove Banda (iperf3 -s / -c primary_host): [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.01 sec 1.46 GBytes 1.26 Gbits/sec 0 sender (P-S1) [ 5] 0.00-10.04 sec 1.46 GBytes 1.25 Gbits/sec receiver [ 5] 0.00-10.00 sec 369 MBytes 310 Mbits/sec 28 sender (P-S2) [ 5] 0.00-10.07 sec 368 MBytes 306 Mbits/sec receiver Valutazione banda necessaria? Facile: con ls -l dei WAL, si ottiene la frequenza e la dimensione Se sono aperte solo le porte del servizio? Meno facile: si usa nc

L'ultimo consiglio sui monitoraggi... sapete qual'e' la causa piu' frequente di blocco delle repliche? Un file system full!

Il monitoraggio degli spazi e' quindi fondamentale per ogni istanza PostgreSQL ed in particolare per la Streaming Replication.

Upgrade

L'aggiornamento di una minor upgrade ad una configurazione in replica non presenta particolari problemi. L'unica avvertenza e' quella di applicare l'aggiornamento a tutti gli standby e quindi per ultimo al database primario. In realta' nella maggior parte dei casi non ci sono problemi anche utilizzando un diverso ordine di applicazione delle patch ma questo e' il modo piu' sicuro perche' la retrocompatibilita' e' sempre garantita.

In caso di major upgrade invece e' opportuno ricostruire le repliche. La presenza di uno standby e' comunque utile perche' si dispone di piu' database e risulta molto semplice un eventuale rollback alla versione precedente in caso di problemi.

Evoluzione

Le funzionalita' della replication in PostgreSQL hanno avuto un'importante evoluzione nel tempo. Ecco le tappe principali:


Titolo: Replica in PostgreSQL 12+
Livello: Esperto (4/5)
Data: 31 Ottobre 2019 🎃 Halloween
Versione: 1.0.4 - 31 Ottobre 2023 🎃 Halloween
Autore: mail [AT] meo.bogliolo.name