Skip to main content

Parallel Gateway (AND)

The Parallel Gateway executes ALL outgoing paths simultaneously and waits for ALL paths to complete before continuing. It's the gateway to use when you need true parallel execution.

Symbol

In BPMN diagrams, it's shown as a diamond with a "+" (plus) marker.

When to Use

Use Parallel Gateways when you need to:

  • Run multiple background checks simultaneously
  • Send notifications through multiple channels at once
  • Collect data from multiple sources in parallel
  • Execute independent tasks that don't depend on each other
  • Any scenario where tasks can and should run at the same time

Visual Example

Result: All three checks run at the same time, process waits for all to complete


Token-Based Execution Model

Parallel Gateways use a token-based execution model to enable true parallelism:

How It Works

Execution Phases

Phase 1: Split (Fork)

  1. Process reaches parallel gateway with one active token
  2. Gateway detects multiple outgoing flows
  3. Creates a NEW execution token for EACH outgoing flow
  4. Each token knows its path and executes independently
  5. Parent token is deactivated

Phase 2: Independent Execution

  1. Each token executes its assigned path
  2. Tokens don't interfere with each other
  3. Each token tracks its own position and state
  4. Process variables are shared across all tokens

Phase 3: Join (Synchronization)

  1. When a token completes its path, it arrives at the join gateway
  2. Join gateway tracks arrivals: 1/3, 2/3, 3/3
  3. Gateway WAITS until ALL tokens arrive
  4. Once all tokens arrive, they merge into a single token
  5. Process continues with the merged token

BPMN XML Structure

Complete Example

<!-- SPLIT: Create parallel execution paths -->
<bpmn:parallelGateway id="Gateway_ParallelSplit"
name="Start Background Checks">
<bpmn:incoming>Flow_In</bpmn:incoming>
<bpmn:outgoing>Flow_CreditCheck</bpmn:outgoing>
<bpmn:outgoing>Flow_EmploymentVerify</bpmn:outgoing>
<bpmn:outgoing>Flow_DocumentReview</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Define the three parallel flows -->
<bpmn:sequenceFlow id="Flow_CreditCheck"
name="Credit Bureau Check"
sourceRef="Gateway_ParallelSplit"
targetRef="Task_CreditBureau" />

<bpmn:sequenceFlow id="Flow_EmploymentVerify"
name="Employment Verification"
sourceRef="Gateway_ParallelSplit"
targetRef="Task_VerifyEmployment" />

<bpmn:sequenceFlow id="Flow_DocumentReview"
name="Document Review"
sourceRef="Gateway_ParallelSplit"
targetRef="Task_ReviewDocuments" />

<!-- Three tasks that execute in parallel -->
<bpmn:serviceTask id="Task_CreditBureau"
name="Credit Bureau Check">
<!-- Task implementation -->
</bpmn:serviceTask>

<bpmn:serviceTask id="Task_VerifyEmployment"
name="Verify Employment">
<!-- Task implementation -->
</bpmn:serviceTask>

<bpmn:userTask id="Task_ReviewDocuments"
name="Review Documents">
<!-- Task implementation -->
</bpmn:userTask>

<!-- Connect all tasks to join gateway -->
<bpmn:sequenceFlow id="Flow_FromCredit"
sourceRef="Task_CreditBureau"
targetRef="Gateway_ParallelJoin" />

<bpmn:sequenceFlow id="Flow_FromEmployment"
sourceRef="Task_VerifyEmployment"
targetRef="Gateway_ParallelJoin" />

<bpmn:sequenceFlow id="Flow_FromDocuments"
sourceRef="Task_ReviewDocuments"
targetRef="Gateway_ParallelJoin" />

