Repository navigation
Conversation
createSchedule, pauseSchedule, resumeSchedule and deleteSchedule now run through runTransactionalStep, so each commits its effect and its step checkpoint in one transaction under the step name it already used, and a refusal is recorded as the step's error. A create refused because the name is taken therefore replays that refusal even once the holder is gone, rather than creating the schedule on recovery. Called from inside a step, each commits only while the caller still owns its workflow; from a client or the Conductor, it runs in a plain transaction. The DAO methods take the caller's connection instead of opening their own.
…elper send, sendBulk, setEvent, writeStream and closeStream already committed their effect and their checkpoint together, each with its own copy of the check-run-record transaction. They now share runTransactionalStep, and so gain what it adds: a failure is recorded as the step's error, so a send refused because its destination does not exist replays that refusal even once the destination is created; a retry after a lost commit acknowledgement replays the checkpoint; and the transaction runs at READ COMMITTED. A checkpoint with neither output nor error, which is what these wrote, now replays as a null output rather than being rejected, so steps recorded by earlier releases replay unchanged. An empty send still records nothing, as before. Their DAO methods are reduced to the write on the caller's connection.
The recv claim, the debounce bounce and the stranded-debouncer takeover each had their own copy of the check-run-record transaction. They now share the helper, and so gain failure recording, a replay of the checkpoint after a lost commit acknowledgement, and READ COMMITTED. runRecordedStep is the helper for work that says what its checkpoint holds; runTransactionalStep is now the case that serializes the value the work returns. The recv claim records the message as it was stored, and the takeover names the workflow it created as the step's child, as before. recv is split into its wait and its claim. Only the claim is the step's transaction and only the claim is retried, now on connection failures as well as conflicts, and it re-checks the step inside that transaction. A timed-out recv records null through the helper too.
devhawk
marked this pull request as draft
October 30, 2026 17:39
…edules-and-messages
… and a takeover collision as nothing taken over send, sendBulk, setEvent, writeStream and closeStream record neither an output nor a serialization again, as they did before they ran through the step helper, instead of a serialized null tagged with this application's serializer, which other readers of the step would have to decode for a step that returns nothing. runRecordedStep takes the step's start time, and a recv records the start of its wait, so a recv that waited shows the wait in its step's duration again. A stranded-debouncer takeover whose create collides with whatever else holds the key now rolls back to a savepoint taken before the takeover, undoing the cancel with it, and records that it took nothing over. The caller already treated the collision as exactly that; recording it as an error made a replay depend on the serializer restoring the exception's class. The recv javadoc no longer claims a retried wait restarts its timeout, whose deadline is durable, and debounceDelayedWorkflow's javadoc no longer describes a caller it does not take. New tests: a send's row replays with no output, a timed-out recv's step spans its wait, a schedule create that fails before commit leaves neither schedule nor checkpoint, and a colliding takeover inside a workflow records no error and replays as null.
…edules-and-messages
…edules-and-messages
…edules-and-messages
…edules-and-messages
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.
Stacked on the fork, rewind and delay PR. This finishes moving DBOS's steps onto
runTransactionalStep.Schedules
createSchedule,pauseSchedule,resumeScheduleanddeleteSchedulenow commit with their checkpoint. A refusal is recorded as the step's error, so a create refused because the name is taken replays that refusal even once the holder is gone.Operations that were already atomic
Send,
setEvent,writeStreamandcloseStreamalready committed their effect with their checkpoint. So did the recv claim, the debounce bounce and the stranded-debouncer takeover. Each did it with its own copy of the check-run-record transaction. They now share the helper, and gain from it:Some details:
runRecordedStepis the variant for work that says what its checkpoint holds. The recv claim records the message as it was stored, and the takeover names the workflow it created as the step's child, both as before.Testing
New tests:
ScheduleCheckpointTest: a refused create replays its refusal.InternalStepTransactionTest: a send to a missing workflow replays its refusal, and a lost commit acknowledgement replays the claimed message.Targeted runs:
SendBulkTest.testSendBulkEmpty, fixed by keeping an empty send unrecorded; after the fix,SendBulkTestandInternalStepTransactionTestpass, 18 of 18.spotlessCheck,compileTestJavaandjavadocpass.🤖 Generated with Claude Code