feat: add custom extension operations API - #607
Conversation
03121c7 to
a73d2d0
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| if (activeThreads.isEmpty() | ||
| && waitState.compareAndSet(FutureWaitState.WAITING, FutureWaitState.SUSPENDED)) { | ||
| suspendForNoActiveThreads(); |
There was a problem hiding this comment.
Codex AI review
[P1] Avoid losing a completion while committing suspension. After the CAS sets SUSPENDED, the future can complete before suspendForNoActiveThreads() runs; completeFutureWait() then returns without re-registering the thread, and execution suspends despite the awaited future being complete. If no durable work remains, this can leave the execution pending indefinitely. Introduce a SUSPENDING handshake so completion can still win until suspension is committed, and add a race test that completes the future immediately after this transition.
| var future = context.reserve(contextName) | ||
| .runInChildContextAsync( | ||
| OperationSubType.WITH_RETRY.getValue(), | ||
| new TypeToken<Object>() {}, |
There was a problem hiding this comment.
Codex AI review
[P1] Preserve the retry result type. This erases T to Object, but ChildContextPrimitive round-trips results through this token by default. With JacksonSerDes, a POJO becomes a LinkedHashMap and generic element types are lost, causing casts from the new public withRetry API to fail on first execution or replay. Require or add Class<T>/TypeToken<T> overloads and pass that token here; cover POJO and generic results across replay.
| return details != null && details.result() != null | ||
| ? deserializeResult(details.result()) | ||
| : extensionConfig.initialState(); |
There was a problem hiding this comment.
Codex AI review
[P1] Distinguish a checkpointed null state from missing state. ExtensionStepResult.retry(null, ...) is valid, but after suspension a PENDING/READY operation has stepDetails.result() == null, so this restores initialState instead of the checkpointed null. That changes extension behavior between initial execution and replay. Determine state presence from the attempt/status or checkpoint an explicit state envelope, and test a non-null initial state followed by retry(null, ...) across replay.
Codex AI reviewFound three correctness issues affecting suspension races, typed retry results, and replayed extension state. Reviewed commit |
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
ExtensionContextandExtensionOperationSPI for composing SDK-owned durable primitives with stable reservations, custom subtypes, stateful step replay, and configurable child contexts.Durable*Operationfacades and move backend engines into theprimitivepackage.DurableContext,ParallelDurableFuture, and compatibility config APIs.Demo/Screenshots
Not applicable. This change adds Java SDK APIs and internal architecture; no visual interface is changed.
Checklist
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:
The OpenTelemetry module also passed 163 tests. Spotless and diff checks pass.