Skip to content

fix(dataprotection): unblock ordered restore after PVC initial step - #10479

Draft
weicao wants to merge 4 commits into
mainfrom
bugfix/dp-postready-provisioned-pvc
Draft

fix(dataprotection): unblock ordered restore after PVC initial step#10479
weicao wants to merge 4 commits into
mainfrom
bugfix/dp-postready-provisioned-pvc

Conversation

@weicao

@weicao weicao commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What changed

This keeps PVC Restore=True reserved for full restore completion after postReady finishes. When VolumePopulator has already finished the PVC prepare/provision step but postReady still needs later component PVCs, the PVC exposes the existing non-terminal Populating=True signal with reason Succeed or Provisioned.

Ordered workload creation now consumes that initial-step signal for restore PVCs:

  • InstanceSet ordered pod creation can create the next ordinal after the predecessor restore PVC initial step is complete.
  • Cluster ordered component creation can create the next component group while a predecessor component is still Restore=Unknown/RestoreRunning, as long as all expected restore PVCs for that predecessor have the initial-step signal.

Scope

This PR touches three ordered-creation layers. Each layer has a v1 and v2 code path:

Layer v1 path v2 path Signal consumed
InstanceSet ordered pod creation pkg/controller/instanceset/reconciler_instance_alignment.gorestorePVCInitialStepCompletedForPod reads PVC conditions directly pkg/controller/instanceset2/reconciler_alignment.gorestorePVCInitialStepCompletedForInstance reads Instance annotation PVC Populating=True (Succeed/Provisioned)
Instance controller (v2 signal propagation) N/A (v1 InstanceSet tree has PVCs) pkg/controller/instance/reconciler_status.goreconcileRestorePVCAnnotation checks PVCs, sets RestorePVCInitialStepCompletedAnnotationKey on Instance PVC Populating=True -> Instance annotation
Cluster ordered component creation controllers/apps/cluster/transformer_cluster_component.gorestorePVCInitialStepCompleted reads PVC conditions via MockReader Same file, same function PVC Populating=True (Succeed/Provisioned)

v2 architecture note

v2 InstanceSet tree only contains Instance + Service objects (no PVCs). PVCs belong to the Instance subtree. Therefore the v2 bypass uses a two-layer annotation propagation: Instance controller checks its PVCs and sets annotation -> InstanceSet v2 alignment reads the annotation on predecessor Instance.

Bypass condition (all layers)

predecessor NOT Available AND restorePVCInitialStepCompleted(predecessor)

The bypass is disqualified when a terminal PVC Restore condition (True or False) exists — this scopes it to in-progress restores only.

Why

A TiDB PITR restore can use a logical Backup whose target selector points at the tidb component while data PVCs belong to tidb-pd / other components. Those PVCs may be provision-only until postReady redirects to the TiDB component. If ordered creation waits for terminal component/pod readiness, DP waits for later component PVCs while those later pods/components are not created yet.

The earlier version of this PR used early Restore=True/Provisioned, but review correctly pointed out that this made Cluster/Component/InstanceSet restore state terminally successful before the postReady Restore finished. This version separates the contracts: Populating=True is the PVC initial-step/unblock signal; Restore=True remains the terminal restore-complete signal.

Fixes #10478.
Related to #10393 and #10466.

Tests

  • go test ./pkg/controller/instanceset -count=1
  • go test ./pkg/controller/instance/... -count=1
  • go test ./pkg/controller/instanceset2/... -count=1
  • KUBEBUILDER_ASSETS="..." go test ./controllers/dataprotection -count=1
  • KUBEBUILDER_ASSETS="..." go test ./controllers/apps/cluster -count=1
  • git diff --check
  • CI: all green

Test coverage

Package Function Cases
pkg/controller/instance restorePVCInitialStepCompleted 5 (no conditions / succeed / provisioned / terminal blocks / failed)
pkg/controller/instance reconcileRestorePVCAnnotation 5 (no restore VCT / PVC not found / succeed sets annotation / no conditions / annotation removed)
pkg/controller/instanceset2 restorePVCInitialStepCompletedForInstance 5 (nil template / no restore VCT / without annotation / with annotation / wrong value)

