Skip to content
Merged
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 @@ -285,9 +285,8 @@ public void onInvocationEnd(InvocationEndInfo info) {

if (invocationSpan == null) return;

// End still-open operation spans without stamping a status — no terminal
// durable.operation.status means still running (STARTED). A later invocation's
// onOperationEnd emits a continuation span with the real terminal status.
// End still-open operation spans with the STARTED status set in onOperationStart.
// A later invocation's onOperationEnd emits a continuation span with the real terminal status.
for (var entry : operationSpans.entrySet()) {
entry.getValue().end();
}
Expand Down Expand Up @@ -377,7 +376,8 @@ public void onOperationStart(OperationInfo info) {
.setParent(parentContext)
.setAttribute(DURABLE_EXECUTION_ARN, durableExecutionArn)
.setAttribute(DURABLE_OPERATION_ID, info.id())
.setAttribute(DURABLE_OPERATION_TYPE, info.type());
.setAttribute(DURABLE_OPERATION_TYPE, info.type())
.setAttribute(DURABLE_OPERATION_STATUS, info.status() != null ? info.status() : "STARTED");

if (info.isReplay()) {
// Operation was already started in a prior invocation — use a random span ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void defaultConstructor_usesGlobalSdkTracerProviderDirectly() {
var defaultPlugin = new ExecutionOtelPlugin();
defaultPlugin.onInvocationStart(new InvocationInfo("req-1", "arn:exec1", true, Instant.now()));
defaultPlugin.onOperationStart(
new OperationInfo("op-1", "step", "STEP", "Step", null, Instant.now(), null, false));
new OperationInfo("op-1", "step", "STEP", "Step", null, Instant.now(), null, null, false));
defaultPlugin.onOperationEnd(new OperationEndInfo(
"op-1", "step", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", null, false, null));
defaultPlugin.onInvocationEnd(
Expand Down Expand Up @@ -227,7 +227,8 @@ void retryingInvocation_invocationSpanUnset_workflowNotExported() {
@Test
void operationSpan_carriesAttemptNumberAtEnd() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "flaky", "STEP", "Step", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "flaky", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-1", "flaky", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", 3, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand Down Expand Up @@ -263,7 +264,7 @@ void operationSpan_startsAtOperationStartTimestamp() {
var opStart = Instant.parse("2026-02-01T10:00:00Z");
var opEnd = Instant.parse("2026-02-01T10:00:03Z");
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "step-a", "STEP", "Step", null, opStart, null, false));
plugin.onOperationStart(new OperationInfo("op-1", "step-a", "STEP", "Step", null, opStart, null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-1", "step-a", "STEP", "Step", null, opStart, opEnd, "SUCCEEDED", null, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand All @@ -278,7 +279,8 @@ void operationSpan_startsAtOperationStartTimestamp() {
@Test
void operationSpan_parentedToWorkflow_linkedToInvocation() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "step-a", "STEP", "Step", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "step-a", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-1", "step-a", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", null, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand All @@ -301,7 +303,8 @@ void operationSpan_parentedToWorkflow_linkedToInvocation() {
@Test
void attemptSpan_childOfOperation_linkedToInvocation() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "compute", "STEP", "Step", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "compute", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onUserFunctionStart(
new UserFunctionStartInfo("op-1", "compute", "STEP", "Step", null, Instant.now(), false, 1));
plugin.onUserFunctionEnd(new UserFunctionEndInfo(
Expand Down Expand Up @@ -333,9 +336,9 @@ void attemptSpan_childOfOperation_linkedToInvocation() {
void childOperation_parentedToParentOperationSpan() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo(
"op-parent", "my-context", "CONTEXT", "RunInChildContext", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-child", "inner-step", "STEP", "Step", "op-parent", Instant.now(), null, false));
"op-parent", "my-context", "CONTEXT", "RunInChildContext", null, Instant.now(), null, null, false));
plugin.onOperationStart(new OperationInfo(
"op-child", "inner-step", "STEP", "Step", "op-parent", Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-child",
"inner-step",
Expand Down Expand Up @@ -418,7 +421,8 @@ void userFunctionSuccess_setsOkOnAttemptSpan() {
@Test
void operationSuccess_setsOkOnOperationSpan() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "step-ok", "STEP", "Step", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "step-ok", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-1", "step-ok", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", null, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand All @@ -434,7 +438,7 @@ void operationEnd_withNonSuccessStatusAndNoError_leavesOperationSpanUnset() {
// stamped OK — the span status stays UNSET.
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(
new OperationInfo("op-cancel", "step-cancel", "STEP", "Step", null, Instant.now(), null, false));
new OperationInfo("op-cancel", "step-cancel", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-cancel",
"step-cancel",
Expand Down Expand Up @@ -482,7 +486,7 @@ void operationEnd_withNullStatusAndNoError_setsOkOnOperationSpan() {
// null status and null error. This is genuine success and must be stamped OK.
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(
new OperationInfo("op-ctx", "my-ctx", "CONTEXT", null, null, Instant.now(), null, false));
new OperationInfo("op-ctx", "my-ctx", "CONTEXT", null, null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-ctx", "my-ctx", "CONTEXT", null, null, Instant.now(), Instant.now(), null, null, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand All @@ -494,7 +498,8 @@ void operationEnd_withNullStatusAndNoError_setsOkOnOperationSpan() {
@Test
void operationNotCompleted_notEndedAtInvocationEnd() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "my-wait", "WAIT", "Wait", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "my-wait", "WAIT", "Wait", null, Instant.now(), null, null, false));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.PENDING, null));

var spans = spanExporter.getFinishedSpanItems();
Expand All @@ -511,7 +516,8 @@ void operationNotCompleted_notEndedAtInvocationEnd() {
void operationOpenedThenCompletedNextInvocation_exportedOnceOnOperationEnd() {
// Invocation 1: operation opens but does not complete.
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "my-wait", "WAIT", "Wait", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "my-wait", "WAIT", "Wait", null, Instant.now(), null, null, false));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.PENDING, null));
assertTrue(
spanExporter.getFinishedSpanItems().stream()
Expand Down Expand Up @@ -541,7 +547,8 @@ void operationOpenedThenCompletedNextInvocation_exportedOnceOnOperationEnd() {
@Test
void allSpansShareTraceId_acrossInvocations() {
plugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
plugin.onOperationStart(new OperationInfo("op-1", "step-1", "STEP", "Step", null, Instant.now(), null, false));
plugin.onOperationStart(
new OperationInfo("op-1", "step-1", "STEP", "Step", null, Instant.now(), null, null, false));
plugin.onOperationEnd(new OperationEndInfo(
"op-1", "step-1", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", null, false, null));
plugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.PENDING, null));
Expand Down Expand Up @@ -641,7 +648,7 @@ void xrayExtraction_allSpansShareExtractedTraceId() {
"Workflow");
xrayPlugin.onInvocationStart(new InvocationInfo("req-1", ARN, true, Instant.now()));
xrayPlugin.onOperationStart(
new OperationInfo("op-1", "step-a", "STEP", "Step", null, Instant.now(), null, false));
new OperationInfo("op-1", "step-a", "STEP", "Step", null, Instant.now(), null, null, false));
xrayPlugin.onOperationEnd(new OperationEndInfo(
"op-1", "step-a", "STEP", "Step", null, Instant.now(), Instant.now(), "SUCCEEDED", null, false, null));
xrayPlugin.onInvocationEnd(new InvocationEndInfo("req-1", ARN, true, InvocationStatus.SUCCEEDED, null));
Expand Down
Loading
Loading