Lock:advisory - Amazon Aurora

Lock:advisory

O evento Lock:advisory ocorre quando uma aplicação PostgreSQL utiliza um bloqueio para coordenar as atividades em várias sessões.

Versões de mecanismos relevantes

As informações sobre eventos de espera são relevantes para o Aurora PostgreSQL versão 9.6 e versões superiores.

Contexto

Bloqueios consultivos do PostgreSQL são bloqueios cooperativos em nível de aplicação, feitos explicitamente e desfeitos pelo código da aplicação do usuário. Uma aplicação pode utilizar bloqueios consultivos do PostgreSQL para coordenar atividades em várias sessões. Ao contrário de bloqueios regulares, ou em nível de objeto ou linha, a aplicação tem controle total ao longo da vida útil do bloqueio. Para obter mais informações, consulte o tópico sobre Bloqueios consultivos na documentação do PostgreSQL.

Bloqueios consultivos podem ser liberados antes que uma transação termine ou podem ser mantidos por uma sessão em todas as transações. Isso não é válido para bloqueios implícitos aplicados pelo sistema, como um bloqueio exclusivo de acesso em uma tabela adquirida por uma instrução CREATE INDEX.

Para obter uma descrição das funções utilizadas para adquirir (bloquear) e liberar (desbloquear) bloqueios consultivos, consulte o tópico sobre Funções de bloqueios consultivos, na documentação do PostgreSQL.

Bloqueios consultivos são implementados sobre o sistema de bloqueio regular do PostgreSQL e ficam visíveis na visualização do sistema pg_locks.

Causas

Esse tipo de bloqueio é controlado exclusivamente por uma aplicação que o utiliza explicitamente. Bloqueios consultivos que são adquiridos para cada linha como parte de uma consulta podem causar um aumento nos bloqueios ou um acúmulo a longo prazo.

Esses efeitos acontecem quando a consulta é executada de uma maneira que adquire bloqueios em mais linhas do que as retornadas pela consulta. A aplicação deve eventualmente liberar todos os bloqueios, mas, se eles forem adquiridos em linhas que não são retornadas, a aplicação não poderá localizar todos os bloqueios.

O exemplo a seguir foi extraído do tópico Bloqueios consultivos na documentação do PostgreSQL.

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

Nesse exemplo, a cláusula LIMIT apenas pode interromper a saída da consulta depois que as linhas já foram selecionadas internamente e seus valores de ID estão bloqueados. Isso pode acontecer repentinamente quando um volume de dados crescente faz com que o planejador escolha um plano de execução diferente que não foi testado durante o desenvolvimento. O acúmulo nesse caso acontece porque a aplicação chama explicitamente pg_advisory_unlock para cada valor de ID bloqueado. No entanto, nesse caso, não é possível encontrar o conjunto de bloqueios adquiridos em linhas que não foram retornadas. Como os bloqueios são adquiridos em nível de sessão, eles não são liberados automaticamente no final da transação.

Outra possível causa para picos em tentativas de bloqueio bloqueadas são conflitos não intencionais. Nesses conflitos, partes não relacionadas da aplicação compartilham o mesmo espaço de ID de bloqueio por engano.

Ações

Revise o uso da aplicação de bloqueios consultivos e detalhe onde e quando no fluxo de aplicação cada tipo de bloqueio consultivo é adquirido e liberado.

Determine se uma sessão está adquirindo muitos bloqueios ou se uma sessão de longa execução não está liberando bloqueios cedo o suficiente, resultando em um acúmulo lento de bloqueios. Você pode corrigir um acúmulo lento de bloqueios em nível de sessão encerrando a sessão com pg_terminate_backend(pid).

Um cliente que aguarda um bloqueio de consultoria aparece em pg_stat_activity com wait_event_type=Lock e wait_event=advisory. É possível obter valores de bloqueio específicos consultando a visualização do sistema pg_locks em busca do mesmo pid, procurando locktype=advisory e granted=f.

Em seguida, identifique a sessão de bloqueio consultando pg_locks em busca do mesmo bloqueio consultivo que possui granted=t, conforme mostrado no exemplo a seguir.

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 as funções de API de bloqueio consultivo têm dois conjuntos de argumentos, um argumento bigint ou dois argumentos integer:

  • Para as funções de API com um único argumento bigint, os 32 bits superiores estão em pg_locks.classid e os 32 bits inferiores estão em pg_locks.objid.

  • Para as funções da API com dois argumentos integer, o primeiro argumento é pg_locks.classid e o segundo é pg_locks.objid.

O valor pg_locks.objsubid indica qual formato de API foi utilizado: 1 significa um argumento bigint; 2significa dois argumentos integer.