Как сделать откат транзакции

Обновлено: 06.07.2024

Если при выполнении распределенной транзакции с Microsoft SQL Server на сервере произошла ошибка I/O или произошло отключение сервера / базы данных, тогда транзакция в координаторе (DTC) и на других базах будет откачена (ROLLBACK), а в пострадавшей базе данных на SQL Server может не откатиться. Это потому, что в результате отключения / сбоя, СУБД технически не могла её откатить.

Зависшая распределенная транзакция будет проявляться, в том числе, тем, что в базе не будет обрезаться журнал (TRANSACTION LOG) даже в режиме восстановления SIMPLE.

Если транзакция провела много изменений в базе, все они так и не принятые будут лежать в журнале и увеличивать размер самого журнала и размер резервных копий. Если транзакция заблокировала ресурсы, по логике, они так же должны оказаться заблокированы до её отката (хотя я это не проверял).

Как диагностировать зависшую распределенную транзакцию?

Чтобы проверить первичные симптомы и убедиться, что зависшая транзакция есть, нужно выполнить:

Эта команда может выдать приблизительно следующее:

Transaction information for database '. '.

Oldest active transaction:
SPID (server process ID): 53
UID (user ID) : -1
Name : DTCXact
LSN : (1517932:234765:1)
Start time : Jul 3 2013 3:41:04:643AM
SID : 0x010600000000000550000000cc62defa0799cb7d28fedbc8b4ce1b1ace9fa59b
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Обратите внимание на имя DTCXact - это означает, что транзакцию инициировал Distributed Transaction Coordinator.

Ок. Узнав SPID, мы можем узнать что за процесс работал в транзакции:

Мы можем его убить (KILL . ). Но не надо этого делать.

Зависшую распределенную транзакцию прекращение клиентского процесса не остановит — она существует теперь только в пострадавшей базе данных, сам процесс и координатор про неё уже забыли.

Как завершить зависшую распределенную транзакцию?

Интернет на вопрос "ROLLBACK DTCXact" и "DTCXact" выдает дурацкие статьи на общие темы или обсуждения без ответа на вопрос.

Недавно, я использовал такой способ — перевести базу в однопользовательский режим с откатом всех транзакций:

Убедился, что транзакция откатилась, и попытался перевести в многопользовательский:

Правда после этого, как я подозреваю, из-за логического повреждения (которое было не следствием, а причиной зависшей распределенной транзакции) база вошла в режим SUSPECT. Пришлось её выключить (Take Off) и включить (Take On). Потребовался, видимо, не только возврат страниц данных "назад" путем отката, но и восстановление части страниц "вперед" по журналу с какого-то момента.

В результате база заработала, распределенная транзакция откатилась, журнал усёкся.

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

Практически все описано в анонсе публикации, но еще чуть-чуть.

Вы разработчик. Пишите код, запускаете отладку, накликиваете за пользователя какие-то данные. Или даже за нескольких пользователей - в нескольких сессиях параллельно, если бизнес-процесс сложный. Или запускаете "накликанный" на эталонных данных сценарный тест. Ловите ошибку, идете в конфигуратор исправлять, чтобы повторить все заново.

Если к следующему циклу "накликивания" надо вернуть исходное состояние данных, то можно написать (лень!) и запускать обработку, которая восстановит данные, или восстанавливать каждый раз в базу резервную копию (каждый раз сохранять наработку в cf, восстанавливать копию, перезапускать конфигуратор, восстанавливать наработки. в общем - отпадает. а если тут еще и хранилище. уууу. ).

Или прицепляем данное расширение, настраиваем фиксацию и автоматическое восстановление данных и после каждого цикла просто перезапускаем отладку. Данные восстанавливаются самостоятельно.

Или вы - инженер сопровождения. У вас тестовая база с "исходными" данными и вы пытаетесь повторить ошибку, возникающую у пользователя. После короткого времени ошибка не повторена, а контекст данных "испорчен".

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

С разработкой видеоинструкций отдельная боль. Собственно, идея оттуда и пришла. Коллега, который занимался созданием видеоинструкций, реализовывал свою версию подобной разработки. Но она не покрывала восстановление данных всех объектов ИБ.

При разработке сценарных тестов тоже должно пригодится!

Ограничения

Слукавил немного. Эта разработка тоже не покрывает восстановление данных ВСЕХ объектов ИБ. Не восстанавливается первоначальное состояние регистрации объектов в узлах планов обмена и хранилищ настроек.

Все же остальное фиксируется в истории и восстанавливается вполне успешно - объекты ссылочных типов, движения регистров любых типов, константы.

