

# Comprensión de conflictos activo-activo
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.replication"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.howconflicts"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflicttypes"></a>

Los conflictos que se pueden producir son:

**Topics**
+ [Conflictos de PRIMARY KEY o UNIQUE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict1)
+ [Conflictos INSERT/INSERT](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict2)
+ [INSERT que infringen múltiples restricciones UNIQUE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict3)
+ [Conflictos UPDATE/UPDATE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict4)
+ [Conflictos UPDATE en la PRIMARY KEY](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5)
+ [UPDATE que infringen varias restricciones UNIQUE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict6)
+ [Conflictos UPDATE/DELETE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7)
+ [Conflictos INSERT/UPDATE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict8)
+ [Conflictos DELETE/DELETE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict9)
+ [Conflictos de restricciones de clave externa](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict10)
+ [Conflictos de restricciones de exclusión](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict11)
+ [Conflictos de datos globales](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict12)
+ [Conflictos de bloqueo y anulaciones de bloqueo](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict13)
+ [Conflictos divergentes](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14)

### Conflictos de PRIMARY KEY o UNIQUE
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict1"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict2"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict3"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict4"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5"></a>

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](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14).

### UPDATE que infringen varias restricciones UNIQUE
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict6"></a>

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](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14).

### Conflictos UPDATE/DELETE
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict8"></a>

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](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7).

### Conflictos DELETE/DELETE
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict9"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict10"></a>

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\$1replication\$1role 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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict11"></a>

 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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict12"></a>

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 de 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 de DML en la tabla `foo` en el Node1 producirá un error cuando se replique en Node2

**Solución:** cree la tabla `foo` en el Node2 con la misma estructura.

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

### Conflictos de bloqueo y anulaciones de bloqueo
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict13"></a>

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14"></a>

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](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5).

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
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.avoidconflicts"></a>

 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](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflicttypes). 

## Registro de conflictos
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflictlogging"></a>

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\$1min\$1messages 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.