Skip to content

Commit f169ab4

Browse files
committed
fix: improve orchestration span handling and error status reporting
1 parent 1d427b0 commit f169ab4

File tree

5 files changed

+27
-25
lines changed

5 files changed

+27
-25
lines changed

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcWorker.java

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -193,47 +193,49 @@ public void startAndBlock() {
193193
? startedEvent.getParentTraceContext() : null;
194194
String orchName = startedEvent != null ? startedEvent.getName() : "";
195195

196-
// Start the orchestration span BEFORE execution so child spans
197-
// (activities, timers) are nested under it in the trace hierarchy.
198-
// Each dispatch creates its own orchestration span (matching JS/dotnet behavior).
199-
Span orchestrationSpan = null;
200-
TraceContext orchestrationSpanContext = null;
201-
if (orchTraceCtx != null) {
196+
// Pass parentTraceContext to executor so child spans
197+
// (activities, timers) reference the correct trace.
198+
TaskOrchestratorResult taskOrchestratorResult;
199+
try {
200+
taskOrchestratorResult = taskOrchestrationExecutor.execute(
201+
orchestratorRequest.getPastEventsList(),
202+
orchestratorRequest.getNewEventsList(),
203+
orchTraceCtx);
204+
} catch (Throwable e) {
205+
if (e instanceof Error) {
206+
throw (Error) e;
207+
}
208+
throw new RuntimeException(e);
209+
}
210+
211+
// Emit a single orchestration span only on the completion dispatch.
212+
// Uses ExecutionStartedEvent timestamp as start time for full lifecycle.
213+
// Java OTel doesn't support SetSpanId() like .NET, so we emit one
214+
// span on completion rather than merging across dispatches.
215+
boolean isCompleting = taskOrchestratorResult.getActions().stream()
216+
.anyMatch(a -> a.getOrchestratorActionTypeCase() == OrchestratorAction.OrchestratorActionTypeCase.COMPLETEORCHESTRATION
217+
|| a.getOrchestratorActionTypeCase() == OrchestratorAction.OrchestratorActionTypeCase.TERMINATEORCHESTRATION);
218+
219+
if (isCompleting && orchTraceCtx != null) {
202220
Map<String, String> orchSpanAttrs = new HashMap<>();
203221
orchSpanAttrs.put(TracingHelper.ATTR_TYPE, TracingHelper.TYPE_ORCHESTRATION);
204222
orchSpanAttrs.put(TracingHelper.ATTR_TASK_NAME, orchName);
205223
orchSpanAttrs.put(TracingHelper.ATTR_INSTANCE_ID, orchestratorRequest.getInstanceId());
206224

207225
Instant spanStartTime = null;
208-
if (startedHistoryEvent.hasTimestamp()) {
226+
if (startedHistoryEvent != null && startedHistoryEvent.hasTimestamp()) {
209227
spanStartTime = DataConverter.getInstantFromTimestamp(
210228
startedHistoryEvent.getTimestamp());
211229
}
212230

213-
orchestrationSpan = TracingHelper.startSpanWithStartTime(
231+
Span orchestrationSpan = TracingHelper.startSpanWithStartTime(
214232
TracingHelper.TYPE_ORCHESTRATION + ":" + orchName,
215233
orchTraceCtx,
216234
SpanKind.SERVER,
217235
orchSpanAttrs,
218236
spanStartTime);
219-
orchestrationSpanContext = TracingHelper.getCurrentTraceContext(orchestrationSpan);
220-
}
221-
222-
TaskOrchestratorResult taskOrchestratorResult;
223-
try {
224-
taskOrchestratorResult = taskOrchestrationExecutor.execute(
225-
orchestratorRequest.getPastEventsList(),
226-
orchestratorRequest.getNewEventsList(),
227-
orchestrationSpanContext);
228-
} catch (Throwable e) {
229-
if (e instanceof Error) {
230-
throw (Error) e;
231-
}
232-
throw new RuntimeException(e);
233-
}
234237

235-
// End the orchestration span, setting error status if the orchestration failed
236-
if (orchestrationSpan != null) {
238+
// Set error status if orchestration failed
237239
for (OrchestratorAction action : taskOrchestratorResult.getActions()) {
238240
if (action.getOrchestratorActionTypeCase() == OrchestratorAction.OrchestratorActionTypeCase.COMPLETEORCHESTRATION) {
239241
CompleteOrchestrationAction complete = action.getCompleteOrchestration();
321 Bytes
Loading
-24.5 KB
Loading
-24.9 KB
Loading
-229 Bytes
Loading

0 commit comments

Comments
 (0)