public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Shan Shaji <s.shaji@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [RFC PATCH proxmox-backup 4/6] fix #7024: cli: show number of processed files during backup
Date: Wed, 10 Jun 2026 20:02:06 +0200	[thread overview]
Message-ID: <20260610180208.801614-5-s.shaji@proxmox.com> (raw)
In-Reply-To: <20260610180208.801614-1-s.shaji@proxmox.com>

earlier, the progress logs only reported the processed stream size and
uploaded size. To improve this, `PxarArchiverProgressStats` is now
created earlier and shared between the archiver and the upload stream.

Signed-off-by: Shan Shaji <s.shaji@proxmox.com>
---
 pbs-client/src/backup_stats.rs                |  5 ++-
 pbs-client/src/backup_writer.rs               | 39 +++++++++++++++++--
 pbs-client/src/pxar/create.rs                 |  9 +++--
 pbs-client/src/pxar/mod.rs                    |  3 +-
 pbs-client/src/pxar_backup_stream.rs          | 13 ++++++-
 proxmox-backup-client/src/main.rs             | 27 +++++++++++--
 .../src/proxmox_restore_daemon/api.rs         |  1 +
 pxar-bin/src/main.rs                          |  1 +
 tests/catar.rs                                |  1 +
 9 files changed, 85 insertions(+), 14 deletions(-)

diff --git a/pbs-client/src/backup_stats.rs b/pbs-client/src/backup_stats.rs
index d7c13784b..aa5ae4889 100644
--- a/pbs-client/src/backup_stats.rs
+++ b/pbs-client/src/backup_stats.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
 use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
 use std::time::Duration;
 
+use crate::pxar::PxarArchiverProgressStats;
 use crate::pxar::create::ReusableDynamicEntry;
 
 /// Basic backup run statistics and archive checksum
@@ -50,11 +51,12 @@ pub(crate) struct UploadCounters {
     injected_stream_len: Arc<AtomicUsize>,
     reused_stream_len: Arc<AtomicUsize>,
     total_stream_len: Arc<AtomicUsize>,
+    pub pxar_progress: Option<Arc<PxarArchiverProgressStats>>,
 }
 
 impl UploadCounters {
     /// Create and zero init new upload counters
-    pub(crate) fn new() -> Self {
+    pub(crate) fn new(pxar_progress: Option<Arc<PxarArchiverProgressStats>>) -> Self {
         Self {
             total_chunk_count: Arc::new(AtomicUsize::new(0)),
             injected_chunk_count: Arc::new(AtomicUsize::new(0)),
@@ -63,6 +65,7 @@ impl UploadCounters {
             injected_stream_len: Arc::new(AtomicUsize::new(0)),
             reused_stream_len: Arc::new(AtomicUsize::new(0)),
             total_stream_len: Arc::new(AtomicUsize::new(0)),
+            pxar_progress: pxar_progress,
         }
     }
 
diff --git a/pbs-client/src/backup_writer.rs b/pbs-client/src/backup_writer.rs
index f0744ecaf..d0246689c 100644
--- a/pbs-client/src/backup_writer.rs
+++ b/pbs-client/src/backup_writer.rs
@@ -28,6 +28,8 @@ use proxmox_human_byte::HumanByte;
 use proxmox_log::{Level, debug, enabled, info, trace, warn};
 use proxmox_time::TimeSpan;
 
+use crate::pxar::PxarArchiverProgressStats;
+
 use super::backup_stats::{BackupStats, UploadCounters, UploadStats};
 use super::inject_reused_chunks::{InjectChunks, InjectReusedChunks, InjectedChunksInfo};
 use super::merge_known_chunks::{MergeKnownChunks, MergedChunkInfo};
@@ -53,6 +55,7 @@ pub struct UploadOptions {
     pub compress: bool,
     pub encrypt: bool,
     pub index_type: IndexType,
+    pub is_metadata_mode: bool,
 }
 
 /// Index type for upload options.
@@ -327,7 +330,7 @@ impl BackupWriter {
             .as_u64()
             .unwrap();
 
-        let mut counters = UploadCounters::new();
+        let mut counters = UploadCounters::new(None);
         let counters_readonly = counters.clone();
 
         let is_fixed_chunk_size = prefix == "fixed";
@@ -377,6 +380,7 @@ impl BackupWriter {
             stream,
             index_csum_2,
             counters_readonly,
+            options.is_metadata_mode,
         )
         .await?;
 
@@ -400,6 +404,7 @@ impl BackupWriter {
         stream: impl Stream<Item = Result<bytes::BytesMut, Error>>,
         options: UploadOptions,
         injections: Option<std::sync::mpsc::Receiver<InjectChunks>>,
+        pxar_archive_progress_stats: Option<Arc<PxarArchiverProgressStats>>,
     ) -> Result<BackupStats, Error> {
         let known_chunks = Arc::new(Mutex::new(HashSet::new()));
 
@@ -478,6 +483,8 @@ impl BackupWriter {
             options.compress,
             injections,
             archive_name,
+            pxar_archive_progress_stats,
+            options.is_metadata_mode,
         )
         .await?;
 
@@ -770,8 +777,10 @@ impl BackupWriter {
         compress: bool,
         injections: Option<std::sync::mpsc::Receiver<InjectChunks>>,
         archive: &BackupArchiveName,
+        pxar_archive_progress_stats: Option<Arc<PxarArchiverProgressStats>>,
+        is_metadata_mode: bool,
     ) -> impl Future<Output = Result<UploadStats, Error>> {
-        let mut counters = UploadCounters::new();
+        let mut counters = UploadCounters::new(pxar_archive_progress_stats);
         let counters_readonly = counters.clone();
 
         let is_fixed_chunk_size = prefix == "fixed";
@@ -855,6 +864,7 @@ impl BackupWriter {
             stream,
             index_csum_2,
             counters_readonly,
+            is_metadata_mode,
         )
     }
 
@@ -866,6 +876,7 @@ impl BackupWriter {
         stream: impl Stream<Item = Result<MergedChunkInfo, Error>>,
         index_csum: Arc<Mutex<Option<Sha256>>>,
         counters: UploadCounters,
+        is_metadata_mode: bool,
     ) -> impl Future<Output = Result<UploadStats, Error>> {
         let append_chunk_path = format!("{prefix}_index");
         let upload_chunk_path = format!("{prefix}_chunk");
@@ -889,7 +900,29 @@ impl BackupWriter {
                     let size_uploaded = HumanByte::from(uploaded_len.load(Ordering::SeqCst));
                     let elapsed = TimeSpan::from(start_time.elapsed());
 
-                    info!("processed {size} in {elapsed}, uploaded {size_uploaded}");
+                    if let Some(pxar_progress) = &counters.pxar_progress {
+                        let n_files = pxar_progress.total_files_count();
+                        let reuse_payload_size = pxar_progress.total_reused_payload_size();
+                        let total_of_logical_file_size = HumanByte::from(
+                            pxar_progress.total_reencoded_size() + reuse_payload_size,
+                        );
+                        let total_reuse_payload_size = HumanByte::from(reuse_payload_size);
+                        let reuse_file_count = pxar_progress.files_reused_count();
+
+                        if is_metadata_mode {
+                            info!(
+                                "scanned {n_files} files with {total_of_logical_file_size} (reusing {reuse_file_count} files with \
+                                {total_reuse_payload_size}) of which processed {size} in {elapsed} and uploaded {size_uploaded}"
+                            );
+                        } else {
+                            info!(
+                                "scanned {n_files} files with {total_of_logical_file_size} of which processed {size} \
+                                in {elapsed} and uploaded {size_uploaded}"
+                            );
+                        }
+                    } else {
+                        info!("processed {size} in {elapsed}, uploaded {size_uploaded}");
+                    }
                 }
             }))
         } else {
diff --git a/pbs-client/src/pxar/create.rs b/pbs-client/src/pxar/create.rs
index 1246f67d9..2d2dfbb3f 100644
--- a/pbs-client/src/pxar/create.rs
+++ b/pbs-client/src/pxar/create.rs
@@ -151,7 +151,7 @@ pub(crate) struct HardLinkInfo {
 }
 
 #[derive(Default)]
-struct PxarArchiverProgressStats {
+pub struct PxarArchiverProgressStats {
     files_reused_count: AtomicU64,
     files_hardlink_count: AtomicU64,
     files_reencoded_count: AtomicU64,
@@ -223,7 +223,7 @@ struct Archiver {
     // an older client without the encode-time check is detected here and the affected file is
     // re-encoded instead of reused.
     last_reusable_offset: Option<u64>,
-    progress_stats: PxarArchiverProgressStats,
+    progress_stats: Arc<PxarArchiverProgressStats>,
     split_archive: bool,
 }
 
@@ -251,6 +251,7 @@ pub async fn create_archive<T, F>(
     options: PxarCreateOptions,
     forced_boundaries: Option<mpsc::Sender<InjectChunks>>,
     suggested_boundaries: Option<mpsc::Sender<u64>>,
+    archiver_progress_stats: Option<Arc<PxarArchiverProgressStats>>,
 ) -> Result<(), Error>
 where
     T: SeqWrite + Send,
@@ -334,7 +335,7 @@ where
         previous_payload_index,
         cache: PxarLookaheadCache::new(options.max_cache_size),
         last_reusable_offset: None,
-        progress_stats: PxarArchiverProgressStats::default(),
+        progress_stats: archiver_progress_stats.unwrap_or_default(),
         split_archive,
     };
 
@@ -2133,7 +2134,7 @@ mod tests {
                 suggested_boundaries: Some(suggested_boundaries),
                 cache: PxarLookaheadCache::new(None),
                 last_reusable_offset: None,
-                progress_stats: PxarArchiverProgressStats::default(),
+                progress_stats: Arc::new(PxarArchiverProgressStats::default()),
                 split_archive: true,
             };
 
diff --git a/pbs-client/src/pxar/mod.rs b/pbs-client/src/pxar/mod.rs
index b88fac33f..d6820d01a 100644
--- a/pbs-client/src/pxar/mod.rs
+++ b/pbs-client/src/pxar/mod.rs
@@ -58,7 +58,8 @@ mod flags;
 pub use flags::Flags;
 
 pub use create::{
-    MetadataArchiveReader, PxarCreateOptions, PxarPrevRef, PxarWriters, create_archive,
+    MetadataArchiveReader, PxarArchiverProgressStats, PxarCreateOptions, PxarPrevRef, PxarWriters,
+    create_archive,
 };
 pub use extract::{
     ErrorHandler, OverwriteFlags, PxarExtractContext, PxarExtractOptions, create_tar, create_zip,
diff --git a/pbs-client/src/pxar_backup_stream.rs b/pbs-client/src/pxar_backup_stream.rs
index 430eee01f..d1b46cf8a 100644
--- a/pbs-client/src/pxar_backup_stream.rs
+++ b/pbs-client/src/pxar_backup_stream.rs
@@ -20,6 +20,7 @@ use proxmox_log::debug;
 use pbs_datastore::catalog::{BackupCatalogWriter, CatalogWriter};
 
 use crate::inject_reused_chunks::InjectChunks;
+use crate::pxar::PxarArchiverProgressStats;
 use crate::pxar::create::PxarWriters;
 
 /// Stream implementation to encode and upload .pxar archives.
@@ -50,6 +51,7 @@ impl PxarBackupStream {
         options: crate::pxar::PxarCreateOptions,
         boundaries: Option<mpsc::Sender<InjectChunks>>,
         separate_payload_stream: bool,
+        archiver_progress_stats: Option<Arc<PxarArchiverProgressStats>>,
     ) -> Result<(Self, Option<Self>), Error> {
         let buffer_size = 256 * 1024;
 
@@ -102,6 +104,7 @@ impl PxarBackupStream {
                 options,
                 boundaries,
                 suggested_boundaries_tx,
+                archiver_progress_stats,
             )
             .await
             {
@@ -145,10 +148,18 @@ impl PxarBackupStream {
         options: crate::pxar::PxarCreateOptions,
         boundaries: Option<mpsc::Sender<InjectChunks>>,
         separate_payload_stream: bool,
+        archiver_progress_stats: Option<Arc<PxarArchiverProgressStats>>,
     ) -> Result<(Self, Option<Self>), Error> {
         let dir = nix::dir::Dir::open(dirname, OFlag::O_DIRECTORY, Mode::empty())?;
 
-        Self::new(dir, catalog, options, boundaries, separate_payload_stream)
+        Self::new(
+            dir,
+            catalog,
+            options,
+            boundaries,
+            separate_payload_stream,
+            archiver_progress_stats,
+        )
     }
 }
 
diff --git a/proxmox-backup-client/src/main.rs b/proxmox-backup-client/src/main.rs
index 2680add78..1036ec7e9 100644
--- a/proxmox-backup-client/src/main.rs
+++ b/proxmox-backup-client/src/main.rs
@@ -31,7 +31,9 @@ use pbs_api_types::{
     RateLimitConfig, ServerIdentity, SnapshotListItem, StorageStatus,
 };
 use pbs_client::catalog_shell::Shell;
-use pbs_client::pxar::{ErrorHandler as PxarErrorHandler, MetadataArchiveReader, PxarPrevRef};
+use pbs_client::pxar::{
+    ErrorHandler as PxarErrorHandler, MetadataArchiveReader, PxarArchiverProgressStats, PxarPrevRef,
+};
 use pbs_client::tools::{
     CHUNK_SIZE_SCHEMA, complete_archive_name, complete_auth_id, complete_backup_group,
     complete_backup_snapshot, complete_backup_source, complete_chunk_size,
@@ -268,6 +270,7 @@ async fn backup_directory<P: AsRef<Path>>(
         bail!("cannot backup directory with fixed chunk size!");
     }
 
+    let pxar_archiver_progress_stats = Arc::new(PxarArchiverProgressStats::default());
     let (payload_boundaries_tx, payload_boundaries_rx) = std::sync::mpsc::channel();
     let (pxar_stream, payload_stream) = PxarBackupStream::open(
         dir_path.as_ref(),
@@ -275,6 +278,7 @@ async fn backup_directory<P: AsRef<Path>>(
         pxar_create_options,
         Some(payload_boundaries_tx),
         payload_target.is_some(),
+        Some(pxar_archiver_progress_stats.clone()),
     )?;
 
     let mut chunk_stream = ChunkStream::new(pxar_stream, chunk_size, None, None);
@@ -289,7 +293,13 @@ async fn backup_directory<P: AsRef<Path>>(
         }
     });
 
-    let stats = client.upload_stream(archive_name, stream, upload_options.clone(), None);
+    let stats = client.upload_stream(
+        archive_name,
+        stream,
+        upload_options.clone(),
+        None,
+        Some(pxar_archiver_progress_stats.clone()),
+    );
 
     if let Some(mut payload_stream) = payload_stream {
         let payload_target = payload_target
@@ -319,6 +329,7 @@ async fn backup_directory<P: AsRef<Path>>(
             stream,
             upload_options,
             Some(payload_injections_rx),
+            Some(pxar_archiver_progress_stats),
         );
 
         match futures::join!(stats, payload_stats) {
@@ -359,7 +370,7 @@ async fn backup_image<P: AsRef<Path>>(
     }
 
     let stats = client
-        .upload_stream(archive_name, stream, upload_options, None)
+        .upload_stream(archive_name, stream, upload_options, None, None)
         .await?;
 
     Ok(stats)
@@ -652,7 +663,13 @@ fn spawn_catalog_upload(
 
     tokio::spawn(async move {
         let catalog_upload_result = client
-            .upload_stream(&CATALOG_NAME, catalog_chunk_stream, upload_options, None)
+            .upload_stream(
+                &CATALOG_NAME,
+                catalog_chunk_stream,
+                upload_options,
+                None,
+                None,
+            )
             .await;
 
         if let Err(ref err) = catalog_upload_result {
@@ -1234,6 +1251,7 @@ async fn create_backup(
                     previous_manifest: previous_manifest.clone(),
                     compress: true,
                     encrypt: crypto.mode == CryptMode::Encrypt,
+                    is_metadata_mode: detection_mode.is_metadata(),
                     ..UploadOptions::default()
                 };
 
@@ -1273,6 +1291,7 @@ async fn create_backup(
                     index_type: IndexType::Fixed(image_file_size),
                     compress: true,
                     encrypt: crypto.mode == CryptMode::Encrypt,
+                    is_metadata_mode: false,
                 };
 
                 let stats =
diff --git a/proxmox-restore-daemon/src/proxmox_restore_daemon/api.rs b/proxmox-restore-daemon/src/proxmox_restore_daemon/api.rs
index ff21c9bee..b7f4fd141 100644
--- a/proxmox-restore-daemon/src/proxmox_restore_daemon/api.rs
+++ b/proxmox-restore-daemon/src/proxmox_restore_daemon/api.rs
@@ -375,6 +375,7 @@ fn extract(
                         options,
                         None,
                         None,
+                        None
                     )
                     .await
                 }
diff --git a/pxar-bin/src/main.rs b/pxar-bin/src/main.rs
index 4b14507ca..953c6e756 100644
--- a/pxar-bin/src/main.rs
+++ b/pxar-bin/src/main.rs
@@ -450,6 +450,7 @@ async fn create_archive(
         options,
         None,
         None,
+        None,
     )
     .await?;
 
diff --git a/tests/catar.rs b/tests/catar.rs
index aed23f866..81ca9d8db 100644
--- a/tests/catar.rs
+++ b/tests/catar.rs
@@ -41,6 +41,7 @@ fn run_test(dir_name: &str) -> Result<(), Error> {
         options,
         None,
         None,
+        None,
     ))?;
 
     Command::new("cmp")
-- 
2.47.3





  parent reply	other threads:[~2026-06-10 18:03 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10 18:02 [RFC PATCH proxmox-backup 0/6] fix #7024: client: add file statistics inside logs Shan Shaji
2026-06-10 18:02 ` [RFC PATCH proxmox-backup 1/6] refactor: rename `ReuseStats` struct to `PxarArchiverProgressStats` Shan Shaji
2026-06-10 18:02 ` [RFC PATCH proxmox-backup 2/6] fix #7024: pxar: show archiver summary for legacy and data mode Shan Shaji
2026-06-10 18:02 ` [RFC PATCH proxmox-backup 3/6] refactor: use atomic values in `PxarArchiverProgressStats` Shan Shaji
2026-06-10 18:02 ` Shan Shaji [this message]
2026-06-10 18:02 ` [RFC PATCH proxmox-backup 5/6] refactor: rename `UploadCounters` to `UploadProgress` Shan Shaji
2026-06-10 18:02 ` [RFC PATCH proxmox-backup 6/6] fix #7024: cli: add option to enable verbose logs Shan Shaji

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260610180208.801614-5-s.shaji@proxmox.com \
    --to=s.shaji@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal