Comprensione dei conflitti in modalità attivo-attivo - Amazon Relational Database Service

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Comprensione dei conflitti in modalità attivo-attivo

Quando utilizzi pgactive in modalità attivo-attivo, la scrittura da più nodi sulle stesse tabelle può creare conflitti di dati. Anche se alcuni sistemi di clustering utilizzano blocchi distribuiti per impedire l’accesso simultaneo, pgactive adotta un approccio ottimistico più adatto per applicazioni distribuite geograficamente.

Alcuni sistemi di clustering di database impediscono l’accesso simultaneo ai dati utilizzando blocchi distribuiti. Sebbene sia efficace quando i server sono nelle immediate vicinanze, questo approccio non supporta applicazioni distribuite geograficamente, perché per offrire buone prestazioni richiede una latenza estremamente bassa. Anziché ricorrere a blocchi distribuiti (approccio pessimistico), l’estensione pgactive utilizza un approccio ottimistico. Questo significa che:

  • Contribuisce a evitare i conflitti quando possibile.

  • Consente il verificarsi di determinati tipi di conflitti.

  • Fornisce la risoluzione dei conflitti quando questi si verificano.

Questo approccio offre una maggiore flessibilità durante la creazione di applicazioni distribuite.

Come avvengono i conflitti

I conflitti tra nodi derivano da sequenze di eventi che non potrebbero verificarsi se tutte le transazioni coinvolte avvenissero contemporaneamente sullo stesso nodo. Poiché i nodi scambiano le modifiche solo dopo il completamento delle transazioni, ogni transazione è valida individualmente sul nodo su cui è stata effettuata, ma non lo sarebbe se eseguita su un altro nodo che nel frattempo ha svolto altre operazioni. Poiché, essenzialmente, pgactive apply riproduce la transazione sugli altri nodi, in caso di conflitto tra una transazione applicata e una transazione che è stata effettuata sul nodo ricevente, l’operazione di riproduzione può fallire.

Il motivo per cui la maggior parte dei conflitti non può verificarsi quando tutte le transazioni vengono eseguite su un singolo nodo è che PostgreSQL dispone di meccanismi di comunicazione tra transazioni che ne consentono la prevenzione, tra cui:

  • Indici UNIQUE

  • SEQUENCE

  • Blocco di righe e relazioni

  • Tracciamento delle dipendenze in modalità SERIALIZABLE

Tutti questi meccanismi consentono la comunicazione tra le transazioni per prevenire problemi di concorrenza indesiderati

pgactive raggiunge una bassa latenza e gestisce correttamente le partizioni di rete, perché non utilizza un gestore di transazioni distribuito o un gestore di blocchi. Questo significa, tuttavia, che le transazioni su nodi diversi vengono eseguite in completo isolamento l’una dall’altra. Sebbene in genere l’isolamento migliori la coerenza del database, in questo caso è necessario ridurlo per prevenire i conflitti.

Tipi di conflitti

I conflitti che possono verificarsi includono:

Conflitti con vincoli PRIMARY KEY o UNIQUE

I conflitti di riga si verificano quando più operazioni tentano di modificare la stessa chiave di riga in modi che non sarebbero consentiti su un singolo nodo. Questi conflitti rappresentano il tipo di conflitti sui dati più comune.

pgactive risolve i conflitti rilevati tramite la gestione last-update-wins o un gestore dei conflitti personalizzato.

I conflitti di riga includono:

  • INSERT/INSERT

  • INSERT/UPDATE

  • UPDATE/DELETE

  • INSERT/DELETE

  • DELETE/DELETE

  • INSERT/DELETE

Conflitti INSERT/INSERT

Questo conflitto, il più comune, si verifica quando operazioni di INSERT su due nodi diversi creano una tupla con gli stessi valori di PRIMARY KEY (o con valori identici per un vincolo UNIQUE, se non esiste una PRIMARY KEY).

