Skip to content

[Bug]: Reconciliation dead-lock during switchover with barman plugin enabled #10981

Description

@jakubmalek

Is there an existing issue already for this bug?

  • I have searched for an existing issue, and could not find anything. I believe this is a new bug.

I have read the troubleshooting guide

  • I have read the troubleshooting guide and I think this is a new bug.

I am running a supported version of CloudNativePG

  • I have read the troubleshooting guide and I think this is a new bug.

Contact Details

No response

Version

1.29 (latest patch)

What version of Kubernetes are you using?

1.34

What is your Kubernetes environment?

Self-managed: kind (evaluation)

How did you install the operator?

Helm

What happened?

The operator seem to get stuck with cluster switchover operation, when Barman Cloud Plugin is enabled on existing cluster.

Reproduce steps:

Step 1. Create new cluster with 2 instances without barman cloud plugin

When cluster is created, the pods are created without sidecar container, as barman cloud plugin is not enabled.

NAME            READY   STATUS    RESTARTS   AGE     INSTANCEROLE
barman-test-1   1/1     Running   0          2m41s   primary
barman-test-2   1/1     Running   0          109s    replica

Step 2. Enable barman cloud plugin

When barman cloud is enabled, the replica pod is re-deployed first (as expected):

NAME            READY   STATUS    RESTARTS   AGE     INSTANCEROLE
barman-test-1   1/1     Running   0          6m16s  primary
barman-test-2   1/2     Running   0          85s     replica

To upgrade primary pod, operator issues switchover, and here the problem starts:

NAME            READY   STATUS    RESTARTS   AGE     INSTANCEROLE
barman-test-1   1/1     Running   0          6m46s   unhealthy
barman-test-2   2/2     Running   0          115s    replica

As soon as barman cloud plugin is enabled the instance-manager complains that it cannot load plugins:

while getting plugin connection: Unknown plugin: barman-cloud.cloudnative-pg.io

This is expected, as barman cloud plugin requires as sidecar container, which can only be created when pod is re-deployed.
To do that as soon as replica is up and running, CNPG performs switchover, but this in effects triggers archiving command on primary instance, which cannot be executed as plugin is not yet enabled:

{
    "level": "error",
    "ts": "2026-06-22T06:52:45.784562329Z",
    "logger": "wal-archive",
    "msg": "failed to run wal-archive command",
    "logging_pod": "barman-test-1",
    "error": "wal archive plugin is not available: barman-cloud.cloudnative-pg.io",
    "stacktrace": "github.com/cloudnative-pg/machinery/pkg/log.(*logger).Error\n\tpkg/mod/github.com/cloudnative-pg/[email protected]/pkg/log/log.go:125\ngithub.com/cloudnative-pg/cloudnative-pg/internal/cmd/manager/walarchive.NewCmd.func1\n\tinternal/cmd/manager/walarchive/cmd.go:70\ngithub.com/spf13/cobra.(*Command).execute\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1015\ngithub.com/spf13/cobra.(*Command).ExecuteC\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1148\ngithub.com/spf13/cobra.(*Command).Execute\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1071\nmain.main\n\tcmd/manager/main.go:71\nruntime.main\n\t/opt/hostedtoolcache/go/1.26.3/x64/src/runtime/proc.go:290"
}

This in effect blocks the switchover operation, with operator continuously logs:

There is a switchover or a failover in progress, waiting for the operation to complete

Even worse, the cnpg.io/instanceRole label is updated to unhealthy on primary pod, even though the primary pod is still accepting connections. This blocks primary pod from being selected in barman-test-rw SVC making primary instance unreachable for the application.

If I understand correctly, the switchover operation results in pg_switch_wal() function call by postres, which then triggers archiving to be executed, blocking the process. My expectation was that archiving command is configured only after plugin is successfully installed on primary pod, not immediately.

Workaround

The current workaround is to either scale down the cluster to single instance, or configure primaryUpdateMethod to restart when barman cloud plugin is enabled for the first time. This allows the upgrade to be applied but the problem is that it results in a temporary unavailability of the cluster, which is not ideal, especially for production.

I also tried installing the plugin first with enabled: false , but this doesn't install sidecar container.

Cluster resource

Before barman cloud plugin is enabled:


apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  labels:
    app.kubernetes.io/instance: barman-test
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: cloudnative-postgresql-database
    app.kubernetes.io/part-of: barman-test
    cnpg.io/cluster: barman-test
    cnpg.io/reload: "true"
  name: barman-test
  namespace: barman-test
