Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fb8af73
fix(insight): key export scheduling by execution ARN
ParidelPooya Sep 17, 2026
4974747
refactor(plugin): create one plugin instance per invocation
ParidelPooya Sep 17, 2026
67eabd0
fix(plugin): give each invocation its own plugin session
ParidelPooya Sep 17, 2026
a67d1a8
Merge branch 'main' into refactor/per-invocation-plugin-instances
ParidelPooya Sep 17, 2026
86e0eef
fix(otel): correlate top-level logs under concurrency
ParidelPooya Sep 17, 2026
1b060b1
fix(insight): contain exporter BaseException; bump to 3.0.0
ParidelPooya Sep 17, 2026
f8b4f31
fix(otel): stamp a log record only from a live claim
ParidelPooya Sep 18, 2026
717ef06
refactor(plugin): make the factory an object with create_plugin
ParidelPooya Sep 18, 2026
750db48
fix(insight): drop a superseded record; lock wrapper installs
ParidelPooya Sep 18, 2026
f8a5678
fix(plugin): reject a factory that returns a non-plugin
ParidelPooya Sep 18, 2026
98a7044
style(sdk): format the factory docstring example
ParidelPooya Sep 18, 2026
c3b8946
fix(insight): drain outside the hook's lock hold
ParidelPooya Sep 18, 2026
3bc7d1e
fix(otel): hold the log-filter claim weakly
ParidelPooya Sep 18, 2026
5d874f1
fix(sdk): reject a factory class at registration
ParidelPooya Sep 18, 2026
9b88343
fix(plugin): fire the end hook on every exit
ParidelPooya Sep 18, 2026
fa6190d
Merge remote-tracking branch 'origin/main' into refactor/per-invocati…
ParidelPooya Sep 18, 2026
ac786f6
fix(plugin): send one end notification per invocation
ParidelPooya Sep 18, 2026
3cbb202
fix(plugin): pair the end hook with the start hook
ParidelPooya Sep 18, 2026
84159aa
fix(plugin): finish the end dispatch, flush only new work
ParidelPooya Sep 18, 2026
2e84c0d
fix(plugin): split exception groups, drain before the build
ParidelPooya Sep 19, 2026
0559917
fix(plugin): dispatch on one thread, keep the real failure
ParidelPooya Sep 19, 2026
aeea6ff
docs(insight): correct two rationales, note drain latency
ParidelPooya Sep 19, 2026
9a7206d
fix(deps): bound the core dependency above
ParidelPooya Sep 19, 2026
869f6b9
fix(insight): flush a record already taken for export
ParidelPooya Sep 19, 2026
f5174ab
fix(insight): release displaced records outside every lock
ParidelPooya Sep 19, 2026
3014e8a
test(insight): make the finalizer probe deterministic
ParidelPooya Sep 19, 2026
fb4c010
fix(plugin): keep factory diagnostics out of customer code
ParidelPooya Sep 19, 2026
3824d1b
fix(plugin): name the invalid return type safely too
ParidelPooya Sep 19, 2026
698693f
fix(plugin): name the rejected class, not its metaclass
ParidelPooya Sep 19, 2026
4face57
fix(insight): make the plugin factory type public
ParidelPooya Sep 19, 2026
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
2 changes: 1 addition & 1 deletion .github/lambda-layer-publish.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[layer]
sdk-version = "2.0.0"
sdk-version = "3.0.0"
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ tests/ # contract tests for the templates and handlers

The 20 invocation and 20 execution requirements reuse the same scenario
handlers; the view is selected per function through the `OTEL_PLUGIN_MODE`
environment variable, which `common.otel_plugin()` reads to pick
`InvocationOtelPlugin` or `ExecutionOtelPlugin`. `template.yaml` deploys only the
view named by its `OtelSuite` parameter.
environment variable, which `common.otel_plugin_factory()` reads to pick
`InvocationOtelPluginFactory` or `ExecutionOtelPluginFactory`. `template.yaml`
deploys only the view named by its `OtelSuite` parameter.

## Scenarios

Expand Down Expand Up @@ -182,8 +182,9 @@ write access; the runner identity needs list, read, and cleanup access.
1. Find or add the requirement in the conformance repository under
`test-requirements/<suite>/<id>.yaml`. New requirement IDs must be registered
there first.
2. Add `src/otel_<n>_<name>.py` exporting `handler`. Select the plugin with
`common.otel_plugin()` and guard the input with `common.require_scenario()`.
2. Add `src/otel_<n>_<name>.py` exporting `handler`. Select the plugin factory
with `common.otel_plugin_factory()` and guard the input with
`common.require_scenario()`.
Use the SDK's real API; never hand-roll behavior to force an expected result.
3. Register the function in `template.yaml` (or `template-long-running.yaml`)
with `Handler: <module>.handler` and `TestDescription: ["<id>"]`, and add a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ version = "0.0.0"
description = "OpenTelemetry conformance test handlers for the AWS Durable Execution SDK for Python, exercised by the aws-durable-execution-conformance-tests OTel suites."
requires-python = ">=3.11"
dependencies = [
"aws-durable-execution-sdk-python==2.0.1",
"aws-durable-execution-sdk-python-otel==1.0.0",
"aws-durable-execution-sdk-python==3.0.0",
"aws-durable-execution-sdk-python-otel==2.0.0",
]

[tool.hatch.build.targets.wheel]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@
from collections.abc import Mapping
from typing import Any

