Перезапуск экземпляра процесса

Этот раздел перенесён из документации Camunda 7 и в дальнейшем будет доработан с учётом особенностей OpenBPM Engine

После завершения (termination) экземпляра процесса его исторические данные всё ещё существуют и могут использоваться для восстановления экземпляра процесса при условии, что уровень истории (history level) установлен в FULL. Это может быть полезно, например, когда завершение прошло не так, как ожидалось. Возможные сценарии использования этого API:

  • Восстановление последнего состояния экземпляров процесса, которые были ошибочно отменены (canceled)

  • Перезапуск экземпляров процесса после завершения, вызванного неверным решением

Для выполнения такой операции process engine предоставляет API перезапуска экземпляра процесса (process instance restart API), вход в которое осуществляется через RuntimeService.restartProcessInstances(…​). Этот API позволяет указать несколько инструкций инстанциации (instantiation instructions) за один вызов, используя fluent builder.

Обратите внимание, что эти операции также доступны через REST: {{< restref page="restartProcessInstance" text="Restart Process Instance" tag="Process-Definition" >}} и {{< restref page="restartProcessInstanceAsyncOperation" text="Restart Process Instance (async)" tag="Process-Definition" >}}

Перезапуск экземпляра процесса на примере (Process Instance Restart by Example)

В качестве примера рассмотрим следующую модель процесса, где красные точки обозначают активные задачи:

Running Process Instance

Предположим, что экземпляр процесса был отменён извне сотрудником (worker) с помощью следующего кода:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.deleteProcessInstance(processInstance.getId(), "any reason");

После этого менеджер решает восстановить последнее состояние этого экземпляра процесса.

runtimeService.restartProcessInstance(processInstance.getProcessDefinitionId())
	.startBeforeActivity("receivePayment")
	.startBeforeActivity("shipOrder")
	.processInstanceIds(processInstance.getId())
	.execute();

Экземпляр процесса был перезапущен с последним набором переменных. Однако в перезапущенном экземпляре процесса устанавливаются только глобальные переменные. Локальные переменные можно установить вручную, например, вызвав RuntimeService.setVariableLocal(…​).

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

Обратите внимание: Идентификаторы (id) исторического и перезапущенного экземпляров процесса различаются.

Операционная семантика (Operational Semantics)

Ниже задокументирована точная семантика функции перезапуска экземпляра процесса. Рекомендуется прочитать этот раздел, чтобы полностью понимать эффекты, возможности и ограничения этой функциональности.

Типы инструкций инстанциации (Instantiation Instruction Types)

Fluent builder перезапуска экземпляра процесса предоставляет следующие инструкции, которые можно отправлять:

  • startBeforeActivity(String activityId)

  • startAfterActivity(String activityId)

  • startTransition(String transitionId)

Выбор экземпляров процесса для перезапуска (Selecting process instances to restart)

Экземпляры процесса можно выбрать для перезапуска, либо передав набор идентификаторов экземпляров процесса, либо передав запрос по историческим экземплярам процесса (historic process instance query). Также можно указать и то и другое: список идентификаторов и query. Тогда экземпляры процесса для перезапуска будут объединением (union) результирующих множеств.

Список экземпляров процесса (List of Process Instances)

Экземпляры процесса, которые нужно перезапустить, можно указать как список идентификаторов экземпляров процесса:

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .execute();

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

ProcessDefinition processDefinition = ...;

HistoricProcessInstance instance1 = ...;
HistoricProcessInstance instance2 = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(instance1.getId(), instance2.getId())
  .execute();

Запрос по историческим экземплярам процесса (Historic Process Instance Query)

Если экземпляры заранее неизвестны, экземпляры процесса можно выбрать с помощью запроса по историческим экземплярам процесса:

HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService
  .createHistoricProcessInstanceQuery()
  .processDefinitionId(processDefinition.getId())
  .finished();

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .historicProcessInstanceQuery(historicProcessInstanceQuery)
  .execute();

