Comprendre les conflits active-active - Amazon Relational Database Service

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Comprendre les conflits active-active

Lorsque vous utilisez pgactive en mode active-active, l’écriture dans les mêmes tables à partir de plusieurs nœuds peut créer des conflits de données. Alors que certains systèmes de mise en cluster utilisent des verrous distribués pour empêcher les accès simultanés, pgactive adopte une approche optimiste qui convient mieux aux applications distribuées géographiquement.

Certains systèmes de mise en cluster de bases de données empêchent l’accès simultané aux données en utilisant des verrous distribués. Bien que cette approche fonctionne lorsque les serveurs sont à proximité, elle ne prend pas en charge les applications distribuées géographiquement, car elle nécessite une latence extrêmement faible pour de bonnes performances. Au lieu d’utiliser des verrous distribués (approche pessimiste), l’extension pgactive utilise une approche optimiste. Cela signifie qu’elle :

  • Vous aide à éviter les conflits dans la mesure du possible.

  • Permet à certains types de conflits de se produire.

  • Permet de résoudre les conflits lorsque des conflits surviennent.

Cette approche vous donne plus de flexibilité lors de la création d’applications distribuées.

Comment se produisent les conflits

Les conflits entre nœuds proviennent de séquences d’événements qui ne pourraient pas se produire si toutes les transactions impliquées se produisaient simultanément sur le même nœud. Comme les nœuds n’échangent des modifications qu’une fois les transactions validées, chaque transaction est valide individuellement sur le nœud sur lequel elle a été validée, mais ne le serait pas si elle était exécutée sur un autre nœud ayant effectué une autre tâche dans l’intervalle. Comme l’application de pgactive réexécute essentiellement la transaction sur les autres nœuds, l’opération de réexécution peut échouer en cas de conflit entre une transaction appliquée et une transaction validée sur le nœud récepteur.

La raison pour laquelle la plupart des conflits ne peuvent pas se produire lorsque toutes les transactions sont exécutées sur un seul nœud est que PostgreSQL dispose de mécanismes de communication entre transactions pour les empêcher, notamment :

  • Index UNIQUE

  • SEQUENCE

  • Verrouillage des lignes et des relations

  • Suivi des dépendances SERIALIZABLE

Tous ces mécanismes sont des moyens de communiquer entre les transactions afin d’éviter les problèmes de simultanéité indésirables.

pgactive atteint une faible latence et gère bien les partitions réseau, car il n’utilise pas de gestionnaire de transactions distribué ni de gestionnaire de verrouillage. Cependant, cela signifie que les transactions sur différents nœuds s’exécutent de manière totalement isolée les unes des autres. Bien que l’isolation améliore généralement la cohérence de la base de données, dans ce cas, vous devez réduire l’isolation pour éviter les conflits.

Types de conflits

Les conflits qui peuvent survenir sont notamment les suivants :

Conflits PRIMARY KEY ou UNIQUE

Les conflits de lignes se produisent lorsque plusieurs opérations tentent de modifier la même clé de ligne d’une manière impossible sur un seul nœud. Ces conflits constituent le type de conflit de données le plus courant.

pgactive résout les conflits détectés grâce à la gestion last-update-wins ou à votre gestionnaire de conflits personnalisé.

Les conflits de lignes incluent :

  • INSERT et INSERT

  • INSERT et UPDATE

  • UPDATE et DELETE

  • INSERT et DELETE

  • DELETE et DELETE

  • INSERT et DELETE

Conflits INSERT/INSERT

Ce conflit le plus courant se produit lorsque des INSERT sur deux nœuds différents créent un tuple avec les mêmes valeurs de PRIMARY KEY (ou des valeurs de contrainte UNIQUE identiques lorsqu’aucune PRIMARY KEY n’existe).

pgactivelink résout les conflits INSERT en utilisant l’horodatage de l’hôte d’origine pour conserver le tuple le plus récent. Vous pouvez remplacer ce comportement par défaut à l’aide de votre gestionnaire de conflits personnalisé. Bien que ce processus ne nécessite aucune action particulière de l’administrateur, sachez que pgactivelink ignore l’une des opérations INSERT sur tous les nœuds. Aucune fusion automatique de données ne se produit à moins que votre gestionnaire personnalisé ne l’implémente.

