AWS  

Benchmarking Lambda Durable Execution for Multi-Step .NET Workflows

Introduction

Multi-step workflows are common in modern applications.

A single business operation may involve payment processing, database updates, inventory management, notifications, document generation, or calls to external APIs. When these operations are executed as a sequence of independent steps, reliability becomes more difficult to measure.

A workflow can fail between steps. A dependency can time out. A function can be retried. A process can terminate after completing an external operation but before recording the result.

Serverless platforms can simplify infrastructure management, but they do not automatically solve workflow reliability.

Durable execution introduces a different model: instead of treating each function invocation as an isolated unit, the workflow maintains enough execution state to resume after interruptions.

For .NET workloads running on AWS Lambda, this creates an interesting engineering question:

What is the performance and operational cost of durable execution compared with a conventional multi-step serverless workflow?

A useful benchmark should measure more than execution time. It should examine latency, state persistence, retries, workflow duration, resource consumption, and the behavior of the system when failures occur.

What Is Durable Execution?

A conventional serverless workflow can look like this:

API Request
    ↓
Lambda A
    ↓
Lambda B
    ↓
Lambda C
    ↓
Complete

Each function performs part of the operation.

The workflow state may be passed explicitly between functions:

Lambda A
   ↓
State
   ↓
Lambda B
   ↓
State
   ↓
Lambda C

A durable workflow adds persistent execution state:

             ┌──────────────────┐
             │ Workflow State   │
             └────────┬─────────┘
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
    Activity A    Activity B    Activity C

If execution is interrupted, the workflow can use its recorded state to determine what should happen next.

The workflow lifetime therefore becomes independent of the lifetime of an individual Lambda execution environment.

Why Benchmark Durable Execution?

Durability introduces additional infrastructure and orchestration work.

A workflow may need to:

  • Persist execution state

  • Record completed activities

  • Schedule future work

  • Resume after failures

  • Coordinate retries

  • Handle timers

  • Process external events

These capabilities provide reliability, but they can introduce overhead.

A benchmark should answer questions such as:

  • How much latency does workflow orchestration add?

  • How does execution time change as the number of steps increases?

  • How does state size affect performance?

  • What happens when an activity fails?

  • How expensive are retries?

  • How does a durable workflow behave during long waits?

  • What is the throughput under concurrent workflows?

The objective is not simply to prove that durable execution works.

The objective is to quantify its trade-offs.

Design a Multi-Step .NET Workflow

Start with a deterministic workflow that contains several independent activities.

For example:

Start
  ↓
Validate Order
  ↓
Calculate Price
  ↓
Reserve Inventory
  ↓
Create Shipment
  ↓
Send Notification
  ↓
Complete

Each activity should perform a measurable amount of work.

Avoid making every activity a no-op because that would primarily measure orchestration overhead.

A useful benchmark should contain both:

Lightweight Activity

and:

Realistic I/O Activity

This allows you to understand how orchestration overhead behaves relative to actual application work.

Create Different Workflow Sizes

Do not benchmark only one workflow.

Create several variants:

WorkflowActivities
Small3
Medium5
Large10
Extended25

This allows you to examine how workflow duration changes as the number of durable steps increases.

For example:

3 Activities
      ↓
Small orchestration overhead

25 Activities
      ↓
More state transitions
      ↓
More orchestration work

The resulting curve is more useful than a single benchmark number.

Keep Activity Work Consistent

Suppose Activity A takes 100 ms and Activity B takes 2 seconds.

A comparison between workflow configurations may then be dominated by Activity B.

For an orchestration benchmark, start with controlled workloads:

Activity A → 100 ms
Activity B → 100 ms
Activity C → 100 ms
Activity D → 100 ms

After establishing a baseline, introduce realistic variability.

This separates workflow-engine overhead from business-logic execution time.

Establish a Baseline Without Durability

Before measuring durable execution, establish a baseline.

For example:

API
 ↓
Lambda A
 ↓
Lambda B
 ↓
Lambda C

Measure:

  • Total execution time

  • Lambda duration

  • Number of invocations

  • Memory consumption

  • Cold starts

  • Network calls

  • Error rate

Then implement the equivalent durable workflow:

Workflow
   ↓
Activity A
   ↓
Activity B
   ↓
Activity C

Now compare the two approaches.

This is much more useful than benchmarking durable execution in isolation.

Measure End-to-End Latency

The first important metric is workflow completion time.

Define:

Workflow Latency =
Completion Timestamp
-
Workflow Start Timestamp

Run enough iterations to calculate:

  • Average

  • Median

  • p95

  • p99

  • Minimum

  • Maximum

Average latency alone can hide important tail behavior.

For example:

Average: 420 ms
p95:     800 ms
p99:     2.4 s

The p99 value may matter much more for a production system handling latency-sensitive workloads.

Measure Per-Activity Latency

Total workflow latency is only part of the picture.

Record each activity:

Workflow
 ├── ValidateOrder       85 ms
 ├── CalculatePrice      42 ms
 ├── ReserveInventory   130 ms
 ├── CreateShipment     160 ms
 └── NotifyCustomer      70 ms

This makes it possible to distinguish orchestration overhead from business execution.

A workflow may appear slow when one downstream dependency is actually responsible for most of the latency.

Measure State Size

Durable execution depends on workflow state.

Benchmark multiple state sizes:

1 KB
10 KB
100 KB
1 MB

The state should represent realistic workflow information.

For example:

public sealed record OrderState(
    Guid OrderId,
    string Status,
    decimal Total,
    string CustomerId,
    IReadOnlyList<OrderItem> Items);

Do not use large state objects merely to produce artificial benchmark numbers.

The objective is to understand how realistic state growth affects workflow performance.

Measure the Cost of Additional Steps

One useful experiment is to increase the number of activities while keeping the activity workload constant.

For example:

3 activities   → X ms
5 activities   → Y ms
10 activities  → Z ms
25 activities  → N ms

Plotting this relationship can reveal whether orchestration overhead grows approximately linearly or whether certain workflow sizes introduce additional bottlenecks.

The important observation is not the absolute number.

It is the shape of the curve.

Benchmark Sequential Workflows

Start with sequential execution:

A
↓
B
↓
C
↓
D

This establishes a simple baseline.

The total duration is approximately:

Ttotal =
TA + TB + TC + TD + Orchestration Overhead

This is the easiest workflow type to benchmark.

Benchmark Parallel Activities

Then introduce independent activities:

        ┌── Activity A ──┐
        │                │
Start ──┼── Activity B ──┼── Complete
        │                │
        └── Activity C ──┘

If the activities are independent, parallel execution can reduce total workflow duration.

Conceptually:

Sequential:

A ── B ── C
|    |    |
100  100  100 ms

≈ 300 ms

versus:

Parallel:

A ── 100 ms ──┐
B ── 100 ms ──┼── Continue
C ── 100 ms ──┘

≈ 100 ms

Actual results will include orchestration and scheduling overhead.

Parallelism should therefore be benchmarked rather than assumed to be free.

Benchmark Failure Recovery

A durable workflow should be tested under controlled failure.

Introduce a failure into one activity:

Activity A
   ↓
Activity B
   X
Activity C

Then measure:

  • Detection time

  • Retry delay

  • Retry count

  • Recovery time

  • Final workflow latency

  • Additional compute

  • Additional state transitions

The benchmark should verify that the workflow eventually reaches the correct state.

Measure Retry Overhead

Suppose an activity succeeds on the third attempt:

Attempt 1 → Failure
Attempt 2 → Failure
Attempt 3 → Success

The total workflow duration now includes:

Initial Execution
+
Failure Handling
+
Retry Delay
+
Second Execution
+
Retry Delay
+
Third Execution

A benchmark should record these components separately.

Otherwise, the result may show only a large latency increase without explaining why.

Test a Non-Idempotent Activity

Failure testing should also consider external side effects.

For example:

ChargeCustomer()

might succeed remotely but return a timeout to Lambda.

If the workflow retries blindly:

ChargeCustomer()
     ↓
Timeout
     ↓
Retry
     ↓
ChargeCustomer()

the customer could potentially be charged twice.

The benchmark environment should therefore use an idempotency key:

var idempotencyKey =
    $"order-{orderId}-payment";

The external operation should treat repeated requests with the same key as the same logical operation.

This is a critical part of measuring durable workflow reliability.

Test Long Waits

One of the most important durable-workflow scenarios is a long wait.

For example:

Order Submitted
      ↓
Wait for Approval
      ↓
Approval Received
      ↓
Continue

Do not test only workflows that complete in milliseconds.

Use scenarios representing:

1 second
1 minute
1 hour
several hours

The important measurement is whether the workflow can remain logically active without requiring a continuously running application process.

Measure Workflow Resume Latency

For event-driven workflows, measure the time between an external event and workflow continuation.

For example:

Approval Event
      ↓
Event Received
      ↓
Workflow Resumed
      ↓
Next Activity Started

Record:

Resume Latency =
Activity Start
-
Event Arrival

This is a separate metric from the total workflow duration.

Cold Starts Matter

AWS Lambda workloads can experience cold starts.

A benchmark should distinguish:

Warm Execution

from:

Cold Execution

Measure both.

For .NET applications, startup behavior can become especially important when activities are short.

If an activity performs only a small amount of work, initialization overhead may represent a large portion of total execution time.

A useful test therefore looks like:

ScenarioCold StartWarm Start
3-step workflow......
5-step workflow......
10-step workflow......

Measure Memory Configuration

Lambda memory configuration affects more than available memory.

It can also affect available compute capacity.

Run the benchmark at multiple memory configurations:

512 MB
1024 MB
2048 MB

Then compare:

  • Duration

  • CPU behavior

  • Cost

  • Throughput

  • Cold-start behavior

The goal is to find the most efficient configuration rather than simply selecting the largest memory allocation.

Measure Cost, Not Just Performance

A faster workflow is not automatically cheaper.

A useful benchmark should estimate:

Total Cost =
Function Execution
+
Workflow Orchestration
+
State Storage
+
Network
+
Other Required Services

The exact billing model depends on the AWS services and architecture being tested.

For that reason, record resource usage during the benchmark and calculate cost using the current pricing model applicable to the deployment.

Avoid publishing fixed cost claims without specifying the region, configuration, workload, and pricing assumptions.

Run High-Concurrency Tests

A workflow that works well for one request may behave differently at scale.

Test increasing concurrency:

10 workflows
50 workflows
100 workflows
500 workflows
1000 workflows

Track:

  • Workflow completion rate

  • p95 latency

  • p99 latency

  • Lambda concurrency

  • Throttling

  • Retry rate

  • Error rate

  • Queue depth where applicable

This identifies scaling bottlenecks that are invisible in sequential tests.

Example Benchmark Matrix

A practical benchmark matrix could look like this:

TestStepsStateConcurrencyFailure
Baseline31 KB1No
Medium510 KB10No
Large10100 KB50No
High Load10100 KB500No
Retry510 KB10Step 3
Recovery10100 KB50Step 5
Long Wait510 KB10Timer/Event

This produces a more complete picture than a single happy-path test.

Benchmark .NET Release Builds

Always benchmark production-like builds.

For example:

dotnet publish -c Release

Do not use Debug builds for performance comparisons.

Keep the following consistent between test runs:

  • .NET version

  • Lambda runtime

  • Region

  • Memory configuration

  • Architecture

  • Workflow definition

  • Payload

  • External dependencies

  • Concurrency

Changing multiple variables simultaneously makes the results difficult to interpret.

Common Benchmarking Mistakes

Measuring Only Average Latency

Tail latency can be much more important under production load.

Ignoring Cold Starts

Short-lived .NET activities can be significantly affected by startup overhead.

Using Unrealistically Small Activities

If every activity does almost no work, orchestration overhead can dominate the results.

Ignoring Failures

Durable execution is primarily valuable when things do not go perfectly.

Measuring Only Successful Workflows

Retry and recovery behavior should be part of the benchmark.

Ignoring Idempotency

Retries around external side effects can produce incorrect business outcomes.

Changing Multiple Variables

Keep the experiment controlled so that observed differences have a clear cause.

Best Practices

  1. Establish a non-durable baseline before measuring durable execution.

  2. Benchmark sequential and parallel workflows.

  3. Test different workflow sizes.

  4. Measure p50, p95, and p99 latency.

  5. Record per-activity execution time.

  6. Measure workflow state size.

  7. Test cold and warm Lambda executions.

  8. Test realistic memory configurations.

  9. Introduce controlled failures.

  10. Measure retry and recovery overhead.

  11. Test long waits and event-driven resumption.

  12. Use idempotency keys for external side effects.

  13. Test realistic concurrency.

  14. Track throttling and error rates.

  15. Include infrastructure and execution costs in the final comparison.

Conclusion

Durable execution provides an important reliability model for multi-step serverless workflows, but reliability features should still be evaluated from a performance and cost perspective.

A useful benchmark should move beyond:

"Does the workflow complete?"

and instead ask:

How fast does it complete?

How does performance change with more steps?

What happens when an activity fails?

How expensive are retries?

How quickly does the workflow resume?

How does concurrency affect tail latency?

What does the complete workflow cost?

For .NET workloads on AWS Lambda, the most valuable benchmark is therefore a controlled comparison across workflow size, state size, concurrency, failures, and execution conditions.

The final result should not be a single claim that durable execution is “faster” or “slower.”

The engineering value comes from identifying where the durability overhead is small, where it becomes significant, and where the reliability benefits justify that overhead.

For long-running business processes, that trade-off is often more important than raw function execution time.