Summary
The Xata branch-operator reconciles its child ObjectStore CR with the Update verb (not Apply), which strips any field not present in its own template. This means fields the upstream barman-cloud-plugin CRD supports — like spec.configuration.wal.maxParallel — can't be customized externally. Every set attempt (SSA, JSON-Patch replace, merge patch) is reverted within milliseconds.
This bites hard during bulk data migrations: barman-cloud-wal-archive defaults to serial uploads (maxParallel: 1), and pg_restore of a multi-tens-of-GB DB generates WAL much faster than serial S3 uploads can ship it. WAL piles up in pg_wal/, fills the PVC, and crashes the postgres pod.
Repro
Verified on ghcr.io/xataio/xata/branch-operator:e1005c161244:
# 1. Try any of: SSA, merge patch, JSON-Patch replace
kubectl -n xata-clusters apply -f - --server-side --field-manager=ops-test --force-conflicts <<'YAML'
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
name: main-<branch>
namespace: xata-clusters
spec:
configuration:
wal:
maxParallel: 8
YAML
# kubectl: "serverside-applied"
# 2. Immediately read
kubectl -n xata-clusters get objectstore main-<branch> -o jsonpath='{.spec.configuration.wal}{"\n"}'
# {"compression":"gzip"} ← maxParallel already gone
managedFields shows the offending operation:
manager: manager
operation: Update ← branch-operator
Update semantics ignore field ownership entirely and replace the whole object server-side. SSA-aware controllers cooperate; Update-based ones can't.
Concrete impact
Real production migration we hit yesterday (~80 GB DB → fresh Xata cluster on g6-dedicated-8, Linode Object Storage):
maxParallel: 1 (the unconfigurable default): pg_restore generated WAL at ~80 MB/s; archive shipped at ~5 MB/s; ~9,500 WAL segments piled up (≈ 151 GB) inside ~30 min and crashed the postgres pod (filesystem full).
maxParallel: 8 (forced via scale-branch-op-to-0 + patch + restart): 8 concurrent uploads, archive kept pace, migration completed cleanly with no WAL backlog.
So this knob materially affects whether bulk migrations succeed on first try. Defaulting to 1 is hostile to the migration-to-Xata workflow that's a primary onboarding case.
Operational workaround (currently the only thing that works)
kubectl -n xata scale deploy branch-operator --replicas=0
kubectl -n xata-clusters patch objectstore main-<branch> --type=merge \
-p '{\"spec\":{\"configuration\":{\"wal\":{\"maxParallel\":8}}}}'
kubectl -n xata-clusters delete pod main-<branch>-1 # so postgres sidecar re-reads config
# … run migration …
kubectl -n xata scale deploy branch-operator --replicas=1
Unergonomic and requires HA-degrading the cluster during the migration window.
Proposed fix
Either of the two would work; (1) is the smaller change:
- Plumb
Branch.spec.backup.wal through to ObjectStore.spec.configuration.wal. Today Branch.spec.backup exposes retention, walArchiving, dbBackup, serverName but not the underlying barman-cloud wal.* knobs (maxParallel, compression, encryption, archiveAdditionalCommandArgs, restoreAdditionalCommandArgs). Forwarding them would let users configure barman without bypassing the operator.
- Switch branch-operator's ObjectStore reconciliation from
Update to SSA (server-side Apply with a field-manager: branch-operator). This is the more general fix — it'd also let third-party tools (Pulumi, ArgoCD, kustomize overlays) own specific fields without getting stomped. Plus it's the modern controller-runtime pattern.
Environment
- branch-operator:
ghcr.io/xataio/xata/branch-operator:e1005c161244
- CNPG operator:
ghcr.io/xataio/xata-cnpg/cloudnative-pg:g4586f2942 (v1.28.0)
- barman-cloud-plugin:
barman-cloud.cloudnative-pg.io v0.12.0
- Kubernetes: LKE 1.35.3
Summary
The Xata branch-operator reconciles its child
ObjectStoreCR with theUpdateverb (notApply), which strips any field not present in its own template. This means fields the upstream barman-cloud-plugin CRD supports — likespec.configuration.wal.maxParallel— can't be customized externally. Every set attempt (SSA, JSON-Patch replace, merge patch) is reverted within milliseconds.This bites hard during bulk data migrations: barman-cloud-wal-archive defaults to serial uploads (
maxParallel: 1), andpg_restoreof a multi-tens-of-GB DB generates WAL much faster than serial S3 uploads can ship it. WAL piles up inpg_wal/, fills the PVC, and crashes the postgres pod.Repro
Verified on
ghcr.io/xataio/xata/branch-operator:e1005c161244:managedFieldsshows the offending operation:Updatesemantics ignore field ownership entirely and replace the whole object server-side. SSA-aware controllers cooperate;Update-based ones can't.Concrete impact
Real production migration we hit yesterday (~80 GB DB → fresh Xata cluster on g6-dedicated-8, Linode Object Storage):
maxParallel: 1(the unconfigurable default):pg_restoregenerated WAL at ~80 MB/s; archive shipped at ~5 MB/s; ~9,500 WAL segments piled up (≈ 151 GB) inside ~30 min and crashed the postgres pod (filesystem full).maxParallel: 8(forced via scale-branch-op-to-0 + patch + restart): 8 concurrent uploads, archive kept pace, migration completed cleanly with no WAL backlog.So this knob materially affects whether bulk migrations succeed on first try. Defaulting to 1 is hostile to the migration-to-Xata workflow that's a primary onboarding case.
Operational workaround (currently the only thing that works)
Unergonomic and requires HA-degrading the cluster during the migration window.
Proposed fix
Either of the two would work; (1) is the smaller change:
Branch.spec.backup.walthrough toObjectStore.spec.configuration.wal. TodayBranch.spec.backupexposesretention,walArchiving,dbBackup,serverNamebut not the underlying barman-cloudwal.*knobs (maxParallel,compression,encryption,archiveAdditionalCommandArgs,restoreAdditionalCommandArgs). Forwarding them would let users configure barman without bypassing the operator.Updateto SSA (server-sideApplywith afield-manager: branch-operator). This is the more general fix — it'd also let third-party tools (Pulumi, ArgoCD, kustomize overlays) own specific fields without getting stomped. Plus it's the modern controller-runtime pattern.Environment
ghcr.io/xataio/xata/branch-operator:e1005c161244ghcr.io/xataio/xata-cnpg/cloudnative-pg:g4586f2942(v1.28.0)barman-cloud.cloudnative-pg.io v0.12.0