Best way to ensure consultation isolation

Asked

Viewed 65 times

0

I have the following scenario:

A table in SQL Server that contains process records that can be run on multiple services (Windows Services in C#) from multiple servers. Something like:

CREATE TABLE Processo
(
    Id          INT IDENTITY NOT NULL,
    Status      VARCHAR(100)
)

GO

INSERT INTO Processo VALUES 
  ('Aguardando processamento'), ('Aguardando processamento'), ('Aguardando processamento')

SELECT  *
FROM    Processo 

/*
| Id  | Status                   |
| 1   | Aguardando processamento | 
| 2   | Aguardando processamento | 
| 3   | Aguardando processamento | 
*/

Services query this table simultaneously searching for a process with "Awaiting processing" status. As soon as one of these services encounters the process, the status is updated to "Processing" in a method more or less like this:

using (SqlConnection conn = new SqlConnection("string de conexão"))
{
    conn.Open();

    using (SqlCommand command = conn.CreateCommand())
    {
        command.CommandType = System.Data.CommandType.Text;
        command.CommandText = "SELECT Top 1 Id FROM Processo WHERE STATUS = 'Aguardando processamento'";
        int processo = (int)command.ExecuteScalar();

        command.CommandText = "UPDATE Processo SET STATUS = 'Processando' WHERE Id = @Id";
        command.Parameters.Add(new SqlParameter("Id", processo));
        command.ExecuteNonQuery();
    }                
}

The problem occurs when in the meantime between the query and the status change to "Processing" in an "A" service, a "B" service finds the same record and also starts processing.

I need to ensure that when a service finds a process, no other can find or change this same record.

What is the best way to ensure this?

EDIT: The purpose of my question is to solve the problem by considering applications . NET on multiple servers, suggested as "possible duplicate" (291665) does not cover the technology used and the answers do not apply to my case.

1 answer

0


You can execute both commands within a transaction using SqlTransaction:

using (SqlTransaction trans = conn.BeginTransaction())
{
    using (SqlCommand command = conn.CreateCommand())
    {
        command.CommandType = System.Data.CommandType.Text;
        command.CommandText = "SELECT Top 1 Id FROM Processo WHERE STATUS = 'Aguardando processamento'";
        int processo = (int)command.ExecuteScalar();

        command.CommandText = "UPDATE Processo SET STATUS = 'Processando' WHERE Id = @Id";
        command.Parameters.Add(new SqlParameter("Id", processo));
        command.ExecuteNonQuery();
    }
    trans.Commit(); 
}

Reference of the MSDN: Sqltransaction

  • 2

    once the code runs on multiple computers, it would be good to put the transaction isolation level as Serializable, the bank will handle the competitive

  • 1

    @Rovannlinhalis is a good idea since the standard is Readcommited. For those who want to go deeper, here is the link to the documentation: https://msdn.microsoft.com/pt-br/library/system.data.isolationlevel(v=vs.100). aspx

  • @Ricardopunctual Thank you. In this case it is best to open the transaction and isolate both select and update (as done), or only select would guarantee isolation ?

  • You arrow the Isolationlevel for the entire transaction, then it will be valid for select and update

  • 1

    Thanks @Ricardopunctual, I used your reply with the conn.BeginTransaction(IsolationLevel.Serializable) to ensure isolation and worked.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.