Skip to content

feat: add custom extension operations API - #607

Open
zhongkechen wants to merge 51 commits into
mainfrom
codex/extension-operation-refactor
Open

feat: add custom extension operations API#607
zhongkechen wants to merge 51 commits into
mainfrom
codex/extension-operation-refactor

Conversation

@zhongkechen

@zhongkechen zhongkechen commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Issue Link, if available

Closes #571

Description

  • Add the public ExtensionContext and ExtensionOperation SPI for composing SDK-owned durable primitives with stable reservations, custom subtypes, stateful step replay, and configurable child contexts.
  • Route customer-facing operations through singular Durable*Operation facades and move backend engines into the primitive package.
  • Add operation-owned nested config types while preserving existing DurableContext, ParallelDurableFuture, and compatibility config APIs.
  • Implement map, parallel, callback, condition, and retry operations on the extension path while preserving checkpoint/replay, plugin, retry, and serialization behavior.
  • Add extension author documentation and ADR-006.
  • PR #611, which migrates the experimental DAG support to the extension SPI, is a concrete example of using this new SPI.

Demo/Screenshots

Not applicable. This change adds Java SDK APIs and internal architecture; no visual interface is changed.

Checklist

  • I have filled out every section of the PR template
  • I have thoroughly tested this change

Testing

Unit Tests

Yes. Added and updated unit coverage for extension reservations, stateful steps, child-context replay, operation facades/config conversion, primitive implementations, deterministic IDs, plugin metadata, and compatibility APIs.

Full SDK result: 1,202 tests passed.

Integration Tests

Yes. Added integration coverage for extension operations, static operation facades, extension concurrency, plugins, replay, callbacks, retries, map, and parallel behavior.

Full integration result: 413 tests passed.

Examples

No new example was required. Existing examples remain source-compatible and passed their test suite: 120 tests passed, with 34 cloud-only tests skipped by default.

Verification command:

mvn clean install

The OpenTelemetry module also passed 163 tests. Spotless and diff checks pass.

@zhongkechen
zhongkechen had a problem deploying to ai-pr-review-runtime August 20, 2026 18:41 — with GitHub Actions Failure
@zhongkechen
zhongkechen temporarily deployed to ai-pr-review-runtime August 20, 2026 18:41 — with GitHub Actions Inactive
Comment thread sdk/src/main/java/software/amazon/lambda/durable/execution/ExecutionManager.java Outdated
@github-actions

This comment has been minimized.

# Conflicts:
#	sdk-integration-tests/src/test/java/software/amazon/lambda/durable/PluginIntegrationTest.java
#	sdk/src/main/java/software/amazon/lambda/durable/execution/DurableExecutor.java
#	sdk/src/main/java/software/amazon/lambda/durable/plugin/PluginInfoConverter.java
#	sdk/src/test/java/software/amazon/lambda/durable/plugin/PluginInfoConverterTest.java
@zhongkechen
zhongkechen had a problem deploying to ai-pr-review-runtime August 27, 2026 05:02 — with GitHub Actions Failure
@zhongkechen
zhongkechen temporarily deployed to ai-pr-review-runtime August 27, 2026 05:02 — with GitHub Actions Inactive
@github-actions

This comment has been minimized.

@zhongkechen
zhongkechen temporarily deployed to ai-pr-review-runtime August 27, 2026 05:39 — with GitHub Actions Inactive
@zhongkechen
zhongkechen had a problem deploying to ai-pr-review-runtime August 27, 2026 05:39 — with GitHub Actions Failure
Throwable throwable) {
var cause = software.amazon.lambda.durable.util.ExceptionHelper.unwrapCompletableFuture(throwable);
if (cause instanceof SuspendExecutionException || cause instanceof UnrecoverableDurableExecutionException) {
return CompletableFuture.failedFuture(cause);

This comment was marked as outdated.

ignored ->
CompletableFuture.completedFuture(ExtensionStepResult.succeed(function.get())),
extensionConfig(config)));
}

This comment was marked as outdated.

@github-actions

This comment has been minimized.

@zhongkechen
zhongkechen temporarily deployed to ai-pr-review-runtime August 31, 2026 19:32 — with GitHub Actions Inactive
@zhongkechen
zhongkechen had a problem deploying to ai-pr-review-runtime August 31, 2026 19:32 — with GitHub Actions Failure
return extensionContext
.reserve(backoffName(name, attempt))
.waitAsync(OperationSubType.WAIT.getValue(), delay)
.thenCompose(ignored -> executeRetryLoop(name, operation, config, attempt + 1));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_efufskzwapiifaigbhd53nhmx6

[P1] Preserve the durable context across the retry backoff. If the wait completes in-process while another operation keeps the invocation active, this continuation runs on the completion/polling thread. executeRetryLoop then reads thread-local durable and extension contexts that were detached when the child returned its incomplete stage, causing IllegalStateException instead of attempt 2. Pass the captured contexts through the loop or restore both around this continuation, and add an integration test with a retrying branch and an active sibling.

return (error, state, attempt) -> {
var decision = retryStrategy.makeRetryDecision(error, attempt);
return decision.shouldRetry()
? ExtensionStepResult.retry(state, decision.delay())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex AI review · Finding arf_v1_wleseolejcs67e5dfazmlwepne

[P2] Preserve legacy zero-delay retry compatibility. RetryDecision.retry(null) normalizes to Duration.ZERO, and existing step handling also accepted an explicit zero as an immediate retry. Passing either value to ExtensionStepResult.retry now invokes duration validation that rejects delays below one second, so existing custom RetryStrategy implementations throw instead of retrying. Handle zero specially in this adapter and cover both null and explicit-zero decisions.

@github-actions

Copy link
Copy Markdown
Contributor

Codex AI review

Two retry regressions remain. Review was static only as requested; tests were not run.

Reviewed commit 2cf8683021bcc60007829a928009e5cb0c9b5285. Workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add a public API for custom extension operations

1 participant