Le pgactivelink ne peut résoudre que les conflits impliquant une seule violation de contrainte. Si un INSERT enfreint plusieurs contraintes UNIQUE, vous devez mettre en œuvre des stratégies de résolution de conflits supplémentaires.

INSERT violant plusieurs contraintes UNIQUE

Un conflit INSERT/INSERT peut violer plusieurs contraintes UNIQUE, y compris la PRIMARY KEY. pgactivelink ne peut gérer que les conflits impliquant une seule contrainte UNIQUE. Lorsque des conflits violent plusieurs contraintes UNIQUE, l’application de travail d’application échoue et renvoie l’erreur suivante :

multiple unique constraints violated by remotely INSERTed tuple.

Dans les anciennes versions, cette situation générait plutôt une erreur de « conflit d’unicité divergente ».

Pour résoudre ces conflits, vous devez effectuer une action manuelle. Effectuez une opération DELETE sur les tuples locaux en conflit ou une opération UPDATE pour supprimer les conflits avec le nouveau tuple distant. Sachez que vous devrez peut-être résoudre plusieurs tuples en conflit. À l’heure actuelle, pgactivelink ne fournit aucune fonctionnalité intégrée permettant d’ignorer, de supprimer ou de fusionner les tuples qui violent plusieurs contraintes uniques.

Note

Pour plus d’informations, consultez les UPDATE qui violent plusieurs contraintes UNIQUE.

Conflits UPDATE/UPDATE

Ce conflit se produit lorsque deux nœuds modifient simultanément le même tuple sans changer sa PRIMARY KEY. pgactivelink résout ces conflits en utilisant la logique last-update-wins ou votre gestionnaire de conflits personnalisé, s’il est défini. Une PRIMARY KEY est essentielle pour l’appariement des tuples et la résolution des conflits. Pour les tables sans PRIMARY KEY, pgactivelink rejette les opérations UPDATE avec l’erreur suivante :

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

Conflits UPDATE sur la PRIMARY KEY

pgactive a des limitations lors de la gestion des mises à jour de PRIMARY KEY. Bien que vous puissiez effectuer une opération UPDATE sur une PRIMARY KEY, pgactive ne peut pas résoudre automatiquement les conflits en utilisant la logique last-update-wins pour ces opérations. Vous devez vous assurer que les mises à jour de votre PRIMARY KEY n’entrent pas en conflit avec les valeurs existantes. Si des conflits surviennent lors des mises à jour de la PRIMARY KEY, ils deviennent des conflits divergents qui nécessitent une intervention manuelle de votre part. Pour plus d’informations sur la gestion de ces situations, consultez Conflits divergents.

UPDATE violant plusieurs contraintes UNIQUE

pgactivelink ne peut pas appliquer la résolution de conflit last-update-wins lorsqu’une UPDATE entrante viole plusieurs contraintes UNIQUE ou valeurs de PRIMARY KEY. Ce comportement est similaire à celui des opérations INSERT impliquant plusieurs violations de contraintes. Ces situations créent des conflits divergents qui nécessitent une intervention manuelle de votre part. Pour plus d’informations, consultez Conflits divergents.

Conflits UPDATE/DELETE

Ces conflits se produisent lorsqu’un nœud UPDATE une ligne tandis qu’un autre nœud la DELETE simultanément. Dans ce cas, un conflit UPDATE/DELETE se produit lors de la relecture. La solution consiste à ignorer toute UPDATE qui arrive après une DELETE, sauf indication contraire de votre gestionnaire de conflits personnalisé.

pgactivelink a besoin d’une PRIMARY KEY pour apparier des tuples et résoudre les conflits. Pour les tables sans PRIMARY KEY, elle rejette les opérations DELETE avec l’erreur suivante :

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

Note

