> [!Note]
> Responses generated with Claude
Not Ceph-specific, and wider than the block data mover — the same erasure affects PodVolumeBackup and the filesystem/kopia path.
Summary
A measured incremental size of zero is indistinguishable from never measured. omitempty on the API status fields drops it, so a perfect incremental — nothing changed, nothing transferred — reports the same as a backup from a version or data mover that never recorded the figure.
velero backup describe --details for a zero-delta run:
Data Movement:
Data Mover: velero-block
Uploader Type: kopia
Moved data Size (bytes): 3221225472
Result: succeeded
Moved data Size is the device size, not the transferred volume. With no incremental line, the best possible CBT outcome displays as if the whole device moved.
To be precise about what is not ambiguous: a genuine full transfer and a whole-device CBT fallback both set a non-zero IncrementalSize and do print Incremental data Size (bytes): N (guard at pkg/cmd/util/output/backup_describer.go:742). Those are distinguishable. The collision is specifically measured-zero vs never-measured — the latter covering backups predating the field, plugin data movers, and any uploader that does not report.
This diverges from agreed intent rather than raising a new opinion: on PR #9357 the reviewer states the expected output for an unchanged backup is INCREMENTAL BYTES: 0.
velero backup describe -o json has the mirror-image defect: backup_structured_describer.go:470-472 gates on Size > 0 || IncrementalSize > 0, so it emits "incrementalSize": 0 — always present, ambiguous in the opposite direction.
The DataUpload printcolumn (pkg/apis/velero/v2alpha1/data_upload_types.go:202) has the same problem: a completed zero-delta DataUpload renders identically to one still running.
Where the zero is lost
pkg/apis/velero/v2alpha1/data_upload_types.go:168-170 and pkg/apis/velero/v1/pod_volume_backup_types.go:121-123 are int64 with omitempty, so a measured 0 is dropped from the serialized status.
pkg/datapath/types.go:37 (the wire struct crossing the data-mover-pod → controller boundary) also matters here, but not as originally described: a plain non-pointer int64 with omitempty is lossless for zero only within a single mover version — omitted on marshal, reconstituted as 0 on unmarshal. It is not lossless across mover versions: a data mover from release-1.17 or earlier predates this field entirely and never writes the key, and a non-pointer field can't distinguish "key absent because this mover doesn't know about it" from "key absent because it happened to omit a zero value." Both collapse to the same reconstituted 0, reintroducing the exact measured-zero-vs-never-measured ambiguity this issue is about, just one hop earlier and for a different reason (mover version, not display formatting). This was caught in review on the fix PR: #10309 (comment), resolved at #10309 (comment).
pkg/uploader/block/snapshot.go:86 produces the right value, pkg/controller/data_upload_controller.go:515 and pkg/controller/pod_volume_backup_controller.go:553 persist it, and pkg/backup/backup.go:1336 copies it into volumeInfo.json.
That the sibling Size field at internal/volume/volumes_information.go:170 carries no omitempty suggests the tag was incidental rather than deliberate.
Why it matters
The design calls for this figure explicitly — "By the end of the backup, incremental size is also returned by the uploader… The size indicates how much data are unique so processed by the uploader". Erasing a measured zero defeats that for exactly the case where CBT performed best.
Suggested fix
pkg/apis/velero/v2alpha1/data_upload_types.go:168-170 and pkg/apis/velero/v1/pod_volume_backup_types.go:121-123 — *int64. A plain omitempty drop is not enough: the field shipped in v1.18.0–v1.18.2, so backups exist whose stored volume info has no incrementalSize, and those would unmarshal to 0 and falsely render a perfect incremental. Both fields already carry +optional, so the generated CRD schema is unchanged and no regeneration is required.
pkg/datapath/types.go:37 — also *int64 with omitempty restored (not a plain non-pointer int64), for the mover-version reason above: an old mover's omitted key unmarshals to nil ("not measured"), a current mover's genuine zero still serializes the key and unmarshals to a non-nil pointer to 0 ("measured zero"), nonzero values are unaffected, and an old controller can still unmarshal a numeric value from a new mover. pkg/controller/data_upload_controller.go:515 and pod_volume_backup_controller.go:553 then assign this field directly to their already-*int64,omitempty CRD status field instead of wrapping it — both sides are the same pointer type.
internal/volume/volumes_information.go:178 and :228 — propagate as *int64.
pkg/cmd/util/output/backup_describer.go:742 and :928 — relax the guards from > 0 to != nil.
Verified with that change: the same zero-delta scenario reports 0 rather than absent and renders Incremental data Size (bytes): 0, while an older backup described with the new client still correctly prints no incremental line.
Related
The display-guard half overlaps #9833 (Backup/restore describe for block data mover) and the two are best implemented together. #9833 also carries the design's other two unimplemented reporting items — the actual backup type in volumeInfo.json (L465-466) and the actual data mover in the header (L468).
Reproduce
Run any successful CBT incremental, then immediately run a second incremental with no intervening writes. .status.incrementalBytes will be absent and velero backup describe --details will show the full device size with no incremental line.
Environment
velero main @ 293f6f6a6; still reproduces on upstream/main @ ae6f40c28. ODF 4.21.10, ceph-csi RBD.
Found during #9714 (Ceph CBT) validation; driver-independent and not limited to the block data mover.
> [!Note]
> Responses generated with Claude
Not Ceph-specific, and wider than the block data mover — the same erasure affects
PodVolumeBackupand the filesystem/kopia path.Summary
A measured incremental size of zero is indistinguishable from never measured.
omitemptyon the API status fields drops it, so a perfect incremental — nothing changed, nothing transferred — reports the same as a backup from a version or data mover that never recorded the figure.velero backup describe --detailsfor a zero-delta run:Moved data Sizeis the device size, not the transferred volume. With no incremental line, the best possible CBT outcome displays as if the whole device moved.To be precise about what is not ambiguous: a genuine full transfer and a whole-device CBT fallback both set a non-zero
IncrementalSizeand do printIncremental data Size (bytes): N(guard atpkg/cmd/util/output/backup_describer.go:742). Those are distinguishable. The collision is specifically measured-zero vs never-measured — the latter covering backups predating the field, plugin data movers, and any uploader that does not report.This diverges from agreed intent rather than raising a new opinion: on PR #9357 the reviewer states the expected output for an unchanged backup is
INCREMENTAL BYTES: 0.velero backup describe -o jsonhas the mirror-image defect:backup_structured_describer.go:470-472gates onSize > 0 || IncrementalSize > 0, so it emits"incrementalSize": 0— always present, ambiguous in the opposite direction.The DataUpload printcolumn (
pkg/apis/velero/v2alpha1/data_upload_types.go:202) has the same problem: a completed zero-delta DataUpload renders identically to one still running.Where the zero is lost
pkg/apis/velero/v2alpha1/data_upload_types.go:168-170andpkg/apis/velero/v1/pod_volume_backup_types.go:121-123areint64withomitempty, so a measured0is dropped from the serialized status.pkg/datapath/types.go:37(the wire struct crossing the data-mover-pod → controller boundary) also matters here, but not as originally described: a plain non-pointerint64withomitemptyis lossless for zero only within a single mover version — omitted on marshal, reconstituted as0on unmarshal. It is not lossless across mover versions: a data mover from release-1.17 or earlier predates this field entirely and never writes the key, and a non-pointer field can't distinguish "key absent because this mover doesn't know about it" from "key absent because it happened to omit a zero value." Both collapse to the same reconstituted0, reintroducing the exact measured-zero-vs-never-measured ambiguity this issue is about, just one hop earlier and for a different reason (mover version, not display formatting). This was caught in review on the fix PR: #10309 (comment), resolved at #10309 (comment).pkg/uploader/block/snapshot.go:86produces the right value,pkg/controller/data_upload_controller.go:515andpkg/controller/pod_volume_backup_controller.go:553persist it, andpkg/backup/backup.go:1336copies it intovolumeInfo.json.That the sibling
Sizefield atinternal/volume/volumes_information.go:170carries noomitemptysuggests the tag was incidental rather than deliberate.Why it matters
The design calls for this figure explicitly — "By the end of the backup, incremental size is also returned by the uploader… The size indicates how much data are unique so processed by the uploader". Erasing a measured zero defeats that for exactly the case where CBT performed best.
Suggested fix
pkg/apis/velero/v2alpha1/data_upload_types.go:168-170andpkg/apis/velero/v1/pod_volume_backup_types.go:121-123—*int64. A plainomitemptydrop is not enough: the field shipped in v1.18.0–v1.18.2, so backups exist whose stored volume info has noincrementalSize, and those would unmarshal to0and falsely render a perfect incremental. Both fields already carry+optional, so the generated CRD schema is unchanged and no regeneration is required.pkg/datapath/types.go:37— also*int64withomitemptyrestored (not a plain non-pointerint64), for the mover-version reason above: an old mover's omitted key unmarshals tonil("not measured"), a current mover's genuine zero still serializes the key and unmarshals to a non-nil pointer to0("measured zero"), nonzero values are unaffected, and an old controller can still unmarshal a numeric value from a new mover.pkg/controller/data_upload_controller.go:515andpod_volume_backup_controller.go:553then assign this field directly to their already-*int64,omitemptyCRD status field instead of wrapping it — both sides are the same pointer type.internal/volume/volumes_information.go:178and:228— propagate as*int64.pkg/cmd/util/output/backup_describer.go:742and:928— relax the guards from> 0to!= nil.Verified with that change: the same zero-delta scenario reports
0rather than absent and rendersIncremental data Size (bytes): 0, while an older backup described with the new client still correctly prints no incremental line.Related
The display-guard half overlaps #9833 (Backup/restore describe for block data mover) and the two are best implemented together. #9833 also carries the design's other two unimplemented reporting items — the actual backup type in
volumeInfo.json(L465-466) and the actual data mover in the header (L468).Reproduce
Run any successful CBT incremental, then immediately run a second incremental with no intervening writes.
.status.incrementalByteswill be absent andvelero backup describe --detailswill show the full device size with no incremental line.Environment
velero main @
293f6f6a6; still reproduces onupstream/main@ae6f40c28. ODF 4.21.10, ceph-csi RBD.Found during #9714 (Ceph CBT) validation; driver-independent and not limited to the block data mover.