Comprensión de conflictos activo-activo - Amazon Relational Database Service

Comprensión de conflictos activo-activo

Cuando se utiliza pgactive en el modo activo-activo, escribir en las mismas tablas desde varios nodos puede crear conflictos de datos. Aunque algunos sistemas de agrupación en clústeres utilizan bloqueos distribuidos para evitar el acceso simultáneo, pgactive adopta un enfoque optimista que se adapta mejor a las aplicaciones distribuidas geográficamente.

Algunos sistemas de agrupación en clústeres de bases de datos impiden el acceso simultáneo a los datos mediante el uso de bloqueos distribuidos. Aunque este enfoque funciona cuando los servidores están muy cerca, no admite aplicaciones distribuidas geográficamente porque requiere una latencia extremadamente baja para lograr un buen rendimiento. En lugar de utilizar bloqueos distribuidos (un enfoque pesimista), la extensión pgactive utiliza un enfoque optimista. Esto significa que:

  • Ayuda a evitar conflictos cuando es posible.

  • Permite que se produzcan determinados tipos de conflictos.

  • Proporciona resolución de conflictos cuando se producen conflictos.

Este enfoque le proporciona más flexibilidad a la hora de crear aplicaciones distribuidas.

Cómo se producen los conflictos

Los conflictos entre nodos surgen de secuencias de eventos que no podrían ocurrir si todas las transacciones implicadas se produjeran simultáneamente en el mismo nodo. Como los nodos solo intercambian cambios una vez confirmadas las transacciones, cada transacción es válida individualmente en el nodo en el que se ha confirmado, pero no lo sería si se ejecutara en otro nodo que haya realizado otras tareas mientras tanto. Dado que la aplicación de pgactive básicamente reproduce la transacción en los demás nodos, la operación de reproducción puede producir un error si hay un conflicto entre una transacción que se está aplicando y una transacción que se ha confirmado en el nodo receptor.

La razón por la que la mayoría de los conflictos no se producen cuando todas las transacciones se ejecutan en un único nodo es que PostgreSQL cuenta con mecanismos de comunicación entre transacciones para evitarlos, entre los que se incluyen:

  • Índices UNIQUE

  • SEQUENCE

  • Bloqueo de filas y relaciones

  • Seguimiento de dependencias SERIALIZABLE

Todos estos mecanismos son formas de comunicarse entre transacciones para evitar problemas de concurrencia no deseados

pgactive logra una baja latencia y maneja bien las particiones de la red porque no utiliza un administrador de transacciones distribuido o un administrador de bloqueos. Sin embargo, esto significa que las transacciones en diferentes nodos se ejecutan completamente aisladas unas de otras. Aunque el aislamiento suele mejorar la coherencia de la base de datos, en este caso es necesario reducir el aislamiento para evitar conflictos.

Tipos de conflictos

Los conflictos que se pueden producir son:

Conflictos de PRIMARY KEY o UNIQUE

Los conflictos de filas se producen cuando varias operaciones intentan modificar la misma clave de fila de formas que no son posibles en un solo nodo. Estos conflictos representan el tipo más común de conflictos de datos.

pgactive resuelve los conflictos detectados mediante el manejo de last-update-wins o el gestor de conflictos personalizado.

Los conflictos de filas incluyen:

  • INSERT frente a INSERT

  • INSERT frente a UPDATE

  • UPDATE frente a DELETE

  • INSERT frente a DELETE

  • DELETE frente a DELETE

  • INSERT frente a DELETE

Conflictos INSERT/INSERT

El conflicto más común se produce cuando los INSERT en dos nodos diferentes crean una tupla con los mismos valores de PRIMARY KEY (o valores de restricción UNIQUE idénticos cuando no existe ninguna PRIMARY KEY).

pgactivelink resuelve los conflictos de INSERT utilizando la marca temporal del host de origen para conservar la tupla más reciente. Puede invalidar este comportamiento predeterminado con el controlador de conflictos personalizado. Aunque este proceso no requiere ninguna acción especial del administrador, tenga en cuenta que pgactivelink descarta una de las operaciones INSERT en todos los nodos. No se produce una fusión automática de datos a menos que el controlador personalizado la implemente.

pgactivelink solo puede resolver conflictos que impliquen una única violación de restricción. Si un INSERT infringe varias restricciones UNIQUE, debe implementar estrategias adicionales de resolución de conflictos.

INSERT que infringen múltiples restricciones UNIQUE

Un conflicto INSERT/INSERT puede infringir varias restricciones UNIQUE, incluida la PRIMARY KEY. pgactivelink solo puede gestionar conflictos que impliquen una única restricción UNIQUE. Cuando los conflictos infringen varias restricciones UNIQUE, el trabajador de aplicación produce un error y devuelve el siguiente error:

multiple unique constraints violated by remotely INSERTed tuple.

En versiones anteriores, esta situación generaba un error de "conflicto de unicidad divergente" en su lugar.

Para resolver estos conflictos, debe tomar medidas manuales. O bien DELETE las tuplas locales en conflicto o UPDATE las mismas para eliminar los conflictos con la nueva tupla remota. Tenga en cuenta que puede que tenga que abordar varias tuplas conflictivas. Actualmente, pgactivelink no proporciona ninguna funcionalidad integrada para ignorar, descartar o fusionar las tuplas que infringen múltiples restricciones únicas.

nota

Para obtener más información, consulte UPDATE que infringen varias restricciones UNIQUE.

Conflictos UPDATE/UPDATE

Este conflicto se produce cuando dos nodos modifican simultáneamente la misma tupla sin cambiar su PRIMARY KEY. pgactivelink resuelve estos conflictos mediante la lógica last-update-wins o el controlador de conflictos personalizado, si está definido. Una PRIMARY KEY es esencial para la coincidencia de tuplas y la resolución de conflictos. En el caso de las tablas sin una PRIMARY KEY, pgactivelink rechaza las operaciones UPDATE con el siguiente error:

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

Conflictos UPDATE en la PRIMARY KEY

Pgactive tiene limitaciones a la hora de gestionar las actualizaciones de PRIMARY KEY. Aunque puede realizar la operación UPDATE en una PRIMARY KEY, pgactive no puede resolver automáticamente los conflictos utilizando la lógica last-update-wins para estas operaciones. Debe asegurarse de que las actualizaciones de la PRIMARY KEY no entren en conflicto con los valores existentes. Si se producen conflictos durante las actualizaciones de PRIMARY KEY, se convierten en conflictos divergentes que requieren su intervención manual. Para obtener más información acerca de cómo manejar estas situaciones, consulte Conflictos divergentes.

UPDATE que infringen varias restricciones UNIQUE

pgactivelink no puede aplicar la resolución de conflictos last-update-wins cuando una UPDATE entrante infringe varias restricciones UNIQUE o valores de PRIMARY KEY. Este comportamiento es similar a las operaciones INSERT con múltiples infracciones de restricciones. Estas situaciones crean conflictos divergentes que requieren su intervención manual. Para obtener más información, consulte Conflictos divergentes.

Conflictos UPDATE/DELETE

Estos conflictos se producen cuando un nodo realiza una UPDATE en una fila mientras que otro nodo la DELETE simultáneamente. En este caso, se produce un conflicto UPDATE/DELETE durante la reproducción. La solución consiste en descartar cualquier UPDATE que llegue después de una DELETE, a menos que el gestor de conflictos personalizado especifique lo contrario.

pgactivelink requiere una PRIMARY KEY para hacer coincidir las tuplas y resolver los conflictos. Para las tablas sin una PRIMARY KEY, rechaza las operaciones DELETE con el siguiente error:

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

nota

pgactivelink no puede distinguir entre los conflictos de UPDATE/DELETE e INSERT/UPDATE. En ambos casos, una UPDATE afecta a una fila que no existe. Debido a la replicación asíncrona y a la falta de orden de reproducción entre los nodos, pgactivelink no puede determinar si la UPDATE es para una fila nueva (aún no se ha recibido INSERT) o para una fila eliminada. En ambos casos, pgactivelink descarta la UPDATE.

Conflictos INSERT/UPDATE

Este conflicto se puede producir en entornos de varios nodos. Ocurre cuando un nodo INSERT una fila, un segundo nodo la UPDATE y un tercer nodo recibe la UPDATE antes que el INSERT original. De forma predeterminada, pgactivelink resuelve estos conflictos descartando la UPDATE, a menos que el desencadenador de conflictos personalizado especifique lo contrario. Tenga en cuenta que este método de resolución puede provocar incoherencias en los datos entre los nodos. Para obtener más información acerca de escenarios similares y su manejo, consulte Conflictos UPDATE/DELETE.

Conflictos DELETE/DELETE

Este conflicto se produce cuando dos nodos diferentes eliminan simultáneamente la misma tupla. pgactivelink considera que estos conflictos son inofensivos porque ambas operaciones DELETE tienen el mismo resultado final. En este escenario, pgactivelink ignora de forma segura una de las operaciones DELETE sin afectar a la coherencia de datos.

Conflictos de restricciones de clave externa

Las restricciones de FOREIGN KEY pueden provocar conflictos al aplicar transacciones remotas a los datos locales existentes. Estos conflictos suelen producirse cuando las transacciones se aplican en una secuencia diferente a su orden lógico en los nodos de origen.

