Repository navigation
Conversation
forkWorkflow, rewindWorkflow, setWorkflowDelay and updateWorkflowAttributes now run through runTransactionalStep, so each commits its effect and its step checkpoint in one transaction under the step name it already used. A crash between the two can no longer make recovery repeat the operation: a second fork under a new random ID, or a second rewind that is refused because the first left the target ENQUEUED. Called from inside a step, each commits only while the caller still owns its workflow; from a client, it runs in a plain transaction. The executor's queue check, and a rewind's clearing of the transactional step factories' checkpoints, still run inside the step, after the checkpoint check, so a replay returns the recorded result without repeating them. A fork's ID is chosen before the transaction, so a retry that re-runs the work fails on the fork it already wrote rather than forking again. A relative delay is resolved inside the work, so a delay past the latest instant is still recorded as the step's error. The DAO methods take the caller's connection instead of opening their own transaction, and the batch fork runs through the same helper with no caller.
devhawk
marked this pull request as draft
October 30, 2026 17:39
…ounts setWorkflowDelay resolved a relative delay inside the step's work, so every retry of the transaction pushed it later by that attempt's backoff, contradicting resolveDelayUntil's own contract. It is now resolved once, before the transaction; a delay that cannot be resolved is thrown from inside the work, so the refusal is still recorded as the step's error. The rewind comments no longer claim nothing can run against a partly cleared history: a rewind that rolls back after the step factories' deletes leaves the workflow terminal, and a resume would still run it without those checkpoints. New tests: attributes and a delay whose step fails before commit change nothing and record nothing, and the lost-acknowledgement rewind test counts the target's runs instead of relying on its status.
The transactional step factories' checkpoints were deleted inside the rewind step's transaction. A factory sharing the system database's pool then asked that pool for a second connection while the step held one, so enough concurrent rewinds could exhaust it, and a rewind from a workflow would record the pool timeout as its permanent error. The deletes now run before the transaction opens, as they did before the rewind became a transactional step, with the queue and rewindable checks ahead of them. A step already recorded skips them, so a replay does not delete again; one recorded by a rival meanwhile is found by the step and replayed. A refusal reached before the transaction is thrown from inside the step, so it is still recorded as the step's error. RewindTest now counts the deletes and checks that recovering a workflow that rewound another does not repeat them.
Before its step's transaction, a rewind asked whether the step was already recorded, which took two round trips through the full step check, and then read the target's status in a third. One statement now reads whether the calling step has a row, whether the caller still owns its workflow, and the target's status. The full step check still runs in the step's transaction, which replays a recorded step and refuses a caller that has lost its workflow; the pre-check only decides whether to clear the step factories' checkpoints first, which a caller that has lost its workflow now leaves alone.
…ewind-delay # Conflicts: # transact/src/test/java/dev/dbos/transact/workflow/InternalStepTransactionTest.java
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 PR that adds
runTransactionalStep. This one movesforkWorkflow,rewindWorkflow,setWorkflowDelayandupdateWorkflowAttributesonto that helper. Each now commits its effect and its step checkpoint in one transaction, under the step name it already used.Before, a crash between the two commits made recovery repeat the operation. A fork ran a second time under a new random ID. A second rewind was refused, because the first had left the target ENQUEUED.
Details
DBOSClientcalls pass no caller.Testing
New tests in
InternalStepTransactionTest:Targeted run: 235 of 235 pass. The classes were:
InternalStepTransactionTest,ForkTest,RewindTest,SelfTargetTestWorkflowAttributesTest,OversizedDurationTest,DelayReleaseClockTestPayloadTableReadTest,WorkflowMgmtTest,SystemDatabaseTestClientTest,DBOSConductorEngineTestspotlessCheck,compileTestJavaandjavadocpass.🤖 Generated with Claude Code