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 clip_image001 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.

  1. Adicionando uma nova conexão de dados:
  2. Caso a janela do Server Exporer não esteja visivel, no menu View, clique em Other Windows e depois clique em Server Explorer.
  3. Na janela Server Explorer, dê um clique com o botão direito do mouse em Data Connections e selecione Add Connection.
  4. 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.
  5. Selecione SQL Server Authentication
  6. Entre com o login name e password
  7. Em Select or enter a database name, coloque “PedidosClientes”, como está demonstrado na figura 1.

clip_image002

Figura 1: Entrando com a autênticação (*)

1.3 Adicionando um LINQ a SQL Class

  1. Essa parte do artigo demonstrará como criar um LINQ query que retornará todos os pedidos dos clientes.
  2. Na janela do solution explorer, clique com o botão direito do mouse no projeto e selecione Add New Item.
  3. Em Data, selecione LINQ to SQL Classes. Nomeie o arquivo CustomerOrders.dml.
  4. Na janela do Server Explorer, expanda o nó Data Connections e localize o banco de dados CustoemrOrders.
  5. Expanda o nó do banco de dados e então expanda os nós de tabelas.
  6. 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.

clip_image004

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.

clip_image006

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

https://social.technet.microsoft.com/wiki/contents/articles/4235.retry-logic-for-transient-failures-in-sql-azure.aspx

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

https://social.technet.microsoft.com/wiki/contents/articles/4235.retry-logic-for-transient-failures-in-sql-azure.aspx

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

https://social.technet.microsoft.com/wiki/contents/articles/sql-azure-connectivity-troubleshooting-guide.aspx