pgactivelink ne peut pas faire la distinction entre les conflits UPDATE/DELETE et INSERT/UPDATE. Dans les deux cas, une opération UPDATE affecte une ligne inexistante. En raison de la réplication asynchrone et de l’absence d’ordre de réexécution entre les nœuds, pgactivelink ne peut pas déterminer si la mise à jour concerne une nouvelle ligne (INSERT non encore reçu) ou une ligne supprimée. Dans les deux scénarios, pgactivelink ignore l’opération UPDATE.

Conflits INSERT/UPDATE

Ce conflit peut se produire dans des environnements à nœuds multiples. Cela se produit lorsqu’un nœud INSERT une ligne, qu’un deuxième nœud la met à jour et qu’un troisième nœud reçoit l’UPDATE avant l’INSERT d’origine. Par défaut, pgactivelink résout ces conflits en supprimant l’UPDATE, sauf indication contraire de votre déclencheur de conflit personnalisé. Sachez que cette méthode de résolution peut entraîner des incohérences de données entre les nœuds. Pour plus d’informations sur des scénarios similaires et leur gestion, consultez Conflits UPDATE/DELETE.

Conflits DELETE/DELETE

Ce conflit se produit lorsque deux nœuds différents suppriment simultanément le même tuple. pgactivelink considère que ces conflits sont inoffensifs, car les deux opérations DELETE ont le même résultat final. Dans ce scénario, pgactivelink ignore en toute sécurité l’une des opérations DELETE sans affecter la cohérence des données.

Conflits de contraintes liées aux clés étrangères

Les contraintes FOREIGN KEY peuvent provoquer des conflits lors de l’application de transactions distantes à des données locales existantes. Ces conflits se produisent généralement lorsque les transactions sont appliquées dans un ordre différent de leur ordre logique sur les nœuds d’origine.

Par défaut, pgactive applique les modifications avec session_replication_role en tant que replica, ce qui permet de contourner les contrôles de clé étrangère lors de la réplication. Dans les configurations active-active, cela peut entraîner des violations de clés étrangères. La plupart des violations sont temporaires et sont résolues une fois que la réplication rattrape son retard. Cependant, des suspensions de clés étrangères peuvent se produire parce que pgactive ne prend pas en charge le verrouillage des lignes entre nœuds.

Ce comportement est inhérent aux systèmes active-active asynchrones tolérants au partitionnement. Par exemple, le nœud A peut insérer une nouvelle ligne enfant tandis que le nœud B supprime simultanément sa ligne parent. Le système ne peut pas empêcher ce type de modification simultanée entre les nœuds.

Afin de minimiser les conflits de clés étrangères, nous vous recommandons de respecter les points suivants :

  • Limitez les relations de clé étrangère aux entités étroitement liées.

  • Modifiez les entités associées à partir d’un seul nœud lorsque cela est possible.

  • Choisissez des entités qui nécessitent rarement des modifications.

  • Mettez en œuvre un contrôle de simultanéité au niveau de l’application pour les modifications.

Conflits de contraintes d’exclusion

pgactivelink ne prend pas en charge les contraintes d’exclusion et restreint leur création.

Note

Si vous convertissez une base de données autonome existante en base de données pgactivelink, supprimez manuellement toutes les contraintes d’exclusion.

Dans un système asynchrone distribué, il n’est pas possible de garantir qu’aucun ensemble de lignes ne viole la contrainte. Cela est dû au fait que toutes les transactions sur les différents nœuds sont totalement isolées. Les contraintes d’exclusion peuvent entraîner des blocages de relecture, dans lesquels la relecture ne peut pas progresser d’un nœud à l’autre en raison de violations des contraintes d’exclusion.

Si vous forcez pgactivelink à créer une contrainte d’exclusion, ou si vous ne supprimez pas les contraintes existantes lors de la conversion d’une base de données autonome en pgactivelink, la réplication risque de s’interrompre. Pour rétablir la progression de la réplication, supprimez ou modifiez les tuples locaux qui entrent en conflit avec un tuple distant entrant afin que la transaction distante puisse être appliquée.

Conflits de données globales