spec:
  affinity:
    podAntiAffinityType: preferred
    topologyKey: topology.kubernetes.io/zone
  bootstrap:
    initdb:
      database: foo
      encoding: UTF8
      localeCType: C
      localeCollate: C
      owner: foo_owner_user
  enablePDB: true
  enableSuperuserAccess: true
  failoverDelay: 0
  imageCatalogRef:
    apiGroup: postgresql.cnpg.io
    kind: ClusterImageCatalog
    major: 15
    name: postgresql
  imagePullPolicy: Always
  instances: 2
  logLevel: info
  maxSyncReplicas: 0
  minSyncReplicas: 0
  postgresGID: 26
  postgresUID: 26
  postgresql:
    parameters:
      archive_mode: "on"
      archive_timeout: 5min
      dynamic_shared_memory_type: posix
      full_page_writes: "on"
      log_destination: csvlog
      log_directory: /controller/log
      log_filename: postgres
      log_rotation_age: "0"
      log_rotation_size: "0"
      log_truncate_on_rotation: "false"
      logging_collector: "on"
      max_parallel_workers: "32"
      max_replication_slots: "32"
      max_worker_processes: "32"
      password_encryption: scram-sha-256
      shared_memory_type: mmap
      shared_preload_libraries: ""
      ssl_max_protocol_version: TLSv1.3
      ssl_min_protocol_version: TLSv1.3
      wal_keep_size: 512MB
      wal_level: logical
      wal_log_hints: "on"
      wal_receiver_timeout: 5s
      wal_sender_timeout: 5s
    pg_hba:
    - host all all all scram-sha-256
    syncReplicaElectionConstraint:
      enabled: false
  primaryUpdateMethod: switchover
  primaryUpdateStrategy: unsupervised
  probes:
    liveness:
      isolationCheck:
        connectionTimeout: 1000
        enabled: true
        requestTimeout: 1000
  replicationSlots:
    highAvailability:
      enabled: true
      slotPrefix: _cnpg_
    synchronizeReplicas:
      enabled: true
    updateInterval: 30
  resources:
    limits:
      memory: 512Mi
    requests:
      cpu: 50m
      memory: 512Mi
  smartShutdownTimeout: 180
  startDelay: 3600
  stopDelay: 1800
  storage:
    resizeInUseVolumes: true
    size: 5Gi
  switchoverDelay: 3600
  walStorage:
    resizeInUseVolumes: true
    size: 3Gi


After barman cloud plugin is enabled:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  labels:
    app.kubernetes.io/instance: barman-test
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: cloudnative-postgresql-database
    app.kubernetes.io/part-of: barman-test
    cnpg.io/cluster: barman-test
    cnpg.io/reload: "true"
  name: barman-test
  namespace: barman-test
spec:
  affinity:
    podAntiAffinityType: preferred
    topologyKey: topology.kubernetes.io/zone
  backup:
    target: prefer-standby
  bootstrap:
    initdb:
      database: foo
      encoding: UTF8
      localeCType: C
      localeCollate: C
      owner: foo_owner_user
  enablePDB: true
  enableSuperuserAccess: true
  failoverDelay: 0
  imageCatalogRef:
    apiGroup: postgresql.cnpg.io
    kind: ClusterImageCatalog
    major: 15
    name: postgresql
  imagePullPolicy: Always
  instances: 2
  logLevel: info
  maxSyncReplicas: 0
  minSyncReplicas: 0
  plugins:
  - enabled: true
    isWALArchiver: true
    name: barman-cloud.cloudnative-pg.io
    parameters:
      barmanObjectName: barman-test-object-store
  postgresGID: 26
  postgresUID: 26
  postgresql:
    parameters:
      archive_mode: "on"
      archive_timeout: 5min
      dynamic_shared_memory_type: posix
      full_page_writes: "on"
      log_destination: csvlog
      log_directory: /controller/log
      log_filename: postgres
      log_rotation_age: "0"
      log_rotation_size: "0"
      log_truncate_on_rotation: "false"
      logging_collector: "on"
      max_parallel_workers: "32"
      max_replication_slots: "32"
      max_worker_processes: "32"
      password_encryption: scram-sha-256
      shared_memory_type: mmap
      shared_preload_libraries: ""
      ssl_max_protocol_version: TLSv1.3
      ssl_min_protocol_version: TLSv1.3
      wal_keep_size: 512MB
      wal_level: logical
      wal_log_hints: "on"
      wal_receiver_timeout: 5s
      wal_sender_timeout: 5s
    pg_hba:
    - host all all all scram-sha-256
    syncReplicaElectionConstraint:
      enabled: false
  primaryUpdateMethod: switchover
  primaryUpdateStrategy: unsupervised
  probes:
    liveness:
      isolationCheck:
        connectionTimeout: 1000
        enabled: true
        requestTimeout: 1000
  replicationSlots:
    highAvailability:
      enabled: true
      slotPrefix: _cnpg_
    synchronizeReplicas:
      enabled: true
    updateInterval: 30
  resources:
    limits:
      memory: 512Mi
    requests:
      cpu: 50m
      memory: 512Mi
  smartShutdownTimeout: 180
  startDelay: 3600
  stopDelay: 1800
  storage:
    resizeInUseVolumes: true
    size: 5Gi
  switchoverDelay: 3600
  walStorage:
    resizeInUseVolumes: true
    size: 3Gi

