Retry Logic para erros transientes no Windows Azure SQL Database (SQL Azure) - Parte 3
Parte 3 Adicionando o Retry Logic
Autor: Marcelo Franceschi de Bianchi - CTS LATAM
Revisor: Roberto Cavalcanti - CTS LATAM
Dando continuidade à série de três artigos relacionados ao Retry Logic, esse é o de número 3 no qual você aprenderá como Adicionar o código Retry Logic na sua aplicação que fará a conexão com o banco de dados Windows Azure SQL Database (SQL Azure).
1. Adicionado o Retry Logic
Caso tenha pulado o artigo dois você poderá realizar o download dos arquivos tutorial files que são referentes à solução do Visual Studio com o nome de RetryLogicTutorial_Queries.sln.
1.1 Referência a CAT retry library:
A primeira coisa a ser feita será realizar a referência a bibliteca do Retry Logic CAT retry logic library:
- Faça o download da biblioteca https://appfabriccat.com/2011/02/transient-fault-handling-framework/
- Faça um build da biblioteca
- Na aplicação vá até as propriedades do projeto e ajuste Target Framework para que use o .Net Framework 4
- Adicione uma referência para Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.dll
O próximo passo será os using statements para o Form.cs
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling;
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.SqlAzure;
Adicione uma nova classe com o nome de MyRetryStrategy que implementa a interface ITransientErrorDetectionStrategy. Essa interface tem um metodo único, IsTransient, no qual irá capturar um objeto Exception e retornará true se a excessão representar um erro transiente.
using System;
using System.Data.SqlClient;
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling;
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.SqlAzure;
namespace RetryLogicTutorial
{
class MyRetryStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
if (ex != null && ex is SqlException)
{
foreach (SqlError error in (ex as SqlException).Errors)
{
switch (error.Number)
{
case 1205:
System.Diagnostics.Debug.WriteLine("SQL Error: Deadlock condition. Retrying...");
return true;
case -2:
System.Diagnostics.Debug.WriteLine("SQL Error: Timeout expired. Retrying...");
return true;
}
}
}
// For all others, do not retry.
return false;
}
}
}
A implementação mostra um padrão tipico, primeiro o filtro para excessões do tipo SqlException. Veja o numero do erro em SqlException.Errors collection. Então retorne true para qualquer error que poderia disparar uma tentativa de retry e retorne false para todos os outros tipos de erros.
void AdoQueryWorker_DoWork(object sender, DoWorkEventArgs e)
{
RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, new TimeSpan(0, 0, 5));
try
{
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.OpenWithRetry(retry);
SqlCommand command = new SqlCommand("select product_name from products");
command.Connection = connection;
command.CommandTimeout = CommandTimeout;
SqlDataReader reader = command..ExecuteReaderWithRetry(retry);
while (reader.Read())
{
(sender as BackgroundWorker).ReportProgress(0, reader["product_name"]);
}
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, "SqlException");
}
}
A classe RetryPolicy<T> implementa o retry logic. O parametro T deverá ser um type que é implementado por ItransientErrorDetectionStrategy. No construtor RetryPolicy você deverá colocar o numero maximo de tentativas de execução e opcionalmente, você poderá também colocar o intervalo entre essas tentativas ou então utilizar um o exponential backoff.
O método OpenWithRetry é um método extendido, está defindo na biblioteca CAT, que adiciona o retry logic ao padrão ADO.NET ao método SqlConnection.Open. Se a excessão acontece, enquanto estiver abrindo a conexão, o objeto RetryPolicy aguarda por um intervalo e então faz a tentativa de execução, até que atinja o número máximo de tentativas que foi configurado.
Similarmente, ExecuteReaderWithRetry adiciona o retry logic para o método SqlCommand.ExecuteReader. Uma extensão dos métodos também é realizada por ExecuteNonQuery, ExecuteScalar e ExecuteXmlReader.
1.2 Adicionando Retry Logic do LINQ para SQL
A chamada da ADO.NET é realizada de forma bem direta. Mas em algumas aplicação que usam o framework tal como WCF ou então LINQ to SQL, no qual realizam chamadas abstratas ao banco de dados. Para esse tipo específico de cenário, a biblioteca CAT retry providência uma forma de empacotar um bloco de código dentro de um escopo que poderá ser feito a reexecução desse trecho de código. Para ver como isso funciona, modifique a função LinqQueryWorker_DoWork como segue:
void LinqQueryWorker_DoWork(object sender, DoWorkEventArgs e)
{
RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5));
try
{
e.Result = retry.ExecuteAction(() =>
{
Deadlock(); // Artificially create a deadlock condition
CustomerOrdersDataContext ctx = new CustomerOrdersDataContext();
ctx.Connection.ConnectionString = builder.ConnectionString;
ctx.CommandTimeout = 3;
var results = from c in ctx.customers
from o in c.orders
from i in o.order_items
select new { c.lname, c.fname, i.product.product_name, i.quantity };
return results.ToList();
});
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, "SqlException");
}
}
O método RetryPolicy.ExecuteAction captura a expressão lambda. O código da expressão lambda é executado pelo menos uma vez. Se um erro transiente acontecer, o objeto RetryPolicy tentará realizar a execução do bloco de código novamente.
1.3 Utilize MyRetryStrategy
A implementação de MyRetryStrategy que foi mostrada nesse artigo é unicamente para demonstrar a retry API. A seguir temos uma lista dos principais erros transientes que você poderá utilizar na sua implmentação do código de Retry Logic:
Número do Erro |
Descrição do Erro |
20 |
The instance of SQL Server does not support encryption. |
64 |
An error occurred during login. |
233 |
Connection initialization error. |
10053 |
A transport-level error occurred when receiving results from the server. |
10054 |
A transport-level error occurred when sending the request to the server. |
10060 |
Network or instance-specific error. |
40143 |
Connection could not be initialized. |
40197 |
The service encountered an error processing your request. |
40501 |
The server is busy. |
40613 |
The database is currently unavailable. |
A biblioteca CAT retry possui uma classe denominada SqlAzureTransientErrorDetectionStrategy que você poderá utilizar, podendo ser um ótimo ponto de partida para a sua implementação da interface ItransientErrorDetectionStrategy.
2. Referências
Retry Logic para erros transientes no SQL Azure parte 1
Retry Logic para erros transientes no SQL Azure parte 2
Retry Logic for Transient Failures in SQL Azure
Download the c sharp class directly the library from https://appfabriccat.com/2011/02/transient-fault-handling-framework/
SQL Azure Retry Logic Sample
https://code.msdn.microsoft.com/windowsazure/SQL-Azure-Retry-Logic-2d0a8401
SQL Azure Connection Retry (CODE WITH PARAMETERS)
https://blogs.msdn.com/b/bartr/archive/2010/06/18/sql-azure-connection-retry.aspx
SQL Azure Connectivity Troubleshooting Guide