

# Lock:tuple
<a name="wait-event.locktuple"></a>

O evento `Lock:tuple` ocorre quando um processo de backend está aguardando para adquirir um bloqueio em uma tupla.

**Topics**
+ [Versões compatíveis do mecanismo](#wait-event.locktuple.context.supported)
+ [Contexto](#wait-event.locktuple.context)
+ [Possíveis causas do maior número de esperas](#wait-event.locktuple.causes)
+ [Ações](#wait-event.locktuple.actions)

## Versões compatíveis do mecanismo
<a name="wait-event.locktuple.context.supported"></a>

Essas informações de eventos de espera são compatíveis com todas as versões do RDS para PostgreSQL.

## Contexto
<a name="wait-event.locktuple.context"></a>

O evento `Lock:tuple` indica que um backend está aguardando para adquirir um bloqueio em uma tupla enquanto outro backend mantém um bloqueio conflitante na mesma tupla. A tabela a seguir ilustra um cenário em que as sessões geram o evento `Lock:tuple`.


|  Tempo  |  Sessão 1  |  Sessão 2  |  Sessão 3  | 
| --- | --- | --- | --- | 
|  t1  |  Inicia uma transação.  |    |    | 
|  t2  |  Atualiza a linha 1.  |    |    | 
|  t3  |    |  Atualiza a linha 1. A sessão adquire um bloqueio exclusivo na tupla e aguarda a sessão 1 liberar esse bloqueio via confirmação ou reversão.  |    | 
|  t4  |    |    |  Atualiza a linha 1. A sessão aguarda a sessão 2 liberar o bloqueio exclusivo na tupla.  | 

Outra alternativa é simular esse evento de espera utilizando a ferramenta de benchmarking `pgbench`. Configure um número elevado de sessões simultâneas para atualizar a mesma linha em uma tabela com um arquivo SQL personalizado.

Para saber mais sobre modos de bloqueio conflitantes, consulte o tópico sobre [Bloqueio explícito](https://www.postgresql.org/docs/current/explicit-locking.html), na documentação do PostgreSQL. Para saber mais sobre `pgbench`, consulte [pgbench](https://www.postgresql.org/docs/current/pgbench.html) na documentação do PostgreSQL.

## Possíveis causas do maior número de esperas
<a name="wait-event.locktuple.causes"></a>

Quando esse evento aparece mais que o normal, possivelmente indicando um problema de performance, as causas típicas incluem:
+ Um número elevado de sessões simultâneas está tentando adquirir um bloqueio conflitante para a mesma tupla executando instruções `UPDATE` ou `DELETE`.
+ Sessões altamente simultâneas estão executando uma instrução `SELECT` utilizando os modos de bloqueio `FOR UPDATE` ou `FOR NO KEY UPDATE`.
+ Diversos fatores fazem com que grupos de aplicações ou conexões abram mais sessões para executar as mesmas operações. À medida que novas sessões estão tentando modificar as mesmas linhas, a carga de banco de dados pode atingir picos e `Lock:tuple` pode surgir.

Para obter mais informações, consulte o tópico sobre [Bloqueios em nível de linha](https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS), na documentação do PostgreSQL.

## Ações
<a name="wait-event.locktuple.actions"></a>

Recomenda-se ações distintas, dependendo dos motivos do evento de espera.

**Topics**
+ [Investigar a lógica da sua aplicação](#wait-event.locktuple.actions.problem)
+ [Localizar a sessão bloqueadora](#wait-event.locktuple.actions.find-blocker)
+ [Reduzir a simultaneidade quando ela estiver elevada](#wait-event.locktuple.actions.concurrency)
+ [Solucionar problemas de gargalos](#wait-event.locktuple.actions.bottlenecks)

### Investigar a lógica da sua aplicação
<a name="wait-event.locktuple.actions.problem"></a>

Descubra se uma sessão bloqueadora está no estado `idle in transaction` por muito tempo. Em caso positivo, considere encerrar a sessão bloqueadora como uma solução de curto prazo. Também é possível utilizar a função `pg_terminate_backend`. Para obter mais informações sobre essa função, consulte [Funções de sinalização de servidor](https://www.postgresql.org/docs/13/functions-admin.html#FUNCTIONS-ADMIN-SIGNAL), na documentação do PostgreSQL.

Para uma solução de longo prazo, faça o seguinte:
+ Ajuste a lógica da aplicação.
+ Use o parâmetro `idle_in_transaction_session_timeout`. Esse parâmetro encerra qualquer sessão com uma transação aberta que tenha ficado ociosa por um tempo maior que o especificado. Para obter mais informações, consulte o tópico sobre [Padrões de conexão de clientes](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT), na documentação do PostgreSQL.
+ Use a confirmação automática o máximo possível. Para obter mais informações, consulte [SET AUTOCOMMIT](https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html), na documentação do PostgreSQL.

### Localizar a sessão bloqueadora
<a name="wait-event.locktuple.actions.find-blocker"></a>

Enquanto o evento de espera `Lock:tuple` está ocorrendo, identifique a sessão bloqueadora e a sessão bloqueada descobrindo quais bloqueios dependem um do outro. Para obter mais informações, consulte o tópico sobre [Informações de dependências de bloqueios](https://wiki.postgresql.org/wiki/Lock_dependency_information), na wiki do PostgreSQL. 

O exemplo a seguir consulta todas as sessões, filtrando em `tuple` e ordenando por `wait_time`.

```
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;
```

### Reduzir a simultaneidade quando ela estiver elevada
<a name="wait-event.locktuple.actions.concurrency"></a>

O evento `Lock:tuple` pode ocorrer constantemente, especialmente em um tempo de workload ocupado. Nessa situação, considere reduzir a simultaneidade elevada para linhas muito ocupadas. Várias vezes, apenas algumas linhas controlam uma fila ou a lógica booleana, o que torna essas linhas muito ocupadas.

Você pode reduzir a simultaneidade utilizando diferentes abordagens com base no seu requisito de negócios, na lógica da aplicação e no tipo de workload. Por exemplo, você pode fazer o seguinte:
+ Reformule sua tabela e a lógica de dados para reduzir a simultaneidade elevada.
+ Altere a lógica da aplicação para reduzir a simultaneidade elevada no nível da linha.
+ Aproveite e reformule consultas com bloqueios em nível de linha.
+ Use a cláusula `NOWAIT` com operações de novas tentativas.
+ Considere utilizar o controle de simultaneidade lógico otimista e bloqueio híbrido.
+ Considere modificar o nível de isolamento do banco de dados.

### Solucionar problemas de gargalos
<a name="wait-event.locktuple.actions.bottlenecks"></a>

O `Lock:tuple` pode ocorrer com gargalos, como falta de CPU ou uso máximo da largura de banda do Amazon EBS. Para diminuir gargalos, considere as seguintes abordagens:
+ Aumente a escala do seu tipo de classe de instância na vertical.
+ Otimize consultas que consomem muitos recursos.
+ Altere a lógica da aplicação.
+ Arquive dados acessados raramente.