Repository navigation
feat(opentelemetry): end the workflow span when a waiting instance is cancelled - #1059
Conversation
|
FYI the I don't think thats anything I've done. I think it was introduced in #1051 . I can fix it here in this PR if you'd like. |
5a22515 to
edf27fc
Compare
|
@edeandrea thank you! I'll have a look. Regarding CI, I'm aware and we have the fix already to be merged. |
You're welcome! I have PRs coming for the other 2 issues I opened today as well! |
There was a problem hiding this comment.
🟢 Approval recommended
All reviewed changes are covered by tests and no unresolved blocking issues were identified.
0 open findings
What changed in this PR
Fixes missing OpenTelemetry workflow spans when waiting instances are cancelled by making span completion idempotent across cleanup and event ordering.
Changes:
- Added cancellation-aware workflow span finalization.
- Added unit and integration coverage for cancellation paths.
- Updated test fixtures, endpoints, dependencies, and documentation.
| File | Description |
|---|---|
opentelemetry/runtime/src/test/java/io/quarkiverse/flow/opentelemetry/runtime/WorkflowInstrumentationContextTest.java |
Tests span completion and cancellation ordering. |
opentelemetry/runtime/src/main/java/io/quarkiverse/flow/opentelemetry/runtime/WorkflowInstrumentationContext.java |
Adds idempotent workflow and task span handling. |
opentelemetry/runtime/src/main/java/io/quarkiverse/flow/opentelemetry/runtime/OTelWorkflowExecutionListener.java |
Handles terminal lifecycle events safely. |
opentelemetry/runtime/pom.xml |
Adds test dependencies. |
opentelemetry/integration-tests/src/test/java/io/quarkiverse/flow/opentelemetry/it/util/SpanInfo.java |
Exposes span event names for assertions. |
opentelemetry/integration-tests/src/test/java/io/quarkiverse/flow/opentelemetry/it/CancelWorkflowIT.java |
Verifies exported spans for cancelled workflows. |
opentelemetry/integration-tests/src/main/resources/flow/otel-listen-task.yaml |
Adds a waiting listen workflow fixture. |
opentelemetry/integration-tests/src/main/java/io/quarkiverse/flow/opentelemetry/it/OTelWorkflowsResource.java |
Adds cancellation test endpoints. |
docs/modules/ROOT/pages/opentelemetry.adoc |
Documents cancellation coverage. |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ricardozanini
left a comment
There was a problem hiding this comment.
LGTM, many thanks! We will wait for the OTel SMEs to have a look before merging.
No problem! I actually discovered these issues while working on a demo app |
|
OOps sorry didn't mean to close it! |
|
🙈 The PR is closed and the preview is expired. |
|
FYI, I opened open-workflow-specification/sdk-java#1751 for the engine-side root cause (open-workflow-specification/sdk-java#1749). It changes This PR is still needed until a quarkus-flow release picks up an sdk-java version with that fix. After that, the |
I'll look into this ASAP. |
wmedvede
left a comment
There was a problem hiding this comment.
Many thanks @edeandrea for this work. Honestly, I think this PR is kind of introducing a workaround due to the issue you pointed out: open-workflow-specification/sdk-java#1749
I can see that you kindly proposed a PR too: open-workflow-specification/sdk-java#1749 , and discussion is ongoing. And, hopefully we'll have a solution for the removed metadata soon.
Since this PR still introduce valuable work with the tests, I think we can:
-
wait some days to get the solution for the removed metadata and getting a new release. (@fjtirado can deliver a new relase quickly)
-
bump to get that new relase
-
remove the workarond from this PR, but keep the tests, since are very much appreciated to should validate the fix in the engine too. And we merge.
WDYT?
Thanks @wmedvede ! I wasn't sure about the relationship between quarkus-flow & open-workflow-specification, so I figured doing something here (more tactical) and there (more strategic) was good. If you say that there is quick turnaround between them then I agree, keeping the solution in one place - the correct strategic solution - is better. If you think there may be some delay in getting the upstream fix in and worked in here, then as a user I'd prefer to have this workaround in place in the short term. We can always add an issue to track the workaround and remove it once upstream lands. I'll leave that decision to you as project maintainer to make. I can re-work this pr so it is just the tests and not the workaround. Obviously until the upstream fix is in and the PR gets rebased on it, the checks will fail. Let me know what you'd like to do. |
Hi @edeandrea , I totally agree, I would have done pretty much similar approach. |
|
Sounds good! Just FYI I'm out of the office most of next week, so if you need/want me to make any changes I'd need to do it before the end of day Friday this week. Otherwise it would have to wait until I get back. |
|
@edeandrea this is the last one to release 1.2.0, can you please rebase? |
On it! |
|
@wmedvede so are we waiting for the SDK? I think I'll have to push this one to 2.0.0. @edeandrea how urgent is this? |
If I'm being selfish, I'd like to have it for a demo i'm doing at a conference next week :) But as a project maintainer I can understand if you'd rather wait until the upstream fix is in (avoiding demo-driven-development :D ). Its already been merged. Just needs a release. See open-workflow-specification/sdk-java#1751 |
… cancelled Cancelling an instance that is WAITING on a listen task makes the engine clear the instance metadata (and close WorkflowInstrumentationContext) before it publishes onWorkflowCancelled, so the listener could no longer find the context and the workflow.execute span was never ended or exported. - WorkflowInstrumentationContext.close() now ends the workflow span with a workflow.cancelled event when the instance status is already CANCELLED - Completed, cancelled and failed events all end the span through endWorkflowSpan(), which only acts once, so the span is never ended twice whichever of the listener event or close() comes first - A cancelled event arriving after the metadata is cleared is logged at DEBUG instead of WARN - WorkflowInstrumentationContextTest unit-tests endWorkflowSpan() and close() for every instance status and both orderings - CancelWorkflowIT covers both cancel orderings end to end (listen and wait tasks) The root cause in the engine is tracked in open-workflow-specification/sdk-java#1749. Fixes quarkiverse#1058
edf27fc to
1ae7c59
Compare
|
@ricardozanini changes in |
|
@edeandrea, @wmedvede adapted the workaround in #1072, and we can implement the long-term fix after we bring the SDK. I'll close this one, many thanks! Once #1072 is green, I'll release. EDIT: reopening now since this one is more complete and have been rebased. Walter tried to move faster on the other one, but Eric did the rebase shortly. |
📊 Quarkus Flow Coverage Report
|
Description
Cancelling a workflow instance that is
WAITINGon alistentask never ended or exported itsworkflow.executespan. The listener loggedNo instrumentation context was found ...instead.The cause is the order of operations in the engine's
WorkflowMutableInstance.cancel(). Cancelling the future the listen task registered completes the start pipeline right away, and itscleanUp()closes and clears the instance metadata, includingWorkflowInstrumentationContext. That happens beforeonWorkflowCancelledis published, so the listener can no longer find the context when the event arrives. An instance cancelled while waiting on awaittask goes the other way: the event is published first and cleanup runs later.This PR makes the OTel extension handle both orders. The root cause in the engine is tracked in open-workflow-specification/sdk-java#1749. Once that is fixed, the
close()path here becomes a safety net.Fixes #1058
Changes
WorkflowInstrumentationContextendWorkflowSpan(eventType, statusSetter): closes any open task spans, records the event and ends the workflow span. Only the first call does anything (anAtomicBooleanguards it).close()now ends the workflow span with OK status and aworkflow.cancelledevent when the instance status is alreadyCANCELLED(it is set before cleanup runs). In every other case it behaves as before and only closes task spans.WorkflowInstanceData.OTelWorkflowExecutionListenerendWorkflowSpan, so the span is never ended twice, whichever of the listener event orclose()comes first.opentelemetry/runtime, the module's first tests: JUnit, AssertJ, Mockito andopentelemetry-sdk-testing, all test-scoped and managed by the Quarkus BOM)WorkflowInstrumentationContextTest(11 tests) runs against a realSdkTracerProviderwith anInMemorySpanExporter. It coversendWorkflowSpan()ending open task spans and the workflow span,endWorkflowSpan()only acting once,close()on aCANCELLEDinstance,close()on every otherWorkflowStatus(parameterized), and both orders ofclose()and the cancelled event.opentelemetry/integration-tests)CancelWorkflowIT, with two tests that each cancel 5 waiting instances. For every instance they assert that exactly its task span and one workflow span are exported, and that the workflow span has exactly oneworkflow.cancelledevent.listentask: metadata is cleared before the cancelled event. This is the bug; the test fails without the fix.waittask: the cancelled event comes before cleanup. This checks the span is still ended only once.otel-listen-taskworkflow andPOST /otel-workflows/{otel-listen-task,otel-wait-task}/cancelendpoints.SpanInfo.getEventNames().opentelemetry.adocnow says workflow spans cover cancellation too.Testing
Test Plan
Ran locally (JDK 25):
./mvnw install -pl opentelemetry/runtime,opentelemetry/deployment,opentelemetry/integration-tests -DskipITs=false. The 11 new unit tests and all 16 OTel ITs pass, and the app log has noNo instrumentation contextwarnings. Without the runtime change, thelistentest inCancelWorkflowITfails because the workflow span of each cancelled instance is missing. Disabling theCANCELLEDbranch inclose()makescloseOnACancelledInstanceEndsTheWorkflowSpanAsCancelledfail.Manual Testing
I ran the integration-test app jar by hand (
java -jar target/quarkus-app/quarkus-run.jar), calledPOST /otel-workflows/otel-listen-task/cancel, and read/otel-span-exporter/spans. The cancelled instance'sworkflow.execute otel-listen-taskspan is exported withworkflow.startedandworkflow.cancelledevents, next to itstask.execute listenTaskspan.There is a standalone reproducer at https://github.com/edeandrea/quarkus-flow-reproducers (
issue1058, against released quarkus-flow versions).Checklist
Before submitting this PR, please ensure:
./mvnw clean install -DskipITs=falseAdditional Notes
Only the OTel modules' build and ITs were run locally, not the full multi-module build. The change is limited to
opentelemetry/and one docs page.The failing Build website check ("Generated documentation is not up to date" for
quarkus-flow-persistence*.adoc) is unrelated to this PR. It comes from #1051 changingHashingPersistenceConfigonmainwithout regenerating those config docs.