Conversation
The export scheduler held a single pending slot for the whole plugin. When two executions were in flight on one plugin instance, execution B's RUNNING snapshot could replace execution A's SUCCEEDED snapshot before the worker claimed it. A's drain() then returned once B's record was flushed and A's terminal record was never exported. Lambda runs one invocation per environment, but the local test runner and the conformance suite drive independent executions concurrently through one shared plugin, where this lost 15-18% of terminal records in a two- thread probe. Key the pending map by executionArn. A snapshot only replaces its own execution's entry, the worker exports entries in first-arrival order, and a flush request is honored only once every pending record is exported, so drain() means everything scheduled so far was delivered. Memory stays bounded by executions in flight, the bound the plugin's _state already has. Coalescing within one execution is unchanged and now pinned by tests instead of hidden: a drain-between-hooks test asserts every on-change record arrives when nothing coalesces, and a blocked-exporter test asserts a burst collapses to first + latest. The README states the literal coalescing condition. Addresses issues 1 and 2 raised in the #719 review.
wangyb-A
force-pushed
the
fix/insight-pending-per-execution-arn
branch
from
September 15, 2026 03:59
6c47260 to
14fca8b
Compare
Contributor
Author
|
/ai review |
Contributor
Codex AI reviewNo actionable findings. Residual risk: the concurrency coverage drives plugin hooks directly rather than exercising concurrent executions through the local runner. Reviewed commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem / Motivation
The Workflow Insight export scheduler keeps one pending snapshot for the whole plugin. When two executions are in flight on one plugin instance, execution B's
RUNNINGsnapshot can replace execution A'sSUCCEEDEDsnapshot before the worker claims it. A'sdrain()then returns as soon as B's record is flushed. A's terminal record is never exported.Raised as issue 1 in #719 (comment). Issue 2 in the same comment says the
on-changecoalescing test was weakened to pass when records are lost, and the README misstates when coalescing happens.Why it matters
In
on-completemode the terminal record is the only record, so this is total data loss for that execution. Lambda runs one invocation per environment, so production is mostly safe. The local test runner (aws-durable-execution-sdk-python-testing) drives independent executions concurrently through one sharedworkflow_insight(...)instance, and the conformance suite runs on top of it. Both are affected.What changed
_export_scheduler.py: the pending map is keyed byexecutionArn. A snapshot only replaces its own execution's entry. The worker exports entries in first-arrival order and honors a flush request only once every pending record is exported, sodrain()means everything scheduled so far was delivered. Memory is bounded by executions in flight, the same bound the plugin's_statealready has.plugin.pyis unchanged.Coalescing within one execution is unchanged. It is now pinned by tests instead of hidden: one test drains between hooks and asserts every
on-changerecord arrives; one blocks the exporter and asserts a burst collapses to first + latest. The README states the literal condition: snapshots for one execution coalesce whenever the worker has not yet claimed the previous one, including when no export is running, and snapshots from different executions never coalesce.flowchart LR subgraph Before A1[exec A schedules SUCCEEDED]:::ctx --> S1[one pending slot]:::removed B1[exec B schedules RUNNING]:::ctx --> S1 S1 --> W1[worker exports B only]:::removed end subgraph After A2[exec A schedules SUCCEEDED]:::ctx --> SA[pending A]:::added B2[exec B schedules RUNNING]:::ctx --> SB[pending B]:::added SA --> W2[worker exports A, then B, then flush]:::changed SB --> W2 end classDef added fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:2px classDef changed fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:2px classDef removed fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-dasharray:4 3 classDef ctx fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E🟩 added · 🟨 changed · 🟥 removed · 🟦 unchanged
Each execution now owns its pending slot, so one execution's snapshot never displaces another's.
Not in this PR, on purpose: a "
RUNNINGmay not supersede a pending terminal" rule, closing execution state inplugin.py, and inline export on worker-start failure. Those were the scope in #726 that drew the two P1 race findings, and none of them is needed for the two issues raised.Tests
test_export_scheduler.py: per-execution keying with a blocked exporter; every pending execution exported before flush; two threads × 50 rounds each deliver every terminal record; same-execution burst coalesces to first + latest.test_plugin.py:test_on_change_delivers_every_record_when_drained_between_hooksrestores the exact["RUNNING", "RUNNING", "RUNNING", "SUCCEEDED"]assertion;test_concurrent_executions_on_one_plugin_each_deliver_terminal_recorddrives two executions through one plugin for 50 rounds in bothon-completeandon-change, asserting 50SUCCEEDEDper ARN and empty_stateafter.commit-code-reviewerpass returned no findings. Residual risk it noted:drain()is a global barrier, so a busy concurrent execution can add latency to another's drain. This is the same over-wait the JSdrain()incurs by awaiting the sharedinFlight, and does not apply on Lambda.Manual verification
N/A — the two-thread concurrency tests are the reviewer's probe made deterministic and committed.
Screenshots / video
N/A — no user interface changes.
Supersedes #726.
no linked issue: raised as a review comment on #719; #687 tracks broader Workflow Insight follow-up work