Skip to content

[BUG][multi-cluster] VolumeExpansion ops fails #10576

Description

@jinuxstyle

Describe the bug

In multi-cluster deployment with enableInstanceAPI: true, VolumeExpansion OpsRequest fails or stays at progress: 0/N until timeout. The Ops Action successfully updates Cluster / Component / InstanceSet volumeClaimTemplates to the target size, but only one member-cluster PVC is actually expanded. Ops cannot detect any PVC progress and eventually fails with a 30-minute timeout.

Reproduced on: mypg — fresh 3-replica PostgreSQL cluster on community latest main (2026-07-07), first VolumeExpansion after cluster reached Running.

# control plane
kubectl -n mypg get ops
NAME                  TYPE              CLUSTER   STATUS   PROGRESS   AGE
expand-mypg-tklpw     VolumeExpansion   mypg      Failed   0/3        38m

kubectl -n mypg get cluster,component,instanceset
mypg                Updating
mypg-postgresql     Updating

NAME                 DESIRED   UP-TO-DATE   READY   AVAILABLE
mypg-postgresql      3         1            3       3

Spec vs actual storage after volume expansion (10Gi → 11Gi):

# cluster.spec.componentSpecs[0].volumeClaimTemplates[0]   = 11Gi  ✅
# component.spec.volumeClaimTemplates[0]                   = 11Gi  ✅
# instanceset.spec.volumeClaimTemplates[0]                 = 11Gi  ✅
# instanceset.status.updatedReplicas                       = 1     ❌ (expected 3)

Member-cluster PVCs (only 1 of 3 expanded):

# kb-member-cluster1
data-mypg-postgresql-0   10Gi   Bound

# kb-member-cluster2
data-mypg-postgresql-1   10Gi   Bound

# kb-member-cluster3
data-mypg-postgresql-2   11Gi   Bound   # only this one

Instance CR VCT spec:

mypg-postgresql-0   VCT=10Gi  kubeblocks.io/generation=1
mypg-postgresql-1   VCT=10Gi  kubeblocks.io/generation=1
mypg-postgresql-2   VCT=11Gi  kubeblocks.io/generation=2

OpsRequest status:

Status:
  Phase:     Failed
  Progress:  0/3
  Conditions:
    Message: Timed out waiting for volume expansion to complete, the timeout value is 30 minutes
  Components:
    postgresql: {}

To Reproduce

  1. Install KubeBlocks (latest main) on control plane + member clusters with multi-cluster enabled:

    --multi-cluster-kubeconfig=/var/run/secrets/kubeblocks.io/multicluster/kubeconfig
    --multi-cluster-contexts=kb-member-cluster1,kb-member-cluster2,kb-member-cluster3
    
  2. Create a new running multi-cluster PostgreSQL cluster with enableInstanceAPI: true, 3 replicas, 10Gi data volumes:

apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  name: mypg
  namespace: mypg
  annotations:
    apps.kubeblocks.io/multi-cluster-placement: kb-member-cluster1,kb-member-cluster2,kb-member-cluster3
spec:
  clusterDef: postgresql
  topology: replication
  terminationPolicy: Delete
  componentSpecs:
    - name: postgresql
      enableInstanceAPI: true
      replicas: 3
      volumeClaimTemplates:
        - name: data
          spec:
            storageClassName: openebs-lvmpv
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 10Gi
  1. Wait until cluster is Running with 3 Pods on member clusters.

  2. Create volume expansion OpsRequest (10Gi → 11Gi):

kubectl create -f - <<EOF
apiVersion: operations.kubeblocks.io/v1alpha1
kind: OpsRequest
metadata:
  generateName: expand-mypg-
  namespace: mypg
spec:
  clusterName: mypg
  type: VolumeExpansion
  volumeExpansion:
    - componentName: postgresql
      volumeClaimTemplates:
        - name: data
          storage: 11Gi
