Skip to main content

Asynchronous Execution

Introduction

Flowable provides the possibility to mark plan items in CMMN models as asynchronous. This controls when and how the plan item is executed, specifically by introducing transaction boundaries in the case execution. Understanding async execution is key to building robust, production-grade cases.

A common misconception is that "async" means the case will skip ahead and execute other plan items in parallel. In reality, the case execution semantics remain unchanged — async controls transaction boundaries, not the order of execution.

Synchronous vs Asynchronous Execution

Synchronous (default)

By default, the Flowable CMMN engine executes as far as possible within a single transaction — from the triggering action (e.g., completing a human task) all the way to the next wait state (e.g., the next human task). If any plan item fails during execution, the entire transaction rolls back, undoing all progress.

This means:

  • The user sees errors immediately
  • Long-running operations block the calling thread (e.g., the UI shows a spinner)
  • A failure in one plan item rolls back all other plan items that were part of the same transaction

Asynchronous

When a plan item is marked as async, the engine introduces a transaction boundary at that point. The engine:

  1. Commits the current transaction, persisting all progress made so far
  2. Creates a job in the database representing the work to be done
  3. Returns control to the caller immediately
  4. The Async Executor picks up the job and executes the plan item in a new transaction on a separate thread

This means:

  • Users get immediate feedback — the API call returns quickly
  • Previously completed work is safe — a failure in the async plan item does not roll back earlier work
  • The engine takes responsibility for retrying failures automatically
  • Shorter transactions reduce database lock contention

The Async Flag

Plan items in CMMN such as service tasks, process tasks, case tasks, and other elements support the async flag. There are two variants:

Async (before)

When Async is enabled on a plan item, the engine pauses execution before the plan item runs. It persists the current state, creates a job, and returns. The Async Executor later picks up the job and executes the plan item in a new transaction.

Async leave (after)

When Async leave is enabled, the plan item itself executes synchronously, but its completion (including leaving the plan item and any lifecycle listeners) happens asynchronously in a new transaction.

When to Use Async

Use async when:

  • The plan item calls an external service that may be slow or unreliable (e.g., an HTTP call, a third-party API)
  • You want the user to get immediate feedback rather than waiting for downstream processing
  • You want the engine to handle retries automatically for transient failures
  • You want to protect already-completed work from being rolled back by a later failure
  • Multiple plan items become active simultaneously in a stage and you want to avoid long-running transactions

Avoid async when:

  • The operation requires immediate confirmation that it completed successfully (e.g., security-critical operations like revoking permissions)
  • You need the calling thread to know the result of the operation before proceeding

The Async Executor

The Async Executor is the engine component responsible for picking up and executing async jobs. It runs within the Flowable application and manages:

  • Job acquisition: Periodically scanning for jobs that are ready to execute
  • Job execution: Running jobs via a configurable thread pool
  • Job persistence: All jobs are stored in the database and survive application restarts
  • Error handling: Automatically retrying failed jobs

Job Types

The engine uses different database tables for different job states:

Job TypeDescription
Async JobsCreated when an async-marked plan item is reached. Represents work ready to be executed.
Timer JobsCreated by timer event listeners. A dedicated thread checks for timers whose due date has passed, then converts them into async jobs.
Suspended JobsJobs belonging to suspended case instances are moved here so they don't interfere with normal job acquisition.
Dead Letter JobsJobs that have exhausted all retry attempts. Require manual intervention.

Error Handling and Retries

When an async job fails during execution:

  1. The job is converted into a timer job with a future due date (providing a cooling-off period)
  2. When the timer fires, the job is converted back into an async job for retry
  3. The job has a retry counter (default: 3 retries)
  4. If the job continues to fail after all retries, it is moved to the dead letter table

Dead letter jobs require manual administrator intervention — the administrator can inspect the exception message, fix the root cause (e.g., restore a downed service), and replay the job. This can be done through the Flowable Control application.

This automatic retry mechanism is particularly valuable for transient failures like network timeouts, temporary service unavailability, or brief database connection issues.

Exclusive Jobs

The Problem

CMMN cases are inherently more dynamic than BPMN processes — multiple plan items within a stage can become active simultaneously. When multiple async plan items within the same case instance execute concurrently, they may modify the same case instance data simultaneously. This can cause optimistic locking exceptions because multiple threads try to update the same database row.

The Exclusive Flag

By default, async jobs are exclusive (Exclusive = true). This ensures that only one exclusive job per case instance executes at a time. Other exclusive jobs for the same case instance wait until the current one completes.

This prevents optimistic locking exceptions, but means concurrent plan items with exclusive async flags execute sequentially within a single case instance. Jobs from different case instances still run fully in parallel.

When to Disable Exclusive

Set Exclusive = false if you explicitly want parallel execution of plan items within the same case instance and can tolerate potential optimistic locking exceptions (which will be automatically retried). Be cautious: retries of non-exclusive jobs can cause duplicate side effects if operations are not idempotent.