Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.191.234.62] |
|
Сообщ.
#1
,
|
|
|
Привет!
Расскажите пожалуйста про транзакционную безопасность. В проекте есть утилитка, которая: 1. Читает файлы, базу и некий сторонний сервис. 2. Применяет высокотехнологичную интеллектуальную логику. 3. Пишет в файлы, в базу и в некий сторонний сервис. Почти в каждой операции с большой вероятностью может произойти ошибка. Что, собственно, и происходит. Ситуация усугубляется тем, что утилитка эта очень аджайловая: очень часто требуется поменять логику. Реже меняется схема базы. "Никогда" не меняются файлы и интерфейс стороннего сервиса. Основная проблема - логика. Если натыкать много обработки исключений, логика сильно размазывается и возникают сложности с её модификациями. Если обработки исключений натыкать мало, получается что некоторые операции, которые семантически хотелось бы видеть атомарными, ведут себя не очень атомарно. Есть очень большое желание иметь логику оформленной достаточно концентрированно - вот чтобы логика и никакого мусора вокруг. (если пока ничего не понятно, в простейшем случае задача звучит так: написать "процедуру" копирования папки с файлами из одного места в другое, процедура должна вести себя атомарно - или всё копируется и все счастливы, или, если не удалось прочитать хотя бы один файл, не копируется ничего). Подскажите, пожалуйста, варианты решений. Вот что сейчас крутится в голове: 1. Возможно, есть какой-то секретный фреймворк, который поможет такого добиться (да, речь, кстати, про .NET 2.0) 2. Нужно сделать фреймворк, который будет реализовывать концепции Atomic Action (Do/Undo) и Transaction (Commit/Rollback), все операции переписать через эти концепции, логику абстрагировать. В голове крутятся паттерны Command/Memento/Interpreter. Больная фантазия где-то на фоне требует полноценный DSL, но пока удаётся её сдерживать. Даже без DSL можно добиться приемлемого результата. 3. Забить и по-тупому работать с исключениями - этого не хочется. Поругайте-похвалите мысли, поделитесь своими :-) Заранее спасибо. |
Сообщ.
#2
,
|
|
|
Черт его знает. Звучит заумно.
В С++ это всё без особых хлопот решается реализацией RAII, например (условный С++ код): class TransactionCtrl { SomeDBCtrl& m_db; bool m_committed; public: TransactionCtrl(SomeDBCtrl& db) : m_db(db), m_committed(false) { m_db.start_transaction(); } ~TransactionCtrl() { if( !m_committed ) m_db.rollback(); } commit() { m_db.commit(); m_committed = true; } }; some_db_updates() { TransactionCtrl tc(db); update ... update ... update ... tc.commit(); } |
Сообщ.
#3
,
|
|
|
Ну, по поводу RAII скажем так... он есть, но некрасивый. Вот я о чём говорил:
... public interface IAction { void Do(IContext context); void Undo(IContext context); } ... // фейковые экшены // GoodAction - действие которое всегда хорошо коммитится и роллбэчится // CustomAction - это такой тул, чтобы описывать экшены прямо инплэйс ... static void Main() { var transaction1 = new Transaction("read 3 files"); // TreatExceptionAsWarningActionDecorator - волшебный декоратор, который трактует эксепшны как ворнинги transaction1.Add(new TreatExceptionAsWarningActionDecorator(new GoodActionWithBadRollback("that will crash on rollback"))); transaction1.Add(new GoodAction("read file 1")); transaction1.Add(new GoodAction("read file 2")); transaction1.Add(new GoodAction("read file 3")); var transaction2 = new Transaction("import data"); transaction2.Add(new GoodAction("create package")); transaction2.Add(transaction1); transaction2.Add(new GoodAction("write to db")); // вот делаем инплэйс экшн transaction2.Add(new CustomAction( "do some stuff 1", delegate // ду { Console.WriteLine("DOING SOME STUFF 1"); throw new InvalidOperationException(); }, delegate // анду { Console.WriteLine("UNDOING SOME STUFF 1"); } )); transaction2.Add(new CustomAction( "do some stuff 2", ctx => Console.WriteLine("DOING SOME STUFF 2"), ctx => Console.WriteLine("UNDOING SOME STUFF 2") )); var rootContext = new RootContext(new ConsoleNotifiable()); try { rootContext.Do(transaction2); // коммитим транзакцию Console.WriteLine("DONE!"); // если попали сюда, значит коммит прошёл rootContext.Undo(transaction2); // пытаемся её откатить (просто отменить всё) } catch (TransactionFailedAndThenRollbackFailed) { // транзакция сломалась где-то посередине. начали откатывать, а откат тоже сломался :-( Console.WriteLine("ERROR: transaction failed and then rollback failed"); } catch (TransactionRollbackFailedException) { // пытались откатить успешную транзакцию, а оно не вышло :-( Console.WriteLine("ERROR: transaction rollback failed"); } catch (TransactionFailedExceptionButRollbackSucceeded) { // транзакция прошла нормально, решили сделать роллбэк, но не сложилось :-( Console.WriteLine("ERROR: transaction failed, but rollback succeeded"); } } И вот такая трассировка: trying to import data trying to create package create package info: good action create package succeeded trying to read 3 files trying to ~that will crash on rollback trying to that will crash on rollback that will crash on rollback succeeded ~that will crash on rollback succeeded trying to read file 1 read file 1 info: good action read file 1 succeeded trying to read file 2 read file 2 info: good action read file 2 succeeded trying to read file 3 read file 3 info: good action read file 3 succeeded read 3 files succeeded trying to write to db write to db info: good action write to db succeeded trying to do some stuff 1 DOING SOME STUFF 1 do some stuff 1 failed: Operation is not valid due to the current state of the object. trying to revert write to db revert write to db succeeded trying to revert read 3 files trying to revert read file 3 revert read file 3 succeeded trying to revert read file 2 revert read file 2 succeeded trying to revert read file 1 revert read file 1 succeeded trying to revert ~that will crash on rollback trying to revert that will crash on rollback revert that will crash on rollback warn: Operation is not valid due to the current state of the object. revert ~that will crash on rollback succeeded revert read 3 files succeeded trying to revert create package revert create package succeeded import data failed: Exception of type 'ACID.TransactionFailedExceptionButRollbackSucceeded' was thrown. ERROR: transaction failed, but rollback succeeded Хреново? |
Сообщ.
#4
,
|
|
|
А разве это
public class Smth : IDisposable { public bool IsCommited { get; set; } public Smth() { IsCommited = false; } public void Commit() { IsCommited = true; } public void Dispose() { if (!IsCommited) { .. } } } using (var smth = new Smth()) { smth.Commit(); } не подходит? |
Сообщ.
#5
,
|
|
|
Это подходит если у тебя одна операция. 2 операции - 2 юзинга писать? 10 операций? Я немного уточню. Есть некий алгоритм, который состоит из последовательности обращений к разным ресурсам. Обращения бывают на запись и на чтение. Что там на чтение - пофиг, но для записи хочется иметь возможность делать откат типа: сделал файл - снёс файл, или снёс файл - вернул файл (типа не удалять, а копировать в секретное место и только потом удалять исходный, а если откат делать - просто копировать обратно). Т.е. буквально нужна штука, которая будет следить сколько пунктов из алгоритма отработало и при необходимости делать откат. Это можно либо руками написать - много кода, либо небольшой фреймворк сделать - меньше кода, но писать немного извращённо придётся. Других вариантов я пока не вижу.
|
Сообщ.
#6
,
|
|
|
Я хз
System.Transactions не из этой области? Я в свое время читал-читал, но так ничего и не понял.. |
Сообщ.
#7
,
|
|
|
Выглядит реально страшно, попробую посмотреть что это. Спасибо.
|
Сообщ.
#8
,
|
|
|
Из моих знакомых это никто так и не осилил
|