From: Christian Ebner <c.ebner@proxmox.com>
To: Shan Shaji <s.shaji@proxmox.com>, pbs-devel@lists.proxmox.com
Subject: Re: [RFC PATCH proxmox-backup 2/4] fix #7908: datastore: create zero-byte markers when re-using datastore
Date: Fri, 25 Sep 2026 14:11:54 +0200 [thread overview]
Message-ID: <9f98a194-bf1e-4f94-a14f-2a1596445a9f@proxmox.com> (raw)
In-Reply-To: <20260915155935.135460-3-s.shaji@proxmox.com>
On 9/15/26 6:00 PM, Shan Shaji wrote:
> Previously, recreating an S3-backed datastore with the --reuse-datastore
> flag fetched only the metadata files into the local cache, without
> creating chunk markers. Garbage collection (GC) had to be run to
> recreate markers for chunks referenced by the backup indexes.
>
> In order to fix this, create local zero-byte chunk markers by listing
> the chunk objects in S3 when reusing a datastore.
This can only work if the whole series depends on [0], so the datastore
config lock is not held during the whole listing and chunk marker
creation, which depending on the number of chunks can take a very long time.
The current s3 refresh and other operations which keep the config lock
for too long were already problematic, but this patch here would greatly
escalate the problem.
[0]
https://lore.proxmox.com/pbs-devel/20260803090747.265683-1-c.ebner@proxmox.com/T/#t
> Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=7908
>
> Signed-off-by: Shan Shaji <s.shaji@proxmox.com>
> ---
> pbs-datastore/src/datastore.rs | 35 +++++++++++++++++++++++++++++++++-
> src/api2/admin/datastore.rs | 10 +++++++---
> src/api2/config/datastore.rs | 6 +++++-
> 3 files changed, 46 insertions(+), 5 deletions(-)
>
> diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
> index f3eab8a28..d00a0ef1c 100644
> --- a/pbs-datastore/src/datastore.rs
> +++ b/pbs-datastore/src/datastore.rs
> @@ -3171,9 +3171,38 @@ impl DataStore {
> *OLD_LOCKING
> }
>
> + fn create_empty_s3_chunk_markers(
> + &self,
> + worker: &dyn WorkerTaskContext,
> + s3_client: &S3Client,
> + ) -> Result<(), Error> {
> + let _guard = self.inner.chunk_store.mutex().lock().unwrap();
Holding the chunk store lock for the whole time here is only acceptable
since the whole s3-refresh runs protected by it's maintenance mode which
grants it exclusive access anyways, so that warrants a dedicated
comment. And the helper should document that requirement as well.
Further, this would greatly benefit from some progress logging, which is
still lacking for phase 2 of GC on S3 backend as well. Log flooding can
be avoided by using e.g. the log throttle implementation recently
introduced by Thomas [0].
[0]
https://git.proxmox.com/?p=proxmox-backup.git;a=blob;f=pbs-client/src/log_throttle.rs;h=cfaa6c4948a01b7878c04cce08081880891b8a47;hb=709be1a29c2797a320b01be93fa9befcbd51a203
> +
> + self.paginate_s3_chunk_objects(worker, s3_client, |object_key, _| -> Result<(), Error> {
> + let (_, digest, _) = match digest_from_object_key(&object_key) {
> + Some(result) => result,
> + None => return Ok(()),
> + };
> +
> + unsafe {
> + self.inner
> + .chunk_store
> + .replace_chunk_with_marker_or_create_marker(&digest)?;
This is not ideal! Any pre-existing, valid locally cached chunk is now
cleared (and might still be referenced by the in-memory LRU cache).
It would rather make sense to implement a dedicated chunk store helper
on top of ChunkStore::create_marker_file() which checks for the file to
pre-exist?
> + }
> +
> + Ok(())
> + })?;
> +
> + Ok(())
> + }
> +
> /// Fetch contents from S3 object store, clear and replace the local cache store contents.
> /// Returns with error for non-S3 datastore backends.
> - pub async fn s3_refresh(self: &Arc<Self>) -> Result<(), Error> {
> + pub async fn s3_refresh(
> + self: &Arc<Self>,
> + rebuild_chunk_markers: bool,
> + worker: &dyn WorkerTaskContext,
> + ) -> Result<(), Error> {
> match self.backend()? {
> DatastoreBackend::Filesystem => bail!("store '{}' not backed by S3", self.name()),
> DatastoreBackend::S3(s3_client) => {
> @@ -3190,6 +3219,10 @@ impl DataStore {
> let _ = std::fs::remove_dir_all(&tmp_base);
> return Err(err);
> }
> +
> + if rebuild_chunk_markers {
> + self.create_empty_s3_chunk_markers(worker, &s3_client)?;
> + }
> }
> }
> Ok(())
> diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
> index bc2b2436e..34907fa0a 100644
> --- a/src/api2/admin/datastore.rs
> +++ b/src/api2/admin/datastore.rs
> @@ -2867,7 +2867,7 @@ pub fn s3_refresh(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Valu
> Some(store.clone()),
> auth_id.to_string(),
> to_stdout,
> - move |worker| do_s3_refresh(&store, &worker),
> + move |worker| do_s3_refresh(&store, &worker, false),
> )?;
>
> Ok(json!(upid))
> @@ -2875,10 +2875,14 @@ pub fn s3_refresh(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Valu
>
> /// Performs an s3 refresh for given datastore. Expects the store to already be in maintenance mode
> /// s3-refresh.
> -pub(crate) fn do_s3_refresh(store: &str, worker: &dyn WorkerTaskContext) -> Result<(), Error> {
> +pub(crate) fn do_s3_refresh(
> + store: &str,
> + worker: &dyn WorkerTaskContext,
> + rebuild_chunk_markers: bool,
> +) -> Result<(), Error> {
> let datastore = DataStore::lookup_datastore(lookup_with(store, Operation::Lookup))?;
> run_maintenance_locked(store, MaintenanceType::S3Refresh, worker, || {
> - proxmox_async::runtime::block_on(datastore.s3_refresh())
> + proxmox_async::runtime::block_on(datastore.s3_refresh(rebuild_chunk_markers, worker))
> })
> }
>
> diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
> index e7028480c..e82e8969a 100644
> --- a/src/api2/config/datastore.rs
> +++ b/src/api2/config/datastore.rs
> @@ -366,7 +366,11 @@ pub fn create_datastore(
> }
>
> if reuse_datastore && backend == DatastoreBackendType::S3 {
> - crate::api2::admin::datastore::do_s3_refresh(&store_name, &worker)?;
> + crate::api2::admin::datastore::do_s3_refresh(
> + &store_name,
> + &worker,
> + reuse_datastore,
> + )?;
> }
> Ok(())
> },
next prev parent reply other threads:[~2026-09-25 12:11 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-15 15:59 [RFC PATCH proxmox-backup 0/4] fix #7903: rebuild local S3 chunk markers Shan Shaji
2026-09-15 15:59 ` [RFC PATCH proxmox-backup 1/4] datastore: factor out s3 chunk objects pagination into a helper Shan Shaji
2026-09-25 12:11 ` Christian Ebner
2026-09-15 15:59 ` [RFC PATCH proxmox-backup 2/4] fix #7908: datastore: create zero-byte markers when re-using datastore Shan Shaji
2026-09-25 12:11 ` Christian Ebner [this message]
2026-09-15 15:59 ` [RFC PATCH proxmox-backup 3/4] datastore: api: add option to rebuild S3 chunk markers Shan Shaji
2026-09-15 15:59 ` [RFC PATCH proxmox-backup 4/4] datastore: backup-manager: expose option to rebuild s3 " Shan Shaji
2026-09-15 16:04 ` [RFC PATCH proxmox-backup 0/4] fix #7903: rebuild local S3 " Shan Shaji
2026-09-25 12:11 ` Christian Ebner
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=9f98a194-bf1e-4f94-a14f-2a1596445a9f@proxmox.com \
--to=c.ebner@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
--cc=s.shaji@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