mirror of
https://github.com/temporalio/temporal.git
synced 2026-08-30 18:41:49 -07:00
## What changed?
On the passive replication-apply path, when `dispatchForNewWorkflow`
finds no current execution record (`currentRunID == ""`), it now
preserves the incoming execution state and persists the run via
`CreateWorkflowModeBypassCurrent` in either of these cases:
- the incoming execution is already `ZOMBIE`; or
- the incoming execution is `COMPLETED` or `CORRUPTED` and already
records a successor through `NewExecutionRunId` or `SuccessorRunId`.
The new `nDCTransactionPolicyCreateBypassCurrent` policy makes this
behavior explicit: it does not call `SuppressBy`, does not transition
the workflow state, and does not create or update the current execution
record. `nDCTransactionPolicyCreateAsZombie` remains reserved for paths
that actually suppress an execution.
A genuinely brand-new workflow, or a completed/corrupted run with no
successor (the latest run), still becomes current as before.
<details>
<summary>Case 1: incoming snapshot is already Zombie</summary>
```text
SOURCE / RETIRED CELL | REPLICATION TARGET
------------------------------------|--------------------------------------
|
R1 [Zombie, non-current] | R1 absent
| cur -> none
|
FORCE-REPLICATE R1 SNAPSHOT |
state = Zombie ==R1==> | apply R1 (create path)
| currentRunID == ""
| |
| v
| BEFORE: CreateAsCurrent (BrandNew)
| -> rejected by persistence:
| "Invalid workflow create mode 0,
| state: Zombie"
|
| AFTER: CreateBypassCurrent
| -> R1 remains Zombie
| -> cur -> none
```
The snapshot is already Zombie before it reaches the transaction
manager. Promoting it to current would violate the Zombie/current
invariant; converting it is also unnecessary. The correct operation is
to preserve it as a non-current execution.
</details>
<details>
<summary>Case 2: closed orphan still points to a deleted
successor</summary>
```text
SOURCE | REPLICATION TARGET
------------------------------------|--------------------------------------
|
[1] R1 continue-as-new -> R2 |
R1 [completed, ->R2] | R1 absent
R2 running | R2 absent
cur -> R2 | cur -> none
|
[2] DELETE R2 (current run) |
R1 [completed, orphan, ->R2] |
R2 [deleted] |
cur -> none | cur -> none
|
[3] FORCE-REPLICATE R1 |
R1 [completed, ->R2] ==R1==> | apply R1 (create path)
| run absent, cur -> none
| |
| v
| BEFORE: CreateAsCurrent (BrandNew)
| -> cur -> R1
| [deleted lineage resurrected]
|
| AFTER: CreateBypassCurrent
| -> R1 remains Completed
| -> cur -> none
| [no resurrection]
```
Because R1 records R2 as its successor, R1 cannot be the lineage head
even though the target no longer has a current record. Bypass-current
preserves R1's history and state without promoting it.
</details>
## Why?
Force replication can reach the create path with a missing current
record in more than one form:
1. A migration/replication snapshot may already be `ZOMBIE`.
`CreateWorkflowModeBrandNew` rejects that state because a Zombie must
never own the current execution record.
2. A reset, continue-as-new, retry, or cron transition followed by
deletion of the successor can leave a completed/corrupted orphan that
still records its successor. Promoting that older run with
`CreateWorkflowModeBrandNew` resurrects a workflow whose lineage has
already moved on.
In both cases, the incoming run is known to be non-current. Persisting
it without changing its state and without writing a current record
preserves the replicated data while maintaining the current-execution
invariants.
## How did you test it?
- [x] built
- [ ] run locally and tested manually
- [ ] covered by existing tests
- [x] added new unit test(s)
- [ ] added new functional test(s)
Added unit coverage verifies that:
- a Zombie without successor metadata is persisted with
`CreateWorkflowModeBypassCurrent` and remains Zombie;
- Completed executions with `NewExecutionRunId` or `SuccessorRunId`
remain Completed;
- a Corrupted execution with a successor remains Corrupted; and
- the preserve-state path does not call suppression or update the
current execution record.
Validated with:
```text
go test ./service/history/ndc -run TestTransactionMgrForNewWorkflowSuite -count=1
```
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>