Repository navigation
fix: send an oversized first operation in a batch - #872
Merged
Merged
Conversation
This comment has been minimized.
This comment has been minimized.
yaythomas
previously approved these changes
Oct 28, 2026
yaythomas
left a comment
Contributor
There was a problem hiding this comment.
![]()
Can this land as one squashed commit with a conventional commit message, for example the PR title?
hln33
had a problem deploying
to
ai-pr-review-runtime
October 28, 2026 18:51 — with
GitHub Actions
Error
A step result above max_batch_size_bytes (750 KB) that reaches the overflow queue is never sent. Both tests fail on the current code with [] == [large_op]. Refs #871
_drain_overflow refused an operation above max_batch_size_bytes even when the batch was empty, so the operation returned to the overflow queue on every pass and was never sent. Its sync caller then blocked forever, and the execution hung until the Lambda timeout. _wait_for_first_operation already accepted an oversized first operation. The rule now lives in _BatchAccumulator.fits: an empty batch accepts an operation of any size. The service enforces the per-operation limit. Fixes #871
CONTRIBUTING asks for return annotations so that mypy checks the function bodies.
hln33
force-pushed
the
fix/871-overflow-oversized-first-op
branch
from
October 28, 2026 18:53
1191360 to
0285013
Compare
Contributor
Codex AI reviewOne test cleanup issue at Reviewed commit |
This branch was successfully deployed
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.
Issue #, if available
Fixes #871
Description of changes
A step result larger than
max_batch_size_bytes(750 KB) was never sent toCheckpointDurableExecution. The handler thread blocked on the sync SUCCEED checkpoint forever, and the execution hung until the Lambda timeout. The next invocation then ran the step again.Cause.
_drain_overflowcalledbatch.fits(size)on the first operation of an empty batch. An operation above the limit failsfitseven in an empty batch. So_drain_overflowreturned it to the overflow queue on every pass._wait_for_first_operationhad the opposite rule and accepted an oversized first operation.Fix.
_BatchAccumulator.fitsnow returnsTruefor an empty batch. So every intake path sends an oversized operation alone, and the service decides whether it is within the per-operation limit. This matches the JS, Java and Rust SDKs, which all add the first queued item regardless of size.Only the default
AT_LEAST_ONCE_PER_RETRYsemantics hit the bug: its START checkpoint is async, so the batcher still holds the START batch when the large SUCCEED arrives, and SUCCEED takes the overflow path.AT_MOST_ONCE_PER_RETRYsends START first, so SUCCEED becomes the first operation of a fresh batch and_wait_for_first_operationaccepted it.Tests.
tests/state_test.py):test_collect_checkpoint_batch_sends_oversized_operation_from_overflowandtest_drain_overflow_takes_oversized_operation_when_batch_is_emptydrive the batcher directly.tests/e2e/large_step_result_int_test.py): runs the reproduction from [Bug]: Step result larger than 750 KB is never checkpointed; execution hangs #871 (an 800 KB step result) throughDurableFunctionTestRunner. It passes in 0.3 s with the fix.Design
not batch.is_empty() andto the condition in_drain_overflowalso fixes the bug, but it leaves the "first operation is never refused" rule stated twice (in_drain_overflowand in_wait_for_first_operation). Moving the rule intofitsstates it once and removes the special case from_wait_for_first_operation's comment. No logic is added beyond the oneis_empty()check.fits, where the size decision is made.Assumptions
MaxCheckpointPayloadBytes= 1,048,576), and the reporter's reproduction in [Bug]: Step result larger than 750 KB is never checkpointed; execution hangs #871 (745 KB completes, 800 KB hangs).CheckpointDurableExecutionrequest carrying one operation above 750 KB is accepted by the API as long as the operation is within the per-operation limit. Evidence: the JS, Java and Rust SDKs already send such requests. The new e2e test runs against the local emulator, and the emulator does not check STEP payload size (checkpoint/validators/checkpoint.py_validate_payload_sizes). So acceptance of a 750 KB–1 MB operation by the real service is unverified from this branch.Decisions not covered by the issue or the design note
_checkpointing_stoppedbefore the second_collect_checkpoint_batch()call, so a regression returns[]and fails instead of blocking the test run forever in_wait_for_first_operation.TimeoutErrorafter 30 s, but the pytest process then does not exit on its own (likely the blocked handler thread). A regression in CI would show the failure and then hold the job until the job timeout. I did not add thread cleanup to the test, because that would change the runner's behavior only to shorten a failure.Checklist
if self.is_empty(): return Truefails both new tests; removing the size comparison failstest_collect_checkpoint_batch_uses_overflow_queueandtest_collect_checkpoint_batch_respects_size_limit.mainwithAssertionError: assert [] == [QueuedOperation(... 'big_step' ...)]. The e2e test (third commit) fails onmainwithTimeoutError: Execution did not complete within timeoutafter 30.19 s.Verification:
hatch run dev-core:test2119 passed;hatch fmt --checkclean;hatch run dev-core:typecheckno issues.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.