pgactivelink risolve i conflitti INSERT utilizzando il timestamp dell’host di origine per mantenere la tupla più recente. Puoi sovrascrivere questo comportamento predefinito con il tuo gestore di conflitti personalizzato. Sebbene questo processo non richieda alcuna azione speciale da parte dell’amministratore, tieni presente che pgactivelink scarta una delle operazioni INSERT su tutti i nodi. Non si verifica alcuna unione automatica dei dati, a meno che il gestore personalizzato non la implementi.

pgactivelink può risolvere solo i conflitti che coinvolgono la violazione di un solo vincolo. Se un INSERT viola più vincoli UNIQUE, è necessario implementare ulteriori strategie di risoluzione dei conflitti.

INSERT che violano più vincoli UNIQUE

Un conflitto INSERT/INSERT può violare più vincoli UNIQUE, incluso PRIMARY KEY. pgactivelink è in grado di gestire solo i conflitti che coinvolgono un unico vincolo UNIQUE. Quando i conflitti violano più vincoli UNIQUE, il worker di applicazione fallisce e restituisce il seguente errore:

multiple unique constraints violated by remotely INSERTed tuple.

Nelle versioni precedenti, questa situazione generava invece un errore di conflitto di unicità divergente.

Per risolvere questi conflitti, è necessario intraprendere un’azione manuale. Esegui operazioni DELETE per le tuple locali in conflitto o operazioni UPDATE per rimuovere i conflitti con la nuova tupla remota. Tieni presente che potresti dover risolvere conflitti tra più tuple. Attualmente, pgactivelink non fornisce alcuna funzionalità integrata per ignorare, scartare o unire le tuple che violano più vincoli unici.

Nota

Per ulteriori informazioni, vedi le operazioni UPDATE che violano più vincoli UNIQUE.

Conflitti UPDATE/UPDATE

Questo conflitto si verifica quando due nodi modificano contemporaneamente la stessa tupla senza cambiarne PRIMARY KEY. pgactivelink risolve questi conflitti utilizzando la logica last-update-wins o un gestore di conflitti personalizzato, se definito. Una PRIMARY KEY è essenziale per la corrispondenza delle tuple e la risoluzione dei conflitti. Per quanto riguarda le tabelle senza PRIMARY KEY, pgactivelink rifiuta le operazioni UPDATE restituendo il seguente errore:

Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.

Conflitti UPDATE sulla PRIMARY KEY

pgactive presenta delle limitazioni nella gestione degli aggiornamenti della PRIMARY KEY. Sebbene sia possibile eseguire l’operazione UPDATE su una PRIMARY KEY, per queste operazioni pgactive non può risolvere automaticamente i conflitti utilizzando la logica last-update-wins. È necessario assicurarsi che gli aggiornamenti della PRIMARY KEY non siano in conflitto con i valori esistenti. Se si verificano conflitti durante gli aggiornamenti della PRIMARY KEY, questi diventano conflitti divergenti, che richiedono l’intervento manuale dell’utente. Per ulteriori informazioni sulla gestione di queste situazioni, consulta Conflitti divergenti.

Operazioni UPDATE che violano più vincoli UNIQUE

pgactivelink non può applicare la risoluzione dei conflitti last-update-wins quando un’operazione UPDATE in entrata viola più vincoli UNIQUE o valori della PRIMARY KEY. Questo funzionamento è simile alle operazioni INSERT con violazioni di più vincoli. Tali situazioni creano conflitti divergenti che richiedono l’intervento manuale dell’utente. Per ulteriori informazioni, consulta Conflitti divergenti.

Conflitti UPDATE/DELETE

Questi conflitti si verificano quando un nodo esegue un’operazione UPDATE su una riga, mentre un altro nodo esegue contemporaneamente un’operazione DELETE. In questo caso si verifica un conflitto UPDATE/DELETE durante la riproduzione. La soluzione consiste nell’eliminare qualsiasi operazione UPDATE successiva a un’operazione DELETE, a meno che il gestore dei conflitti personalizzato non specifichi diversamente.

pgactivelink richiede una PRIMARY KEY per abbinare le tuple e risolvere i conflitti. Per quanto riguarda le tabelle senza PRIMARY KEY, pgactivelink rifiuta le operazioni DELETE restituendo il seguente errore:

Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.

Nota