EOF
  1. Observe:
    • Cluster / Component / InstanceSet spec storage becomes 11Gi
    • Only one member-cluster PVC expands (in our case postgresql-2 on kb-member-cluster3)
    • Other PVCs stay 10Gi
    • InstanceSet.status.updatedReplicas stays 1
    • OpsRequest progress stays 0/3 with empty status.components.postgresql
    • After 30 minutes, OpsRequest phase: Failed with timeout message

Expected behavior

  • InstanceSet controller updates all Instance CRs' volumeClaimTemplates to 11Gi.
  • Instance controller patches all member-cluster PVCs to request 11Gi.
  • Storage provider expands volumes; Ops detects N/N PVCs at target size.
  • OpsRequest completes with phase: Succeed and progress: 3/3.
  • Cluster returns to Running.

Root cause (analysis)

Two independent bugs in the enableInstanceAPI + instanceset2 path:

Bug 1: Ops cannot discover PVCs created by Instance controller

pkg/operations/ops_runtime.goloadVolumes lists PVCs with:

client.MatchingLabels{
    constant.AppInstanceLabelKey:    clusterName,      // app.kubernetes.io/instance
    constant.KBAppComponentLabelKey: compName,         // apps.kubeblocks.io/component-name
}

PVCs created by pkg/controller/instance/utils.gobuildInstancePVCs only have:

apps.kubeblocks.io/instance-name=<instance-name>
apps.kubeblocks.io/pod-name=<instance-name>
apps.kubeblocks.io/vct-name=data

They lack app.kubernetes.io/instance and apps.kubeblocks.io/component-name.

In handleVCTExpansionProgress (pkg/operations/volume_expansion.go):

  • GetInstance finds the Pod via multi-cluster Get
  • newPodInstanceloadVolumes returns empty map
  • GetVolume("data") returns falsecontinue without writing progressDetails
  • completedProgressCount stays 0progress: 0/N until 30-minute timeout → Failed

Bug 2: instanceset2 update budget exhausted after first Instance update

pkg/controller/instanceset2/reconciler_update.go limits rolling updates per reconcile:

unavailable := maxUnavailable - currentUnavailable  // maxUnavailable defaults to 1
// ...
if updatingInstances >= min(replicas, unavailable, updateCount) {
    break
}

With memberUpdateStrategy: BestEffortParallel and roleful InstanceSet, updateCount = 1 per reconcile.

When the first Instance (mypg-postgresql-2) is updated, its Generation advances while ObservedGeneration lags and UpToDate becomes false. IsInstanceAvailable returns false, so currentUnavailable = 1 and unavailable = 0. All subsequent reconciles can no longer update any Instance; the remaining PVCs stay at the old size.

KubeBlocks logs show only mypg-postgresql-2 was updated during the entire 30-minute window:

update Instance mypg-postgresql-2 in InstanceSet mypg-postgresql successful  (x7)

Suggested fixes

  1. Ops: Extend loadVolumes to find PVCs by pod volume claim names or Instance API labels (apps.kubeblocks.io/pod-name); alternatively add cluster/component labels when Instance controller creates PVCs.

  2. instanceset2: For storage-only VCT changes (volume expansion), bypass the unavailable gate and update all Instance CRs in parallel; or fix the unavailable = maxUnavailable - currentUnavailable formula so a temporarily unavailable instance does not zero out the update budget permanently.

Environment

  • KubeBlocks: community latest main (2026-07-07 build)
  • Addon: postgresql-16-1.1.0-main-186f-jun18
  • Cluster: mypg, namespace mypg
  • Multi-cluster placement: kb-member-cluster1,kb-member-cluster2,kb-member-cluster3
  • StorageClass: openebs-lvmpv (allowVolumeExpansion: true)
  • enableInstanceAPI: true
  • memberUpdateStrategy: BestEffortParallel

Additional context

Attachment has more info about the cluster.

mypg-volume-expansion-debug-bundle-2026-07-07.zip

Metadata

Metadata

Labels

kind/bugSomething isn't working

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions