Тупик при ВЫБРАТЬ / ОБНОВИТЬ

У меня проблема с тупиком при SELECT / UPDATE на SQL Server 2008. Я читаю ответы из этой ветки:SQL Server блокируется между выбором / обновлением или множественным выбором но я до сих пор не понимаю, почему я зашел в тупик.

Я воссоздал ситуацию в следующем тестовом примере.

У меня есть таблица:

CREATE TABLE [dbo].[SessionTest](
    [SessionId] UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL,
    [ExpirationTime] DATETIME NOT NULL,
    CONSTRAINT [PK_SessionTest] PRIMARY KEY CLUSTERED (
        [SessionId] ASC
    ) WITH (
        PAD_INDEX  = OFF, 
        STATISTICS_NORECOMPUTE  = OFF, 
        IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS  = ON, 
        ALLOW_PAGE_LOCKS  = ON
    ) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[SessionTest] 
    ADD CONSTRAINT [DF_SessionTest_SessionId] 
    DEFAULT (NEWID()) FOR [SessionId]
GO

Сначала я пытаюсь выбрать запись из этой таблицы и, если запись существует, установить время истечения до текущего времени плюс некоторый интервал. Это достигается с помощью следующего кода:

protected Guid? GetSessionById(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
    Logger.LogInfo("Getting session by id");
    using (SqlCommand command = new SqlCommand())
    {
        command.CommandText = "SELECT * FROM SessionTest WHERE SessionId = @SessionId";
        command.Connection = connection;
        command.Transaction = transaction;
        command.Parameters.Add(new SqlParameter("@SessionId", sessionId));

        using (SqlDataReader reader = command.ExecuteReader())
        {
            if (reader.Read())
            {
                Logger.LogInfo("Got it");
                return (Guid)reader["SessionId"];
            }
            else
            {
                return null;
            }
        }
    }
}

protected int UpdateSession(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
    Logger.LogInfo("Updating session");
    using (SqlCommand command = new SqlCommand())
    {
        command.CommandText = "UPDATE SessionTest SET ExpirationTime = @ExpirationTime WHERE SessionId = @SessionId";
        command.Connection = connection;
        command.Transaction = transaction;
        command.Parameters.Add(new SqlParameter("@ExpirationTime", DateTime.Now.AddMinutes(20)));
        command.Parameters.Add(new SqlParameter("@SessionId", sessionId));
        int result = command.ExecuteNonQuery();
        Logger.LogInfo("Updated");
        return result;
    }
}

public void UpdateSessionTest(Guid sessionId)
{
    using (SqlConnection connection = GetConnection())
    {
        using (SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable))
        {
            if (GetSessionById(sessionId, connection, transaction) != null)
            {
                Thread.Sleep(1000);
                UpdateSession(sessionId, connection, transaction);
            }
            transaction.Commit();
        }
    }
}

Затем, если я пытаюсь выполнить тестовый метод из двух потоков, и они пытаются обновить одну и ту же запись, я получаю следующий вывод:

[4] : Creating/updating session
[3] : Creating/updating session
[3] : Getting session by id
[3] : Got it
[4] : Getting session by id
[4] : Got it
[3] : Updating session
[4] : Updating session
[3] : Updated
[4] : Exception: Transaction (Process ID 59) was deadlocked 
on lock resources with another process and has been 
chosen as the deadlock victim. Rerun the transaction.

Я не могу понять, как это может произойти с использованием Serializable Isolation Level. Я думаю, что первый выбор должен заблокировать строку / таблицу и не позволит другому выбрать для получения каких-либо блокировок. Пример написан с использованием командных объектов, но он только для целей тестирования. Первоначально я использую linq, но я хотел показать упрощенный пример. Sql Server Profiler показывает, что взаимоблокировка - это блокировка ключа. Я обновлю вопрос через несколько минут и опубликую график с сервера профилирования SQL. Любая помощь будет оценена. Я понимаю, что решением этой проблемы может быть создание критического раздела в коде, но я пытаюсь понять, почему Serializable Isolation Level не работает.

А вот и график тупиковой ситуации:тупик http://img7.imageshack.us/img7/9970/deadlock.gif

Заранее спасибо.

Ответы на вопрос(1)

Ваш ответ на вопрос