Пропуск listener’ов и input/output mappings (Skipping Listeners and Input/Output Mappings)

Можно пропустить вызовы execution и task listeners, а также input/output mappings для транзакции, которая выполняет перезапуск. Это может быть полезно, когда перезапуск выполняется в системе, которая не имеет доступа к развёртываниям (deployments) соответствующих process applications и содержащимся в них классам.

В API для этого предназначены два метода #skipCustomListeners и #skipIoMappings:

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .skipCustomListeners()
  .skipIoMappings()
  .execute();

Перезапуск экземпляра процесса с исходным набором переменных (Restarting a Process Instance With the Initial Set of Variables)

По умолчанию экземпляр процесса перезапускается с последним набором переменных. Чтобы вместо этого использовать исходный набор переменных, применяется метод initialSetOfVariables.

Эта функция не просто копирует переменные старта (start variables), но копирует первую версию всех переменных процесса, которые были установлены в стартовой активности (start activity) старого экземпляра процесса.

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .initialSetOfVariables()
  .execute();

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

Не переносить business key исторического экземпляра процесса (Omitting the Business Key of a Historic Process Instance)

По умолчанию экземпляр процесса перезапускается с тем же business key, что и исторический экземпляр процесса. При использовании метода withoutBusinessKey business key перезапущенного экземпляра процесса не устанавливается.

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .withoutBusinessKey()
  .execute();

Выполнение (Execution)

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

Ниже приведены причины предпочесть один вариант другому:

  • Используйте синхронное выполнение, если:

  • количество экземпляров процесса небольшое

  • перезапуск должен быть атомарным, то есть должен быть выполнен немедленно и должен завершиться ошибкой, если хотя бы один экземпляр процесса не может быть перезапущен

  • Используйте асинхронное выполнение, если:

  • количество экземпляров процесса большое

  • все экземпляры процесса должны быть перезапущены независимо от других экземпляров, то есть каждый экземпляр перезапускается в своей собственной транзакции

  • перезапуск должен выполняться другим потоком, то есть job executor должен обработать выполнение

Синхронное выполнение (Synchronous execution)

Чтобы выполнить перезапуск синхронно, используется метод execute. Он будет блокировать выполнение до тех пор, пока перезапуск не завершится.

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .execute();

Перезапуск считается успешным, если все экземпляры процесса могут быть перезапущены.

Асинхронное batch-выполнение (Asynchronous batch execution)

Чтобы выполнить перезапуск асинхронно, используется метод executeAsync. Он сразу вернёт ссылку на batch, который выполняет перезапуск.

ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;

Batch batch = runtimeService.restartProcessInstances(processDefinition.getId())
  .startBeforeActivity("activity")
  .processInstanceIds(processInstanceIds)
  .executeAsync();

При использовании batch перезапуск экземпляров процесса разбивается на несколько jobs, которые выполняются асинхронно. Эти batch jobs выполняются job executor’ом. См. раздел batch для дополнительной информации. Batch считается завершённым, если все batch execution jobs успешно завершены. Однако, в отличие от синхронного выполнения, не гарантируется, что будут перезапущены либо все, либо ни один экземпляр процесса. Поскольку перезапуск разбит на несколько независимых jobs, каждая отдельная job может завершиться ошибкой или успехом.

Если restart job завершается ошибкой, job executor выполняет повторные попытки (retries), и если повторов не осталось, создаётся incident. В этом случае, чтобы завершить batch‑перезапуск, требуется ручное вмешательство: можно увеличить число retries у job или удалить job. Удаление отменяет перезапуск конкретного экземпляра, но не влияет на batch сверх этого.

Лицензия и атрибуция

Эта документация была создана на базе материала "Camunda 7 Docs" от Camunda, находится под лицензией Creative Commons Attribution-ShareAlike 3.0 Unported License .

Оригинал документации: https://docs.camunda.org