All posts
MermaidConcurrencySequence DiagramsAsynchronousArchitecture

Mermaid parallel processing diagrams: concurrent systems explained

7 min readThe MermaidCreator team

Modern systems are parallel by default. A user clicks a button and triggers ten things at once: fetch user data, log analytics, load recommendations, check inventory—all happening in parallel. Traditional flowcharts force you to serialize this: "do A, then B, then C." But that's not how software actually works.

Mermaid's par blocks let you show parallel operations honestly—multiple sequences running concurrently, blocking points where they wait for each other, and race conditions where timing matters. This guide teaches you how to diagram parallelism clearly.

What parallel processing means

Parallelism = multiple things happening at the same time (or appearing to, in async systems).

Examples:

  • A web request triggers three database queries in parallel
  • A background job processes records while the UI responds to users
  • A payment system validates fraud while charging the card while updating inventory
  • A cache refreshes while users are still reading stale data

Showing this as "step 1, step 2, step 3" is misleading. A par block shows the truth: these steps overlap.

The par block syntax

In Mermaid sequence diagrams, a par block groups parallel operations:

sequenceDiagram
    participant User
    participant API
    participant Analytics
    participant Cache

    User->>API: Click "buy now"
    par Parallel processing
        API->>Analytics: Log purchase event
    and
        API->>Cache: Invalidate product cache
    end
    API-->>User: 200 OK

Read it: When the user clicks, the API sends logs to Analytics AND invalidates the cache (in parallel), then responds to the user.

Key syntax:

  • par <description> — start a parallel block
  • and — separate parallel streams
  • end — close the block

Any operations inside the same par block run concurrently. Operations outside it run sequentially.

Real-world examples

Microservice payment processing

When a payment comes in, the system validates, charges, and updates inventory at the same time:

sequenceDiagram
    participant Frontend
    participant PaymentAPI
    participant Validator
    participant Processor
    participant Inventory

    Frontend->>PaymentAPI: POST /checkout { amount, items }
    
    par Parallel validation and charge
        PaymentAPI->>Validator: Validate card
        Validator-->>PaymentAPI: Valid ✓
    and
        PaymentAPI->>Processor: Process charge
        Processor-->>PaymentAPI: Charge OK
    and
        PaymentAPI->>Inventory: Check stock
        Inventory-->>PaymentAPI: In stock ✓
    end
    
    PaymentAPI->>Inventory: Reserve stock
    Inventory-->>PaymentAPI: Reserved
    
    PaymentAPI-->>Frontend: 201 Order created

What this shows:

  1. Three parallel operations (validation, charge, inventory check) happen together
  2. After they're all done (the par block ends), a sequential operation (reserve stock) waits
  3. Finally, the API responds to the frontend

Without the par block, readers might think: "Validate, then charge, then check." With it, they see: "All three at once, then reserve, then respond."

Web page loading (progressive rendering)

A page loads in parallel streams: HTML structure, images, CSS, JavaScript, third-party analytics:

sequenceDiagram
    participant Browser
    participant Server
    participant CDN
    participant Analytics
    participant AdNetwork

    Browser->>Server: GET /product/123
    Server-->>Browser: HTML + critical CSS
    
    par Loading assets
        Browser->>CDN: GET style.css
        CDN-->>Browser: CSS loaded
    and
        Browser->>CDN: GET product-image.jpg
        CDN-->>Browser: Image loaded
    and
        Browser->>Analytics: Track pageview
        Analytics-->>Browser: Tracked
    and
        Browser->>AdNetwork: Load banner ads
        AdNetwork-->>Browser: Ads loaded (slower)
    end
    
    Browser->>Browser: Render page
    Browser-->>Browser: Paint to screen

Insight: The page doesn't wait for ads to load before showing content. The par block shows that ads load in parallel with content, and the browser paints once critical assets are ready.

Database transaction with parallel reads

Read operations can happen in parallel; writes must be serial:

sequenceDiagram
    participant App
    participant DB
    participant Cache

    App->>DB: BEGIN TRANSACTION
    
    par Parallel reads
        App->>DB: SELECT users.balance WHERE id=1
        DB-->>App: $100
    and
        App->>DB: SELECT daily_limit WHERE account=1
        DB-->>App: $1000
    and
        App->>Cache: GET exchange_rate USD->EUR
        Cache-->>App: 0.92
    end
    
    App->>App: Calculate: can_send = balance * rate < limit?
    App->>DB: UPDATE users SET balance = balance - amount
    DB-->>App: Updated
    App->>DB: COMMIT
    DB-->>App: OK

Why parallel matters: The three reads can query the database concurrently. They all complete faster than if they were serial (first read, then second, then third). Once all reads are done (par ends), the write happens.

Common patterns

Race condition: Who wins?

When two parallel operations both try to modify shared state, the result depends on timing. Show this with rect (a highlight box) or a note:

sequenceDiagram
    participant User1
    participant User2
    participant Database

    par User1 saves
        User1->>Database: Update inventory: qty = 9
        Database-->>User1: OK
    and User2 saves
        User2->>Database: Update inventory: qty = 8
        Database-->>User2: OK
    end
    
    Note over Database: Who's right? Depends on which write happened last.
    Database->>Database: Final value: qty = 8 (last write wins)

