Skip to content

Recover orphaned child workflows after force failover - #11388

Open
jiechenz wants to merge 2 commits into
mainfrom
jiechenz/parent-child-ff-issue
Open

Recover orphaned child workflows after force failover#11388
jiechenz wants to merge 2 commits into
mainfrom
jiechenz/parent-child-ff-issue

Conversation

@jiechenz

@jiechenz jiechenz commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What changed?

Allow a parent to mark an open child from a losing initiation as zombie and create its replacement atomically when the reuse policy permits.

Validate ownership and initiation metadata under the child lock while preserving normal deduplication for accepted children.

  • Added a namespace-scoped dynamic config, EnableOrphanedChildWorkflowReplacement, disabled by default.
  • When starting a child encounters an existing open execution, allow replacement only when:
    • the feature is enabled;
    • the reuse policy permits another run;
    • the existing child belongs to the requesting parent;
    • the existing child is still open (CREATED or RUNNING);
    • the existing child’s initiation event ID/version differs from the parent’s accepted initiation;
    • no pending child on the parent’s accepted branch already uses the same workflow ID.

Why?

After a force failover, the new active cluster may accept a parent branch that does not contain the initiation responsible for an already-replicated child. Retrying the child start then fails with a workflow ID conflict, leaving the parent unable to make progress.
The conflicting child cannot safely be relinked because it belongs to a losing initiation. Marking it zombie prevents it from making progress on the new active cluster, while atomically creating a replacement lets the accepted parent branch continue.

How did you test it?

  • built
  • run locally and tested manually
  • covered by existing tests
  • added new unit test(s)
  • added new functional test(s)

Potential risks

The losing child remains in zombie state until replication catches it up or closes it.

jiechenz added 2 commits July 31, 2026 16:46
Allow a parent to mark an open child from a losing initiation as
zombie and create its replacement atomically when the reuse policy
permits.

Validate ownership and initiation metadata under the child lock while
preserving normal deduplication for accepted children.
@jiechenz jiechenz self-assigned this Aug 1, 2026
@jiechenz
jiechenz requested review from a team as code owners August 1, 2026 00:10
@jiechenz jiechenz added the team/cgs-foundation Require cgs foundation team review label Aug 1, 2026
@jiechenz
jiechenz requested a review from Copilot August 1, 2026 00:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables a guarded recovery path for “orphaned child” workflow ID collisions after force failover by optionally zombifying the conflicting child (after validating parent ownership/initiations under the child lock) and atomically creating a replacement child run.

Changes:

  • Added a namespace-scoped dynamic config EnableOrphanedChildWorkflowReplacement (default disabled) and wired it into History service config.
  • Extended History StartWorkflowExecution internal request with zombify_conflicting_child and implemented atomic zombify + replace logic in duplicate workflow ID resolution.
  • Added unit + functional coverage, including an NDC reproduction suite for force-failover orphan scenarios.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/ndc/child_workflow_force_failover_test.go New functional NDC test suite reproducing and validating orphaned-child replacement behavior across multiple child-history states.
service/history/transfer_queue_active_task_executor.go Adds decision logic for when to request orphaned-child zombify on child-start conflicts; refactors child start call plumbing.
service/history/transfer_queue_active_task_executor_test.go Adds unit coverage for the zombify eligibility gate (canZombifyConflictingChild).
service/history/configs/config.go Adds/wires the new namespace-scoped dynamic config into History service config.
service/history/api/workflow_id_dedup.go Introduces ZombifyConflictingChildAction with validation under the conflicting child lock and state transition to zombie.
service/history/api/workflow_id_dedup_test.go Adds unit tests for the zombify action validation and state transition behavior.
service/history/api/startworkflow/api.go Routes eligible duplicate-start conflicts to zombify action based on request flag/policy/state.
proto/internal/temporal/server/api/historyservice/v1/request_response.proto Adds zombify_conflicting_child to internal StartWorkflowExecutionRequest.
common/dynamicconfig/constants.go Defines the new dynamic config key and documentation.
api/historyservice/v1/request_response.pb.go Regenerated protobuf output for the new request field.
Files not reviewed (1)
  • api/historyservice/v1/request_response.pb.go: Generated file

Comment on lines +1217 to +1222
for initiatedEventID, pendingChild := range mutableState.GetPendingChildExecutionInfos() {
if initiatedEventID != childInfo.GetInitiatedEventId() &&
pendingChild.GetStartedWorkflowId() == childInfo.GetStartedWorkflowId() {
return false
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not valid, different target NS feature is disabled. Parent and child can only live in the same NS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team/cgs-foundation Require cgs foundation team review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants