La decodificación lógica es el proceso de extraer todos los cambios persistentes en las tablas de una base de datos en un formato coherente y fácil de entender que pueda interpretarse sin un conocimiento detallado del estado interno de la base de datos.
En PostgreSQL, la decodificación lógica se implementa decodificando el contenido del registro de escritura anticipada (write-ahead log), que describe los cambios a nivel de almacenamiento, en una forma específica de la aplicación, como un flujo de tuplas o sentencias SQL.
En el contexto de la replicación lógica, una ranura (slot) representa un flujo de cambios que se pueden reproducir en un cliente en el orden en que se realizaron en el servidor de origen. Cada ranura transmite una secuencia de cambios desde una sola base de datos.
PostgreSQL también tiene ranuras de replicación en flujo (consulta la Section 26.2.5), pero allí se utilizan de manera un poco diferente.
Una ranura de replicación tiene un identificador que es único en todas las bases de datos de un clúster de PostgreSQL. Las ranuras persisten independientemente de la conexión que las utilice y son seguras ante fallos.
Una ranura lógica emitirá cada cambio solo una vez en funcionamiento normal. La posición actual de cada ranura persiste solo en el checkpoint, por lo que en caso de una caída del servidor, la ranura podría volver a un LSN anterior, lo que hará que los cambios recientes se envíen de nuevo cuando el servidor se reinicie. Los clientes de decodificación lógica son responsables de evitar efectos no deseados al manejar el mismo mensaje más de una vez. Los clientes pueden desear registrar el último LSN que vieron al decodificar y omitir cualquier dato repetido o (cuando utilicen el protocolo de replicación) solicitar que la decodificación comience desde ese LSN en lugar de dejar que el servidor determine el punto de inicio. La función de seguimiento del progreso de la replicación está diseñada para este propósito, consulta los orígenes de replicación.
Pueden existir múltiples ranuras independientes para una sola base de datos. Cada ranura tiene su propio estado, lo que permite a diferentes consumidores recibir cambios desde diferentes puntos en el flujo de cambios de la base de datos. Para la mayoría de las aplicaciones, se requerirá una ranura independiente para cada consumidor.
Una ranura de replicación lógica no sabe nada sobre el estado del receptor o receptores. Incluso es posible tener múltiples receptores diferentes utilizando la misma ranura en diferentes momentos; simplemente obtendrán los cambios que sigan a partir del momento en que el último receptor dejó de consumirlos. Solo un receptor puede consumir cambios de una ranura en un momento dado.
También se puede crear una ranura de replicación lógica en un standby en caliente (hot standby). Para evitar
que VACUUM elimine las filas necesarias de los catálogos del sistema,
se debe establecer hot_standby_feedback en el standby. A pesar de eso,
si se elimina alguna fila necesaria, la ranura se invalida. Se recomienda encarecidamente
utilizar una ranura física entre el primario y el standby. De lo contrario,
hot_standby_feedback funcionará pero solo mientras la conexión esté activa
(por ejemplo, un reinicio del nodo la rompería). Entonces, el primario puede eliminar filas del catálogo
del sistema que podrían ser necesarias para la decodificación lógica en el standby (ya que no
conoce el catalog_xmin en el standby).
Las ranuras lógicas existentes en el standby también se invalidan si el nivel de wal
(wal_level) en el primario se reduce a menos de logical.
Esto se hace tan pronto como el standby detecta dicho cambio en el flujo de WAL.
Significa que, para los walsenders que están retrasados (si los hay), algunos registros de WAL hasta
el cambio del parámetro wal_level en el primario no se decodificarán.
La creación de una ranura lógica requiere información sobre todas las transacciones actualmente
en curso. En el primario, esta información está disponible directamente, pero en un standby,
esta información debe obtenerse del primario. Por lo tanto, la creación de la ranura puede tener que esperar
a que ocurra alguna actividad en el primario. Si el primario está inactivo, la creación de una ranura lógica en el
standby puede llevar un tiempo notable. Esto se puede acelerar llamando a la función
pg_log_standby_snapshot en el primario.
Las ranuras de replicación persisten a través de las caídas del servidor y no saben nada sobre el estado
de su(s) consumidor(es). Evitarán la eliminación de los recursos necesarios incluso cuando no haya
ninguna conexión que las utilice. Esto consume almacenamiento porque ni el WAL requerido ni las filas
requeridas de los catálogos del sistema pueden ser eliminados por VACUUM mientras sean
requeridos por una ranura de replicación. En casos extremos, esto podría causar el apagado de la base de
datos para evitar el desbordamiento de ID de transacción (consulta la Section 24.1.5).
Por lo tanto, si una ranura ya no es necesaria, debe eliminarse.
Las ranuras de replicación lógica en el primario se pueden sincronizar con el hot standby
utilizando el parámetro failover de
pg_create_logical_replication_slot, o utilizando la
opción
failover de CREATE SUBSCRIPTION durante la
creación de la ranura. Además, se requiere habilitar
sync_replication_slots
en el standby. Al habilitar sync_replication_slots
en el standby, las ranuras de failover se pueden sincronizar periódicamente en el trabajador
slotsync. Para que la sincronización funcione, es obligatorio tener una ranura de replicación física
entre el primario y el standby (es decir,
primary_slot_name debe estar configurado
en el standby), y hot_standby_feedback
debe estar habilitado en el standby. También es necesario especificar un dbname válido
en el primary_conninfo.
Se recomienda encarecidamente que dicha ranura de replicación física se nombre en la lista
synchronized_standby_slots
en el primario, para evitar que el suscriptor consuma cambios más rápido que el hot standby. Incluso
cuando está configurado correctamente, se espera cierta latencia al enviar cambios a los suscriptores
lógicos debido a la espera en las ranuras nombradas en
synchronized_standby_slots.
Cuando se utiliza synchronized_standby_slots, el servidor primario no se apagará
por completo hasta que los standbys correspondientes, asociados con las ranuras de replicación física
especificadas en synchronized_standby_slots, hayan confirmado la recepción del WAL
hasta la última posición purgada en el servidor primario.
Si bien habilitar
sync_replication_slots permite la sincronización periódica automática
de las ranuras de failover, también se pueden sincronizar manualmente utilizando la función
pg_sync_replication_slots
en el standby. Sin embargo, esta función está destinada principalmente para pruebas y depuración
y debe usarse con precaución. A diferencia de la sincronización automática, no incluye reintentos
cíclicos, lo que la hace más propensa a fallos de sincronización, particularmente durante los escenarios
de sincronización inicial donde los archivos WAL requeridos o las filas de catálogo para la ranura ya
podrían haber sido eliminados o corren el riesgo de ser eliminados en el standby. En contraste, la
sincronización automática a través de sync_replication_slots proporciona actualizaciones
continuas de las ranuras, lo que permite un failover sin problemas y admite alta disponibilidad.
Por lo tanto, es el método recomendado para sincronizar ranuras.
Cuando la sincronización de ranuras está configurada como se recomienda, y la sincronización inicial
se realiza automáticamente o manualmente a través de pg_sync_replication_slots,
el standby puede persistir la ranura sincronizada solo si se cumple la siguiente condición:
La ranura de replicación lógica en el primario debe retener los WALs y las filas del catálogo del sistema
que aún están disponibles en el standby. Esto garantiza la integridad de los datos y permite que la
replicación lógica continúe sin problemas después de la promoción.
Si los WALs o las filas del catálogo requeridos ya se han purgado del standby, la ranura no se persistirá
para evitar la pérdida de datos. En tales casos, puede aparecer el siguiente mensaje de registro:
LOG: could not synchronize replication slot "failover_slot" DETAIL: Synchronization could lead to data loss, because the remote slot needs WAL at LSN 0/3003F28 and catalog xmin 754, but the standby has LSN 0/3003F28 and catalog xmin 756.
Si la ranura de replicación lógica es utilizada activamente por un consumidor, no se necesita intervención
manual; la ranura avanzará automáticamente y la sincronización se reanudará en el próximo ciclo. Sin embargo,
si no hay ningún consumidor configurado, es aconsejable avanzar manualmente la ranura en el primario
utilizando pg_logical_slot_get_changes o
pg_logical_slot_get_binary_changes,
permitiendo que proceda la sincronización.
La capacidad de reanudar la replicación lógica después del failover depende del valor de
pg_replication_slots.synced
para las ranuras sincronizadas en el standby en el momento del failover. Solo las ranuras persistentes
que hayan alcanzado el estado de sincronización como verdadero en el standby antes del failover se
pueden utilizar para la replicación lógica después del failover. Las ranuras sincronizadas temporales no
se pueden utilizar para la decodificación lógica, por lo tanto, la replicación lógica para esas ranuras
no se puede reanudar. Por ejemplo, si la ranura sincronizada no pudo volverse persistente en el standby
debido a una suscripción deshabilitada, entonces la suscripción no se puede reanudar después del failover
incluso cuando esté habilitada.
Para reanudar la replicación lógica después del failover desde las ranuras lógicas sincronizadas, el
'conninfo' de la suscripción debe modificarse para apuntar al nuevo servidor primario. Esto se hace
utilizando ALTER SUBSCRIPTION ... CONNECTION.
Se recomienda deshabilitar primero las suscripciones antes de promover el standby y volver a habilitarlas
después de modificar la cadena de conexión.
Existe la posibilidad de que el antiguo primario vuelva a estar activo durante la promoción y, si las suscripciones no están deshabilitadas, los suscriptores lógicos pueden continuar recibiendo datos del antiguo servidor primario incluso después de la promoción hasta que se modifique la cadena de conexión. Esto podría dar lugar a problemas de inconsistencia de datos, evitando que los suscriptores lógicos puedan continuar la replicación desde el nuevo servidor primario.
Los plugins de salida transforman los datos de la representación interna del registro de escritura anticipada al formato que desea el consumidor de una ranura de replicación.
Cuando se crea una nueva ranura de replicación utilizando la interfaz de replicación en flujo
(consulta la CREATE_REPLICATION_SLOT), se exporta una
instantánea (snapshot) (consulta la Section 9.28.5), que mostrará
exactamente el estado de la base de datos a partir del cual se incluirán todos los cambios en el flujo
de cambios. Esto se puede utilizar para crear una nueva réplica utilizando
SET TRANSACTION SNAPSHOT para leer el estado
de la base de datos en el momento en que se creó la ranura. Esta transacción se puede utilizar entonces para
volcar el estado de la base de datos en ese momento, el cual posteriormente se puede actualizar utilizando
el contenido de la ranura sin perder ningún cambio.
Las aplicaciones que no requieran la exportación de instantáneas pueden suprimirla con la opción
SNAPSHOT 'nothing'.