Login Timeout e Query Timeout no SQL Server

Um cenário que ocorreu mais de uma vez em ambientes que eu trabalho/trabalhei foi que aplicações começaram a apresentar Timeout durante conexão com o banco de dados. Nesse cenário os desenvolvedores ou responsáveis pelas aplicações chegaram a afirmar que do lado da aplicação o Timeout teria sido configurado e que o problema é que o mesmo não havia sido configurado do lado do SQL Server.

Podemos verificar que realmente existem configurações de Timeout do lado do SQL Server. Execute o comando abaixo e verifique que existem as opções PH timeout (s), remote query timeout (s) e remote login timeout (s).

exec sp_configure

PH timeout (s) está relacionado ao tempo máximo do processo de estabelecimento de conexão entre o serviço do Full-Text e o SQL Server. Veja mais aqui.

Remote login timeout (s) é o tempo máximo de espera no estabelecimento da conexão do SQL Server com uma aplicação de destino. Em outras palavras, o tempo máximo que o SQL Server deve aguardar um OK ao estabelecer uma conexão a partir do SQL Server para outra aplicação (outro SGBDR, por exemplo). Veja mais aqui.

Remote query timeout (s) é o tempo máximo de espera da resposta da execução de uma query do SQL Server na aplicação de destino. Em outras palavras o tempo máximo que o SQL Server deve esperar pela resposta da query (result set) da outra aplicação (outro SGBDR, por exemplo). Veja mais aqui.

Vamos fazer um teste rápido para provar tais afirmações. Estarei criando um linked server para outro servidor SQL Server e executando algumas coisas.

USE [master]
GO
EXEC master.dbo.sp_addlinkedserver 
@server = N'my_linked_server', 
@srvproduct=N'', 
@provider=N'SQLNCLI10', 
@datasrc=N'myServer\myInstance'
GO
EXEC master.dbo.sp_addlinkedsrvlogin 
@rmtsrvname = N'my_linked_server', 
@locallogin = N'myUser', 
@useself = N'False', 
@rmtuser = N'my_liked_server_user', 
@rmtpassword = N'liked_server_user_password'
GO

No meu segundo SQL Server, que é o linked server, crio uma base e uma tabela e populo essa tabela com duas linhas

create database testTimeoutDB
GO
USE testTimeoutDB
GO
create table testTimeoutLikedServer
(
id int
)
GO
insert into testTimeoutLinkedServer values (1),(2)
GO

Agora vamos reconfigurar o valor para o remote query timeout (s).

sp_configure 'remote query timeout (s)', 10
reconfigure

Agora no nosso segundo servidor (linked server) vamos executar um update na tabela dentro de uma transação para criamos um lock exclusive e mantermos esse lock por um tempo.

begin tran
update testTimeoutDB.dbo.testTimeoutLinkedServer set id = 3

No primeiro servidor também executaremos um update contra essa tabela.

update my_linked_server.testTimeout.dbo.testTimeoutLinkedServer set id = 5

Verifique o erro e o tempo da execução.

Agora vamos aumentar um pouco o valor dessa configuração de tempo e executar novamente o update acima e verificar o tempo de duração da execução.

sp_configure 'remote query timeout (s)', 60
reconfigure

Agora vamos testar o Remote login timeout (s). Reconfiguramos o valor da opção no primeiro servidor, paramos o serviço do SQL Server no segundo servidor e com um simples select testamos o login timout.

sp_configure 'remote login timeout (s)', 10
reconfigure
GO
select * from my_linked_server.master.sys.databases

Confira o tempo de execução, reconfigure o valor do login timeout para um tempo maior, rode a query mais uma vez e confira o tempo. Acho que isso confirma o que foi dito.

Agora, se minha aplicação está com o parâmetro configurado de Timeout e a configuração desse parâmetro no SQL Server não influencia a minha aplicação, por que minha aplicação continua informando o erro abaixo?

Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
The statement has been terminated.

Talvez você não configurou o parâmetro correto. No código C# abaixo eu demonstro a configuração de dois parâmetros de Timeout. Lembrando que não estou me preocupando com qualidade de código. 😛