Понятно что разработка построена на событиях. Поэтому главное ограничение - версия платформы. Подписки на события в расширениях появились в 8.3.17. Поэтому использовать данную разработку на более старых версиях платформы не получится. А вот режим совместимости конфигурации (не забудьте синхронизировать режим расширения с ним) может быть достаточно "старым" - от 8.3.12.

Также при добавлении расширения в конфигурацию желательно снять флаги безопасного режима и защиты от опасных действий.

Механика


Из объектов, несущих данные, в расширении есть справочник для фиксации истории изменений и настроечный регистр сведений.

В справочнике фиксируются объекты в состоянии "до изменения". Прирост времени выполнения при включенной фиксации данных по моим замерам составляет до 10%. Для работы в тестовой базы для процессов сопровождения/разработки /настройки считаю показатель вполне приемлемым.

При восстановлении данных восстанавливаются только самые первые версии измененных объектов. То есть если документ (или регистр по определенному отбору) меняли десять раз, то в истории изменения зафиксируются все, но восстановится он только один раз - по самой первой фиксации. Это значительно сокращает время восстановления.

При восстановлении объекты имеют ОбменДанными.Загрузка = Истина. Объекты восстанавливаются в порядке, обратном порядку записи истории, хотя при восстановлении "среза первых" это необязательный атрибут. Документы при этом не проводятся, поскольку наборы записей регистров фиксируются и восстанавливаются отдельно.

Восстановление происходит в транзакции. После успешного восстановления история изменений очищается.

Можно восстановиться до определенной записи в истории, если сможете правильно определить эту самую нужную вам запись. Тогда история зачистится только до этой строки.

А еще можно поставить закладки в историю в нужные вам моменты (спасибо коллеге, подсказавшему идею в комментариях) и восстанавливаться до них.

Настройка


Она элементарна.
Можно включить тотальную фиксацию изменений (первая на скриншоте). Тогда фиксироваться будет все, в том числе изменения данных в фоновых заданиях. Именно такой вариант я и рекомендую.
При этом варианте можно дополнительно настроить автоматическую очистку данных при старте или заверешении работы системы.

Можно включить фиксацию изменений для отдельного пользователя (он "сам" должен это сделать) и для текущей сессии.
Такие варианты могут использоваться с дополнительными оговорками, поскольку не гарантируется целостность восстанавливаемых данных из-за того, что в истории не фиксируются действия других пользователей.
Но, возможно, кому-то это будет полезно.

При записи истории изменений может достаточно быстро расти размер базы. Но я не рекомендую использовать запись истории без периодического восстановления данных или очистки истории (справочник легко чистится непосредственным удалением элементов) на продолжительном отрезке времени.

В любом случае, это расширение не предназначено для работы в "боевой" базе. Это инструмент исключительно для IT-специалистов и использования исключительно в тестовых базах!

Заключение

Разработка тестировалась на платформе 8.3.18.1289 на базах ЗУП (3.1.16.134) и ERP (2.4.12.64 и 2.5.6.98).
Разработкой активно пользуются коллеги, занимающиеся видеоинструкциями и сценарными тестами.

Жду обратной связи, всем спасибо за внимание!

Версия 1.0.0.2 (от 21.06.2021)

Изменения в версии:

Версия 1.0.0.3 (от 22.07.2021)

Изменения в версии:

Специальные предложения

Electronic Software Distribution

Интеграция 1С с системой Меркурий

Алкогольная декларация

Готовые переносы данных

54-ФЗ

Управление проектом на Инфостарте

Траектория обучения 1С-разработчика

- заменил слово, которое не могу написать (из заголовка), на Ролбэк - т.к. движок Инфостарта его заменял на * (смотрю - сейчас и в заголовке стала "*")
Думал, тут что-то хитрое придумано, но

Хотя, всё-равно, это хорошая штука для тестовых баз.
И для демо-серверов

Можно включить фиксацию изменений для отдельного пользователя (он "сам" должен это сделать) и для текущей сессии.
Такие варианты могут использоваться с дополнительными оговорками, поскольку не гарантируется целостность восстанавливаемых данных из-за того, что в истории не фиксируются действия других пользователей.

По хорошему, тут как раз можно было бы и заморочиться. Обычно это нужно как раз в рабочей базе и как раз по одному пользователю-тестировщику (+ все остальные). Тогда можно было:
1. Зафиксировать версию объекта до изменения тестировщиком
2. Для остальных пользователей при чтении объекта читать зафиксированную версию (это самое сложное)
3. Фиксировать отдельно версию после изменений других пользователей.
Или даже проще
1. Фиксировать отдельно версию тестировщика (оставляя оригинальные данные - вернее сразу восстанавливая их)
2. При чтении данных тестировщиком - читать зафиксированную отдельно версию