pgactivelink non fa distinzione tra i conflitti UPDATE/DELETE e INSERT/UPDATE. In entrambi i casi, un’operazione UPDATE influisce su una riga inesistente. A causa della replica asincrona e della mancanza di un ordine di riproduzione tra i nodi, pgactivelink non è in grado di determinare se l’operazione UPDATE si riferisce a una nuova riga (operazione INSERT non ancora ricevuta) o a una riga eliminata. In entrambi gli scenari, pgactivelink elimina l’operazione UPDATE.

Conflitti INSERT/UPDATE

Questo conflitto può verificarsi in ambienti con più nodi. Si verifica quando un nodo esegue un’operazione INSERT su una riga, un secondo nodo ne esegue una UPDATE e un terzo nodo riceve l’operazione UPDATE prima dell’operazione INSERT originale. Per impostazione predefinita, pgactivelink risolve questi conflitti eliminando l’operazione UPDATE, a meno che il trigger per la gestione dei conflitti personalizzato non specifichi diversamente. Tieni presente che questo metodo di risoluzione può causare incongruenze nei dati tra i nodi. Per ulteriori informazioni su scenari simili e su come gestirli, consulta Conflitti UPDATE/DELETE.

Conflitti DELETE/DELETE

Questo conflitto si verifica quando due nodi diversi eliminano contemporaneamente la stessa tupla. pgactivelink considera questi conflitti innocui, perché entrambe le operazioni DELETE hanno lo stesso risultato finale. In questo scenario, pgactivelink ignora in modo sicuro una delle operazioni DELETE senza influire sulla coerenza dei dati.

Conflitti con vincoli di chiave esterna

I vincoli FOREIGN KEY possono causare conflitti quando si applicano transazioni remote a dati locali esistenti. Questi conflitti si verificano, in genere, quando le transazioni vengono applicate in una sequenza diversa rispetto al loro ordine logico sui nodi di origine.

Per impostazione predefinita, pgactive applica le modifiche con session_replication_role come replica, che ignora i controlli delle chiavi esterne durante la replica. Nelle configurazioni in modalità attivo-attivo, questo può portare a violazioni delle chiavi esterne. La maggior parte delle violazioni è temporanea e si risolve una volta ripristinata la replica. Tuttavia, possono verificarsi chiavi esterne non valide perché pgactive non supporta il blocco delle righe tra nodi.

Questo comportamento è inerente ai sistemi in modalità attivo-attivo asincroni con tolleranza della partizione. Ad esempio, il nodo A potrebbe inserire una nuova riga secondaria mentre il nodo B elimina contemporaneamente la riga principale. Il sistema non può impedire questo tipo di modifica simultanea tra i nodi.

Per ridurre al minimo i conflitti tra chiavi esterne, è consigliabile:

  • Limitare le relazioni con chiave esterna a entità strettamente correlate.

  • Modificare le entità correlate da un singolo nodo, quando possibile.

  • Scegliere entità che raramente richiedono modifiche.

  • Implementare il controllo della concorrenza a livello di applicazione per le modifiche.

Conflitti con vincoli di esclusione

pgactive link non supporta i vincoli di esclusione e ne limita la creazione.

Nota

Se converti un database autonomo esistente in un database pgactivelink, elimina manualmente tutti i vincoli di esclusione.

In un sistema asincrono distribuito, non è possibile garantire che nessun insieme di righe violi il vincolo. Questo perché tutte le transazioni su nodi diversi sono completamente isolate. I vincoli di esclusione possono portare a deadlock della riproduzione, che le impediscono di passare da un nodo all’altro a causa delle violazioni dei vincoli di esclusione.

Se forzi pgactive Link a creare un vincolo di esclusione o se non elimini quelli esistenti durante la conversione di un database autonomo in pgactive Link, è probabile che la replica si interrompa. Per ripristinare l’avanzamento della replica, rimuovi o modifica le tuple locali in conflitto con una tupla remota in entrata, in modo da poter applicare la transazione remota.

Conflitti di dati globali

Quando utilizzi pgactivelink, possono verificarsi conflitti se i nodi hanno dati globali diversi a livello di sistema PostgreSQL, ad esempio i ruoli. Questi conflitti possono determinare il successo e il commit delle operazioni, soprattutto quelle di tipo DDL, su un nodo, impedendone tuttavia l’applicazione sugli altri nodi.

Se un utente esiste su un nodo ma non su un altro, possono verificarsi problemi di replica:

  • Su Node1 è presente un utente denominato fred, che risulta però inesistente su Node2

  • Quando fred crea una tabella su Node1, questa viene replicata con fred come nome di proprietario

  • Quando questo comando DDL viene applicato a Node2, l’esito è negativo, perché l’utente fred non esiste

  • Questo errore genera un’operazione ERROR nei log PostgreSQL su Node2 e incrementa il conteggio di pgactive.pgactive_stats.nr_rollbacks

Risoluzione: creazione dell’utente fred su Node2. L’utente non necessita di autorizzazioni identiche, ma deve esistere su entrambi i nodi.

Se una tabella esiste su un nodo ma non su un altro, le operazioni di modifica dei dati non riusciranno:

  • In Node1 è presente una tabella denominata foo che risulta inesistente in Node2

  • Qualsiasi operazione DML sulla tabella foo su Node1 avrà esito negativo se replicata su Node2

Risoluzione: creazione della tabella foo su Node2 con la stessa struttura.

Nota

pgactivelink attualmente non replica i comandi CREATE USER o le operazioni DDL. La replica DDL verrà introdotta in versioni future.

Conflitti di blocco e interruzioni per deadlock

Poiché funzionano come normali sessioni utente, i processi di applicazione di pgactive seguono le regole standard di blocco delle righe e delle tabelle. È quindi possibile che i processi di applicazione di pgactivelink restino bloccati dalle transazioni degli utenti o da altri processi di applicazione.

I seguenti tipi di blocchi possono influire sui processi di applicazione:

  • Blocco esplicito a livello di tabella (LOCK TABLE...) da parte delle sessioni utente

  • Blocco esplicito a livello di riga (SELECT... FOR UPDATE/FOR SHARE) da parte delle sessioni utente

  • Blocco da chiavi esterne

  • Blocco implicito dovuto a operazioni UPDATE, INSERT o DELETE sulle righe, derivanti da attività locali o applicate da altri server

I deadlock possono verificarsi tra:

  • Un processo di applicazione di pgactivelink e una transazione utente

  • Due processi di applicazione

Quando si verifica un deadlock, il rilevatore di deadlock di PostgreSQL interrompe una delle transazioni che causa problemi. Se il processo del worker di applicazione di pgactivelink viene terminato, esegue automaticamente un nuovo tentativo che, in genere, ha esito positivo.

Nota
  • Questi problemi sono temporanei e di norma non richiedono l’intervento dell’amministratore. Se un processo di applicazione viene bloccato per un periodo prolungato a causa del blocco di una sessione utente inattiva, è possibile interrompere tale sessione per riprendere la replica. Questa situazione è simile a quando un utente mantiene un blocco prolungato che influisce su un’altra sessione utente.

  • Per identificare i ritardi di riproduzione legati ai blocchi, abilita la funzionalità log_lock_waits in PostgreSQL.

Conflitti divergenti

I conflitti divergenti si verificano quando i dati che dovrebbero essere identici tra i nodi differiscono inaspettatamente. Questi conflitti non dovrebbero verificarsi, ma nell’implementazione attuale non è possibile evitarli tutti in modo affidabile.

Nota

La modifica della PRIMARY KEY di una riga può causare conflitti divergenti se un altro nodo cambia la chiave della stessa riga prima che tutti i nodi elaborino la modifica. Evita di modificare le chiavi primarie o limita le modifiche a un nodo designato. Per ulteriori informazioni, consulta Conflitti UPDATE sulla PRIMARY KEY .

I conflitti divergenti che coinvolgono i dati delle righe richiedono, in genere, l’intervento dell’amministratore. Per risolvere tali conflitti, è necessario modificare manualmente i dati su un nodo in modo che corrispondano a quelli di un altro, disabilitando temporaneamente la replica ricorrendo a pgactive.pgactive_do_not_replicate. Questi conflitti non dovrebbero verificarsi quando si utilizza pgactive come prescritto e si evitano impostazioni o funzioni contrassegnate come non sicure.