Lorsque vous utilisez pgactivelink, des conflits peuvent survenir lorsque les nœuds possèdent des données globales PostgreSQL différentes à l’échelle du système, telles que des rôles. Ces conflits peuvent entraîner la réussite et la validation d’opérations (principalement DDL) sur un nœud, mais ne pas s’appliquer aux autres nœuds.

Si un utilisateur existe sur un nœud mais pas sur un autre, des problèmes de réplication peuvent survenir :

  • Node1 a un utilisateur nommé fred, mais cet utilisateur n’existe pas sur Node2

  • Lorsque fred crée une table sur Node1, la table est répliquée avec fred en tant que propriétaire

  • Lorsque cette commande DDL est appliquée à Node2, elle échoue, car l’utilisateur fred n’existe pas

  • Cet échec génère une ERROR dans les journaux PostgreSQL sur Node2 et incrémente le compteur pgactive.pgactive_stats.nr_rollbacks

Résolution : créez l’utilisateur fred sur Node2. L’utilisateur n’a pas besoin d’autorisations identiques mais doit exister sur les deux nœuds.

Si une table existe sur un nœud mais pas sur un autre, les opérations de modification des données échouent :

  • Node1 possède une table nommée foo qui n’existe pas sur Node2

  • Toute opération DML sur la table foo de Node1 échoue une fois répliquée sur Node2

Résolution : créez la table foo sur Node2 avec la même structure.

Note

pgactivelink ne réplique actuellement pas les commandes CREATE USER ni les opérations DDL. La réplication DDL est prévue pour une future version.

Conflits de verrou et abandons les blocages

Comme les processus d’application pgactive fonctionnent comme des sessions utilisateur normales, ils suivent les règles standard de verrouillage des lignes et des tables. Cela peut amener les processus d’application de pgactivelink à attendre les verrous détenus par les transactions des utilisateurs ou par d’autres processus d’application.

Les types de verrous suivants peuvent affecter les processus d’application :

  • Verrouillage explicite au niveau de la table (LOCK TABLE ...) par les sessions utilisateur

  • Verrouillage explicite au niveau des lignes (SELECT ... FOR UPDATE/FOR SHARE) par sessions utilisateur

  • Verrouillage à partir de clés étrangères

  • Verrouillage implicite dû à des UPDATE, à des INSERT ou à des DELETE de lignes, soit en raison d’une activité locale, ou d’une application d’autres serveurs

Des blocages peuvent se produire entre :

  • Un processus d’application pgactivelink et une transaction utilisateur

  • Deux processus d’application

Lorsque des blocages se produisent, le détecteur de blocage de PostgreSQL met fin à l’une des transactions problématiques. Si le processus de l’application de travail d’application pgactivelink est arrêté, il réessaie automatiquement et réussit généralement.

Note
  • Ces problèmes sont temporaires et ne nécessitent généralement pas l’intervention de l’administrateur. Si un processus d’application est bloqué pendant une période prolongée par le verrouillage d’une session utilisateur inactive, vous pouvez mettre fin à la session utilisateur pour reprendre la réplication. Cette situation est similaire à celle où un utilisateur détient un long verrou qui affecte une autre session utilisateur.

  • Pour identifier les délais de relecture liés au verrouillage, activez la fonctionnalité log_lock_waits dans PostgreSQL.

Conflits divergents

Des conflits divergents se produisent lorsque des données qui devraient être identiques entre les nœuds diffèrent de façon inattendue. Bien que ces conflits ne devraient pas se produire, ils ne peuvent pas tous être évités de manière fiable dans le cadre de la mise en œuvre actuelle.

Note

La modification de la PRIMARY KEY d’une ligne peut provoquer des conflits divergents si un autre nœud modifie la clé de la même ligne avant que tous les nœuds n’aient traité la modification. Évitez de modifier les clés primaires ou limitez les modifications à un nœud désigné. Pour plus d’informations, consultez Conflits UPDATE sur la PRIMARY KEY .

Les conflits divergents impliquant des données de ligne nécessitent généralement l’intervention de l’administrateur. Pour résoudre ces conflits, vous devez ajuster manuellement les données d’un nœud pour qu’elles correspondent à celles d’un autre tout en désactivant temporairement la réplication à l’aide de pgactive.pgactive_do_not_replicate. Ces conflits ne devraient pas se produire lorsque vous utilisez pgactive comme indiqué et que vous évitez les paramètres ou les fonctions marqués comme non sécurisés.

