Serialazble isolation level on rows that does not exists
Recently I was asked how to solve this problem. An external service makes a call to the application, to create / update an entity. This call can arrive in one of few endpoints. The catch is that sometimes the external service send the call to create a new entity to all the end points at the same time. This obviously caused issue when trying to insert the same row twice.
I suggested using serializable isolation level to handle this scenario, but I wasn't sure what kind of guarantees is makes for rows that do not exists. I decided to write this simple test case (warning: test code / quick hack, don't write real code like this!).
private static void InsertToDatabase(object state) { int value = 0; while (true) { value += 1; try { using (SqlConnection connection = new SqlConnection("data source=localhost;integrated security=sspi;initial catalog=test")) { connection.Open(); using (SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) using (SqlCommand command = connection.CreateCommand()) { command.CommandText = "SELECT Id FROM Items WHERE Id = @id"; command.Parameters.AddWithValue("id", value); command.Transaction = transaction; if (command.ExecuteScalar() != null) continue; command.CommandText = "INSERT INTO Items (Id) VALUES(@id)"; command.ExecuteNonQuery(); Console.WriteLine("{1}: Wrote {0}", value, Thread.CurrentThread.ManagedThreadId); transaction.Commit(); } } } catch (SqlException e) { if (e.Number == 1205) //transaction deadlock { Console.WriteLine("{0}: Deadlock recovery", Thread.CurrentThread.ManagedThreadId); continue; } Console.WriteLine(e); } } }
As a note, performance for this really sucks if you have contention, and I got a lot of transaction deadlocks when running it with multiply threads.
What I have observed was that it indeed inserted only a single id. All other isolation levels (including snapshot) will produce duplicate rows in this scenario.
Assumption proven, now I only need to find what kind of a lock it takes on a row that doesn't exists.

Comments
Comment preview