In qualità di amministratore, devi risolvere questi conflitti manualmente. A seconda del tipo di conflitto, è necessario utilizzare opzioni avanzate come pgactive.pgactive_do_not_replicate. Utilizza queste opzioni con cautela, poiché un uso improprio può peggiorare la situazione. A causa della varietà dei possibili conflitti, non possiamo fornire istruzioni universalmente valide per la risoluzione.

I conflitti divergenti si verificano quando i dati che dovrebbero essere identici tra i nodi differiscono inaspettatamente. Questi conflitti non dovrebbero verificarsi, ma nell’implementazione attuale non è possibile evitarli tutti in modo affidabile.

Come evitare o tollerare i conflitti

Nella maggior parte dei casi, per evitare conflitti è possibile utilizzare un design appropriato dell’applicazione o rendere l’applicazione tollerante nei confronti dei conflitti.

I conflitti si verificano solo quando operazioni simultanee avvengono su più nodi. Per evitare conflitti:

  • Scrivi su un solo nodo

  • Scrivi su sottoinsiemi di database indipendenti su ogni nodo (ad esempio, assegna a ogni nodo uno schema separato)

Per i conflitti INSERT/INSERT, utilizza le sequenze Global che prevengono completamente i conflitti.

Se i conflitti non sono accettabili per il tuo caso d’uso, prendi in considerazione l’implementazione del blocco distribuito a livello di applicazione. Spesso, l’approccio migliore è progettare l’applicazione in modo che funzioni con i meccanismi di risoluzione dei conflitti di pgactive piuttosto che cercare di prevenire tutti i conflitti. Per ulteriori informazioni, consulta Tipi di conflitti.

Registrazione di log dei conflitti

pgactivelink registra gli incidenti di conflitto nella tabella pgactive.pgactive_conflict_history per aiutarti a diagnosticare e gestire i conflitti in modalità attivo-attivo. La registrazione di log dei conflitti in questa tabella si verifica solo se pgactive.log_conflicts_to_table è impostato su true. L’estensione pgactive registra i conflitti anche nel file di log di PostgreSQL quando log_min_messages è impostato su LOG o lower, indipendentemente dall’impostazione pgactive.log_conflicts_to_table.

Utilizza la tabella della cronologia dei conflitti per:

  • Misurare la frequenza con cui l’applicazione crea conflitti

  • Identificare dove si verificano i conflitti

  • Migliorare l’applicazione per ridurre i tassi di conflitto

  • Rilevare i casi in cui la risoluzione dei conflitti non produce i risultati desiderati

  • Stabilire dove è necessario utilizzare i trigger per la gestione dei conflitti definiti dall’utente o modificare la progettazione delle applicazioni

Per i conflitti di riga, puoi facoltativamente registrare i valori di riga. Questa operazione è controllata dall’impostazione pgactive.log_conflicts_to_table. Note:

  • Questa è un’opzione globale per l’intero database

  • Non esiste un controllo per tabella sulla registrazione di log dei valori di riga

  • Ai numeri di campo, agli elementi dell’array o alla lunghezza dei campi non viene applicato alcun limite

  • L’attivazione di questa funzionalità potrebbe non essere consigliabile se si lavora con righe da più megabyte che potrebbero causare conflitti

Poiché la tabella della cronologia dei conflitti contiene i dati di ogni tabella del database (ognuna con schemi potenzialmente diversi), i valori delle righe registrati vengono archiviati come campi JSON. Il file JSON viene creato utilizzando row_to_json, in modo simile a una chiamata diretta da SQL. PostgreSQL non dispone di una funzione json_to_row, quindi per ricostruire una tupla di tipo composito dal JSON registrato avrai bisogno di codice specifico per la tabella (in PL/pgSQL, PL/Python, PL/Perl, ecc.).

Nota

Il supporto dei conflitti definiti dall’utente verrà introdotto come funzionalità di estensione futura.