De forma predeterminada, pgactive aplica los cambios con session_replication_role como replica, lo que evita las comprobaciones de claves externas durante la replicación. En las configuraciones activo-activo, esto puede provocar infracciones de claves externas. La mayoría de las infracciones son temporales y se resuelven una vez que la replicación se pone al día. Sin embargo, pueden producirse claves externas huérfanas porque pgactive no admite el bloqueo de filas entre nodos.

Este comportamiento es inherente a los sistemas activo-activo asíncronos tolerantes a las particiones. Por ejemplo, el nodo A puede insertar una nueva fila secundaria mientras que el nodo B elimina simultáneamente su fila principal. El sistema no puede impedir este tipo de modificación concurrente entre nodos.

Para minimizar los conflictos de claves externas, recomendamos lo siguiente:

  • Limitar las relaciones de claves externas a entidades estrechamente relacionadas.

  • Modificar las entidades relacionadas desde un solo nodo cuando sea posible.

  • Elegir entidades que rara vez requieran modificación.

  • Implementar un control de concurrencia en el nivel de aplicación para las modificaciones.

Conflictos de restricciones de exclusión

pgactive link no admite restricciones de exclusión y restringe su creación.

nota

Si convierte una base de datos independiente existente en una base de datos de pgactivelink, elimine manualmente todas las restricciones de exclusión.

En un sistema asíncrono distribuido, no es posible garantizar que ningún conjunto de filas infrinja la restricción. Esto se debe a que todas las transacciones en los distintos nodos están completamente aisladas. Las restricciones de exclusión pueden provocar bloqueos de reproducción, donde la reproducción no puede avanzar de un nodo a otro debido a infracciones de las restricciones de exclusión.

Si obliga a pgactive Link a crear una restricción de exclusión o si no elimina las existentes al convertir una base de datos independiente en pgactive Link, es probable que la replicación se interrumpa. Para restaurar el progreso de la replicación, elimine o modifique las tuplas locales que entren en conflicto con una tupla remota entrante para poder aplicar la transacción remota.

Conflictos de datos globales

Cuando se utiliza pgactivelink, se pueden producir conflictos cuando los nodos tienen diferentes datos globales de todo el sistema PostgreSQL, como los roles. Estos conflictos pueden provocar que las operaciones (principalmente DDL) se ejecuten correctamente en un nodo, pero que no se apliquen a otros nodos.

Si un usuario existe en un nodo pero no en otro, pueden producirse problemas de replicación:

  • Node1 tiene un nombre de usuario fred, pero este usuario no existe en Node2

  • Cuando fred crea una tabla en Node1, la tabla se replica con fred como el propietario

  • Cuando este comando DDL se aplica al Node2, se produce un error porque el usuario fred no existe

  • Este error genera un ERROR en los registros de PostgreSQL en Node2 e incrementa el contador pgactive.pgactive_stats.nr_rollbacks

Solución: cree el usuario fred en Node2. El usuario no necesita permisos idénticos, sino que debe existir en ambos nodos.

Si existe una tabla en un nodo pero no en otro, las operaciones de modificación de datos producirán un error:

  • Node1 tiene una tabla llamada foo que no existe en Node2

  • Cualquier operación DML en la tabla foo en el Node1 producirá un error cuando se replique en Node2

Resolución: cree la tabla foo en el Node2 con la misma estructura.

nota

Actualmente, pgactivelink no replica los comandos CREATE USER ni las operaciones DDL. La replicación de DDL está prevista para una versión futura.

Conflictos de bloqueo y anulaciones de bloqueo

Como los procesos de aplicación de pgactive funcionan como sesiones de usuario normales, siguen las reglas estándar de bloqueo de filas y tablas. Esto puede provocar que los procesos de aplicación de pgactivelink esperen por bloqueos mantenidos por transacciones de usuarios o por otros procesos de aplicación.

Los siguientes tipos de bloqueos pueden afectar a los procesos de aplicación:

  • Bloqueo explícito en el nivel de tabla (LOCK TABLE...) por sesiones de usuario

  • Bloqueo explícito en el nivel de fila (SELECT ... FOR UPDATE/FOR SHARE) por sesiones de usuario

  • Bloqueo desde claves foráneas

  • Bloqueo implícito debido a UPDATE, INSERT o DELETE de filas, ya sea por actividad local o aplicación desde otros servidores

Los bloqueos pueden ocurrir entre:

  • Un proceso de aplicación de pgactivelink y una transacción de usuario

  • Dos procesos de aplicación

Cuando se producen bloqueos, el detector de bloqueos de PostgreSQL finaliza una de las transacciones problemáticas. Si se termina el proceso del trabajador de aplicación de pgactivelink, este reintenta automáticamente y normalmente tiene éxito.

nota
  • Estos problemas son temporales y generalmente no requieren la intervención del administrador. Si un proceso de aplicación está bloqueado durante un periodo prolongado por un bloqueo en una sesión de usuario inactiva, puede terminar la sesión de usuario para reanudar la replicación. Esta situación es similar a cuando un usuario mantiene un bloqueo prolongado que afecta a la sesión de otro usuario.

  • Para identificar los retrasos en la reproducción relacionados con el bloqueo, habilite la instalación log_lock_waits en PostgreSQL.