Utilizando o mesmo banco e tabela criada no segundo ambiente de SQL Server e fazendo uso do mesmo update para criação de lock na tabela, execute o código abaixo fazendo as devidas modificações.

    using System;
    using System.Text;
    using System.Data.SqlClient;

    class Program
    {
        static void Main(string[] args)
        {
            // Teste Connection Timeout Default value
            try
            {
                using (SqlConnection con = new SqlConnection(@"Data Source=myServer\incorrectInstance;Initial Catalog=testTimeoutDB;User ID=myUser;Password=myPassword;Application Name=testeTimeout;"))
                {
                    using (SqlCommand com = new SqlCommand())
                    {
                        com.Connection = con;
                        Console.WriteLine("-= Teste Connection Timeout Default value =-");
                        Console.WriteLine("Inicio da conexão: " + DateTime.Now.ToString() + " \n");
                        con.Open();

                        com.CommandText = "select @@version";
                        com.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message + " \n");
            }
            finally
            {
                Console.WriteLine("Fim da execuçaõ: " + DateTime.Now.ToString() + " \n");
            }

            // Teste Connection Timeout custom value
            try
            {
                using (SqlConnection con = new SqlConnection(@"Data Source=myServer\incorrectInstance;Initial Catalog=testTimeoutDB;User ID=myUser;Password=myPassword;Application Name=testeTimeout;Timeout=60;"))
                {
                    using (SqlCommand com = new SqlCommand())
                    {
                        com.Connection = con;
                        Console.WriteLine("-= Teste Connection Timeout custom value (60 seconds) =-");
                        Console.WriteLine("Inicio da conexão: " + DateTime.Now.ToString() + " \n");
                        con.Open();

                        com.CommandText = "select @@version";
                        com.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message + " \n");
            }
            finally
            {
                Console.WriteLine("Fim da execuçaõ: " + DateTime.Now.ToString() + " \n");
            }

            // Teste Timeout Command Default value
            try
            {
                using (SqlConnection con = new SqlConnection(@"Data Source=myServer\instance1;Initial Catalog=testTimeoutDB;User ID=myUser;Password=myPassword;Application Name=testeTimeout;"))
                {
                    using (SqlCommand com = new SqlCommand())
                    {
                        com.Connection = con;
                        con.Open();
                        using (SqlTransaction tran = con.BeginTransaction())
                        {
                            com.Transaction = tran;
                            com.CommandText = "update testTimeoutLikedServer set id = 6";
                            Console.WriteLine("-= Teste Command Timeout Default value =-");
                            Console.WriteLine("Inicio da execução: " + DateTime.Now.ToString() + " \n");
                            com.ExecuteNonQuery();
                            tran.Commit();
                        }
                    }
                }
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message + " \n");
            }
            finally
            {
                Console.WriteLine("Fim da execuçaõ: " + DateTime.Now.ToString() + " \n");
            }

            // Teste Timeout Command custom value
            try
            {
                using (SqlConnection con = new SqlConnection(@"Data Source=myServer\instance1;Initial Catalog=testTimeoutDB;User ID=myUser;Password=myPassword;Application Name=testeTimeout;"))
                {
                    using (SqlCommand com = new SqlCommand())
                    {
                        com.Connection = con;
                        com.CommandTimeout = 60;
                        con.Open();
                        using (SqlTransaction tran = con.BeginTransaction())
                        {
                            com.Transaction = tran;
                            Console.WriteLine("-= Teste Command Timeout custom value (60 seconds) =-");
                            com.CommandText = "update testTimeoutLikedServer set id = 6";
                            Console.WriteLine("Inicio da execução: " + DateTime.Now.ToString() + " \n");
                            com.ExecuteNonQuery();
                            tran.Commit();
                        }
                    }
                }
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message + " \n");
            }
            finally
            {
                Console.WriteLine("Fim da execuçaõ: " + DateTime.Now.ToString() + " \n");
            }
            Console.ReadKey();
        }
    }

Resultado:
Query / Connection Timeout

Maiores informações sobre os parâmetros.

Sqlcommand.commandtimeout
Sqlconnection.connectiontimeout

Visto tal exemplo, o correto não seria apenas aumentar o valor dos parâmetros e sim identificar o problema e resolve-lo. O primeiro cenário que indicaria a verificar é: Longas transações que geram lock nas tabelas que sua aplicação está usando.

edit: Adicionado imagem.

Esse post foi publicado em .NET, SQLServer e marcado , , , , , . Guardar link permanente.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s