Como Funciona o Comando SET LOCK_TIMEOUT no SQL Server
O comando SET LOCK_TIMEOUT define por quantos milissegundos uma conexão vai esperar pela liberação de um registro que está bloqueado (em lock) por outra conexão, antes de desistir e retornar um erro. É um comando útil para evitar que uma sessão fique presa indefinidamente esperando um recurso ser liberado.
Testando na prática
Para ver o comportamento do LOCK_TIMEOUT, abra duas conexões separadas no SQL Server Management Studio (SSMS).
Na primeira conexão, execute:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE Customers SET Region = 'AB';
Repare que essa transação não tem COMMIT nem ROLLBACK, então ela permanece aberta, mantendo um lock sobre a tabela Customers.
Na segunda conexão, execute:
SET LOCK_TIMEOUT 2000;
SELECT * FROM Customers;
O SELECT da segunda conexão vai tentar acessar a tabela Customers, que está bloqueada pela primeira conexão, e vai esperar no máximo 2000 milissegundos (2 segundos) por essa liberação. Se o lock não for liberado dentro desse tempo (ou seja, se você não der COMMIT ou ROLLBACK na primeira conexão), o SQL Server retorna o erro:
Msg 1222, Level 16, State 51, Line 1
Lock request time out period exceeded.
Para liberar o lock e permitir que o SELECT complete normalmente, basta rodar COMMIT ou ROLLBACK na primeira conexão, dentro do tempo configurado.
Valores possíveis para LOCK_TIMEOUT
- Valor positivo (em milissegundos): tempo máximo de espera pelo lock.
-
0: a conexão não espera nada; se o recurso estiver bloqueado, o erro é retornado imediatamente. -
-1: espera indefinidamente até o lock ser liberado. Esse é o comportamento padrão do SQL Server quandoLOCK_TIMEOUTnão é configurado.
Para voltar ao comportamento padrão (esperar indefinidamente) depois de ter configurado um valor:
SET LOCK_TIMEOUT -1;
Verificando o valor atual configurado na sessão
É possível consultar o valor de LOCK_TIMEOUT configurado na sessão atual com:
SELECT @@LOCK_TIMEOUT;
Um detalhe importante: escopo do comando
SET LOCK_TIMEOUT vale apenas para a sessão atual (a conexão em que o comando foi executado), não afeta outras conexões nem se torna uma configuração global do servidor. Isso significa que, em uma aplicação, é comum configurar esse valor logo no início de cada conexão, se o objetivo for aplicar esse comportamento de forma consistente.
Tratando o erro de timeout na aplicação
Ao usar LOCK_TIMEOUT, é uma boa prática capturar o erro 1222 com TRY/CATCH, em vez de deixar a exceção estourar sem tratamento:
SET LOCK_TIMEOUT 2000;
BEGIN TRY
SELECT * FROM Customers;
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 1222
PRINT 'Não foi possível obter o lock a tempo. Tente novamente mais tarde.';
ELSE
THROW;
END CATCH;
Quando considerar alternativas ao LOCK_TIMEOUT
LOCK_TIMEOUT evita que uma sessão fique travada indefinidamente esperando um lock, mas não resolve a causa do bloqueio em si. Se locks e timeouts estiverem acontecendo com frequência em produção, vale investigar:
-
Nível de isolamento
READ_COMMITTED_SNAPSHOT: reduz bastante os bloqueios entre leituras e escritas, já que leituras passam a enxergar uma versão consistente dos dados sem esperar locks de escrita serem liberados. - Transações mais curtas: quanto mais tempo uma transação fica aberta, mais tempo ela mantém locks, aumentando a chance de outras conexões esperarem ou estourarem o timeout.
- Retry na aplicação: para operações que podem eventualmente esbarrar em um lock (picos de concorrência, por exemplo), implementar uma lógica de nova tentativa na aplicação costuma ser mais robusto do que só aumentar o valor do timeout.
Conclusão
SET LOCK_TIMEOUT é uma ferramenta simples para evitar que uma sessão fique presa esperando um lock indefinidamente, útil especialmente em aplicações que não podem ficar travadas esperando outra transação terminar. Para casos de bloqueio recorrente, vale olhar além do timeout e investigar a causa dos locks, considerando isolamento com versionamento de linha e transações mais curtas.