Repository navigation
Commit 984510d
authored
Record a child enqueue's deduplication collision as the parent's step (#625)
Closes #602.
When a workflow enqueued a child whose deduplication ID another workflow
held, `DBOSQueueDuplicatedException` reached the parent with nothing
recorded at the parent's step. A parent that caught it and took another
path, then was recovered after the holder finished, enqueued the child
for real on replay, and its later steps diverged from their checkpoints.
#602's second problem, the child's insert and the parent's step
committing separately, was already fixed by #616:
`initChildWorkflowStatus` writes both in one transaction.
## Changes
**1. The collision is the parent's step result.** When a child enqueue
collides, the parent records `DBOSQueueDuplicatedException` as its
step's result, with no child ID, then rethrows it. The record is fenced
on the parent's execution: a parent that no longer owns its workflow is
refused, and stops. If recording fails, that failure is thrown with the
collision attached as suppressed; throwing the collision unrecorded
would let the parent take a path its replay can't repeat.
**2. A replay rethrows it.** The replay checks before a child start or
enqueue (`executeWorkflow`, `enqueueWorkflowByName`, `enqueueDebounced`)
go through one helper, `recordedChild`. `checkChildWorkflow` now returns
the recorded step rather than only its child ID, so a recorded collision
is thrown again instead of looking like an empty slot.
**3. The recorded step must be this call's.** A replay accepts what is
recorded at the slot only when it was recorded under the name of the
workflow being started; otherwise the step is a different one and the
replay throws `DBOSUnexpectedStepException`, as `checkStepResult` does
for any other step. This applies to a recorded child as well as a
recorded collision. Every release so far recorded a child under the
workflow's name, so their histories still replay.
- **Exemption:** releases before 1.2 recorded the debouncer workflow
they started in a debounced enqueue's slot, under
`Constants.DEBOUNCER_WORKFLOW_NAME`. `enqueueDebounced` accepts that
name there, and returns the recorded child itself rather than leave it
to `executeWorkflow`. The exemption is kept until 2.0, which can drop
replay support for workflows that debounced before 1.2.
- **Behaviour change:** a slot holding a different step is now reported
as `DBOSUnexpectedStepException` when the replay reads it, rather than
as `DBOSStepNondeterminismException` when the child's start writes it.
**4. Debouncer's own replay of a collision stays.** It throws the
collision again when the slot recorded nothing and the next slot is
`DBOS.lookupDebouncer`. New histories record the collision, so the
general replay handles them, but histories recorded before this change
still need it.
Matches Python (`_init_workflow` and `_check_recorded_enqueue` in
`_core.py`) and TypeScript (`enqueue_workflow.ts`), which record the
collision as the parent's step and rethrow it on replay. They also
record a rejected workflow-ID reuse the same way; Java has no ID reuse
policy yet.
## Tests
`ChildEnqueueErrorTest`:
- A parent that catches a deduplicated child enqueue records the
collision as its step, and when recovered after the holder finishes,
still falls back and enqueues no child. Fails without the recording.
- A collision recorded under another step's name, and a child recorded
under another step's name, each make the recovered parent throw
`DBOSUnexpectedStepException`. Each fails without its name check.
`DebouncerTest.replaysADebounceThatStartedADebouncerWorkflow`, which
seeds the pre-1.2 slot, covers the exemption; it fails without it.
Run locally: the queue, child-workflow, debouncer, ownership-fence,
ownership and invocation tests. The full suite has not been run locally;
this PR's CI run is that pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)1 parent 110c8d7 commit 984510d
4 files changed
Lines changed: 352 additions & 19 deletions
File tree
- transact/src
- main/java/dev/dbos/transact
- database
- dao
- execution
- test/java/dev/dbos/transact/queue
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1530 | 1530 | | |
1531 | 1531 | | |
1532 | 1532 | | |
1533 | | - | |
| 1533 | + | |
| 1534 | + | |
1534 | 1535 | | |
1535 | 1536 | | |
1536 | 1537 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2110 | 2110 | | |
2111 | 2111 | | |
2112 | 2112 | | |
2113 | | - | |
| 2113 | + | |
| 2114 | + | |
| 2115 | + | |
| 2116 | + | |
| 2117 | + | |
2114 | 2118 | | |
2115 | 2119 | | |
2116 | 2120 | | |
2117 | 2121 | | |
2118 | | - | |
| 2122 | + | |
| 2123 | + | |
2119 | 2124 | | |
2120 | 2125 | | |
2121 | 2126 | | |
| |||
2127 | 2132 | | |
2128 | 2133 | | |
2129 | 2134 | | |
2130 | | - | |
2131 | | - | |
| 2135 | + | |
| 2136 | + | |
| 2137 | + | |
| 2138 | + | |
| 2139 | + | |
| 2140 | + | |
| 2141 | + | |
| 2142 | + | |
| 2143 | + | |
2132 | 2144 | | |
2133 | 2145 | | |
2134 | 2146 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
530 | 530 | | |
531 | 531 | | |
532 | 532 | | |
533 | | - | |
534 | | - | |
535 | | - | |
536 | | - | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
537 | 538 | | |
538 | 539 | | |
539 | 540 | | |
| |||
554 | 555 | | |
555 | 556 | | |
556 | 557 | | |
557 | | - | |
558 | | - | |
559 | | - | |
560 | | - | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
561 | 568 | | |
562 | 569 | | |
563 | 570 | | |
| |||
2166 | 2173 | | |
2167 | 2174 | | |
2168 | 2175 | | |
2169 | | - | |
2170 | | - | |
2171 | | - | |
| 2176 | + | |
| 2177 | + | |
| 2178 | + | |
2172 | 2179 | | |
2173 | 2180 | | |
2174 | 2181 | | |
| |||
2373 | 2380 | | |
2374 | 2381 | | |
2375 | 2382 | | |
| 2383 | + | |
| 2384 | + | |
| 2385 | + | |
| 2386 | + | |
| 2387 | + | |
| 2388 | + | |
| 2389 | + | |
| 2390 | + | |
| 2391 | + | |
| 2392 | + | |
| 2393 | + | |
| 2394 | + | |
| 2395 | + | |
| 2396 | + | |
| 2397 | + | |
| 2398 | + | |
| 2399 | + | |
| 2400 | + | |
| 2401 | + | |
| 2402 | + | |
| 2403 | + | |
| 2404 | + | |
| 2405 | + | |
| 2406 | + | |
| 2407 | + | |
| 2408 | + | |
| 2409 | + | |
| 2410 | + | |
| 2411 | + | |
| 2412 | + | |
| 2413 | + | |
| 2414 | + | |
| 2415 | + | |
| 2416 | + | |
| 2417 | + | |
| 2418 | + | |
2376 | 2419 | | |
2377 | 2420 | | |
2378 | 2421 | | |
| |||
2384 | 2427 | | |
2385 | 2428 | | |
2386 | 2429 | | |
2387 | | - | |
2388 | | - | |
2389 | | - | |
| 2430 | + | |
| 2431 | + | |
| 2432 | + | |
2390 | 2433 | | |
2391 | 2434 | | |
2392 | 2435 | | |
| |||
2702 | 2745 | | |
2703 | 2746 | | |
2704 | 2747 | | |
| 2748 | + | |
2705 | 2749 | | |
2706 | 2750 | | |
2707 | 2751 | | |
| |||
2719 | 2763 | | |
2720 | 2764 | | |
2721 | 2765 | | |
| 2766 | + | |
| 2767 | + | |
| 2768 | + | |
| 2769 | + | |
| 2770 | + | |
| 2771 | + | |
| 2772 | + | |
| 2773 | + | |
| 2774 | + | |
| 2775 | + | |
| 2776 | + | |
| 2777 | + | |
| 2778 | + | |
| 2779 | + | |
| 2780 | + | |
| 2781 | + | |
| 2782 | + | |
| 2783 | + | |
| 2784 | + | |
| 2785 | + | |
| 2786 | + | |
| 2787 | + | |
| 2788 | + | |
| 2789 | + | |
| 2790 | + | |
| 2791 | + | |
2722 | 2792 | | |
2723 | 2793 | | |
2724 | 2794 | | |
| |||
0 commit comments