Follow-up hardening (commit 9c3294b)

  • Scope the initial-step bypass to in-progress restores: a terminal PVC Restore condition (True or False) now disqualifies the bypass in both the InstanceSet and Cluster consumers. Previously Populating=True persisted after postReady completion, keeping the bypass active until the VCT restore annotations were cleaned up asynchronously.
  • Cluster ordered creation resolves the exact expected restore PVC names from the predecessor's InstanceSet spec (shared pod-name builder + ComposePVCName) instead of counting label-matched PVCs, so leftover PVCs from a previous incarnation cannot substitute for expected ones. The component-spec count cross-check makes a stale InstanceSet spec fall back to the strict phase gate.
  • MockReader.Get now matches the concrete object type, since a Component and its InstanceSet share the same key.

Remaining gate

  • TiDB exact-head runtime validation (blocked on L1 vcluster PVC sync environment issue)

@weicao
weicao requested review from a team, ldming and wangyelei as code owners June 30, 2026 01:39
@apecloud-bot

Copy link
Copy Markdown
Collaborator

Auto Cherry-pick Instructions

Usage:
  - /nopick: Not auto cherry-pick when PR merged.
  - /pick: release-x.x [release-x.x]: Auto cherry-pick to the specified branch when PR merged.

Example:
  - /nopick
  - /pick release-1.1

CLA Recheck Instructions

Usage:
  - /recheck-cla: Trigger a re-check of CLA status for this pull request.
Example:
  - /recheck-cla

@github-actions github-actions Bot added the size/L Denotes a PR that changes 100-499 lines. label Jun 30, 2026
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.39614% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.09%. Comparing base (138ea3a) to head (84f8782).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
...lers/apps/cluster/transformer_cluster_component.go 67.36% 20 Missing and 11 partials ⚠️
...oller/instanceset/reconciler_instance_alignment.go 75.00% 7 Missing and 4 partials ⚠️
pkg/controller/instance/reconciler_status.go 88.00% 4 Missing and 2 partials ⚠️
controllers/apps/util/mock_reader.go 0.00% 3 Missing ⚠️
...kg/controller/instanceset2/reconciler_alignment.go 86.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10479      +/-   ##
==========================================
- Coverage   62.13%   62.09%   -0.04%     
==========================================
  Files         533      533              
  Lines       63861    64055     +194     
==========================================
+ Hits        39682    39777      +95     
- Misses      20566    20641      +75     
- Partials     3613     3637      +24     
Flag Coverage Δ
unittests 62.09% <74.39%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

if err := r.updatePVCPopulatingCondition(reqCtx, pvc, reason, message); err != nil {
return err
}
if err := r.updatePVCRestoreCondition(reqCtx, pvc, reason, message); err != nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] This marks the PVC Restore condition True as soon as the PV has been rebound/released, before ensurePostReadyRestoreCompleted creates or finishes the postReady Restore. The same PVC condition is aggregated by InstanceSet, then Component, then Cluster, and those layers treat True/False as terminal with early returns. In the provision-only/postReady case this can make the upper restore condition permanently True while postReady is still pending or later fails, so users can observe a false restore success. Keep the workload-unblock signal separate from the restore-complete contract, or only set Restore=True after postReady has completed.

Keep PVC Restore=True reserved for full restore completion after postReady actions finish. When a VolumePopulator restore PVC finishes its prepare/provision step but postReady still needs later component PVCs, publish the existing Populating=True initial-step signal instead.

Teach ordered component and InstanceSet creation to consume that non-terminal Populating signal for restore PVCs, so later ordinals/components can be created without marking the restore terminally successful.

Fixes #10478
@weicao
weicao force-pushed the bugfix/dp-postready-provisioned-pvc branch from e892d71 to 22ba22e Compare June 30, 2026 03:01
@weicao weicao changed the title fix(dataprotection): mark provisioned PVC restore complete fix(dataprotection): unblock ordered restore after PVC initial step Jun 30, 2026
@weicao

weicao commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Updated head to 22ba22e.

The early terminal PVC restore condition is removed: PVC Restore=True is only written by the final UpdatePVCConditions path after postReady completes. The ordered-unblock path now uses the non-terminal PVC Populating=True signal with reason Succeed/Provisioned, and both InstanceSet and Cluster ordered creation consume that signal only while the predecessor component is still Restore=Unknown/RestoreRunning.

Local tests run:

  • go test ./pkg/controller/instanceset -count=1
  • KUBEBUILDER_ASSETS="/Users/wei/Library/Application Support/io.kubebuilder.envtest/k8s/1.26.1-darwin-arm64" go test ./controllers/dataprotection -count=1
  • KUBEBUILDER_ASSETS="/Users/wei/Library/Application Support/io.kubebuilder.envtest/k8s/1.26.1-darwin-arm64" go test ./controllers/apps/cluster -count=1
  • git diff --check

@weicao weicao added the nopick Not auto cherry-pick when PR merged label Jun 30, 2026
leon-ape
leon-ape previously approved these changes Jul 2, 2026

@leon-ape leon-ape left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No blocking findings on the current head.

@apecloud-bot apecloud-bot added the approved PR Approved Test label Jul 2, 2026
@leon-ape
leon-ape dismissed their stale review July 2, 2026 09:06

Dismissed because kb controller PR reviews should report conclusions only and must not be approved directly from this workflow.

@github-actions github-actions Bot removed the size/L Denotes a PR that changes 100-499 lines. label Jul 2, 2026
@apecloud-bot apecloud-bot removed the approved PR Approved Test label Jul 2, 2026
@github-actions github-actions Bot added the size/XL Denotes a PR that changes 500-999 lines. label Jul 2, 2026
@CLAassistant

CLAassistant commented Jul 2, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

… restores

The ordered-creation bypass introduced for restore PVCs had two gaps:

- A PVC keeps Populating=True after the full restore completes (the
  condition is never removed), so the InstanceSet-level bypass stayed
  active after Restore=True landed, weakening OrderedReady semantics
  until the VCT restore annotations were cleaned up asynchronously by
  another controller. Treat any terminal Restore condition (True or
  False) as out of the initial-step window in both consumers.

- The Cluster-level gate counted restore-annotated PVCs by component
  labels, so leftover PVCs (e.g. retained from a previous incarnation)
  could substitute for expected ones that do not exist yet. Resolve the
  exact expected PVC names from the predecessor's InstanceSet spec via
  the shared pod-name builder and require every named PVC to pass,
  cross-checked against the component-spec count so a stale InstanceSet
  spec falls back to the strict phase gate.

Also make MockReader.Get type-aware since a Component and its
InstanceSet share the same object key.
@weicao
weicao force-pushed the bugfix/dp-postready-provisioned-pvc branch from 9c3294b to 366994e Compare July 2, 2026 14:54
weicao and others added 2 commits July 3, 2026 00:24
The v2 instanceset alignment reconciler was missing the restore PVC
initial-step bypass that v1 already had. During ordered-ready restore,
the predecessor instance may not be available because its PVC is being
populated, blocking creation of subsequent instances.

The fix uses a two-layer approach:
1. Instance controller status reconciler checks PVC Populating conditions
   and sets an annotation when all restore PVCs complete their initial step.
2. InstanceSet v2 alignment reconciler reads this annotation to allow
   creating the next instance even when the predecessor is not yet available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added size/XXL Denotes a PR that changes 1000+ lines. and removed size/XL Denotes a PR that changes 500-999 lines. labels Jul 5, 2026
@weicao

weicao commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Update: v2 instanceset sync (commit 84f8782)

What changed

Added restorePVCInitialStepCompleted bypass to v2 instanceset alignment reconciler, syncing with v1.

Files:

  • pkg/constant/annotations.go — New annotation key RestorePVCInitialStepCompletedAnnotationKey
  • pkg/controller/instance/reconciler_status.go — Instance controller propagates PVC restore initial-step status as annotation
  • pkg/controller/instanceset2/reconciler_alignment.go — v2 alignment reconciler checks annotation for ordered-ready bypass
  • Tests added for both Instance and InstanceSet v2 layers

Why this approach

v2 InstanceSet tree only contains Instance objects (no PVCs), unlike v1 which has PVCs in the tree. Solution: Instance controller checks its PVCs and sets an annotation; InstanceSet v2 reads the annotation.

How verified

  • go test ./pkg/controller/instance/... ./pkg/controller/instanceset2/... ./pkg/controller/instanceset/... — all PASS
  • go build ./... — clean

@weicao

weicao commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Re: Loki review — the v1/v2 instanceset sync gap has been addressed in the current head (84f8782da). The v2 instanceset alignment reconciler now has the restorePVCInitialStepCompletedForInstance bypass, propagated via Instance controller annotation. Tests added for both layers (15 cases total). Agent peer review (Stella): GREEN / 0 blocker.

TiDB exact-head runtime evidence is still pending — will follow up.

@weicao

weicao commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

PR body updated: added Scope section with v1/v2 layer enumeration table, v2 architecture note, bypass condition specification, test coverage table, and remaining gate.

@weicao

weicao commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@wangyelei @ldming could one of you please take the maintainer review for the current head?\n\nCurrent state:\n- head: 84f8782da4dbcc3295c29bcc27fdeca71f0b2673\n- CI / CLA: green\n- current scope: v1/v2 ordered restore unblock sync; v2 now propagates the PVC initial-step signal through Instance annotation and consumes it from InstanceSet v2\n- prior review gap is addressed in the current head and summarized in the PR body/comments\n\nTiDB exact-head runtime evidence is still pending separately; this request is for code/design review on the current PR head.

@leon-ape leon-ape left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] Do not derive a cross-controller ordered-unblock contract from DataProtection implementation details

The public InstanceSet contract says OrderedReady waits for each predecessor Pod to become ready. This PR silently overrides that contract by teaching workload and cluster controllers to interpret Populating=True with the Succeed or Provisioned reason as a new restore lifecycle state. The same meaning is reconstructed in three places, and upper layers now need to know DataProtection reason strings, PVC naming, and InstanceTemplate storage details.

This is a module-boundary and API consistency issue. A non-terminal state that is authoritative for ordered unblock must be explicitly defined by the DP/workload API and aggregated through stable status, rather than inferred from internal condition reasons and annotations.

[P1] Compressed InstanceTemplates still fall back to the original deadlock

expectedRestorePVCNamesForComponent builds InstanceSetExt with an empty ObjectTree. Compressed templates are stored in a ConfigMap and cannot be resolved from that tree; the resulting name set is incomplete and the count check deliberately falls back to the strict component phase gate. For a legal component using compressed templates, the predecessor still waits for later PVCs while the later component waits for the predecessor to become Running, so the deadlock this PR targets remains.

[P1] The v2 annotation does not preserve the terminal-state invariant

v1 and Cluster read current PVC conditions and immediately reject the bypass after Restore becomes True or False. v2 reads only an asynchronously maintained boolean Instance annotation. After initial-step success, a later postReady failure can make the PVC terminal while the annotation remains true until another Instance reconciliation removes it; during that window, or during controller restart/failure, InstanceSet v2 can still create the successor. The PR claim that any terminal Restore condition disqualifies the bypass is therefore not true for v2, and the v1/v2 semantics are not equivalent.

@weicao
weicao marked this pull request as draft July 14, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

nopick Not auto cherry-pick when PR merged size/XXL Denotes a PR that changes 1000+ lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DP VolumePopulator does not mark provisioned PVC restore complete before postReady wait

4 participants