<!-- JOIN: Wait for all paths to complete -->
<bpmn:parallelGateway id="Gateway_ParallelJoin"
name="Wait for All Checks">
<bpmn:incoming>Flow_FromCredit</bpmn:incoming>
<bpmn:incoming>Flow_FromEmployment</bpmn:incoming>
<bpmn:incoming>Flow_FromDocuments</bpmn:incoming>
<bpmn:outgoing>Flow_Continue</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Continue after all complete -->
<bpmn:sequenceFlow id="Flow_Continue"
sourceRef="Gateway_ParallelJoin"
targetRef="Task_MakeDecision" />

Fields Reference

Split Gateway Fields

FieldLocationRequiredDescriptionExample
idGateway element✅ YesUnique gateway identifierGateway_ParallelSplit
nameGateway element⚪ OptionalDescriptive name"Start Background Checks"
incomingGateway element✅ YesSingle incoming flow<incoming>Flow_In</incoming>
outgoingGateway element✅ YesMultiple outgoing flowsMultiple <outgoing> tags

⚠️ Critical: Split gateway MUST have multiple <outgoing> flows (2 or more)

Join Gateway Fields

FieldLocationRequiredDescriptionExample
idGateway element✅ YesUnique gateway identifierGateway_ParallelJoin
nameGateway element⚪ OptionalDescriptive name"Wait for All Checks"
incomingGateway element✅ YesMultiple incoming flowsMultiple <incoming> tags
outgoingGateway element✅ YesSingle outgoing flow<outgoing>Flow_Continue</outgoing>

⚠️ Critical: Join gateway MUST have multiple <incoming> flows matching the split count

Sequence Flow Fields

FieldLocationRequiredDescriptionExample
idFlow element✅ YesUnique flow identifierFlow_CreditCheck
nameFlow element⚪ OptionalDescriptive flow name"Credit Bureau Check"
sourceRefFlow element✅ YesSource element IDGateway_ParallelSplit
targetRefFlow element✅ YesTarget element IDTask_CreditBureau

⚠️ NO CONDITIONS: Parallel Gateway flows should NEVER have <conditionExpression> - all paths always execute!


Execution Flow

Split Execution

┌─────────────────────────────────────┐
│ Token arrives at Parallel Gateway │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Count outgoing flows: N │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Create N new execution tokens │
│ (one for each outgoing flow) │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Assign each token to its flow path │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Deactivate parent token │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ All tokens execute independently │
└─────────────────────────────────────┘

Join Execution

┌─────────────────────────────────────┐
│ Token arrives at join gateway │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Record token arrival │
│ (track which token arrived) │
└───────────────┬─────────────────────┘


┌─────────────────────────────────────┐
│ Deactivate this token │
└───────────────┬─────────────────────┘


┌───────────────┐
│ All arrived? │
└───┬───────┬───┘
NO │ │ YES
▼ ▼
┌──────────┐ ┌────────────────┐
│ WAIT │ │ Create merged │
│ │ │ token │
└──────────┘ └───────┬────────┘


┌─────────────────┐
│ Continue process│
└─────────────────┘

Execution Log Example

Split Log

[PARALLEL GATEWAY] Evaluating parallel gateway: Gateway_ParallelSplit (Start Background Checks)
[PARALLEL GATEWAY] Incoming flows: 1
[PARALLEL GATEWAY] Outgoing flows: 3
[PARALLEL GATEWAY] Type: SPLIT/FORK (multiple outgoing flows detected)
[PARALLEL GATEWAY] Creating 3 parallel execution paths:
[PARALLEL GATEWAY] → Token 2a3f7e8b-1c4d-4a2f-9e5a-3b7c8d9e1f2a
[PARALLEL GATEWAY] Path: Credit Bureau Check → Task_CreditBureau
[PARALLEL GATEWAY] → Token 9c1d4a2f-7b6e-4c9d-8a5f-2e3d4c5a6b7c
[PARALLEL GATEWAY] Path: Employment Verification → Task_VerifyEmployment
[PARALLEL GATEWAY] → Token 7b6e3c9d-5a4f-4e8c-9b7a-1d2e3f4a5b6c
[PARALLEL GATEWAY] Path: Document Review → Task_ReviewDocuments
[PARALLEL GATEWAY] ✓ Parallel split complete. 3 tokens now active.
[PARALLEL GATEWAY] Deactivating parent token.

[TOKEN PROCESSOR] Detected 3 active tokens. Processing parallel paths...

Parallel Execution Logs

[TOKEN 2a3f7e8b] Processing element: Task_CreditBureau (Credit Bureau Check)
[TOKEN 2a3f7e8b] Executing service task...
[TOKEN 2a3f7e8b] ✓ Task completed successfully

[TOKEN 9c1d4a2f] Processing element: Task_VerifyEmployment (Verify Employment)
[TOKEN 9c1d4a2f] Executing service task...
[TOKEN 9c1d4a2f] ✓ Task completed successfully

[TOKEN 7b6e3c9d] Processing element: Task_ReviewDocuments (Review Documents)
[TOKEN 7b6e3c9d] User task assigned to: ReviewerGroup
[TOKEN 7b6e3c9d] ⏸ Waiting for user completion...

Join Log

[PARALLEL GATEWAY] Token 2a3f7e8b arrived at join: Gateway_ParallelJoin
[PARALLEL GATEWAY] Expected tokens: 3
[PARALLEL GATEWAY] Arrived so far: 1
[PARALLEL GATEWAY] Tokens arrived: [2a3f7e8b]
[PARALLEL GATEWAY] ⏳ WAITING - Not all paths completed yet (1/3)

[PARALLEL GATEWAY] Token 9c1d4a2f arrived at join: Gateway_ParallelJoin
[PARALLEL GATEWAY] Expected tokens: 3
[PARALLEL GATEWAY] Arrived so far: 2
[PARALLEL GATEWAY] Tokens arrived: [2a3f7e8b, 9c1d4a2f]
[PARALLEL GATEWAY] ⏳ WAITING - Not all paths completed yet (2/3)

[PARALLEL GATEWAY] Token 7b6e3c9d arrived at join: Gateway_ParallelJoin
[PARALLEL GATEWAY] Expected tokens: 3
[PARALLEL GATEWAY] Arrived so far: 3
[PARALLEL GATEWAY] Tokens arrived: [2a3f7e8b, 9c1d4a2f, 7b6e3c9d]
[PARALLEL GATEWAY] ✓ ALL PATHS COMPLETED (3/3)
[PARALLEL GATEWAY] Merging tokens and continuing...
[PARALLEL GATEWAY] Created merged token: 4f8a9b2c-3d5e-4f7a-8c6b-5d7e9f1a2b3c
[PARALLEL GATEWAY] Moving to: Task_MakeDecision

Use Cases

Use Case 1: Loan Application Background Checks

Scenario: Perform credit check, employment verification, and identity verification simultaneously.

Why Parallel: All three checks are independent and can run at the same time. No need to wait for one to complete before starting another.

Implementation:

<bpmn:parallelGateway id="Gateway_StartChecks" name="Start All Checks">
<bpmn:incoming>Flow_ApplicationSubmitted</bpmn:incoming>
<bpmn:outgoing>Flow_ToCreditCheck</bpmn:outgoing>
<bpmn:outgoing>Flow_ToEmployment</bpmn:outgoing>
<bpmn:outgoing>Flow_ToIdentity</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Three parallel service tasks -->
<bpmn:serviceTask id="Task_CreditCheck" name="Credit Check" />
<bpmn:serviceTask id="Task_Employment" name="Employment Verification" />
<bpmn:serviceTask id="Task_Identity" name="Identity Verification" />

<!-- Join after all complete -->
<bpmn:parallelGateway id="Gateway_WaitChecks" name="Wait for All Checks">
<bpmn:incoming>Flow_FromCredit</bpmn:incoming>
<bpmn:incoming>Flow_FromEmployment</bpmn:incoming>
<bpmn:incoming>Flow_FromIdentity</bpmn:incoming>
<bpmn:outgoing>Flow_ToDecision</bpmn:outgoing>
</bpmn:parallelGateway>

Visual Flow:

Benefits:

  • All checks start immediately
  • Total time = longest check (not sum of all)
  • If credit check takes 5s, employment 3s, identity 4s → Total: 5s (not 12s)

Use Case 2: Multi-Channel Notifications

Scenario: Send approval notification via email, SMS, and push notification simultaneously.

Implementation:

<bpmn:parallelGateway id="Gateway_SendNotifications" name="Send All Notifications">
<bpmn:incoming>Flow_Approved</bpmn:incoming>
<bpmn:outgoing>Flow_ToEmail</bpmn:outgoing>
<bpmn:outgoing>Flow_ToSMS</bpmn:outgoing>
<bpmn:outgoing>Flow_ToPush</bpmn:outgoing>
</bpmn:parallelGateway>

<bpmn:serviceTask id="Task_SendEmail" name="Send Email" />
<bpmn:serviceTask id="Task_SendSMS" name="Send SMS" />
<bpmn:serviceTask id="Task_SendPush" name="Send Push Notification" />

<bpmn:parallelGateway id="Gateway_WaitNotifications" name="Wait for All Sent">
<bpmn:incoming>Flow_FromEmail</bpmn:incoming>
<bpmn:incoming>Flow_FromSMS</bpmn:incoming>
<bpmn:incoming>Flow_FromPush</bpmn:incoming>
<bpmn:outgoing>Flow_Continue</bpmn:outgoing>
</bpmn:parallelGateway>

Note: If some channels are optional (e.g., only send SMS if customer has phone), use Inclusive Gateway instead.


Use Case 3: Parallel Document Processing

Scenario: Process multiple uploaded documents simultaneously.

<bpmn:parallelGateway id="Gateway_ProcessDocuments">
<bpmn:outgoing>Flow_ProcessPassport</bpmn:outgoing>
<bpmn:outgoing>Flow_ProcessUtilityBill</bpmn:outgoing>
<bpmn:outgoing>Flow_ProcessBankStatement</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Each document gets processed independently -->
<bpmn:serviceTask id="Task_ValidatePassport" name="Validate Passport">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Command" value="ValidateIdentityDocument" />
<custom:property name="Parameters" value='{"documentType": "passport"}' />
</custom:properties>
</bpmn:extensionElements>
</bpmn:serviceTask>

<bpmn:serviceTask id="Task_ValidateUtility" name="Validate Utility Bill">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Command" value="ValidateAddressDocument" />
<custom:property name="Parameters" value='{"documentType": "utility"}' />
</custom:properties>
</bpmn:extensionElements>
</bpmn:serviceTask>

<bpmn:serviceTask id="Task_ValidateBank" name="Validate Bank Statement">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Command" value="ValidateFinancialDocument" />
<custom:property name="Parameters" value='{"documentType": "bank_statement"}' />
</custom:properties>
</bpmn:extensionElements>
</bpmn:serviceTask>

Use Case 4: Nested Parallel Flows

Scenario: Main parallel split, with one path containing its own parallel split.

<!-- Main parallel split -->
<bpmn:parallelGateway id="Gateway_Main_Split">
<bpmn:outgoing>Flow_PathA</bpmn:outgoing>
<bpmn:outgoing>Flow_PathB</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Path A continues to a task -->
<bpmn:serviceTask id="Task_A" name="Process A" />

<!-- Path B splits again (nested parallel) -->
<bpmn:parallelGateway id="Gateway_Nested_Split">
<bpmn:incoming>Flow_PathB</bpmn:incoming>
<bpmn:outgoing>Flow_B1</bpmn:outgoing>
<bpmn:outgoing>Flow_B2</bpmn:outgoing>
</bpmn:parallelGateway>

<bpmn:serviceTask id="Task_B1" name="Process B1" />
<bpmn:serviceTask id="Task_B2" name="Process B2" />

<!-- Nested join -->
<bpmn:parallelGateway id="Gateway_Nested_Join">
<bpmn:incoming>Flow_FromB1</bpmn:incoming>
<bpmn:incoming>Flow_FromB2</bpmn:incoming>
<bpmn:outgoing>Flow_B_Complete</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- Main join -->
<bpmn:parallelGateway id="Gateway_Main_Join">
<bpmn:incoming>Flow_FromA</bpmn:incoming>
<bpmn:incoming>Flow_B_Complete</bpmn:incoming>
<bpmn:outgoing>Flow_Continue</bpmn:outgoing>
</bpmn:parallelGateway>

Visual:

       ◇+ Main Split
╱ ╲
A ◇+ Nested Split
╱ ╲
B1 B2
╲ ╱
◇+ Nested Join
│ │
└─────┬─────┘

◇+ Main Join

Token Flow:

  1. Main split creates Token A and Token B
  2. Token A executes Path A
  3. Token B reaches nested split, creates Token B1 and Token B2
  4. Token B1 and B2 execute, arrive at nested join
  5. Nested join merges B1 and B2 into Token B'
  6. Token A and Token B' arrive at main join
  7. Main join merges into final token

Best Practices

✅ DO

  1. Use for independent tasks - tasks that don't depend on each other's results
  2. Match split and join - if you split 3 ways, join 3 ways
  3. Keep paths simple - each parallel path should be straightforward
  4. Use descriptive names - "Start Checks" and "Wait for All Checks"
  5. Test all paths complete - ensure no path gets stuck

❌ DON'T

  1. Don't add conditions to flows - parallel gateway flows should have NO conditions
  2. Don't access variables set in parallel paths - race conditions may occur
  3. Don't forget the join - parallel paths must synchronize
  4. Don't mismatch split/join counts - must be equal
  5. Don't use for dependent tasks - if Task B needs Task A's result, use sequence not parallel

Common Patterns

Pattern 1: Simple Parallel Execution

    ◇+ Split
╱ ╲
A B
╲ ╱
◇+ Join

Use when: A and B are independent

Pattern 2: Unbalanced Parallel Paths

    ◇+ Split
╱ | ╲
A B C
(5s)(2s)(10s)
╲ | ╱
◇+ Join

Join waits for slowest path (10s in this case)

Pattern 3: Parallel with User Tasks

       ◇+ Split
╱ ╲
ServiceTask UserTask
│ │
(completes) (waits for user)
│ │
└────┬─────┘

◇+ Join

Join waits until user completes their task

Pattern 4: Multiple Parallel Sections

  ◇+ Split A
╱ ╲
Work1 Work2
╲ ╱
◇+ Join A


◇+ Split B
╱ ╲
Work3 Work4
╲ ╱
◇+ Join B

Sequential parallel sections


Troubleshooting

Issue: Process hangs at join gateway

Symptoms: Join shows ⏳ WAITING indefinitely, never continues

Possible Causes:

  1. One parallel path hit an error
[TOKEN abc123] Processing Task_A...
[TOKEN abc123] ✓ Complete
[TOKEN def456] Processing Task_B...
[TOKEN def456] ✗ ERROR: Service unavailable
[PARALLEL GATEWAY] Waiting at join: 1/2 paths completed ← Stuck here

Solution: Check logs for errors in parallel paths. Fix the failing task.

  1. One path has uncompleted UserTask
[TOKEN abc123] Task_A complete
[TOKEN def456] Task_B (UserTask) - Waiting for user
[PARALLEL GATEWAY] Waiting: 1/2 paths ← Waiting for user

Solution: Complete the user task, or check if it's assigned correctly.

  1. Mismatch between split and join counts
<!-- ❌ Wrong - split creates 3 paths, join expects 2 -->
<bpmn:parallelGateway id="Gateway_Split">
<bpmn:outgoing>Flow_A</bpmn:outgoing>
<bpmn:outgoing>Flow_B</bpmn:outgoing>
<bpmn:outgoing>Flow_C</bpmn:outgoing> ← 3 paths created
</bpmn:parallelGateway>

<bpmn:parallelGateway id="Gateway_Join">
<bpmn:incoming>Flow_FromA</bpmn:incoming>
<bpmn:incoming>Flow_FromB</bpmn:incoming> ← Only 2 incoming
</bpmn:parallelGateway>

Solution: Ensure split and join have same number of paths.


Issue: Only one path executes instead of all

Symptoms: Log shows only one token created, not multiple

Check:

[PARALLEL GATEWAY] Outgoing flows: 1  ← Should be 2+

Cause: Gateway only has one <outgoing> flow

Solution:

<!-- ❌ Wrong - only one outgoing -->
<bpmn:parallelGateway id="Gateway_1">
<bpmn:outgoing>Flow_A</bpmn:outgoing>
</bpmn:parallelGateway>

<!-- ✅ Correct - multiple outgoing -->
<bpmn:parallelGateway id="Gateway_1">
<bpmn:outgoing>Flow_A</bpmn:outgoing>
<bpmn:outgoing>Flow_B</bpmn:outgoing>
</bpmn:parallelGateway>

Issue: Variables modified in parallel paths cause issues

Symptoms: Race conditions, unexpected variable values

Cause: Multiple tokens modifying same variable simultaneously

<!-- ❌ Problematic - both paths modify same variable -->
<bpmn:scriptTask id="Task_A">
<custom:property name="Script" value="counter = counter + 1;" />
</bpmn:scriptTask>
<bpmn:scriptTask id="Task_B">
<custom:property name="Script" value="counter = counter + 1;" />
</bpmn:scriptTask>

Solution: Use different variables for parallel paths, combine after join

<!-- ✅ Better - separate variables -->
<bpmn:scriptTask id="Task_A">
<custom:property name="Script" value="counterA = counterA + 1;" />
</bpmn:scriptTask>
<bpmn:scriptTask id="Task_B">
<custom:property name="Script" value="counterB = counterB + 1;" />
</bpmn:scriptTask>

<!-- Combine after join -->
<bpmn:scriptTask id="Task_Combine">
<custom:property name="Script" value="totalCounter = counterA + counterB;" />
</bpmn:scriptTask>

Current Implementation Notes

Sequential Execution (Current Behavior)

The current implementation executes tokens sequentially (one after another) rather than using true OS-level threading:

Time →
0s ──→ Token A starts ──→ Token A ends (5s)
5s ──────────────────→ Token B starts ──→ Token B ends (10s)
15s ──────────────────────────────────→ Token C starts ──→ Token C ends (20s)
Total: 20s

True Async Parallelism (Future Enhancement)

For true simultaneous execution:

Time →
0s ──→ Token A starts ──→ Token A ends (5s)
──→ Token B starts ──────────────→ Token B ends (10s)
──→ Token C starts ──→ Token C ends (3s)
Total: 10s (max of all)

Future Enhancement: The engine will support true concurrent execution where all paths run simultaneously, reducing total execution time to the duration of the slowest path.

Current Benefits

Even with sequential execution, the token model provides:

  • ✅ Clean separation of parallel paths
  • ✅ Proper join synchronization
  • ✅ State isolation per path
  • ✅ Complete execution history per token
  • ✅ Foundation for future true parallelism

Summary

Parallel Gateway (AND):

  • ✅ Executes ALL outgoing paths
  • ✅ Creates execution token for each path
  • ✅ Join waits for ALL tokens to arrive
  • ✅ Use for independent, simultaneous tasks
  • ✅ No conditions allowed on flows
  • ✅ Split and join must match in path count

Next Steps: