Изоляция снэпшотов в SQL/блокировках кода при чтении

Я подозреваю, что не совсем понимаю, что происходит, или происходит что-то странное. (Первый случай более вероятен, я думаю.)

Большая картинка:

  • Я пытаюсь заставить веб-службу выполнять определенные операции асинхронно, поскольку они могут занимать много времени, и я не хочу, чтобы клиент ждал завершения операций (просто время от времени запрашивайте результаты, чтобы увидеть, что операция Выполнено).
  • Асинхронный код завернут в транзакцию — если что-то пойдет не так, я хочу иметь возможность откатить любые изменения.
  • К сожалению, последним шагом асинхронного кода является вызов ДРУГОЙ службы, которая запрашивает ту же базу данных.
  • Несмотря на то, что все это заключено в транзакцию Snapshot, последний шаг завершается ошибкой, поскольку служба не может прочитать данные из базы данных.
  • В этом отношении, пока выполняется асинхронная операция, я также не могу выполнять простые операторы SELECT из базы данных.

Вот пример кода, который я сейчас использую для тестирования транзакций (сначала используя модель Entity Framework 5):

        using (var transaction = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.RequiresNew, new System.Transactions.TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.Snapshot }))
        {
            var db = new DataModelContainer();
            Log test = new Log();
            test.Message = "TEST";
            test.Date = DateTime.UtcNow;
            test.Details = "asd";
            test.Type = "test";
            test.StackTrace = "asd";
            db.LogSet.Add(test);
            db.SaveChanges();
            using (var suppressed = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Suppress))
            {
                var newDb = new DataModelContainer();
                var log = newDb.LogSet.ToArray();  //deadlock here... WHY?
            }
            test = db.LogSet.Where(l => l.Message == "TEST").Single();
            db.LogSet.Remove(test);
            db.SaveChanges();
            transaction.Complete();
        }

Код создает простую запись в журнале в базе данных (да, я сейчас играюсь, поэтому значения - мусор). Я настроил базу данных SQL, чтобы разрешить изоляцию моментальных снимков, и, насколько мне известно, чтение по-прежнему должно быть разрешено (они тестируются в этом коде с использованием новой подавленной транзакции и нового DataModelContainer). Однако я не могу запросить LogSet в подавленной транзакции или в SQL Management Studio - вся таблица заблокирована!

Итак... почему? Почему он заблокирован, если область транзакции определена как таковая? Я также пробовал другие уровни изоляции (например, ReadUncommited), но все равно не могу запросить таблицу.

Может ли кто-нибудь дать объяснение такому поведению?


person Shaamaan    schedule 24.01.2014    source источник
comment
включена ли изоляция моментальных снимков в вашей БД? Изоляция моментальных снимков должна быть включена путем установки параметра базы данных ALLOW_SNAPSHOT_ISOLATION ON, прежде чем он будет использоваться в транзакциях.   -  person Moho    schedule 24.01.2014
comment
@ Мохо Да, это так. Я настроил базу данных SQL на изоляцию моментальных снимков. В любом случае, проблема сохраняется при использовании ДРУГИХ режимов изоляции (менее строгих, чем Serializable), таких как ReadUncommited, и этот режим даже не требует включения изоляции моментальных снимков на сервере SQL!   -  person Shaamaan    schedule 24.01.2014


Ответы (1)


В SSMS установите текущий уровень изоляции на SNAPSHOT и посмотрите, решит ли это вашу проблему — вероятно, он установлен на READ COMMITTED и, следовательно, будет блокироваться из-за ожидающих обновлений.

Обновлять:

Вы можете разрешить READ COMMITTED доступ к версионным строкам всей БД, изменив следующую опцию (и избежав необходимости постоянно устанавливать текущий уровень изоляции на SNAPSHOT):

ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
person Moho    schedule 24.01.2014
comment
Создается впечатление, что настройка уровня изоляции в C# вообще не используется. Изоляция моментальных снимков — это крайний случай, когда блокировка не должна происходить, но проблема возникает и с другими уровнями изоляции. Проверьте обновленный код C# — на данный момент мне не нужно использовать SSMS для выполнения тестов. Кроме того, установка READ_COMMITTED_SNAPSHOT является частичным решением — как уже говорилось, она не объясняет, почему изоляция транзакций установлена ​​неправильно (если она вообще установлена). - person Shaamaan; 24.01.2014
comment
какие уровни изоляции ваших других сеансов имеют значение, как я указал в решении. Если вы установили READ COMMITTED, он будет заблокирован, пока вы не зафиксируете изменения. Вот и вся цель настройки READ_COMMITTED_SNAPSHOT - предотвратить блокировку во время ожидания коммита SNAPSHOT - person Moho; 24.01.2014
comment
Ааа, кажется, я понимаю, в чем проблема. У меня сложилось впечатление, что мне нужно явно открыть транзакцию (например, при выполнении простого запроса SELECT SSMS). - person Shaamaan; 24.01.2014