En tant qu’administrateur, vous devez résoudre ces conflits manuellement. Selon le type de conflit, vous devez utiliser des options avancées telles que pgactive.pgactive_do_not_replicate. Utilisez ces options avec prudence, car une mauvaise utilisation peut aggraver la situation. En raison de la variété des conflits possibles, nous ne pouvons pas fournir d’instructions de résolution universelles.

Des conflits divergents se produisent lorsque des données qui devraient être identiques dans des nœuds différents diffèrent de façon inattendue. Bien que ces conflits ne devraient pas se produire, ils ne peuvent pas tous être évités de manière fiable dans le cadre de la mise en œuvre actuelle.

Éviter ou tolérer les conflits

Dans la plupart des cas, vous pouvez utiliser une conception d’application appropriée pour éviter les conflits ou faire en sorte que votre application tolère les conflits.

Les conflits se produisent uniquement lorsque des opérations simultanées ont lieu sur plusieurs nœuds. Pour éviter les conflits :

  • Écrivez sur un seul nœud

  • Écrivez dans des sous-ensembles de base de données indépendants sur chaque nœud (par exemple, attribuer à chaque nœud un schéma distinct)

Pour les conflits INSERT et INSERT, utilisez des séquences globales pour éviter complètement les conflits.

Si les conflits ne sont pas acceptables pour votre cas d’utilisation, envisagez d’implémenter le verrouillage distribué au niveau de l’application. Souvent, la meilleure approche consiste à concevoir votre application pour qu’elle fonctionne avec les mécanismes de résolution de conflits de pgactive plutôt que d’essayer de prévenir tous les conflits. Pour plus d’informations, consultez Types de conflits.

Journalisation des conflits

pgactivelink journalise les incidents de conflit dans la table pgactive.pgactive_conflict_history pour vous aider à diagnostiquer et à gérer les conflits active-active. La journalisation des conflits dans cette table ne se produit que lorsque vous définissez pgactive.log_conflicts_to_table sur la valeur true. L’extension pgactive journalise également les conflits dans le fichier journal de PostgreSQL lorsque log_min_messages est défini sur LOG ou lower, quel que soit le paramètre pgactive.log_conflicts_to_table.

Utilisez la table d’historique des conflits pour :

  • Mesurer la fréquence à laquelle votre application crée des conflits

  • Identifier les endroits où les conflits se produisent

  • Améliorer votre application pour réduire les taux de conflit

  • Détecter les cas où la résolution des conflits ne produit pas les résultats souhaités

  • Déterminer où vous avez besoin de déclencheurs de conflits définis par l’utilisateur ou de modifications de la conception de l’application

Pour les conflits de lignes, vous pouvez éventuellement journaliser les valeurs des lignes. Ceci est contrôlé par le paramètre pgactive.log_conflicts_to_table. Remarque :

  • Il s’agit d’une option à l’échelle de la base de données globale

  • Il n’y a aucun contrôle par table sur la journalisation des valeurs des lignes

  • Aucune limite n’est appliquée aux numéros de champs, aux éléments du tableau ou à la longueur des champs

  • L’activation de cette fonctionnalité peut être déconseillée si vous utilisez des lignes de plusieurs mégaoctets susceptibles de déclencher des conflits

Étant donné que la table d’historique des conflits contient les données de chaque table de la base de données (chacune avec des schémas potentiellement différents), les valeurs des lignes journalisées sont stockées sous forme de champs JSON. Le JSON est créé en utilisant row_to_json, comme si vous l’appeliez directement depuis SQL. PostgreSQL ne fournit pas de fonction json_to_row, vous aurez donc besoin d’un code spécifique à la table (en PL/pgSQL, PL/Python, PL/Perl, etc.) pour reconstruire un tuple de type composite à partir du JSON journalisé.

Note

La prise en charge des conflits définis par l’utilisateur est prévue dans le cadre d’une future fonctionnalité d’extension.