[pbs-devel] [PATCH proxmox-backup 1/2] datastore: add tuning options for the number of reading threads

Dominik Csapak d.csapak at proxmox.com
Tue Apr 30 11:39:38 CEST 2024


adds a new 'read-threads' tuning options to datastores that control
how many threads are used for verification operations

depending on the underlying storage and the used cpu, this can make a
big difference in verification time.

the default remains at the current 4 threads.

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 pbs-api-types/src/datastore.rs |  9 +++++++++
 pbs-datastore/src/datastore.rs | 14 ++++++++++++++
 src/backup/verify.rs           |  2 +-
 www/Utils.js                   |  5 +++++
 www/datastore/OptionView.js    |  8 ++++++++
 5 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 31767417a..2ad2ae063 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -209,6 +209,13 @@ pub enum DatastoreFSyncLevel {
             type: ChunkOrder,
             optional: true,
         },
+        "read-threads": {
+            description: "Controls how many threads are used for reading from the datastore for verification.",
+            type: usize,
+            optional: true,
+            minimum: 1,
+            default: 4,
+        },
     },
 )]
 #[derive(Serialize, Deserialize, Default)]
@@ -220,6 +227,8 @@ pub struct DatastoreTuning {
     pub chunk_order: Option<ChunkOrder>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub sync_level: Option<DatastoreFSyncLevel>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub read_threads: Option<usize>,
 }
 
 pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new("Datastore tuning options")
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index f95da7615..2c23bd1f1 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -14,6 +14,7 @@ use proxmox_schema::ApiType;
 use proxmox_sys::error::SysError;
 use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
 use proxmox_sys::fs::{lock_dir_noblock, DirLockGuard};
+use proxmox_sys::linux::procfs::read_proc_stat;
 use proxmox_sys::process_locker::ProcessLockSharedGuard;
 use proxmox_sys::WorkerTaskContext;
 use proxmox_sys::{task_log, task_warn};
@@ -61,6 +62,7 @@ pub struct DataStoreImpl {
     chunk_order: ChunkOrder,
     last_digest: Option<[u8; 32]>,
     sync_level: DatastoreFSyncLevel,
+    read_threads: usize,
 }
 
 impl DataStoreImpl {
@@ -75,6 +77,7 @@ impl DataStoreImpl {
             chunk_order: Default::default(),
             last_digest: None,
             sync_level: Default::default(),
+            read_threads: 0,
         })
     }
 }
@@ -313,6 +316,7 @@ impl DataStore {
             chunk_order: tuning.chunk_order.unwrap_or_default(),
             last_digest,
             sync_level: tuning.sync_level.unwrap_or_default(),
+            read_threads: tuning.read_threads.unwrap_or(4),
         })
     }
 
@@ -1377,6 +1381,16 @@ impl DataStore {
         Ok(())
     }
 
+    /// returns the number of read thread that should be used for operations
+    /// limits to the number of available cores (if reading from /proc/stat succeeds)
+    pub fn get_read_threads(&self) -> usize {
+        // if we cannot get the real cpu count, don't limit us
+        let core_count = read_proc_stat()
+            .map(|proc| proc.cpu_count as usize)
+            .unwrap_or(usize::max_value());
+        self.inner.read_threads.clamp(1, core_count)
+    }
+
     /// Destroy a datastore. This requires that there are no active operations on the datastore.
     ///
     /// This is a synchronous operation and should be run in a worker-thread.
diff --git a/src/backup/verify.rs b/src/backup/verify.rs
index c972e5328..4537dbdc9 100644
--- a/src/backup/verify.rs
+++ b/src/backup/verify.rs
@@ -125,7 +125,7 @@ fn verify_index_chunks(
 
     let decoder_pool = ParallelHandler::new(
         "verify chunk decoder",
-        4,
+        datastore2.get_read_threads(),
         move |(chunk, digest, size): (DataBlob, [u8; 32], u64)| {
             let chunk_crypt_mode = match chunk.crypt_mode() {
                 Err(err) => {
diff --git a/www/Utils.js b/www/Utils.js
index 1d7351a32..8fd102486 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -790,6 +790,11 @@ Ext.define('PBS.Utils', {
 	sync = PBS.Utils.tuningOptions['sync-level'][sync ?? '__default__'];
 	options.push(`${gettext('Sync Level')}: ${sync}`);
 
+	let readThreads = tuning['read-threads'];
+	delete tuning['read-threads'];
+	readThreads ??= Proxmox.Utils.defaultText + ` (4)`;
+	options.push(`${gettext('Read Threads')}: ${readThreads}`);
+
 	for (const [k, v] of Object.entries(tuning)) {
 	    options.push(`${k}: ${v}`);
 	}
diff --git a/www/datastore/OptionView.js b/www/datastore/OptionView.js
index e1f38af6f..ee90f1dca 100644
--- a/www/datastore/OptionView.js
+++ b/www/datastore/OptionView.js
@@ -271,6 +271,14 @@ Ext.define('PBS.Datastore.Options', {
 			    deleteEmpty: true,
 			    value: '__default__',
 			},
+			{
+			    xtype: 'proxmoxintegerfield',
+			    name: 'read-threads',
+			    fieldLabel: gettext('Read Threads'),
+			    min: 0,
+			    emptyText: '4',
+			    deleteEmpty: true,
+			},
 		    ],
 		},
 	    },
-- 
2.39.2





More information about the pbs-devel mailing list