BPM execution sequence, states, and transactions

Understand the sequence of actions performed when a process instance is created and when a task is performed by a user; know the flow nodes states, and learn about the transactions

Sequence of events at process instantiation through an instantiation form

  1. An application display the process instantiation form to the user

  2. The user enters data in the form

  3. The button of the form holding the Start process action in the UI Designer sends a POST request with the form data to start the process

  4. The submitted data are validated against the process instantiation contract

  5. If the contract is valid, the process instance is created and input values are persisted in the database

  6. Process data instances are created and initialized in the order of declaration using:

    • An operation declared on the process that saves its value to the process data

    • The initial value expression if no operation is declared

    • null if no expression or operation is defined

  7. Bonita Engine creates and initializes business data

  8. Bonita Engine creates and initializes search keys

  9. Bonita Engine initializes process instance Documents with default value coming from the definition or with submitted files

  10. Operations (that does not initialize data) are executed. Operations at process instantiation level can either come from calling process instances (for processes stared by call-activities)

  11. Bonita Engine instantiates and executes the "on enter" connectors (evaluate input expression, execute, evaluate output operation). Connectors are executed in the order of declaration:

    • Community: Execution duration is not limited. If the connector never finishes, the process instantiation will never finish

    • Subscription: Execution duration of a connector is limited to a maximum of 5 minutes by default. This timeout value can be changed by configuration. Above this limit, the connector execution is aborted and the process instantiation is considered failed.

  12. The process is instantiated, the API call finishes and the Engine executes the process flow asynchronously

Sequence of events at user task initialization

During, user task execution, Bonita Engine:

  1. Initializes task variables with their default values

  2. Executes actor filters and then potentially assigns the task to a user

  3. Launches the "on enter" connectors

  4. Evaluates the task dynamic name and description. This will be evaluated only once

The user task is now in READY state and is waiting for a user to perform and submit it.

Sequence of events at user task execution

  1. An application displays the form to the user

  2. The user enters data in the form

  3. The button of the form holding the Submit task action in the UI Designer sends a POST request with the form data to start the step

  4. The submitted data are validated against the step contract

  5. If the contract is valid input values are persisted in database and the execution continues asynchronously. Otherwise an exception is thrown and the step stays in the same state

  6. Bonita Engine executes the Operations defined for the step

  7. Bonita Engine launches the "on finish" connectors

  8. Bonita Engine processes the Description after step completion

  9. The task is complete.

Flow node states and transitions

A flow node is defined by a set of states.
Each state has a specific behaviour and its execution is wrapped into an ACID transaction.

A state is either stable, terminal, or unstable:

  • Stable: the flow node is not yet finished, but paused as it waits for input. A stable state is waiting for either:

    • A Human interaction to change (Ready/Failed).

    • A BPMN event (Waiting).

      • intermediateCatch

      • receive

      • boundary

    • Children to finish (Execution/Cancelling/Aborting/CompletingSubTaskOfManualTask…​.).

      • Loop

      • MultiInstance

      • CallActivity…​

A flownode will stay in a stable state until the event it is waiting for occurs. No work is generated in the meantime. The transaction is committed.

  • Terminal: A terminal state is a stable state. It is the last state reached by a flow node. Executing the flow node in terminal state will archive the flow node, delete it and trigger the asynchronous execution of the following elements of the process.
    You can not "come back" from a terminal step. The one exception is the transition COMPLETED -> FAILED.

  • Unstable

All other states are unstable. When a flow node reaches an unstable state, a work will be generated to change to another state. Examples: ExecutingAutomaticActivity, InitializingAndExecutingFlownode, EndingIntermediateCatchEvent…​

Transitioning from state to state

Executing a state of a flownode is running the code of the execute() method on the current state, and then moving the flownode to the next state (determined by the transitions).
When executing the state of a flownode, a return code indicates if the execution of the state is finished, meaning we can move on to the next state, or not finished, meaning we stay on the same state, some background task will trigger the move to the next state later (Eg. connector execution).

If the state is finished, the State Machine determines the next state:
Each flownode type has an ordered list of states.
We take the next state in the list, and ask the state to determine whether we should execute the state or not.
If not, the next state is "skipped" and the second next state is checked the same way, until a state determines it should be executed.
If so, the next state is executed, in another background task (if terminal or not stable).

State sequence at activity execution

  1. Get the current state of the activity

  2. Execute the state’s behavior

  3. Find the next state of the flow node and set it as the current state

    1. If the state is stable, the transaction is committed, and the API call is returned

    2. If the state is terminal, the transaction is committed, Bonita Engine triggers the asynchronous execution of the followings elements of the process and the API call is returned

    3. If the state is neither stable nor terminal, the transition to the next state is scheduled asynchronously

Work service mechanism

Diagram of the details of user task execution
  1. Bonita Engine commits the transaction and then submits a work to execute the connectors asynchronously. The connectors are executed outside any transaction and thus are not a problem for the data integrity if the execution takes too long

  2. As soon as there is a free slot in the Work Service, it executes the work, which is in fact the connector execution

  3. When a connector execution is finished, if there are other connectors, they are executed in the same way. If there are no more connectors, Bonita Engine continues to execute the state’s behavior by triggering a new work

  4. When the engine executes a state’s behavior, it updates the display name, and then sets the activity to the state Ready. As this is a stable state, the engine commits the transaction and stops

  5. The state READY will then be executed through an API call

Short transactions and asynchronism

Transactions in Bonita Engine are as small as possible, and each transaction is committed as soon as possible.
Each unit of work uses a non-blocking queued executor mechanism and is thus asynchronous. There is a dedicated queue for asynchronous executions. (Connector execution is handled in a separate execution queue.)

As a consequence of the design, when an asynchronous work unit originates from an API call (which might be a result of a human action), then the call returns and ends the transaction.
The work unit is then executed as soon as possible, asynchronously, in a separate transaction.
For this reason, a task that is being initialized might not yet be ready for execution, but will be executable after a short while, depending on the work executor availability.
A client application therefore needs to poll regularly to check when the asynchronous work unit is finished, or write an event handler in order to be notified.

As a general rule, 1 API call = 1 transaction. When an API call is made, a transaction is automatically opened and this transaction is also automatically closed at the end of the API call.

There are a few exceptions: user login/logout, platform start/stop/clean & the entirety of platformMonitoringAPI.
As an example, calling processAPI.searchXXX() is done in a single transaction. In that case, 2 SQL queries are executed: one for the total count, one for the paginated list of results. As Bonita transactions are ACID, the results of the 2 queries are consistent with each other.

Connector cost in terms of transactions

If there is a connector to execute in the state’s behavior, then the transaction is committed and the connector is executed outside of any transaction.
The flow node stays in the current state while the connector is being executed.
When the execution of the last connector is complete, the state’s behaviour is completed. If you are using a Bonita Subscription edition, a timeout limit can be set for connector execution.

Example: User task

The diagrams below show the transactions and states when a user task is executed. The vertical line represents the condition necessary to execute the current state. The first state is initializing: it is automatically executed and the flow node goes to next state (READY) but is executed only after an API call.

In the first diagram, the task contains connectors

Diagram of the states and transactions when a user task with connectors is executed

In the second diagram, there are no connectors in the task

Diagram of the states and transactions when a user task with connectors is executed

As you can see in these illustrations, there is a non-negligible cost when adding some connectors on an activity:

  • If there is no connector to execute, then the state executes in one transaction

  • If there is at least one connector to execute in the state, the state execution requires at least three transactions:

    • The first transaction is committed just before the execution of the connectors. There is one transaction for this, whatever the number of connectors

    • The connectors are not transactional. Nevertheless, a transaction is needed to save the output data of the connector execution. There will be a transaction for each connector that is executed

    • The last transaction is used to continue to execute the current state’s behavior, and to set the state to the next reachable one (but not execute it)

If the connector execution never ends because the external system does not have a timeout, the connector instance is re-executed at next server startup (or automatically by the recovery mechanism, if your Bonita Platform is Bonita 2021.1 or greater).

Abort/Cancel procedure

Aborting is the action of changing the flow of transitions from the normal flow to the ABORTING sequence of states. It is triggered by the Bonita Engine itself.
Cancelling is the same notion, but triggered by a human interaction. The flow is the CANCELLING sequence of states.

Aborting / cancelling a flow node is only setting its flag stateCategory to ABORTING / CANCELLING + registering works to execute the flow nodes.

Aborting a flow node

When the execution of a flow node sees that the state category of the flow node is not the same as the state category of the state (determined by the State Machine), then the current state is not executed (to the contrary of a normal case).-
Then the next state is the first state of the aborting flow sequence for that type of flow node.
Then the state is executed in a background task, as usual, and then follows the aborting flow sequence of states, until it reaches the last state in that sequence.

Cancelling a flow node

Cancelling a flow node is exactly the same as aborting a flow node, but the flow sequence of states is the CANCELLING sequence.

Special States

Non executing states

To determine the next state, we execute the shouldExecuteState() code. If this method returns false, the execute() method is not run. We then execute the shouldExecuteState() code of the next state (determined by the state transition manager), etc. until the method returns true.

Summary of state types

  • INITIALIZING: indicates that an activity is being initialized.

  • READY: indicates that a user or manual task has been initialized but is not yet being executed.

  • WAITING: indicates that a RECEIVE_TASK, BOUNDARY_EVENT or INTERMEDIATE_CATCH_EVENT activity is waiting for some external trigger.

  • EXECUTING: indicates that an activity is being executed.

  • FAILED: indicates that a task has failed because of a problem in execution, for example because of an exception that was not anticipated, a connector that fails, or bad expression design.

  • SKIPPED: indicates that a task that failed because of connector execution failure is being skipped instead of re-executed. Skipping a task skips the execution of any connectors not already executed and proceeds to task completion.

  • CANCELLED: indicates that an activity is cancelled by a user.

  • ABORTING: indicates that an activity is cancelled by the system. For example, an interrupting event sub-process can trigger ABORTS for all other active paths.

  • COMPLETED: indicates an activity that is complete.

  • ERROR: not currently used.