Skip to content

fix(insight): schedule exports off the hook threads - #703

Open
wangyb-A wants to merge 2 commits into
mainfrom
workflow-insight-export-scheduler
Open

fix(insight): schedule exports off the hook threads#703
wangyb-A wants to merge 2 commits into
mainfrom
workflow-insight-export-scheduler

Conversation

@wangyb-A

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

No linked issue. Supersedes the alternative approach in #699, which stays open only for comparison.

Description

Workflow Insight exporters ran inline inside the plugin hooks. In ON_CHANGE mode that put exporter I/O on the SDK threads that deliver checkpoint callbacks, so a slow exporter slowed the execution.

Records are now handed to a background scheduler. It exports at most one record at a time. Each record is a complete snapshot of the execution, so while an export is in flight newer updates coalesce into a single pending slot and only the latest one is exported next. Exporters for one record run concurrently and independently, so one failing or slow exporter never blocks the others.

onInvocationEnd is the hook the SDK awaits. It schedules the final record, waits for the scheduler to drain, and then flushes every exporter once. The final record is therefore always delivered before the invocation returns. If no worker can be started, the drain exports the pending record itself at that boundary, so nothing is silently dropped.

Behavior changes visible to exporters:

  • export() runs on a worker thread, never on a hook thread.
  • flush() is called once per invocation end (including non-terminal suspends), instead of after every export().
  • Intermediate RUNNING snapshots may be superseded by a newer snapshot while an export is in flight.

Demo/Screenshots

N/A. Behavior is covered by unit tests.

Checklist

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

Testing

Unit Tests

Yes. ExportSchedulerTest (new, 11 tests) covers: exports run off the scheduling thread; updates arriving before or during an export collapse into the latest record; a record scheduled after a pump finishes starts a new pump; drain() is idempotent when idle and blocks until the in-flight and pending records are exported; a failing exporter never blocks the others for the same record; exporters for one record run concurrently; when no worker can be started the pending record is exported inline by drain() and a later schedule() retries the worker.

WorkflowInsightHookTest adds plugin-level coverage: bursts coalesce with the final record always delivered last and never on the hook thread; exporters are flushed once per invocation end even when nothing was emitted. Existing tests that read records right after onInvocationStart now drain through a package-private seam.

mvn clean verify passes across all modules. The concurrency-sensitive tests were repeated 8 times with no failures.

Integration Tests

No new integration tests. The existing LocalDurableTestRunner-based tests in WorkflowInsightPluginTest and TransformContractTest exercise the plugin end to end and still pass.

Examples

N/A. No public API change.

Exporter I/O previously ran inline inside onInvocationStart,
onOperationChange, and onInvocationEnd, so a slow exporter delayed the
SDK's checkpoint callbacks in ON_CHANGE mode.

Records are now handed to a background scheduler that exports at most one
record at a time. Each record is a complete snapshot, so updates arriving
while an export is in flight coalesce into a single pending slot and only
the latest is exported next. Exporters for one record run concurrently and
independently. onInvocationEnd drains the scheduler and then flushes every
exporter once, so the final record is always delivered before the
invocation returns; if no worker can be started, the drain exports the
pending record itself at that boundary.
@wangyb-A

Copy link
Copy Markdown
Contributor Author

/ai review

@github-actions

This comment has been minimized.

- Reject operation-change hooks once invocation end has begun. A checkpoint
  for an unawaited asynchronous operation can complete while the end record
  is draining; its RUNNING snapshot must not replace or follow the final
  record. The closed flag and the final schedule are set under one lock.
- Complete the pump handle when worker submission fails, so a drain() that
  already observed the handle wakes up and exports the pending record inline
  instead of blocking forever.
- Flush exporters concurrently and independently, matching how exports fan
  out, so one slow or failing flush never delays the others.
@wangyb-A

Copy link
Copy Markdown
Contributor Author

/ai review

@wangyb-A
wangyb-A marked this pull request as ready for review September 10, 2026 23:18
@wangyb-A
wangyb-A requested a review from a team September 10, 2026 23:18
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 10, 2026 23:18 — with GitHub Actions Active
@wangyb-A
wangyb-A deployed to ai-pr-review-runtime September 10, 2026 23:28 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown
Contributor

Codex AI review

Found one P2 correctness issue in the asynchronous record handoff. Review was static only, as required.

Reviewed commit 987d69966ad152a2c9101bda14ac6e469b5a7db4. 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.

1 participant