Организовать чтение зафиксированную версии из отдельного источника, пожалуй, самое сложное - причём именно поймать момент чтения и подменить одни данные на другие.

Можно восстановиться до определённой записи в истории, если сможете правильно определить эту самую нужную вам запись. Тогда история зачистится только до этой строки.

На эту тему рекомендую сделать простую доработку - явно назначаемые временнЫе маркеры (с комментариями) - которыми можно было бы фиксировать (интерактивно или програмно) временные отсечки - и потом просто восстанавливаться на состояние перед этим маркером.

А вот это можете пояснить?

Поэтому главное ограничение - версия платформы. Подписки на события в расширениях появились в 8.3.17. Поэтому использовать расширения на более старых версиях платформы не получится. А вот режим совместимости может быть достаточно "старым"

И вот это тоже, хотелось бы пояснить подробнее

(1) Спасибо за развернутый комментарий и хорошие вопросы. Талантом писателя, в том числе и технического, к сожалению, не наделен)

Самый простой способ "откатить" изменение данных - выполнять изменения в транзакции, которую потом не фиксировать. Здесь как раз нет никаких транзакций (кроме платформенных и прописанных в коде конфигурации, разумеется) именно при изменениях данных. А восстанавливаются они действительно в транзакции, дабы сохранить целостность данных и истории изменения в случае возникновения ошибки при восстановлении.
Накликивать данные можно часами, восстановление в транзакции будет занимать десятки секунд или минуты.

Ну, это чисто мое мнение. использовать-то можно. на свой страх и риск. Но я бы не стал :-)

Про фиксацию данных и их подмену для разных пользователей. Тема интересна чисто с технической точки зрения. Но тут вопрос скорее к овчинке и выделке.
В тех эко-системах серверов и ПО, где работаю я, восстановление довольно свежего "боевого" бэкапа в тестовую базу занимает совсем непродолжительное время и такие доработки и сопутствующие риски неактуальности данных в боевой базе - вещи нецелесообразные.
Но код открыт полностью - все в Ваших руках)

Про "временные маркеры" и комментарии к ним. Очень хорошая идея, постараюсь реализовать в ближайшее время. Спасибо!

А вот это можете пояснить?
Поэтому главное ограничение - версия платформы. Подписки на события в расширениях появились в 8.3.17. Поэтому использовать расширения на более старых версиях платформы не получится. А вот режим совместимости может быть достаточно "старым"

Тут очепятка, сори (если я правильно Вас понял). Сейчас отредактирую: "Поэтому использовать расширения" заменю на "Поэтому использовать данную разработку"

И вот это тоже, хотелось бы пояснить подробнее
Объекты восстанавливаются в порядке, обратном порядку записи


Тут как раз все просто: справочник истории с числовым кодом и с автонумерацией. В процессе восстановления данных определяется набор восстанавливаемых элементов и восстанавливаются они по отсортированному по этому коду в обратном порядке списку.

(2)Вопросы был про вот эту фразу - видимо я что-то не знаю о режимах совместимости и о требованиях к ним в расширениях

Тут как раз все просто: справочник истории с числовым кодом и с автонумерацией. В процессе восстановления данных определяется набор восстанавливаемых элементов и восстанавливаются они по отсортированному по этому коду в обратном порядке списку.

Нее. я не понимаю. Допустим восстановление идёт на некую строку (как Вы пишите) - по факту - на некоторый момент времени - тут сразу на ум приходит периодический регистр сведений - но у Вас справочник - тогда нужен просто срез последних данных (ключа данных - и это тот ещё вопрос - т.к. для ссылочных типов всё просто - это их ссылка, а для регистров (особенно неподчинённых и не периодических) всё куда сложнее); да и как Вы храните версию - целиком - или только изменённую часть - если только изменённую - то боле-менее понятно что Вы делаете) - получили срез - и просто перезаписали объекты из версии среза - если они хранятся целиком - никакого обхода по версиям не нужно делать.
А, вот, если Вы храните данные по изменённым полям (например у документа изменили дату - то сохраняете в своё хранилище - по ключу ссылке - имя реквизита "Дата" и его прошлое значение), то на срезе не будет плоской таблицы текущих версий - нужно сделать обход в глубину (в прошлое) и восстановить каждый изменённый реквизит каждого объекта - причём один раз (только последний) - а это уже куда сложнее и дольше.
А с регистрами - так вообще можно только полные версии хранить - всего набора. или нужно целиком хранить ключ - измерения - и далее имя изменившегося ресурса/реквизита и значения - но обычно ключи тут как раз очень длинные - и проще весь набор хранить (в идеалае запаковав по методу колоночных баз данных).

Я это всё говорю не просто так - так как имею опыт разработки системы версионирования данных на 1С - а Ваше решение по сути таковым и является

Поначалу я настройку сделал на константах. При тестировании очередной базой, к которой я "прилепил" расширение, была ERP 2.4. У нее режим совместимости был 8.3.14 и он не пропускал констант в расширении. Они появились в появились в 8.3.16. Я переделал настройки на регистр сведений в итоге.
Это и явилось причиной появления данной фразы в публикации.

Вы сейчас в комментариях вытягиваете все, что я недосказал в публикации)). По сути, просите пересказать все внутренности разработки. Ну что ж, код открыт, секрета никакого, извольте, коли не хотите скачивать, тратить мани и время на изучение чужого кода. Искренний технический интерес хороших специалистов мною очень уважаем и льстит мне не меньше лайков :-)

Действительно, "обратный" порядок восстановления данных не играет никакой роли. Сделал так "на всякий случай". Даже прогрессбар работает в форме восстановления от 100% к нулю :-) Ведь о т к а т же :-) (и правда ИС "запикивает" это слово!)

Главное - локализовать "первичные" версии объектов и наборов данных, которые и будем восстанавливать.
И тут действительно есть идентификатор данных, его поле видно на одном из скринов. И в случае со ссылочным типом данных там действительно "сидит" уникальный идентификатор из ссылки.

А вот в случае регистров (на скрине как раз наборы записей различных регистров, в историю запакованные) там находится. назовем это неким "хэшем" отбора регистра, созданным на основе сериализованного в JSON массива, содержащего все элементы отбора.

Таким образом имя метаданных (разумеется полное, вида "РегистрСведений.СостоянияСотрудников") и данный "хэш" дадут нам уникальный ключ данных.

Сами же данные хранятся в виде JSON-представления объекта (всего набора записей, например), будь то ссылочный тип, или набор данных регистра, или значение константы. Я его даже не упаковываю в хранилище со сжатием, пытаясь таким образом не повлиять на скорость выполнения основных операций.
Именно поэтому я говорю, что длительное накопление истории приведет к увеличению размера базы.

(4)Ничего не вытягиваю, просто пытался на скорую руку понять как глубоко Вы копали. Оказалось не глубоко, и это всё как раз обусловлено теми сценариями, которые Вы предлагаете к использованию (и тем как использовали на практике). Они вполне себе имею право на жизнь. для данной разработки.

То что версия объекта записывается целиком как раз может сильно влиять и на производительность записи и на производительность восстановления (но да - так проще, хотя и не компактно) когда они очень большие (а это не редкость, скажем, для документов в оптовой и и в розничной торговле, или при бюджетировании и много где ещё), а модифицируют в них обычно как раз "помелчи". Но для тестовых баз и так сойдёт.

Хеши отбора регистра - вещь правильная - но ни один алгоритм хеширования не гарантирует уникальности хеша. Поэтому все хеш-коллекции используют хеш-только как первый индекс, сравнивая потом полные ключи. Но для тестовых баз, наверное, и так сойдёт.

Обратный порядок - замедляет восстановления - Вам нужен только срез до выбранного момента
Обратный отсчёт индикатора - идея плохая - она намекает об отмене процесса (который до этого шёл в прямом направлении) - а у Вас идёт процесс отмены изменений - это вполне себе нормальный процесс - лучше измените на прямое направление - не смущайте народ - вот не применится транзакция этого процесса (а это легко может произойти, скажем, из-за блокировок) - вот тогда можете визуализировать декриментирование индикатора прогресса.

Сжимать JSON можно отдельно - в фоновом задании - это не будет шибко тормозить основную работу - вообще в фоне можно много оптимизации сделать

Восстанавливать тоже можно в фоне. рисуя красивый прогресс и его обратку на клиенте без блокировки сессии - если транзакция не будет принята - пока будет идти фоновая отмена этой транзакции (хотя момент завершения отмены в СУБД не поймать)


Это всё равно не понимаю. Так какой режим совместимости конфигурации нужен, чтобы поставить на неё данное расширение?

откат — (1) Отмена всех или некоторых ожидающих фиксации изменений данных, сделанных в текущей транзакции, с помощью SQL оператора ROLLBACK. Вы можете выполнить откат части транзакции, возвращаясь к точке сохранения. (2) В Oracle Forms выполнить откат… … Справочник технического переводчика

подуровень транзакции — В межсетевом шлюзе, разработанном пользователем, концептуальное группирование интерфейсных вызовов, выполняющих действия на уровне транзакции. Этими действиями могут быть инициация транзакции в системе назначения, фиксация или откат транзакции… … Справочник технического переводчика

Транзакционная NTFS — (TxF) технология в Windows Vista и последующих операционных системах, позволяющая производить файловые операции на разделе с файловой системой NTFS при помощи транзакций, обеспечивая поддержку семантики атомарности, согласованности,… … Википедия

Уровни изолированности транзакций — Уровень изолированности транзакций значение, определяющее уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность… … Википедия

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

Координатор распределённых транзакций — (DTC) компонент Microsoft Windows, предназначенный для координации изменения данных на двух или более сетевых компьютерных системах. Координатор распределённых транзакций основан на технологии COM+ и включает в себя: менеджер транзакций;… … Википедия

Обработка транзакций — Обработкой транзакции,в информационных технологиях, называется обработка информации, разделенная на отдельные неделимые операции, называемые транзакциями. Каждая транзакция должна быть успешной или неудачной как единое целое; она не может… … Википедия

Rollback — У этого термина существуют и другие значения, см. Откат назад. ROLLBACK (откат) оператор языка SQL, который применяется для того, чтобы: отменить все изменения, внесённые начиная с момента начала транзакции или с какой то точки сохранения… … Википедия

Rollback (SQL) — У этого термина существуют и другие значения, см. Rollback. Правильный заголовок этой статьи ROLLBACK. Он показан некорректно из за технических ограничений. ROLLBACK (откат) оператор языка SQL, который применяется для того, чтобы:… … Википедия

Журнализация изменений — функция СУБД, которая сохраняет информацию, необходимую для восстановления базы данных в предыдущее консистентное состояние в случае логических или физических отказов. В простейшем случае журнализация изменений заключается в последовательной… … Википедия

Журнализация транзакций — Журнализация изменений функция СУБД, которая сохраняет информацию, необходимую для восстановления базы данных в предыдущее консистентное состояние в случае логических или физических отказов. В простейшем случае журнализация изменений… … Википедия

В Oracle оператор ROLLBACK используется для отмены работы, выполняемой текущей транзакцией или транзакции, которая сомнительна.

Синтаксис

Параметры или аргументы

WORK
Необязательный. Он был добавлен в Oracle, чтобы быть SQL-совместимым. Выдача ROLLBACK с или без параметра WORK приведет к такому же результату.

TO SAVEPOINT savepoint_name
Необязательный. Оператор ROLLBACK отменяет все изменения для текущей сессии до точки сохранения указанного savepoint_name . Если этот пункт опущен, то все изменения отменяются.

FORCE 'string'
Необязательный. Он используется, чтобы принудительно откатить транзакцию, которая может быть сомнительна или повреждена. С помощью этого параметра вы указываете ID (идентификатор) транзакции в одинарные кавычки, как строку. Вы можете найти ID (идентификатор) транзакции в представлении системы под названием DBA_2PC_PENDING.

Примечание

  • Вы должны иметь DBA привилегии для доступа к системным представлениям - DBA_2PC_PENDING и V$CORRUPT_XID_LIST.
  • Вы не можете откатить к точке сохранения сомнительную транзакцию.

Пример

Рассмотрим на примере, который показывает, как оформить откат в Oracle с помощью оператора ROLLBACK.

Этот ROLLBACK пример будет выполняться так же, как следующий:

В этом примере ключевое слово WORK означает, что первые 2 ROLLBACK утверждения эквивалентны. Эти примеры откатывают текущую транзакцию.

Savepoint

Рассмотрим пример ROLLBACK, который показывает, как использовать откат к определенной savepoint (точке сохранения).
Например, вы можете написать ROLLBACK c savepoint двумя способами:

Поскольку ключевое слово WORK всегда подразумевается, оба примера ROLLBACK откатят текущую транзакцию до savepoint называемой savepoint 1.

Force

И наконец, рассмотрим пример ROLLBACK, который показывает, как принудительно откатить сомнительную транзакцию.
Например, вы можете написать ROLLBACK для сомнительных транзакций двумя способами:

Поскольку ключевое слово WORK всегда подразумевается, оба из этих примеров ROLLBACK принудят к откату поврежденной или сомнительной транзакции, идентифицированной - ID транзакции '23 .15.68 '.

Читайте также: