Retry Logic para erros transientes no Windows Azure SQL Database (SQL Azure) - Parte 2
Parte 2 Adicionando Consultas
Autor: Marcelo Franceschi de Bianchi - CTS LATAM
Revisor: Roberto Cavalcanti - CTS LATAM
Dando continuidade a série de três artigos relacionados ao Retry Logic, esse artigo é o de número 2 no qual você aprenderá como adicionar as consultas a sua aplicação que conterá o Retry Logic e fará a conexão com o banco de dados Windows Azure SQL Database (SQL Azure).
1. Adicionando Consultas
Você verá nessa etapa o básico sobre as consultas SQL, como criar e disparar um transient failure.
1.1 Configurando a connection string
Faça o download dos arquivos de projeto project files e abra a solução com o nome de RetryLogicTutorial_Skeleton.sln. Com o botão direito do mouse clique em Form1.cs e selecione View Code. Então você deverá adicionar a váriavel membro SqlConnectionStringBuilder ao formulário Form1 class.
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
In the Form1 constructor, initialize the SqlConnectionStringBuilder for the CusomerOrders database:
builder.DataSource = "xxxxxxxxxx.database.windows.net";
builder.InitialCatalog = "CustomerOrders";
builder.Encrypt = true;
builder.TrustServerCertificate = false;
builder.UserID = "xxxxxxxx";
builder.Password = "xxxxxxxx";
Substitua o nome do servidor atual, user ID and password pelo seu servidor SQL Database.
1.2 Adicionando um ADO.Net Query
Localize a função com o nome de AdoQueyWorker_DoWork e adicione o seguinte código:
try
{
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand("select product_name from products with (READCOMMITTEDLOCK)");
command.Connection = connection;
command.CommandTimeout = 3;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
(sender as BackgroundWorker).ReportProgress(0, reader["product_name"]);
}
}
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, "SqlException");
}
A função é chamada de uma background worker thread. A consulta retorna uma lista de nomes de produtos, no qual serão passados para UI thread pela chamada de BackgroundWorker.ReportProgress. O parametro READCOMMITTEDLOCK bloqueia a consulta se a tabela está com um lock.
- Adicionando uma nova conexão de dados:
- Caso a janela do Server Exporer não esteja visivel, no menu View, clique em Other Windows e depois clique em Server Explorer.
- Na janela Server Explorer, dê um clique com o botão direito do mouse em Data Connections e selecione Add Connection.
- Na caixa de dialogo Add Connection, entre com o Fully qualified name do seu SQL Database server p. Ex. (xxxxxxxx.database.windows.net) no campo Server Name.
- Selecione SQL Server Authentication
- Entre com o login name e password
- Em Select or enter a database name, coloque “PedidosClientes”, como está demonstrado na figura 1.
Figura 1: Entrando com a autênticação (*)
1.3 Adicionando um LINQ a SQL Class
- Essa parte do artigo demonstrará como criar um LINQ query que retornará todos os pedidos dos clientes.
- Na janela do solution explorer, clique com o botão direito do mouse no projeto e selecione Add New Item.
- Em Data, selecione LINQ to SQL Classes. Nomeie o arquivo CustomerOrders.dml.
- Na janela do Server Explorer, expanda o nó Data Connections e localize o banco de dados CustoemrOrders.
- Expanda o nó do banco de dados e então expanda os nós de tabelas.
- Arraste todas as quatro tabelas (customers, order_items, orders e products) para o designer e você visualizará todas as tabelas graficamente como está demonstrado na figura 2.
Figura 2: Modelo do exemplo de banco de dados do SQL Database. (*)
O próximo passo será localizar a função denominada de LinqQueryWorker_DoWork e então adicionar o seguinte código:
try
{
Deadlock();
CustomerOrdersDataContext ctx = new CustomerOrdersDataContext();
ctx.Connection.ConnectionString = builder.ConnectionString;
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 };
e.Result = results.ToList();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message, "SqlException");
}
Então você deverá rodar a aplicação e clicar no botão “ADO.NET Query” e no botão “LINQ Query”. Você verá os resultados da consulta populados na Interface Gráfica da aplicação, de Retry Logic como está demonstrado na figura 3.
Figura 3: Interface Gráfica da aplicação exemplo de Retry Logic. (*)
1.4 Adicionando uma Block Transaction
Um aspecto interessante sobre o retry logic é que podemos induzir um erro transiente para efeito de teste. Para esse objetivo, intensionalmente iremos causar um deadlock.
Localize a função denominada longTransaction_DoWork e adicione o seguinte código:
System.Diagnostics.Debug.WriteLine("Starting long transaction");
try
{
using (SqlConnection con = new SqlConnection(builder.ConnectionString))
{
con.Open();
SqlTransaction transaction = con.BeginTransaction();
SqlCommand cmd = new SqlCommand("UPDATE products WITH (TABLOCKX) SET product_name = 'x'");
cmd.Connection = con;
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
for (int i = 1; i <= LongTransactionTime; i++)
{
Thread.Sleep(1000);
longTransactionWorker.ReportProgress((int)(i * 100) / LongTransactionTime);
}
cmd = new SqlCommand("SELECT * FROM customers with (READCOMMITTEDLOCK)");
cmd.Connection = con;
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
System.Diagnostics.Debug.WriteLine("Roll back long transaction");
transaction.Rollback();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Essa função começa uma nova transação SQL e então permanece com um exlucisve lock na tabela Products. O tempo de espera será de 30 segundos antes da realização do rolling back da transação.
Então o próximo passo será localizar a função denominada Deadlock e então adicionar o seguinte código:
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.Open();
SqlTransaction transaction = connection.BeginTransaction(System.Data.IsolationLevel.Serializable);
SqlCommand command = new SqlCommand(
"UPDATE customers WITH (TABLOCKX) SET lname = 'x'; " +
"SELECT * FROM PRODUCTS with (READCOMMITTEDLOCK)"
);
command.Connection = connection;
command.Transaction = transaction;
command.ExecuteNonQuery();
transaction.Rollback();
}
Rode a aplicação e clique no botão Deadlock. Enquanto a barra de progresso avança, clique no botão ADO.NET Query ou então no botão LINQ Query. Você verá uma mensagem de erro, SQL Error -2 (timeout) ou então SQL Error 1205 (deadlock).
Vá agora para o artigo Retry Logic para erros transientes no SQL Azure parte 3, para a continuação da implementação do Retry Logic na aplicação que irá realizar o acesso ao banco de dados SQL Azure.
(*) Essas imagens foram retiradas do artigo Retry Logic for Transient Failures in SQL Azure
2. Referências
Retry Logic para erros transientes no SQL Azure parte 1
Retry Logic para erros transientes no SQL Azure parte 3
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