Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,17 @@
},
"path": "./src/comprehensive_operations/comprehensive_operations.py"
},
{
"name": "Many Operations",
"description": "Runs many durable steps and child contexts to exercise long execution history retrieval",
"handler": "many_operations.handler",
"integration": true,
"durableConfig": {
"RetentionPeriodInDays": 7,
"ExecutionTimeout": 300
},
"path": "./src/comprehensive_operations/many_operations.py"
},
{
"name": "Create Callback Concurrency",
"description": "Demonstrates multiple concurrent createCallback operations using context.parallel",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Example with many durable step operations."""

from aws_durable_execution_sdk_python.context import (
DurableContext,
durable_with_child_context,
)
from aws_durable_execution_sdk_python.execution import durable_execution


TOP_LEVEL_STEP_COUNT = 12
CHILD_CONTEXT_COUNT = 4
CHILD_STEPS_PER_CONTEXT = 2


def make_step(value: int):
return lambda _: value


@durable_with_child_context
def child_group(
child_context: DurableContext, group_index: int, steps_per_context: int
) -> list[int]:
values = []
for step_index in range(steps_per_context):
value = group_index * 100 + step_index
values.append(
child_context.step(
make_step(value),
name=f"child-{group_index:02d}-step-{step_index:02d}",
)
)
return values


@durable_execution
def handler(_event, context: DurableContext) -> dict[str, object]:
"""Run many named step operations across parent and child contexts."""

step_results = []
for index in range(TOP_LEVEL_STEP_COUNT):
step_results.append(
context.step(
make_step(index),
name=f"many-step-{index:03d}",
)
)

child_results = []
for group_index in range(CHILD_CONTEXT_COUNT):
child_results.append(
context.run_in_child_context(
child_group(group_index, CHILD_STEPS_PER_CONTEXT),
name=f"child-context-{group_index:02d}",
)
)

child_values = [value for child_result in child_results for value in child_result]

return {
"topLevelStepCount": TOP_LEVEL_STEP_COUNT,
"childContextCount": CHILD_CONTEXT_COUNT,
"childStepCount": CHILD_CONTEXT_COUNT * CHILD_STEPS_PER_CONTEXT,
"totalStepCount": TOP_LEVEL_STEP_COUNT
+ CHILD_CONTEXT_COUNT * CHILD_STEPS_PER_CONTEXT,
"firstStep": step_results[0] if step_results else None,
"lastStep": step_results[-1] if step_results else None,
"topLevelStepTotal": sum(step_results),
"childStepTotal": sum(child_values),
}
18 changes: 18 additions & 0 deletions packages/aws-durable-execution-sdk-python-examples/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,24 @@
}
}
},
"ManyOperations": {
"Type": "AWS::Serverless::Function",
"Properties": {
"CodeUri": "build/",
"Handler": "many_operations.handler",
"Description": "Runs many durable steps and child contexts to exercise long execution history retrieval",
"Role": {
"Fn::GetAtt": [
"DurableFunctionRole",
"Arn"
]
},
"DurableConfig": {
"RetentionPeriodInDays": 7,
"ExecutionTimeout": 300
}
}
},
"CallbackConcurrency": {
"Type": "AWS::Serverless::Function",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Tests for many_operations."""

import pytest
from aws_durable_execution_sdk_python.execution import InvocationStatus

from src.comprehensive_operations import many_operations
from test.conftest import deserialize_operation_payload


TOP_LEVEL_STEP_COUNT = 12
CHILD_CONTEXT_COUNT = 4
CHILD_STEPS_PER_CONTEXT = 2
TOTAL_STEP_COUNT = TOP_LEVEL_STEP_COUNT + CHILD_CONTEXT_COUNT * CHILD_STEPS_PER_CONTEXT


@pytest.mark.example
@pytest.mark.durable_execution(
handler=many_operations.handler,
lambda_function_name="Many Operations",
)
def test_fetches_many_steps_and_child_context_operations(durable_runner):
"""Verify result history includes many steps and child context operations."""
with durable_runner:
result = durable_runner.run(input=None, timeout=60)

assert result.status is InvocationStatus.SUCCEEDED

result_data = deserialize_operation_payload(result.result)
assert result_data == {
"topLevelStepCount": TOP_LEVEL_STEP_COUNT,
"childContextCount": CHILD_CONTEXT_COUNT,
"childStepCount": CHILD_CONTEXT_COUNT * CHILD_STEPS_PER_CONTEXT,
"totalStepCount": TOTAL_STEP_COUNT,
"firstStep": 0,
"lastStep": TOP_LEVEL_STEP_COUNT - 1,
"topLevelStepTotal": sum(range(TOP_LEVEL_STEP_COUNT)),
"childStepTotal": sum(
group_index * 100 + step_index
for group_index in range(CHILD_CONTEXT_COUNT)
for step_index in range(CHILD_STEPS_PER_CONTEXT)
),
}

all_ops = result.get_all_operations()
top_level_step_ops = [
op
for op in all_ops
if op.operation_type.value == "STEP"
and op.name is not None
and op.name.startswith("many-step-")
]
assert len(top_level_step_ops) == TOP_LEVEL_STEP_COUNT

child_context_ops = [
op
for op in all_ops
if op.operation_type.value == "CONTEXT"
and op.name is not None
and op.name.startswith("child-context-")
]
assert len(child_context_ops) == CHILD_CONTEXT_COUNT

child_step_ops = [
op
for op in all_ops
if op.operation_type.value == "STEP"
and op.name is not None
and op.name.startswith("child-")
]
assert len(child_step_ops) == CHILD_CONTEXT_COUNT * CHILD_STEPS_PER_CONTEXT
Original file line number Diff line number Diff line change
Expand Up @@ -1181,11 +1181,28 @@ def _fetch_execution_history(
Raises:
ClientError: If lambda client encounter error
"""
history_dict = self.lambda_client.get_durable_execution_history(
DurableExecutionArn=execution_arn,
IncludeExecutionData=True,
)
history_response = GetDurableExecutionHistoryResponse.from_dict(history_dict)
events: list[Event] = []
next_marker: str | None = None

while True:
request = {
"DurableExecutionArn": execution_arn,
"IncludeExecutionData": True,
}
if next_marker:
request["Marker"] = next_marker

history_dict = self.lambda_client.get_durable_execution_history(**request)
history_response = GetDurableExecutionHistoryResponse.from_dict(
history_dict
)
events.extend(history_response.events)

next_marker = history_response.next_marker
if not next_marker:
break

history_response = GetDurableExecutionHistoryResponse(events=events)

logger.info("Retrieved %d events from history", len(history_response.events))

Expand Down
Loading