Способы запуска процесса остановленного на связи между действиями

Версия - 3.2.0.86.

Итак, любой процесс при его выполнении создает записи в таблице tbl_WorkflowItem. К созданной записи "цепляется" текущее состояние процесс в виде xml файла помещаемого в поле типа BLOB (все той же таблицы). При восстановлении процесса по WorkflowItemID необходимо выполнить поиск последнего не выполненного WorkflowItemID.
Поиск этого ID осуществляется с помощью фильтра для поиска, который назван - "LastForWorkflowID". Фильтр осуществляет вызов функции возвращающие в качестве результата последний WorkflowItemID.
Текст этого SQL-фильтра таков:

tbl_WorkflowItem.ID = dbo.fn_GetLastWFItemID(:LastForWorkflowID)

Текст функции fn_GetLastWFItemID таков:

ALTER FUNCTION [dbo].[fn_GetLastWFItemID]
(
        @WFID uniqueidentifier
)
RETURNS uniqueidentifier
AS
BEGIN
        DECLARE @WFIID uniqueidentifier;
        DECLARE @CurWFID uniqueidentifier;
        SET @CurWFID = @WFID;
        DECLARE @COUNTER INT;
        SET @COUNTER = 0;
        LOOP:
        SELECT TOP(1) @WFIID = WFI.ID,
                @CurWFID = PWF.ID
                FROM tbl_WorkflowItem WFI
                LEFT OUTER JOIN tbl_Workflow PWF ON
                        WFI.ID = PWF.ParentSubProcessItemID
                WHERE WFI.StateID != '{C4C4E809-E2B1-492F-B180-7B5A41035849}' AND
                        WFI.WorkflowID = @CurWFID
                ORDER BY WFI.CreatedOn DESC    
        IF (@CurWFID IS NOT NULL AND
                @WFIID IS NOT NULL AND
                @COUNTER 15)
        BEGIN
                SET @COUNTER = @COUNTER + 1;
                GOTO LOOP;
        END
        RETURN @WFIID;
END

А теперь рассмотрим ситуацию, когда абсолютно "здоровый" процесс ложится и восстановить его "штатными" средствами не представляется возможным.

Итак, параллелизма у нас никакого нету, поэтому гарантирована все действия в процессе выполняются последовательно. Исходя из этого, при определенном стечении обстоятельств, может возникнуть ситуация, когда предыдущее действие процесса было завершено, а следующее не успело быть создано. Т.е. грубо говоря процесс по некоторым причинам (Выключение питание, Закрытие Terrasofta, Системный сбой) "лег" на link-e.
В этом случае стандартный вызов функции fn_GetLastWFItemID не вернет результата, а следовательно, вызвать процесс методом WorkflowEngine.ProcessWorkflowItem() будет не возможно.

Как решить эту проблему ?
Мое временное решение - программно изменить состояние действия и выполнить запуск процесса именно с этого WorkflowItemID.

Нравится

8 комментариев

Присоединяюсь к вопросу. Ситуация актуальна и для версии 3.3.2

Добрый день!

Данный вопрос передан на 4-ю линию - в отдел разработки.
Сейчас ведутся тестирования и исправления.

Белецкий Арсений
и.о. Руководителя I линии поддержки

Дополнительный вопрос! Есть ли такая беда под версиями 3.4.х?

Краткая логика работы БП:
1. Вызов функции WorkflowEngine.ProcessWorflowItem(WorkflowItemID)
2. Для этого Item’а устанавливается признак «Выполнен»
3. Создается запись следующего процесса с признаком «В ожидании»
4. Запускается следующий Item

Данная проблема может проявиться только в том случае, если обрыв связи произойдет между пунктами 2 и 3. Вероятность падения системы между этими пунктами достаточно мала. Больше вероятности падения между пунктами 3 и 4, так как между ними происходит обработка в конфигурации. Поэтому, временное решение, которое Вы предложили, на данный момент, является оптимальным. Его также можно дополнить:
1) Искать первый элемент с состоянием «В ожидании» после последнего элемента с состоянием «Выполнен» (это в том случае, когда «падение» произошло после п.3)
2) Если не найден, то действительно нужно у последнего выполненного изменять состояние и «толкать дальше»
Также можно в конфигурации перед вызовом ProcessWorkflowItem запускать транзакцию, а после – подтверждать.

Естественно можно и в ядре выполнять пункты 2, 3 в одной транзакции, но это может привести к другим последствиям, вероятность которых будет выше чем подобное «падение» процесса.

Но и с транзакционным подходом есть проблемы, например:
Вы поставили результат задачи в состояние «Выполнена» - вызывается WorkflowEngine.ProcessWorflowItem(). Происходит «падение», транзакция не подтверждена – в итоге этот Item имеет состояние «В процессе» и следующий процесс не запустился. В этом случае также нужно искать последний, не выполненный процесс и запускать его, или же, в случае задачи – изменить ее состояние.

В версиях 3.4.х логика идентичная

Спасибо, Дмитрий!

Не совсем понятна фраза "Также можно в конфигурации перед вызовом ProcessWorkflowItem запускать транзакцию, а после – подтверждать". Хотелось бы больше узнать о возможностях использования транзакций.
У вас есть готовое решение как имея WorkflowID получить последний валидный WorkflowItemID или эту логику нам необходимо разработать самим?

Геннадий,

Использование транзакций из конфигурации:
Connector.DBEngine.StartTransaction();
Connector.DBEngine.CommitTransaction();
Connector.DBEngine.RollbackTransaction();

Если написать так:
Connector.DBEngine.StartTransaction();
NextWorkflowItemID = WorkflowEngine.ProcessWorflowItem(WorkflowItemID);
Connector.DBEngine.CommitTransaction();

Если же произойдет «падение» во время выполнения NextWorkflowItemID = WorkflowEngine.ProcessWorflowItem(WorkflowItemID), то элемент с ID = WorkflowItemID не перейдет в состояние «Выполнен», так как транзакция не подтверждена.

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

Спасибо!
Очень бы хотелось услышать ответ на вопрос.

"Березкин Геннадий" написал:У вас есть готовое решение как имея WorkflowID получить последний валидный WorkflowItemID или эту логику нам необходимо разработать самим?

Геннадий, к сожалению, как такового готового решения у нас нету. Если исходить из того, что последним валидным WorkflowItemID будет тот у которого состояние = "выполен", то необходимо профильтровать датасет ds_WorkflowItemID по WorkflowID, полученный результат профильтровать по StateID = "выполнен", и далее выбрать тот WorkflowItemID у которого значение даты в поле ActualExecuteDate максимальное.

Показать все комментарии