Conflictos divergentes

Los conflictos divergentes se producen cuando los datos que deberían ser idénticos en todos los nodos difieren inesperadamente. Aunque estos conflictos no se deberían producir, no todos se pueden prevenir de forma fiable con la implementación actual.

nota

La modificación de PRIMARY KEY de una fila puede provocar conflictos divergentes si otro nodo cambia la clave de la misma fila antes de que todos los nodos procesen el cambio. Evite cambiar las claves principales o restrinja los cambios a un nodo designado. Para obtener más información, consulte Conflictos UPDATE en la PRIMARY KEY .

Los conflictos divergentes relacionados con los datos de las filas suelen requerir la intervención del administrador. Para resolver estos conflictos, debe ajustar manualmente los datos de un nodo para que coincidan con otro mientras desactiva temporalmente la replicación mediante pgactive.pgactive_do_not_replicate. Estos conflictos no se deberían producir cuando se utiliza pgactive tal como se ha documentado y se evitan las configuraciones o funciones marcadas como inseguras.

Como administrador, debe resolver estos conflictos manualmente. En función del tipo de conflicto, necesitará utilizar opciones avanzadas como pgactive.pgactive_do_not_replicate. Use estas opciones con precaución, ya que un uso incorrecto puede empeorar la situación. Debido a la variedad de posibles conflictos, no podemos proporcionar instrucciones de resolución universales.

Los conflictos divergentes se producen cuando los datos que deberían ser idénticos en diferentes nodos difieren inesperadamente. Aunque estos conflictos no deberían producirse, no todos se pueden prevenir de forma fiable con la implementación actual.

Evitar o tolerar los conflictos

En la mayoría de los casos, puede utilizar un diseño de aplicación adecuado para evitar conflictos o hacer que la aplicación tolere los conflictos.

Los conflictos solo se producen cuando se realizan operaciones simultáneas en varios nodos. Para evitar conflictos:

  • Escriba solo en un nodo

  • Escriba en subconjuntos de bases de datos independientes en cada nodo (por ejemplo, asigne a cada nodo un esquema independiente)

Para conflictos entre INSERT e INSERT, utilice secuencias globales para evitar conflictos por completo.

Si los conflictos no son aceptables para su caso de uso, considere implementar el bloqueo distribuido a nivel de aplicación. A menudo, el mejor enfoque es diseñar la aplicación para que funcione con los mecanismos de resolución de conflictos de pgactive en lugar de intentar prevenir todos los conflictos. Para obtener más información, consulte Tipos de conflictos.

Registro de conflictos

pgactivelink registra los incidentes de conflicto en la tabla pgactive.pgactive_conflict_history para ayudarle a diagnosticar y gestionar los conflictos activo-activo. El registro de conflictos en esta tabla solo se produce cuando se establece pgactive.log_conflicts_to_table en verdadero. La extensión pgactive también registra los conflictos en el archivo de registro de PostgreSQL cuando log_min_messages se establece en LOG o lower, independientemente de la configuración de pgactive.log_conflicts_to_table.

Utilice la tabla del historial de conflictos para:

  • Medir la frecuencia con la que la aplicación crea conflictos

  • Identificar dónde se producen los conflictos

  • Mejorar la aplicación para reducir las tasas de conflictos

  • Detectar casos en los que las resoluciones de conflictos no producen los resultados deseados

  • Determinar dónde necesita desencadenantes de conflictos definidos por el usuario o cambios en el diseño de la aplicación

Para los conflictos de filas, puede registrar opcionalmente los valores de las filas. Esto se controla mediante la configuración de pgactive.log_conflicts_to_table. Tenga en cuenta que:

  • Esta es una opción global para toda la base de datos

  • No hay ningún control por tabla sobre el registro de valores de fila

  • No se aplican límites a los números de campo, los elementos de matriz o las longitudes de los campos

  • Puede que no sea recomendable activar esta característica si trabaja con filas de varios megabytes que podrían desencadenar conflictos

Como la tabla del historial de conflictos contiene datos de todas las tablas de la base de datos (cada una con posibles esquemas diferentes), los valores de las filas registradas se almacenan como campos JSON. El JSON se crea mediante row_to_json, de forma similar a llamarlo directamente desde SQL. PostgreSQL no proporciona una función json_to_row, por lo que necesitará código específico de tabla (en PL/pgSQL, PL/Python, PL/Perl, etc.) para reconstruir una tupla de tipo compuesto a partir del JSON registrado.

nota

La compatibilidad para conflictos definidos por el usuario está planificada como una característica de extensión futura.