from aws_durable_execution_sdk_python.plugin import DurableInstrumentationPlugin
from aws_durable_execution_sdk_python.plugin import (
DurableInstrumentationPluginFactory,
)
from aws_durable_execution_sdk_python_otel import (
ExecutionOtelPlugin,
InvocationOtelPlugin,
ExecutionOtelPluginFactory,
InvocationOtelPluginFactory,
OtelPluginConfig,
)


def otel_plugin() -> DurableInstrumentationPlugin:
"""Select the telemetry view configured for this deployed function."""
def otel_plugin_factory() -> DurableInstrumentationPluginFactory:
"""Select the telemetry view configured for this deployed function.

Returns a factory, which is what ``durable_execution(plugins=[...])`` takes:
the SDK calls its ``create_plugin`` once per invocation to build that
invocation's plugin. The view is still resolved once, when the handler module
is imported.
"""

if os.environ.get("OTEL_PLUGIN_MODE") == "execution":
return ExecutionOtelPlugin(OtelPluginConfig())
return InvocationOtelPlugin(OtelPluginConfig())
return ExecutionOtelPluginFactory(OtelPluginConfig())
return InvocationOtelPluginFactory(OtelPluginConfig())


def require_scenario(event: Mapping[str, Any], expected: str) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.types import WaitForCallbackContext
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


def submit_callback(
Expand All @@ -19,7 +19,7 @@ def submit_callback(
return None


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "wait-for-callback")
return context.wait_for_callback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from typing import Any

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(
event: dict[str, Any],
context: DurableContext,
Expand All @@ -25,7 +25,7 @@ def handler(
)


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def target_handler(
event: dict[str, Any],
_context: DurableContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
durable_execution,
durable_with_child_context,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_with_child_context
def fail_child_context(_context: DurableContext) -> None:
raise RuntimeError("Intentional child-context failure")


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "child-context-failure")
context.run_in_child_context(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
durable_parallel_branch,
)
from aws_durable_execution_sdk_python.config import ParallelConfig
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_parallel_branch(name="otel-failed-parallel-branch")
def fail_parallel_branch(_context: DurableContext) -> None:
raise RuntimeError("Intentional parallel branch failure")


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "parallel-failure")
result = context.parallel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.config import MapConfig
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


def fail_map_item(
Expand All @@ -22,7 +22,7 @@ def fail_map_item(
raise RuntimeError("Intentional map iteration failure")


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "map-failure")
result = context.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.config import Duration
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "wait-interrupted")
context.wait(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
WaitForConditionConfig,
WaitForConditionDecision,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


def fail_condition_check(
Expand All @@ -31,7 +31,7 @@ def continue_condition(
return WaitForConditionDecision.continue_waiting(Duration.from_seconds(1))


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "wait-for-condition-failure")
context.wait_for_condition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.types import WaitForCallbackContext
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


def submit_failed_callback(
Expand All @@ -19,7 +19,7 @@ def submit_failed_callback(
return None


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "wait-for-callback-failure")
context.wait_for_callback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from typing import Any

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "chained-invoke-failure")
context.invoke(
Expand All @@ -22,7 +22,7 @@ def handler(event: dict[str, Any], context: DurableContext) -> None:
)


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def target_handler(
_event: dict[str, Any],
_context: DurableContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
from typing import Any

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], _context: DurableContext) -> None:
require_scenario(event, "execution-failure")
raise RuntimeError("Intentional execution failure")
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
durable_execution,
durable_step,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
def complete_successfully(_step_context: StepContext) -> str:
return "success"


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "success")
return context.step(complete_successfully(), name="otel-success")
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
durable_with_child_context,
)
from aws_durable_execution_sdk_python.config import ChildConfig
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_with_child_context
def run_virtual_context(_context: DurableContext) -> str:
return "virtual-complete"


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "virtual-context")
return context.run_in_child_context(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
durable_step,
)
from aws_durable_execution_sdk_python.config import Duration
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
def complete_after_resume(_step_context: StepContext) -> str:
return "resumed"


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "wait-resume")
context.wait(Duration.from_seconds(1), name="otel-wait")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
RetryStrategyConfig,
create_retry_strategy,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
Expand All @@ -28,7 +28,7 @@ def succeed_on_retry(step_context: StepContext) -> str:
return "retried"


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "retry")
retry_strategy = create_retry_strategy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
RetryStrategyConfig,
create_retry_strategy,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
def fail_terminally(_step_context: StepContext) -> None:
raise RuntimeError("Intentional terminal failure")


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> None:
require_scenario(event, "terminal-failure")
retry_strategy = create_retry_strategy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
durable_step,
durable_with_child_context,
)
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
Expand All @@ -27,7 +27,7 @@ def run_child_workflow(context: DurableContext) -> str:
return context.step(complete_child_step(), name="otel-child-step")


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> str:
require_scenario(event, "child-context")
return context.run_in_child_context(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
durable_step,
)
from aws_durable_execution_sdk_python.config import ParallelConfig
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
Expand All @@ -39,7 +39,7 @@ def run_parallel_branch_b(context: DurableContext) -> str:
)


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> list[str]:
require_scenario(event, "parallel-hierarchy")
return context.parallel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
durable_step,
)
from aws_durable_execution_sdk_python.config import MapConfig
from common import otel_plugin, require_scenario
from common import otel_plugin_factory, require_scenario


@durable_step
Expand All @@ -35,7 +35,7 @@ def process_map_item(
)


@durable_execution(plugins=[otel_plugin()])
@durable_execution(plugins=[otel_plugin_factory()])
def handler(event: dict[str, Any], context: DurableContext) -> list[int]:
require_scenario(event, "map-hierarchy")
return context.map(
Expand Down
Loading
Loading