Better approach: Use locking or transactions to prevent the race:

sequenceDiagram
    participant User1
    participant User2
    participant Database

    User1->>Database: BEGIN TRANSACTION + LOCK
    User1->>Database: SELECT qty (qty = 10)
    User1->>Database: UPDATE qty = 9
    
    par User2 waits
        User2->>Database: BEGIN TRANSACTION + LOCK
        Note over Database: User2 blocked, waiting for lock
    and User1 continues
        User1->>Database: COMMIT
        Database-->>User1: OK
        Database-->>User2: Lock acquired
    end
    
    User2->>Database: SELECT qty (qty = 9)
    User2->>Database: UPDATE qty = 8
    User2->>Database: COMMIT
    Database-->>User2: OK

Timeout in parallel operations

Show what happens when one parallel task is too slow:

sequenceDiagram
    participant Client
    participant API
    participant EmailService
    participant Cache

    Client->>API: POST /register
    
    par Fast operations
        API->>API: Generate user ID
        API->>Cache: Store session
    and Potentially slow operation
        API->>EmailService: Send welcome email
        Note over EmailService: (Can be slow)
    end
    
    Note over API: API responds immediately (doesn't wait for email)
    API-->>Client: 201 User created
    
    EmailService-->>EmailService: Email sent 5 seconds later (async)

Key insight: The API responds to the client before the email is sent. The par block ends, and the client gets their response. Email sending continues in the background.

Flowcharts don't show parallelism (clearly)

Flowcharts can show parallel paths using swimlanes, but they're less clear than sequence diagrams:

flowchart TD
    subgraph API["API Service"]
        A["Receive request"]
        B["Validate"]
        C["Update inventory"]
        D["Return response"]
    end
    
    subgraph Analytics["Analytics Service"]
        E["Log event"]
    end
    
    A --> B
    B --> C
    C --> D
    
    A -.->|happens at same time| E

The dotted line suggests parallelism, but it's ambiguous. A sequence diagram with par is clearer.

Nested par blocks (sequential phases, each parallel)

When a process has multiple phases, each with parallel operations:

sequenceDiagram
    participant Client
    participant OrderService
    participant PaymentService
    participant InventoryService
    participant NotificationService

    Client->>OrderService: Create order
    
    par Phase 1: Validation
        OrderService->>PaymentService: Validate payment method
        PaymentService-->>OrderService: Valid ✓
    and
        OrderService->>InventoryService: Check stock
        InventoryService-->>OrderService: In stock ✓
    end
    
    par Phase 2: Charging and booking
        OrderService->>PaymentService: Charge card
        PaymentService-->>OrderService: Charged
    and
        OrderService->>InventoryService: Reserve stock
        InventoryService-->>OrderService: Reserved
    end
    
    par Phase 3: Notifications (fire and forget)
        OrderService->>NotificationService: Send confirmation
    and
        OrderService->>NotificationService: Send fulfillment request
    end
    
    OrderService-->>Client: 201 Order created

Structure:

  • Phase 1: Validate payment AND check stock (parallel)
  • Phase 2: Charge card AND reserve stock (parallel, but after Phase 1)
  • Phase 3: Send notifications (parallel, but after Phase 2)

Each phase has internal parallelism; phases are sequential.

Comparison: par vs. alt vs. loop

BlockMeansUse for
parParallel operations (concurrent)Multiple things at once, race conditions
altAlternative paths (if-then-else)Different outcomes based on conditions
loopRepeated operationsRetries, polling, iterations

A realistic scenario uses all three:

sequenceDiagram
    participant Client
    participant API
    participant Cache

    loop Retry up to 3 times
        Client->>API: GET /data
        alt Cache hit
            API->>Cache: Check cache
            Cache-->>API: Found
            API-->>Client: 200 OK
            break Found, exit loop
        else Cache miss
            par Parallel fetch and invalidate
                API->>API: Query database
                API-->>API: Result
            and
                API->>Cache: Invalidate old entry
                Cache-->>API: OK
            end
            API-->>Client: 200 OK
            break Success, exit loop
        end
    end

FAQ

Can flowcharts show parallelism?
Yes, with swimlanes. But it's less clear than a sequence diagram. Flowcharts are better for showing logic; sequence diagrams are better for showing timing and interactions.

What's the difference between par and simultaneous execution?
In a true multi-threaded system, par operations run truly simultaneously. In JavaScript/async, par operations might run on the same thread but interleave their work. For diagramming purposes, par means "no guaranteed order of completion"—they could be parallel or concurrent.

Does the order of "and" blocks matter?
No. All operations in a par block start at the same logical time. The and keyword just separates them visually. Internally, Mermaid renders them in the order listed, but semantically they're concurrent.

How do I show a dependency between parallel operations?
If operation B depends on operation A's result, they can't be truly parallel. Show them sequentially within the par block, or move B outside the par block to the next sequential step.

Can I have a par block inside an alt block?
Yes. You can nest any blocks:

sequenceDiagram
    alt Condition
        par Parallel ops under condition
            A->>B: msg1
        and
            A->>C: msg2
        end
    end

Visualize concurrent systems in the MermaidCreator editor—sketch a microservice call with parallel API requests, then add a race condition to see how timing affects the outcome.

Related posts