Comments 9

Спасибо за материал, возможно при прохождении какого нибудь собеседования понадобится, как и многопоточный сиглтон:) За 8 лет работы на проектах разного уровня, сталкивался только со вторым и то… На собеседованиях)

Как координатор может помочь в следующей ситуации?
Допустим есть две базы, запускается общая транзакция, изменения в каждой базе произведены успешно, настало время сохранить их — в первой базе commit прошел успешно, во второй — упал и изменения откатились.
Хороший вопрос, заставляет задуматься!

Применительно к модели КТР, когда упал второй commit в записи транзакции фиксируем ошибку. И тут возможны несколько вариантов которые очеквидны, но которые я не детализировал.

1. Самый правильный вариант — откатить всю транзакцию. В составе КТР вам нужен механизм отката изменений по первому commit. Соответственно, как только есть ошибка, срабатывает этот механизм, вы используете данные до изменения (ну вы где-то их храните) и возвращаете все в первоначальный вид в первой базе. И удалаете всю транзакцию.

2. Второй вариант — повторы — это указано в модели. Просто накруичваете N повоторов по второму commit. Если исчерпано кол-во поторов — - восстановление данных по первому комиту и откат распределнной транзакции. Вариант с повторами — проблематичный.

В модели КТР — не рассмотрены механизмы «отката» в связи с вашим случаем. Но принципиально их может быть два основных:

1. Вы сохраняете изменяемые данные по ВСЕМ базам и в вашем случае возвращаете все на место. Не важно сколько баз было. Лучше всего сделать аналогичную таблицу/ы и туда копировать запись предполагаемую на изменение.

2. Когда есть возможность, использовать спец. поле RecordState добавляемое в каждую таблицу изменяемую транзакцией. У RecordState тип данных bit/boolean/int, где 0 = not ready 1= ready. Ставите 1 когда все ок по комиту. Если нет то 0 так и остается и записи позже просто удаляете. Но система использующая данные таблицы должна игнорировать записи с RecordState = 0. Этот вариант требует доработки системы и он сложнее.

Надеюсь, я смог ответить на ваш вопрос.

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

Вопрос по организации подобной штуки на собеседовании для меня бы был странным. Но я не шарпист, где это может в порядке вещей.

P.S. Откатить уже подтвержденную транзакцию вовсе не тривиальная задача, если транзакция затронула несколько таблиц в базе.
>>> P.S. Откатить уже подтвержденную транзакцию вовсе не тривиальная задача, если транзакция затронула несколько таблиц в базе.

Ну это да. Скорее всего тут надо задуматься о полной реорганизации системы, так как в тяжелом и запущенном случае если хаос доминирует, то никакие транзакции реализованы быть не могут.
Для уменьшения вероятности такого случая есть древний (лично видел его ещё в API Interbase 4.0, который в комплекте с самой первой Delphi шел) механизм: двухфазная фиксация.
Идея в том, что завершение транзакции разбивается на две фазы. Все (ну, в реальности — почти все, потому как «стопроцентную гарантию дает только страховой полис»© ) операции, которые могут вызвать отказ фиксации, выполняются в первой фазе транзакции. А во второй фазе выполняются только операции, отказ при которых «невозможен»: транзакция помечается как завершенная, освобождаются все связанные с ней ресурсы (блокировки, в частности) и т.п.
Соответственно, координатор транзакций — само приложение или специально обученная программа типа MS DTC — сначала выполняет первую фазу у всех участников (в терминологии MS DTC или System.Transactions — менеджеров транзакций), а потом — вторую фазу у них же.
А вот, что происходит, если между первой и второй перерывчик подзатянулся (например, один из менеджеров транзакций упал) — за это я не скажу, потому что программист я не настоящий, а это надо в конкретную документацию по конкретному координатору лезть.
PS Ну, а теорию все наверное помнят: что в распределенной системе с ненадежным каналом связи между частями невозможно гарантировать, что все части находятся в согласованном состоянии. Так что, если в принципе, то место для ошибки найдется при любой схеме координации транзакций. И речь идет лишь о том, чтобы снизить вероятность ошибки.

Конкретно в interbase (firebird) проблемные транзакции становятся «in limbo» и затронутые ими записи остаются залочены. По сути, база ломается, т.к. надо вручную чинить
firebirdsql.org/manual/gfix-transactions.html
Надеюсь этот подход показался вам интересным, пишите что вы об этом думаете, коллеги и друзья!

Думаю, что в случае использования платформ от Microsoft (Windows Server, Azure, .Net, ODBC/OLE DB/ADO.Net совместимые поставщики данных, которые есть и для сторонних баз данных) есть хороший шанс обойтись без изобретения своего велосипеда — самописного координатора распределенных транзакций.
А вместо этого использовать классы из System.Transactions, с которыми многие такие поставщики интегрируются как менеджеры транзакций (в терминологии System.Transactions)
А если не интегрируются «из коробки», то можно попробовать обойтись малой кровью — реализацией своего менеджера транзакций для источника, как написано в документации. Насколько мне известно, этот механизм остался во всех последующих версиях .Net Framework.
А также — перенесен в актуальные версии .Net Core — по крайней мере, при беглом взгляде на документацию по нему нужные классы, интерфейсы и методы, вроде как, есть. Но сам я ничего в этой области не писал, так что как в реальности там обстоят дела, утверждать с уверенностью не могу.
Тут не хватает главной опции: не делать распределенные транзакции, использовать distributed workflow.
Only those users with full accounts are able to leave comments. Log in, please.