PXB-3747: report backup size and uncompressed size - #1737
Conversation
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
581da53 to
c949905
Compare
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
c949905 to
73622c2
Compare
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. |
73622c2 to
78af66e
Compare
1172be9 to
abcbaa3
Compare
b1cf81f to
8f392cb
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds byte-accurate backup size reporting to xtrabackup by plumbing size counters through the datasink pipeline and emitting the results in xtrabackup_info and the error log. It also introduces end-to-end shell tests that assert strict equality between reported sizes and on-disk/on-stream artifacts across compressed/uncompressed, encrypted, streamed, incremental, and sparse scenarios.
Changes:
- Add
backup_sizereporting based on a leaf datasink byte counter (local/stdout/fifo) and log it at backup completion. - Add
uncompressed_backup_sizereporting for--compressvia per-file metrics tracked at top-levelds_open()entrypoints (pre-compression logical bytes). - Add shared test helpers and new test suites validating strict byte-perfect invariants across many backup configurations.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| storage/innobase/xtrabackup/test/t/backup_size_compress.sh | New end-to-end coverage validating backup_size + uncompressed_backup_size under --compress across multiple scenarios. |
| storage/innobase/xtrabackup/test/t/backup_size_basic.sh | New end-to-end coverage validating backup_size for non-compress backups and asserting absence of uncompressed_backup_size. |
| storage/innobase/xtrabackup/test/suites/reducedlock/backup_size_reduced.sh | New reduced-lock TRUNCATE regression test for byte-perfect backup_size. |
| storage/innobase/xtrabackup/test/inc/common.sh | Adds shared helpers for parsing xtrabackup_info and performing strict size assertions. |
| storage/innobase/xtrabackup/src/xtrabackup.h | Declares size-reporting APIs and introduces xb_get_metrics() helper for conditional tracking under --compress. |
| storage/innobase/xtrabackup/src/xtrabackup.cc | Implements size getters, ensures tracked opens for key backup artifacts, and reorders operations so size sampling includes late-written files. |
| storage/innobase/xtrabackup/src/space_map.cc | Switches tablespace map output to tracked open for inclusion in uncompressed metrics when compressing. |
| storage/innobase/xtrabackup/src/redo_log.cc | Switches redo logfile creation to tracked open for uncompressed metrics. |
| storage/innobase/xtrabackup/src/keyring_plugins.cc | Switches key dump output to tracked open for uncompressed metrics. |
| storage/innobase/xtrabackup/src/ds_xbstream.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_tmpfile.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_stdout.cc | Adds leaf byte counting + get_bytes_written implementation for stdout leaf and ensures file initialization. |
| storage/innobase/xtrabackup/src/ds_local.cc | Adds leaf byte counting + get_bytes_written implementation for local leaf (including sparse trailing-byte fix accounting). |
| storage/innobase/xtrabackup/src/ds_fifo.cc | Adds leaf byte counting + get_bytes_written implementation for FIFO leaf. |
| storage/innobase/xtrabackup/src/ds_encrypt.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_decrypt.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_decompress_zstd.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_decompress_lz4.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_decompress.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_compress_zstd.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_compress_lz4.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_compress.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/ds_buffer.cc | Updates datasink vtable for new API slot and ensures ds_init_file() is called. |
| storage/innobase/xtrabackup/src/datasink.h | Extends datasink interface with get_bytes_written, introduces metrics plumbing on ds_file_t, and adds tracked open helpers. |
| storage/innobase/xtrabackup/src/datasink.cc | Implements tracked open + metrics accumulation, adds ds_leaf(), and adjusts ds_create initialization. |
| storage/innobase/xtrabackup/src/backup_mysql.cc | Embeds backup_size and conditionally appends uncompressed_backup_size into xtrabackup_info. |
| storage/innobase/xtrabackup/src/backup_copy.cc | Switches top-level opens to tracked opens and logs human-readable size summaries + compression ratio. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c83b2ad to
1a7cc75
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1a7cc75 to
7b99d0a
Compare
|
Pushed follow-up commit What it addresses:
Verified by rebuilding in Debug and running |
f4a0a74 to
c935a9d
Compare
|
Follow-up on c935a9d (force-pushed): dropped the redundant |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
jakub-nowakowski-percona
left a comment
There was a problem hiding this comment.
Approved with optional remarks.
|
Follow-up commit 8d20d4b — PXB-3747 : Address Jakub's review comments (will squash before merge). What it addresses from Jakub's review round:
Left as-is with the existing replies standing: J1 ( C1 (Compression ratio Verified: Debug rebuild clean; |
https://perconadev.atlassian.net/browse/PXB-3747 Problem: -------- xtrabackup never tells the operator how large the backup it just produced actually is on disk (or on the stream). Automation, retention, and capacity tooling have had to measure the artifact out-of-band, which is fragile across --target-dir, --stream, and --encrypt transports. Analysis: --------- The datasink pipeline already has exactly one authoritative component: the leaf. Every byte that lands in the backup artifact passes through ds_local / ds_stdout / ds_fifo on its way out. Wrapper datasinks (encrypt, xbstream, buffer, tmpfile, compress*) only transform and forward bytes. ds_data (head) | ds_write() v [ wrappers: xbstream / encrypt / compress / ... ] | pipe_ctxt v leaf (ds_local | ds_stdout | ds_fifo) authoritative byte count | v my_write() / fwrite() There is no framework API for the top of the pipeline to read a metric off the leaf, and adding one-off getters per metric would not generalize to future needs (per-file checksums, stats, ...). Fix: ---- Add a small per-datasink metrics API: each datasink may publish named {name, value} pairs via an optional report_metrics vtable slot, and callers read one off a specific node with ds_find_metric. The three leaves publish an atomic "bytes_written" counter bumped on successful writes. get_final_backup_size() reads it off the leaf; backup_size=N is written into xtrabackup_info and logged. Move Tablespace_map::serialize() and the transition-key dumper ahead of backup_finish() so their bytes are included in the count. Tests: ------ t/backup_size_basic.sh covers --target-dir, --stream=xbstream, --encrypt on both, incremental chains, sparse tablespaces, and a full restore round-trip, with strict byte-for-byte checks. How to use this feature: ------------------------ No flags required. On every successful backup, xtrabackup now embeds: backup_size = <bytes> as an extra line in the xtrabackup_info file, and logs the same value to the error log, e.g.: Backup size: 123.45 MiB (129456789 bytes) xtrabackup_info inside the backup directory follows the transport the user selected: it is compressed under --compress, encrypted under --encrypt, and packed into the stream under --stream=xbstream. To always obtain an unencrypted, uncompressed copy of xtrabackup_info, pass --extra-lsndir=<dir>; the file written there is the plaintext version.
https://perconadev.atlassian.net/browse/PXB-3747 Problem: -------- For --compress backups, backup_size reports post-compression bytes on disk, which says nothing about the logical volume that was backed up. Two artifacts of the same database taken on different dates with different compressibility are only comparable at the uncompressed layer, and the compression ratio is only computable if both numbers are reported. Analysis: --------- Under --compress xtrabackup builds TWO pipelines, because some files must skip compression (they are already encrypted or pre-compressed -- re-compressing them is wasteful): ds_data head --> buffer --> compress --> encrypt --> leaf [regular files] ^ | (shared) v ds_uncompressed_data head ---------> encrypt ------> leaf [encrypted ibd, SST] (buffer and encrypt are optional per invocation; diagram shows a representative full chain.) Counting in ds_ctxt_t would double-count: the leaf sees every byte once per head, and the leaf ctxt is shared across both heads. Counting on the head has the opposite problem -- ds_meta aliases the ds_data head for bookkeeping, and ds_uncompressed_data can alias a mid-chain node under --stream, so head-level counters miss some writes and over-count others. The right place is ds_file_t. Each logical top-level open produces exactly one ds_file_t on the head of a pipeline, and that file object sees every byte of its file exactly once, whatever the downstream shape is. Fix: ---- Add a backup-run aggregate (xb_uncomp_bytes_counter) and enable it on each top-level ds_file_t via a small ds_open_track_uncomp() wrapper. ds_write / ds_write_sparse bump the counter when enabled; internal wrapper opens stay on plain ds_open() so each logical byte is counted exactly once at the top. xtrabackup_info gains uncompressed_backup_size=N under --compress, and the compression ratio is logged alongside. Tests: ------ t/backup_size_compress.sh covers compress, compress+encrypt, compress+stream=xbstream, compress+stream+encrypt, and --extra-lsndir variants; each scenario decrypts/decompresses in place and asserts uncompressed_backup_size matches the plaintext sum byte for byte. How to use this feature: ------------------------ No flags required beyond --compress. Under --compress, xtrabackup additionally embeds: uncompressed_backup_size = <bytes> in xtrabackup_info, and emits two extra error-log lines: Uncompressed backup size: 456.78 MiB (479123456 bytes) Compression ratio: 3.70x uncompressed_backup_size is the pre-compression logical volume (tablespaces + redo + metadata + server-encrypted IBDs + RocksDB SSTs that skip the compressor). It lets operators estimate the disk space required to extract a compressed backup before running xbstream -x / xtrabackup --decompress, and makes backups comparable across dates regardless of compressibility. The field and log lines are omitted for non-compress runs since backup_size already captures the on-disk volume in that case.
8d20d4b to
1d57a2c
Compare
|
Squashed the two follow-up commits into the two base commits. PR now shows exactly the intended two-commit history:
What was folded:
Commit 2's "How to use this feature" section now shows Verification (byte-exact against the pre-fold tip): Re-ran both strict-size suites against the new history (Debug build,
Earlier per-thread review comments (Copilot's C1/C2; Jakub's J1/J2/J3/J4/J5/J6) reference commit IDs that no longer exist after the squash, so GitHub will mark them as outdated. The actual changes those comments were about are all in the two final commits. |
Summary
xtrabackup now embeds two byte-accurate size fields in
xtrabackup_infoand mirrors them to the error log:
backup_size(bytes) — final bytes the backup wrote to disk(or to the stream). Always reported.
uncompressed_backup_size(bytes) — logical pre-compressionbytes (data + redo + metadata + server-encrypted InnoDB files +
RocksDB SSTs that skip the compressor). Reported only when
--compressis used.Both values are byte-perfect against the actual on-disk artifact for
every combination of
--compress{,-lz4,-zstd}/--encrypt/--stream=xbstream/--extra-lsndir/ incremental / sparse.Strict-equality asserts in
t/backup_size_basic.shandt/backup_size_compress.shcover 15+ scenarios end-to-end.JIRA: https://perconadev.atlassian.net/browse/PXB-3747
How to use this feature
No new flags or options — the behavior is enabled for every backup.
Final backup size (
backup_size) — always present inxtrabackup_info, e.g.:and logged:
Uncompressed size under
--compress(uncompressed_backup_size) —added only when
--compress{,-lz4,-zstd}is used:along with two extra log lines:
This helps operators estimate the disk space required to extract
a compressed backup before running
xbstream -x/xtrabackup --decompress, and makes backups taken at differentdates comparable regardless of compressibility.
Where
xtrabackup_infolives — the copy inside the backupdirectory follows the transport the user picked: it is compressed
under
--compress, encrypted under--encrypt, and wrapped inthe xbstream under
--stream=xbstream. To always obtain anunencrypted, uncompressed copy of
xtrabackup_info, pass--extra-lsndir=<dir>— the file written there is plaintextregardless of
--compress/--encrypt.Error-log output — the two (or three, under
--compress)lines above appear at the end of the backup, just before the
"completed OK!" banner.
Commits
Split into two focused, bisect-friendly commits:
PXB-3747 : Report final on-disk backup_size in xtrabackup_info— adds
backup_sizewith zero--compressawareness; includesthe shared strict-size test infrastructure.
PXB-3747 : Report uncompressed_backup_size for --compress backups— adds
uncompressed_backup_size, the "Uncompressed backup size"log line, and the "Compression ratio" log line, wired up only
for
--compressruns.Commit 1 design —
backup_sizeThe datasink pipeline has exactly one authoritative component: the
leaf. Every byte that lands in the backup artifact passes
through
ds_local/ds_stdout/ds_fifo. Wrapper datasinks(encrypt, xbstream, buffer, tmpfile, compress) only transform and
forward bytes.
Rather than adding one-off
get_foo()vtable slots per metric, thepatch introduces a tiny per-datasink metrics API: each datasink
can publish named
{name, value}pairs via an optionalreport_metricsslot, and callers read one off a specific nodewith
ds_find_metric. The three leaves publish an atomicbytes_writtencounter bumped on every successful write.get_final_backup_size()walks to the leaf and reads it;backup_size = Nis written toxtrabackup_infoand logged.Tablespace_map::serialize()and the transition-key dumper aremoved ahead of
backup_finish()so their bytes are included inthe reported
backup_size.Commit 2 design —
uncompressed_backup_sizeWhy two data pipelines exist
xtrabackup maintains two top-level data pipelines because not
every file benefits from compression:
ds_data— normal InnoDB tablespaces, redo, metadata.Compressed when
--compressis on.ds_uncompressed_data— server-encrypted InnoDB tablespaces(re-compressing ciphertext is wasteful) and RocksDB SST files
(already compressed internally). Bypasses the compress wrapper.
Both pipelines share their encrypt / xbstream / leaf tail as an
init-time context-sharing optimization:
Why the counter cannot live on any
ds_ctxt_tThe shared tail makes "attach a counter to the ctxt" broken no
matter which ctxt is picked:
--stream=xbstream,ds_data,ds_redo,ds_meta, andds_uncompressed_dataall terminate at the sameleaf. A leaf-ctxt counter would sum bytes from every pipeline,
and for the compressed pipeline those bytes are post-compression
— neither the logical total nor the on-disk total.
ds_dataandds_uncompressed_datashare these too. Counting there mixespost-compression bytes (from
ds_data) with raw bytes (fromds_uncompressed_data); the sum is meaningless.Per-file
ds_file_thandle is the right anchorThe counter attaches where a single logical write unambiguously
enters exactly one pipeline — on the per-file handle returned by
the top-level
ds_open(). Each top-level open creates a freshds_file_t, the caller decides whether that file's bytes count,and
ds_write/ds_write_sparsebump the counter with thelength supplied at the entry, before any wrapper transforms it:
Tracking is opt-in and scoped to the top-level opens on
ds_data/
ds_redo/ds_meta/ds_uncompressed_data, so the hot-pathcost is one atomic add per write on
--compressruns; non-compressruns skip even that. The same counter aggregates both pipelines,
so server-encrypted IBDs and RocksDB SSTs are counted exactly
once, at their logical size.
xb_global_track_uncomp_bytes()returns a real counter only when--compressis active, so non-compress runs emit neither thefield nor the log lines.
Test plan
t/backup_size_basic.sh—--target-dir,--stream=xbstream,--encrypton both, incremental chains, sparse tablespaces, fullrestore round-trip. Every scenario asserts
backup_sizematchesthe on-disk / on-stream byte count exactly;
uncompressed_backup_sizemust not be present.
t/backup_size_compress.sh—--compresstarget-dir,--compressxbstream,--compress+--encrypt, incrementalchains with compression, RocksDB, server-encrypted InnoDB,
redo-log encryption, sparse +
--compress, and--extra-lsndir.Every scenario asserts
backup_sizematches the compressedon-disk size and, after decompression,
uncompressed_backup_sizematches the decompressed tree size byte-for-byte.
suites/reducedlock/backup_size_reduced.sh— reduced-lock-s compression— 12/12 runnable tests pass; sharedstrict-size helpers live in
inc/common.sh.