IDbTransaction.Фиксация возвращает до завершения транзакции

#c# #sql-server #ado.net #ddl

#c# #sql-сервер #ado.net #ddl

Вопрос:

У меня есть сервер, который (редко) динамически создает строки базы данных для размещения модели данных, которую настраивает пользователь. Во время запуска серверу может потребоваться создать около тысячи строк, а также несколько вставок в существующие таблицы.

Когда все это сделано, он фиксирует транзакцию и рассылает уведомления о новой модели данных всем, кто может их слушать. Проблема в том, что транзакция.Функция Commit() возвращается до того, как база данных фактически завершила внесение изменений, поэтому, если клиент отправляет запрос на сервер после отправки уведомлений, клиент может получить пустой результат. Я предполагал, что ожидание транзакции.Commit() гарантирует, что вся транзакция была выполнена и зафиксирована.

Причина, по которой клиент получает пустой результат, заключается в том, что когда не выполняются операции DDL, база данных использует изоляцию моментальных снимков, поэтому очевидно, что снимок делается до завершения операции DDL (но задолго до возврата transaction.commit)

Порядок операций таков:

  1. Начать транзакцию
  2. Выполните тысячи операций в базе данных (DDL)
  3. Фиксация транзакции
  4. Отправка уведомлений об изменениях
  5. Клиент запрашивает данные (изоляция моментального снимка)
  6. Данные (состояние до транзакции) клиенту не возвращаются.

Почему транзакция.Завершение фиксации () до завершения транзакции? Как я могу заставить сервер ждать полного завершения транзакции, прежде чем приступить к отправке уведомлений?

Редактировать: ясность.

Комментарии:

1. Может быть, у вас одновременно выполняется внешняя транзакция? Тогда ваша фиксация фактически не применяет изменения до тех пор, пока эта внешняя транзакция не будет зафиксирована.

2. Это, конечно, возможно (это довольно зрелая структура, поэтому не очень легко ориентироваться), но я совершенно уверен, что это не так. Я проведу расследование, хотя, как я писал ниже, после фиксации я закрываю соединение, поэтому родительская транзакция вряд ли может быть заблокирована. Если мы предположим, что это не так, это, похоже, происходит только тогда, когда задействован DDL. Обычные обновления базы данных, по-видимому, полностью завершаются после завершения фиксации.

3. Когда вы говорите DDL, вы имеете в виду инструкции CREATE ? DDL будет использовать блокировки модификации схемы и блокировать другие сеансы независимо от уровня изоляции транзакции, поэтому, возможно, вы имеете в виду DML (вставки). Я предлагаю вам запустить трассировку для проверки последовательности транзакций. Симптомы предполагают, что запрос клиента (# 5) выполняется в транзакции моментального снимка, которая началась до шага # 1.

4. Я имею в виду DDL — не в таблице, которую я прочитал. Я добавляю столбцы в некоторые таблицы и строки в другие для ведения бухгалтерского учета в одной транзакции. @Evk У меня есть внешний TransactionScope, однако его подавление не решает проблему.

5. @Evk — Это действительно был внешний TransactionScope, вызывающий проблемы! Сделайте это правильным ответом, и вы получите надлежащую церемонию.

Ответ №1:

Как мы выяснили в комментариях, одной из возможных причин этого является ожидающая внешняя транзакция. Как указано в документации:

При использовании во вложенных транзакциях фиксации внутренних транзакций не освобождают ресурсы и не делают их изменения постоянными. Изменения данных становятся постоянными, а ресурсы освобождаются только при фиксации внешней транзакции. Каждая ТРАНЗАКЦИЯ ФИКСАЦИИ, выданная, когда @@TRANCOUNT больше единицы, просто уменьшает @@TRANCOUNT на 1. Когда @@TRANCOUNT окончательно уменьшается до 0, вся внешняя транзакция фиксируется. Поскольку компонент Database Engine игнорирует transaction_name, выдача ТРАНЗАКЦИИ ФИКСАЦИИ, ссылающейся на имя внешней транзакции, при наличии незавершенных внутренних транзакций уменьшает @@TRANCOUNT только на 1.

Таким образом, несмотря Commit() на успешное выполнение, если присутствует внешняя транзакция — никакие изменения в базе данных сохраняться не будут, пока эта внешняя транзакция тоже не будет зафиксирована.

Комментарии:

1. Обратите внимание, что внешняя транзакция может быть проигнорирована путем добавления Enlist=False в строку подключения SQL.

Ответ №2:

Это естественное поведение «изоляции моментального снимка». Проверьте эту ссылку: https://www.sqlshack.com/snapshot-isolation-in-sql-server /

«Когда другой сеанс считывает те же данные, возвращается зафиксированная версия данных на момент начала транзакции чтения».

Вы должны завершить сеанс, чтобы другая транскрипция могла его прочитать. Или измените его на «Чтение зафиксировано».

Комментарии:

1. Я понимаю, что изоляция моментального снимка будет считывать состояние последней завершенной транзакции. Моя проблема в том, что транзакция DDL не завершена, когда я ожидаю, что она будет завершена, поэтому я начинаю отправлять уведомления слишком рано — поэтому запрос клиента (который является моментальной изоляцией) считывает неправильные данные. Моя проблема связана с записью (и не ожиданием ее завершения), а не с чтением.

2. Поскольку при изоляции моментального снимка текущая версия строки копируется в базу данных tempDb. Если кто-то захочет его прочитать, база данных вернет старые данные. Это делается для предотвращения блокировки. Мой совет — попробуйте изменить уровень изоляции на committed, который блокирует незафиксированные строки. Но в ms sql server есть способ преодолеть это, добавить ‘with (nolock)’ конец инструкции select .

3. На самом деле нет установки «слишком рано». Вы будете считывать старые данные, пока не завершите сеанс.

4. Сервер фиксирует транзакцию. Затем он закрывает соединение с базой данных. Затем он отправляет уведомления. Затем клиент отправляет запрос на сервер, который создает новый сеанс (изоляция моментального снимка) и считывает данные. Если я прочитаю зафиксированные блокировки, это гарантирует, что последнее чтение ожидалось. Однако мне нужно использовать изоляцию моментальных снимков. Моя проблема в том, что IDbTransaction . Фиксация возвращается до того, как эти данные были полностью зафиксированы.

5. В этой цитате нет ничего, указывающего на то, что вы будете считывать одни и те же данные для всего сеанса. «возвращается версия данных на момент начала транзакции чтения» — не при запуске сеанса.