Lock:advisory - Amazon Relational Database Service

Lock:advisory

El evento Lock:advisory ocurre cuando una aplicación PostgreSQL utiliza un bloqueo para coordinar la actividad entre varias sesiones.

Versiones del motor relevantes

La información de eventos de espera es relevante para RDS para PostgreSQL versión 9.6 y posteriores.

Contexto

Los bloqueos consultivos de PostgreSQL son bloqueos cooperativos a nivel de aplicación, bloqueados y desbloqueados explícitamente por el código de la aplicación del usuario. Una aplicación puede usar los bloqueos consultivos de PostgreSQL para coordinar la actividad a través de varias sesiones. A diferencia de los bloqueos regulares a nivel de objeto o de fila, la aplicación tiene control total sobre el tiempo de vida del bloqueo. Para más información, consulte Advisory Locks en la documentación de PostgreSQL.

Los bloqueos consultivos se pueden liberar antes de que finalice una transacción o mantenerse en una sesión a través de las transacciones. Esto no es verdadero para los bloqueos implícitos, forzados por el sistema, como un bloqueo de acceso exclusivo a una tabla adquirido por una instrucción CREATE INDEX.

Para una descripción de las funciones que se utilizan para adquirir (bloquear) y liberar (desbloquear) los bloqueos de asesoramiento, consulte Advisory Lock Functions en la documentación de PostgreSQL.

Los bloqueos consultivos se implementan sobre el sistema de bloqueo regular de PostgreSQL y son visibles en la vista del sistema pg_locks.

Causas

Este tipo de bloqueo es controlado exclusivamente por una aplicación que lo utiliza de forma explícita. Los bloqueos consultivos que se adquieren para cada fila como parte de una consulta pueden causar un pico de bloqueos o una acumulación a largo plazo.

Estos efectos se producen cuando la consulta se ejecuta de forma que adquiere bloqueos en más filas de las que devuelve la consulta. La aplicación debe liberar finalmente todos los bloqueos, pero si se adquieren bloqueos en filas que no se devuelven, la aplicación no puede encontrar todos los bloqueos.

El siguiente ejemplo proviene de Advisory Locks en la documentación de PostgreSQL.

SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100;

En este ejemplo, la cláusula LIMIT solo puede detener la salida de la consulta después de que las filas se hayan seleccionado internamente y sus valores de ID se hayan bloqueado. Esto puede ocurrir de forma repentina cuando un volumen de datos creciente hace que el planificador elija un plan de ejecución diferente que no fue probado durante el desarrollo. La acumulación en este caso ocurre porque la aplicación llama explícitamente a pg_advisory_unlock para cada valor de ID que fue bloqueado. Sin embargo, en este caso no se puede encontrar el conjunto de bloqueos adquiridos en las filas que no fueron devueltas. Como los bloqueos se adquieren a nivel de sesión, no se liberan automáticamente al final de la transacción.

Otra posible causa de los picos de intentos de bloqueo son los conflictos involuntarios. En estos conflictos, partes no relacionadas de la aplicación comparten el mismo espacio de ID de bloqueo por error.

Acciones

Revisar el uso de la aplicación de los bloqueos consultivos y detallar dónde y cuándo en el flujo de la aplicación se adquiere y libera cada tipo de bloqueo consultivo.

Determine si una sesión adquiere demasiados bloqueos o si una sesión de larga duración no libera los bloqueos con suficiente antelación, lo que provoca una acumulación lenta de bloqueos. Puede corregir una acumulación lenta de bloqueos de sesión si finaliza la sesión con pg_terminate_backend(pid).

Un cliente en espera de un bloqueo consultivo aparece en pg_stat_activity con wait_event_type=Lock y wait_event=advisory. Puede obtener valores de bloqueo específicos al consultar la vista del sistema pg_locks para el mismo pid, buscando locktype=advisory y granted=f.

A continuación, puede identificar la sesión de bloqueo al consultar pg_locks para el mismo bloqueo consultivo con granted=t, como se muestra en el siguiente ejemplo.

SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid, blocked_activity.usename AS blocked_user, blocking_activity.usename AS blocking_user, now() - blocked_activity.xact_start AS blocked_transaction_duration, now() - blocking_activity.xact_start AS blocking_transaction_duration, concat(blocked_activity.wait_event_type,':',blocked_activity.wait_event) AS blocked_wait_event, concat(blocking_activity.wait_event_type,':',blocking_activity.wait_event) AS blocking_wait_event, blocked_activity.state AS blocked_state, blocking_activity.state AS blocking_state, blocked_locks.locktype AS blocked_locktype, blocking_locks.locktype AS blocking_locktype, blocked_activity.query AS blocked_statement, blocking_activity.query AS blocking_statement FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid != blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT blocked_locks.GRANTED;

Todas las funciones de la API de bloqueo consultivo tienen dos conjuntos de argumentos, un argumento bigint o dos argumentos integer:

  • Para las funciones API con un argumento bigint, los 32 bits superiores están en pg_locks.classid y los 32 bits inferiores están en pg_locks.objid.

  • Para las funciones de la API con dos argumentos integer, el primer argumento es pg_locks.classid y el segundo argumento es pg_locks.objid.

El valor de pg_locks.objsubid indica qué forma de la API se utilizó: 1 significa un argumento bigint; 2 significa dos argumentos integer.