Здравствуйте, Ikemefula, Вы писали:
I>Еще вариант — использовать простейший DSL, на основе например парсер комбинаторов, тогда логика превращается в набор простых и понятных правил, которые четко соответсвуют постановке задачи.
Да из пушки по воробьям это воркфлоу, а особенно DSL — не та задача, чтоб навороты лепить . Хотя в будущем возможно придется DSL городить, но уж явно не ради именно этой задачи, а чтоб последовательность шагов можно было быстро конфигурировать, но пока рано — нет необходимости. Я мемоизацией обошелся — именно то, что мне нужно, получилось и просто, и поддерживаемо, а также в стиле остального приложения. Заодно и мемоизатор чуток доработал, а то все руки не доходили.
Re: Восстановления состояния исполнения после ошибки
Вы совершенно точно смотрите не в ту сторону. Причем, как-то совсем не в ту — трединг. У вас на конкаренси столько сил уйдет, что рано или поздно вы взвоете. К тому же, масшабируемось такого подхода находится под очень большим вопросом (хотя, может быть, вам она и не нужна). Есть два принципиальных варианта, как решаются такие проблемы:
1) Обычный хэндлинг в коде. Например, перед "началом выполнения блока проверить, нет ли результат его выполнения в БД". Ну то есть выполнение каждого блока изменяет состояние системы. Вы знаете, как именно оно изменяется, от этого и пляшете.
2) Контекст. Ничего в этом сложного нет. Что мешает, например, при брошенном эксепшне прикрепить к нему в каком-то виде информацию о том, какие шаги уже выполнены, передать это клиенту. Тот исправляет ошибки, отдает обратно, вы сразу перескакиваете на нужный шаг. Это абсолютно рутинный подход, весь Web только этим и живет — куки, сессии, скрытые параметры.
Так что, я бы не рекомендовал вам изобретать велосипед на пустом месте, если на то нет веских причин.
Re[2]: Восстановления состояния исполнения после ошибки
Здравствуйте, Ikemefula, Вы писали:
I>В джаве есть библиотека, которая умеет короутины. После компиляции вызывается постпроцессор, который патчит байт-код и получаешь внятные короутины.
Здравствуйте, elmal, Вы писали:
E>Пока склоняюсь к мемоизации результатов — поведение будет именно таким, какое мне нужно, лучше и проще похоже не сделать.
Если вы можете мемоизировать результат, то непонятно почему не можете выставить флаг, что эта функция уже выполнялась
E> Говнокод типа if else, скрытые параметры типа все связано со всем не вариант — это не аутсорс, и проект поддерживать мне еще много лет, подход наклепал и забыл не прокатит, если там код будет такой, что сам черт голову сломит, то проекту кирдык, ибо денег на раздутый штат индусокодеров нет.
В "говнорешении" с if разберется любой нормальный программер, а с вашими потоками... не уверен.
Кроме того, я не уверен, что вы сами не накосячите при реализации этого решения. Вот в if-ах накосячить сложнее.
E> И да, я в курсе, что большинство в этом случае просто спагетти нагородит и не будет париться
Это не спагетти, а прагматизм. Буржуи даже базворд для него придумали KISS
Ну и еще: решение с запоминанием выполненных операций тоже универсальное, но совсем не содержит поточной магии
СУВ, Aikin
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали: E>Какие еще варианты может кто подсказать?
Может я что-то недопонимаю? Но выглядит все просто, у вас есть Executor который содержит в себе процесс. Executor знает как по шагам запускать процесс и какие данные в него передавать. У процесса может быть контекст выполнения, который хранит параметры с которыми были вызваны методы процесса.
По сути есть Excutor и есть пара процесс и контекст. Процесс выполняет бизнес логику, контекст хранит состояние в котором вызывался процесс или куда перешел.
public class Process
{
Step1(param1)
Step2(param2)
Step3(param3)
}
public Enum Steps
{
Step1
Step2
Step3
}
public class ProcessContext
{
Steps ExecutionStep;
KeyValueMap<argName,ArgValue> Params
}
public class Executor
{
KeyValueMap<Proccess, Context>
Do(Process process, par1, par2, par3)
{
Context c = getOrCreateContext();
while(c.ExecutionStep != Steps.Step3)
switch c
{
Step1: step1(process, c); break;
Step2:
}
}
step1(Process p, Context c)
{
param = ProcessContext.Params[]
p.Step1(params);
}
}
По сути это псевдо-код, но идея должна быть понятной. Основная задача сохранить и восстановить контекст выполнения процесса. это все можно запускать хоть в одном потоке хоть в 1000, без разницы.
Re[2]: Восстановления состояния исполнения после ошибки
Здравствуйте, diez_p, Вы писали:
_>По сути есть Excutor и есть пара процесс и контекст. Процесс выполняет бизнес логику, контекст хранит состояние в котором вызывался процесс или куда перешел.
_>
_>public class Process
_>{
_> Step1(param1)
_> Step2(param2)
_> Step3(param3)
_>}
_>}
_>
_>По сути это псевдо-код, но идея должна быть понятной. Основная задача сохранить и восстановить контекст выполнения процесса. это все можно запускать хоть в одном потоке хоть в 1000, без разницы.
Это называется внутренний DSL В таком виде его хорошо использовать для простых вещей. Для более сложных лучше взять всякие парсер-комбинаторы, это например если классов и степов очень много.
Думаю как лучше сделать. Имею операцию, состоящую из последовательности шагов (на самом деле в текущий момент это метод, который может вызывать другие методы). Каждый шаг (под шагом подразумевается крупный блок) может бросить исключение или нормально выполниться. В случае, если кидается исключение, нужно уведомить об этом вызывающий метод и соответствующим образом обработать.
Но... Нужно сделать, чтобы при повторном выполнении этой операции начиналось бы не с начала все, а именно с операции, которая выбросила исключение (так как возможно, что уведомление прочитали, исправили проблему и в результате можно продолжать — теперь операция пройдет).
Соответсвенно думаю как это лучше сделать. Можно сделать что то вроде chain of responsibility с дополнительными наворотами, состояние хранить явно в кеше, в случае ошибки сначала искать соотвествующую цепочку, восстанавливать контекст, передавать управление и тому подобное.
Но ИМХО это слишком сложно, затруднит чтение, придется делать неслабые рефакторинги, да и сама цепочка будет весьма навороченная, так как могут быть условия выполнения, которые придется обрабатывать. Короче говнокод будет на ровном месте, нет ли варианта попроще?
Например сейчас думаю над тем, чтобы этот метод пускать в отдельном потоке, операции, которые могут кинуть исключение аннотировать таким образом, чтобы уведомлять родительский поток а самому переходить в состояние ожидание. А саму ссылку на поток помещать в кеш, ключем которого будут стартовые аргументы.
Выглядит весьма круто и многообещающе, в этом случае за меня кучу работы сделает сама JVM, реализовать будет проще, код будет гораздо более читабельный и все пойдет практически без модификации исходного кода, правки по мелочи. Но есть проблема — если таких ошибок будет много, то могу запросто превысить максимальное число потоков и рухнуть по OutOfMemoryError.
Хотя выглядит наиболее чисто, понятно и прямо, и если бы было ключевое слово yeald с аргументом, без запуска лишнего потока, то выглядело бы вполне симпатично
Вижу третий вариант — делать мемоизацию тех участков, которые нельзя вызывать повторно, соответственно самого вызова не будет и он достанется из кеша, а выполнение продолжится с незакешированного момента. Вроде решает многие проблемы, практически вообще без модификации кода, и сейчас это основной вариант.
Какие еще варианты может кто подсказать?
12.06.13 09:54: Перенесено модератором из 'Java' — Blazkowicz
Re: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
E>Выглядит весьма круто и многообещающе, в этом случае за меня кучу работы сделает сама JVM, реализовать будет проще, код будет гораздо более читабельный и все пойдет практически без модификации исходного кода, правки по мелочи. Но есть проблема — если таких ошибок будет много, то могу запросто превысить максимальное число потоков и рухнуть по OutOfMemoryError. E>Хотя выглядит наиболее чисто, понятно и прямо, и если бы было ключевое слово yeald с аргументом, без запуска лишнего потока, то выглядело бы вполне симпатично
В джаве есть библиотека, которая умеет короутины. После компиляции вызывается постпроцессор, который патчит байт-код и получаешь внятные короутины.
Re[2]: Восстановления состояния исполнения после ошибки
Здравствуйте, seno, Вы писали:
S>Так что, я бы не рекомендовал вам изобретать велосипед на пустом месте, если на то нет веских причин.
Веские причины есть. Была б возможность, я бы просто в рамках одной транзакции все делал, в случае проблем rollback и можно повторять. Собственно раньше так все и делалось и я горя не знал. После чего в новой версии транзакции убрали и приходится вот на такие извращения идти. И у меня доступное мне API не позволяет ни отследить состояние, ни откатить руками — ничего. А завишу я вообще от сторонней системы сторонней конторы, пока они изменят API — год может пройти, а решение нужно ASAP.
Пока склоняюсь к мемоизации результатов — поведение будет именно таким, какое мне нужно, лучше и проще похоже не сделать. Говнокод типа if else, скрытые параметры типа все связано со всем не вариант — это не аутсорс, и проект поддерживать мне еще много лет, подход наклепал и забыл не прокатит, если там код будет такой, что сам черт голову сломит, то проекту кирдык, ибо денег на раздутый штат индусокодеров нет. И да, я в курсе, что большинство в этом случае просто спагетти нагородит и не будет париться, насмотрелся уже на такие решения, что потом провал по срокам года на 2 при фантастическом бюджете в результате. И задача эта типовая, может еще и пригодиться, так что мне нужно простое но в тоже время универсальное решение, ибо уже достало что большинство АПИ кривое до ужаса и хрен что сделаешь.
Re[3]: Восстановления состояния исполнения после ошибки
Ну черт его знает, я всех нюансов вашей задачи не знаю — сколько у вас этих блоков, насколько сильно ухудшит читаемость кода немного спагетти, и т.д. Это может сильно влиять на конечное решение. НО! Я могу вам привести два примера из практики.
1) Сейчас я занимаюсь системным программированием. У нас продукт на полмиллиона строк кода. И знаете — там все связано со всем. Фаулер бы покончил жизнь самоубийством, увидев этот код. И спагетти там много. И этот код не только успешно работает, и генерирует деньги, но и успешно поддерживается и развивается. Ведь спагетти не плохи сами по себе, только потому, что это спагетти. Спагетти плохи тогда, когда они используются без понимания последствий.
2) Несколько лет назад я работал над своеобразной BPM системой. По описанию, очень похожей на ваш кейс. У меня было несколько десятков активностей, разбросанных по 4м независимым системам (веб-сервисы и голый HTTP). Активности объединялись в процессы. Процесс — это граф транзишнов между активностями, описанный в XML. Результатом работы каждой активности являлся числовой код. То есть это было что-то вроде: "если активность A вернула код 100, идти в активность B". И это дело прекрасно справлялось с оркестрированием 4х независимых систем без всяких там distributed transactions и прочего дерьма. Вы можете идти в этом же направлении. Дернули сервис А. Отработал успешно? Идете в сервис B. Вернул какой-нибудь "DuplicateIdException"? Значит идете проскакиваете B и С, и идете прямиком в D.
Там логики то, что бы запрограммировать такое в общем виде — раз два, и готово.
И самое главное — это решение естественно, сравнивая с тем же взаиодействием между потоками, которое вы предложили.
Re: Восстановления состояния исполнения после ошибки
Здравствуйте, diez_p, Вы писали:
_>По сути это псевдо-код, но идея должна быть понятной. Основная задача сохранить и восстановить контекст выполнения процесса. это все можно запускать хоть в одном потоке хоть в 1000, без разницы.
Угу — это первый вариант, который рассматривал. На деле он будет несколько сложнее, так как разные шаги зависят друг от друга, и в зависимости от каких то условий может пойти одна ветка либо другая ветка исполнения, так что там будет не список шагов, а граф шагов. И один черт эти объекты придется кешировать и все объекты контекста выполнения держать в памяти. Короче слишком сложно получается, запутывание на ровном месте, и отказался я от такого быстро.
Re[3]: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
E>Здравствуйте, diez_p, Вы писали:
_>>По сути это псевдо-код, но идея должна быть понятной. Основная задача сохранить и восстановить контекст выполнения процесса. это все можно запускать хоть в одном потоке хоть в 1000, без разницы. E>Угу — это первый вариант, который рассматривал. На деле он будет несколько сложнее, так как разные шаги зависят друг от друга, и в зависимости от каких то условий может пойти одна ветка либо другая ветка исполнения, так что там будет не список шагов, а граф шагов. И один черт эти объекты придется кешировать и все объекты контекста выполнения держать в памяти. Короче слишком сложно получается, запутывание на ровном месте, и отказался я от такого быстро.
Я наверное плохо объяснил. Есть список шагов, а Executor уже сам решает, каким будет следующий шаг, в зависимости от условий.
Re[3]: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
_>>По сути это псевдо-код, но идея должна быть понятной. Основная задача сохранить и восстановить контекст выполнения процесса. это все можно запускать хоть в одном потоке хоть в 1000, без разницы. E>Угу — это первый вариант, который рассматривал. На деле он будет несколько сложнее, так как разные шаги зависят друг от друга, и в зависимости от каких то условий может пойти одна ветка либо другая ветка исполнения, так что там будет не список шагов, а граф шагов. И один черт эти объекты придется кешировать и все объекты контекста выполнения держать в памяти. Короче слишком сложно получается, запутывание на ровном месте, и отказался я от такого быстро.
Это называется воркфлоу. В джаве наверняка полно решений которые уже умеют такие вещи:
Здравствуйте, Aikin, Вы писали:
A>В "говнорешении" с if разберется любой нормальный программер, а с вашими потоками... не уверен. A>Кроме того, я не уверен, что вы сами не накосячите при реализации этого решения. Вот в if-ах накосячить сложнее.
Вопрос не в том, сможет или не сможет, а в том, сколько это займет времени, людей, бюджета и какое будет качество работы.
Разобраться в сложном воркфлоу очень сложно даже имея соответсвующие графические тулы.
Насколько ясно из описания у ТС нетривиальная задача.
Собственно реализовывать сложный конечный автомат в десяток бинарных состояний с двумя-тремя десятками переходов, что практически ни о чем, на ифах уже само по себе самоубийство, а задача прочесть такой код и найти косяк превращается в эпопею.
Если воркфлоу это нечто посложнее конечного автомата, то за ифы нужно убивать уже за само предложение
Вариант с тредами довольно простой, если знать короутины, но ресурсоемкий, работать будет примерно так
var workflow = target.restoreState().coroutine(c => {
//здесь задаетcя сам воркфлоу
...
switch(c.currentState)
{
case Actions.DeleteTempFile:
{
var result = c.yield(new ActionDeleteTempFile());
}
case Actions.ExecuteAnotherWorkflow:
{
c.yield(anotherWorkdlow);
}
}
...
});
foreach(var action in workflow){
// здесь выполняются операции, обрабатываются исключения, сохраняется результат и тд и тд
action.execute();
}
Если использовать либу, что я показал, то многопоточность и вовсе исключается. В любом случае воркфлоу все таки надо задавать графическими инструментами, а не кодом. Так же желательно что бы графические инструменты давали на выходе код, а не XML, иначе отладка воркфлоу превращается в ночной кошмар.
Еще вариант — использовать простейший DSL, на основе например парсер комбинаторов, тогда логика превращается в набор простых и понятных правил, которые четко соответсвуют постановке задачи.
Re[3]: Восстановления состояния исполнения после ошибки
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, diez_p, Вы писали:
_>>По сути есть Excutor и есть пара процесс и контекст. Процесс выполняет бизнес логику, контекст хранит состояние в котором вызывался процесс или куда перешел.
I>Это называется внутренний DSL В таком виде его хорошо использовать для простых вещей. Для более сложных лучше взять всякие парсер-комбинаторы, это например если классов и степов очень много.
Тут уже как говорится "it depends". Судя по описанию задачи автора у него процессы не очень сложные, но прежде чем писать я бы пощупал уже готовое.
Re[3]: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
E>И у меня доступное мне API не позволяет ни отследить состояние, ни откатить руками — ничего. А завишу я вообще от сторонней системы сторонней конторы, пока они изменят API — год может пройти, а решение нужно ASAP.
Не понятно. Что значит "откатить руками не позволяет". Что же ты будешь делать, если в процессе выполнения блока произойдет исключение?
Re[4]: Восстановления состояния исполнения после ошибки
Здравствуйте, Ilinichev, Вы писали:
I>Не понятно. Что значит "откатить руками не позволяет". Что же ты будешь делать, если в процессе выполнения блока произойдет исключение?
А вот именно то, что сейчас . Сообщать про исключение пользователю, и как только пользователь все поправит и повторит операцию — восстановить с момента ошибки. АПИ то там такое, что по другому никак. Грубо говоря, АПИ позволяет мне только создать новое, но методов для удаления, модификации, и даже получения данных у меня нет ни черта. И не могут сделать, так как безопасность — я создал, и все, у меня уже нет прав на отмену, то, что я создал — другому юзеру уходит, и его данные я ни прочитать, ни удалить не могу — я пароль и логин пользователя не знаю другого, кому это все ушло. А создавать я должен много, там далеко не одна операция, и каждая может упасть. Для других АПИ я пытаюсь откатить в случае ошибки, но это не всегда срабатывает — у меня зачастую прав не оказывается на откат, но хотя бы могу попытаться. А транзакционность нормальную большинство сделать не может ни черта. Сам в требованиях пишу — сделайте транзакционность, или добавьте методы чтоб я сам мог транзакционность сделать, если сделаете — будет минимум проблем! А не могут, блин, говорят что год для этого потребуется минимум. Вот и приходится выкручиваться на своей стороне как только можно и даже как нельзя, и делать максимум возможного.
Re[6]: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
E>Я мемоизацией обошелся — именно то, что мне нужно, получилось и просто, и поддерживаемо, а также в стиле остального приложения. Заодно и мемоизатор чуток доработал, а то все руки не доходили.
Если тебе хватило мемоизации, то, похоже, тебе не надо было ничего на форум писать, т.к. все было решено уже до того, как как ты "Отправить" нажал
Re[5]: Восстановления состояния исполнения после ошибки
Здравствуйте, elmal, Вы писали:
E>Сам в требованиях пишу — сделайте транзакционность, или добавьте методы чтоб я сам мог транзакционность сделать, если сделаете — будет минимум проблем!
извне транзакционность по нетранзакционному протоколу (http/tcp) ты не добавишь, а если её нет сразу, то ты без гарантии идемпотентности операций (а по косвенным признакам там ей не пахнет), не сможешь надёжно совместить свою персистентность полученных промежуточных результатов с внутренним состоянием внешней системы.
в сущности, идемпотентности вызова достаточно для внешнего пользователя для обеспечения семантики "ровно 1". а транзакции — внутреннее дело реализации каждого вызова на стороне внешней системы, зря ты о них беспокоишься.
но я к тому веду, что не имея достаточных гарантий существования надёжной реализации ты вправе писать простой вариант реализации на своей стороне, не пытающийся улучшить ситуацию — только время зря потратишь