Relevant log output

{
    "level": "error",
    "ts": "2026-06-22T06:52:45.784562329Z",
    "logger": "wal-archive",
    "msg": "failed to run wal-archive command",
    "logging_pod": "barman-test-1",
    "error": "wal archive plugin is not available: barman-cloud.cloudnative-pg.io",
    "stacktrace": "github.com/cloudnative-pg/machinery/pkg/log.(*logger).Error\n\tpkg/mod/github.com/cloudnative-pg/[email protected]/pkg/log/log.go:125\ngithub.com/cloudnative-pg/cloudnative-pg/internal/cmd/manager/walarchive.NewCmd.func1\n\tinternal/cmd/manager/walarchive/cmd.go:70\ngithub.com/spf13/cobra.(*Command).execute\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1015\ngithub.com/spf13/cobra.(*Command).ExecuteC\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1148\ngithub.com/spf13/cobra.(*Command).Execute\n\tpkg/mod/github.com/spf13/[email protected]/command.go:1071\nmain.main\n\tcmd/manager/main.go:71\nruntime.main\n\t/opt/hostedtoolcache/go/1.26.3/x64/src/runtime/proc.go:290"
}

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. dosubot commented on Jun 22, 2026

    @dosubot

    Hi @jakubmalek! I'm Dosu and I'm helping the CloudNativePG team.

    This is a confirmed issue that matches a bug reported in plugin-barman-cloud#901 — "Primary instance won't restart when switching during migration from in-tree to plugin." The exact same deadlock scenario (plugin added to existing cluster → switchover triggered → WAL archiving fails because sidecar not yet present on primary) was reproduced there with identical error messages.

    The root cause is that the archive_command is always set to /controller/manager wal-archive, which delegates to the plugin at runtime [1]. When the cluster spec is updated with the plugin configuration, the instance manager immediately tries to use the plugin for archiving, but the sidecar container (required for the plugin to function) can only be injected when the pod is recreated. This creates a circular dependency: switchover needs archiving to complete, but archiving needs the sidecar, which needs the pod to be recreated, which is what the switchover is trying to accomplish.

    This was fixed in PR #8236 [2]. The fix introduces:

    1. A new exit code (MissingWALArchivePlugin = 5) so the instance manager can signal that the configured archiver plugin is unavailable [3]
    2. Early detection at pod startup — if the plugin is configured but not loaded, the process exits immediately with that code instead of retrying indefinitely
    3. A new reconciliation step (requireWALArchivingPluginOrDelete) in the cluster controller that detects pods terminated due to missing plugins and automatically deletes/recreates them with the sidecar included [3]

    Since you're on v1.29 (latest patch), you should have this fix. However, given that you're still hitting the deadlock, it's possible the fix didn't fully cover your scenario (fresh plugin enablement vs. the in-tree-to-plugin migration path that #901 originally reported). Your reproduction is slightly different in that you're adding the barman-cloud plugin to a cluster that previously had no WAL archiver configured via plugin at all, whereas #901 was about migrating from the in-tree barman-cloud to the plugin version.

    Workaround (confirming yours): Setting primaryUpdateMethod: restart when initially enabling the plugin avoids the deadlock by skipping the switchover entirely. Once all pods have been restarted with the sidecar, you can switch back to switchover.

    To reply, just mention @dosu.


    Share context across your team and agents. Try Dosu.

    Leave Feedback Learn about cloudnative-pg with Dosu Add Dosu to your team

  2. added
    bug 🐛Something isn't working
    and removed
    triagePending triage
    on Jun 24, 2026
  3. added theissue type on Jun 24, 2026
  4. added this to the 1.30.0 milestone on Jun 24, 2026
  5. 2 remaining items

  6. added 2 commits that reference this issue on Jun 26, 2026
    72df4c1
    af3da8f
  7. added a commit that references this issue on Jun 26, 2026
    6b275c3
  8. added 8 commits that reference this issue on Jun 26, 2026
    9746000
    09d4368
    932c210
    beb7732
    e5aeca8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bug 🐛Something isn't working

Type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions