* [pbs-devel] [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection @ 2025-04-16 9:17 Christian Ebner 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader Christian Ebner ` (2 more replies) 0 siblings, 3 replies; 6+ messages in thread From: Christian Ebner @ 2025-04-16 9:17 UTC (permalink / raw) To: pbs-devel Fixes a rare race in phase 1 of garbage collection, as described in more detail in the commit message of patch 2. This patches are intended as stop gap for the time being, until a different approach to guarantee the presence of index files also after pruning has been explored and implemented, as suggested by Thomas [0]. Changes since version 3: - Refactor open_index_reader to allow distinction between missing index file and ignored archive type blob. - Iterate snapshots from newest to oldest to further reduce race window and simplify accounting logic. [0] https://lore.proxmox.com/pbs-devel/7bac779b-e564-4f24-b58c-a4411c2a59aa@proxmox.com/ Christian Ebner (2): garbage collection: distinguish variants for failed open index reader garbage collection: fix rare race in chunk marking phase pbs-datastore/src/datastore.rs | 141 ++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 45 deletions(-) -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader 2025-04-16 9:17 [pbs-devel] [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner @ 2025-04-16 9:17 ` Christian Ebner 2025-04-16 10:00 ` Fabian Grünbichler 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase Christian Ebner 2025-04-16 10:51 ` [pbs-devel] superseded: [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner 2 siblings, 1 reply; 6+ messages in thread From: Christian Ebner @ 2025-04-16 9:17 UTC (permalink / raw) To: pbs-devel In preparation for adapting the garbage collection to be able to distinguish cases for the index file not being accessible because it is no longer present or because it is a blob and therefore should be ignored. Signed-off-by: Christian Ebner <c.ebner@proxmox.com> --- pbs-datastore/src/datastore.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs index aa38e2ac1..4630b1b39 100644 --- a/pbs-datastore/src/datastore.rs +++ b/pbs-datastore/src/datastore.rs @@ -1033,11 +1033,14 @@ impl DataStore { // Similar to open index, but ignore index files with blob or unknown archive type. // Further, do not fail if file vanished. - fn open_index_reader(&self, absolute_path: &Path) -> Result<Option<Box<dyn IndexFile>>, Error> { + fn open_index_reader( + &self, + absolute_path: &Path, + ) -> Result<IndexReaderOption<Box<dyn IndexFile>>, Error> { let archive_type = match ArchiveType::from_path(absolute_path) { - Ok(archive_type) => archive_type, // ignore archives with unknown archive type - Err(_) => return Ok(None), + Ok(ArchiveType::Blob) | Err(_) => return Ok(IndexReaderOption::NoneWrongType), + Ok(archive_type) => archive_type, }; if absolute_path.is_relative() { @@ -1047,7 +1050,9 @@ impl DataStore { let file = match std::fs::File::open(absolute_path) { Ok(file) => file, // ignore vanished files - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None), + Err(err) if err.kind() == io::ErrorKind::NotFound => { + return Ok(IndexReaderOption::NoneNotFound) + } Err(err) => { return Err(Error::from(err).context(format!("can't open file '{absolute_path:?}'"))) } @@ -1057,14 +1062,14 @@ impl DataStore { ArchiveType::FixedIndex => { let reader = FixedIndexReader::new(file) .with_context(|| format!("can't open fixed index '{absolute_path:?}'"))?; - Ok(Some(Box::new(reader))) + Ok(IndexReaderOption::Some(Box::new(reader))) } ArchiveType::DynamicIndex => { let reader = DynamicIndexReader::new(file) .with_context(|| format!("can't open dynamic index '{absolute_path:?}'"))?; - Ok(Some(Box::new(reader))) + Ok(IndexReaderOption::Some(Box::new(reader))) } - ArchiveType::Blob => Ok(None), + ArchiveType::Blob => Ok(IndexReaderOption::NoneWrongType), } } @@ -1155,8 +1160,8 @@ impl DataStore { path.push(file); let index = match self.open_index_reader(&path)? { - Some(index) => index, - None => { + IndexReaderOption::Some(index) => index, + IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { unprocessed_index_list.remove(&path); continue; } @@ -1191,8 +1196,8 @@ impl DataStore { let mut strange_paths_count = unprocessed_index_list.len(); for path in unprocessed_index_list { let index = match self.open_index_reader(&path)? { - Some(index) => index, - None => { + IndexReaderOption::Some(index) => index, + IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { // do not count vanished (pruned) backup snapshots as strange paths. strange_paths_count -= 1; continue; @@ -1695,3 +1700,9 @@ impl DataStore { *OLD_LOCKING } } + +enum IndexReaderOption<T> { + Some(T), + NoneWrongType, + NoneNotFound, +} -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader Christian Ebner @ 2025-04-16 10:00 ` Fabian Grünbichler 0 siblings, 0 replies; 6+ messages in thread From: Fabian Grünbichler @ 2025-04-16 10:00 UTC (permalink / raw) To: Proxmox Backup Server development discussion On April 16, 2025 11:17 am, Christian Ebner wrote: > In preparation for adapting the garbage collection to be able to > distinguish cases for the index file not being accessible because it > is no longer present or because it is a blob and therefore should be > ignored. this seems very involved.. > > Signed-off-by: Christian Ebner <c.ebner@proxmox.com> > --- > pbs-datastore/src/datastore.rs | 33 ++++++++++++++++++++++----------- > 1 file changed, 22 insertions(+), 11 deletions(-) > > diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs > index aa38e2ac1..4630b1b39 100644 > --- a/pbs-datastore/src/datastore.rs > +++ b/pbs-datastore/src/datastore.rs > @@ -1033,11 +1033,14 @@ impl DataStore { > > // Similar to open index, but ignore index files with blob or unknown archive type. > // Further, do not fail if file vanished. > - fn open_index_reader(&self, absolute_path: &Path) -> Result<Option<Box<dyn IndexFile>>, Error> { > + fn open_index_reader( > + &self, > + absolute_path: &Path, > + ) -> Result<IndexReaderOption<Box<dyn IndexFile>>, Error> { > let archive_type = match ArchiveType::from_path(absolute_path) { > - Ok(archive_type) => archive_type, > // ignore archives with unknown archive type > - Err(_) => return Ok(None), > + Ok(ArchiveType::Blob) | Err(_) => return Ok(IndexReaderOption::NoneWrongType), or filter the snapshot.files by archive type first to only call this with index paths? see below the unprocessed_index_list never contains blobs anyway.. > + Ok(archive_type) => archive_type, > }; > > if absolute_path.is_relative() { > @@ -1047,7 +1050,9 @@ impl DataStore { > let file = match std::fs::File::open(absolute_path) { > Ok(file) => file, > // ignore vanished files > - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None), > + Err(err) if err.kind() == io::ErrorKind::NotFound => { > + return Ok(IndexReaderOption::NoneNotFound) > + } > Err(err) => { > return Err(Error::from(err).context(format!("can't open file '{absolute_path:?}'"))) > } > @@ -1057,14 +1062,14 @@ impl DataStore { > ArchiveType::FixedIndex => { > let reader = FixedIndexReader::new(file) > .with_context(|| format!("can't open fixed index '{absolute_path:?}'"))?; > - Ok(Some(Box::new(reader))) > + Ok(IndexReaderOption::Some(Box::new(reader))) > } > ArchiveType::DynamicIndex => { > let reader = DynamicIndexReader::new(file) > .with_context(|| format!("can't open dynamic index '{absolute_path:?}'"))?; > - Ok(Some(Box::new(reader))) > + Ok(IndexReaderOption::Some(Box::new(reader))) > } > - ArchiveType::Blob => Ok(None), > + ArchiveType::Blob => Ok(IndexReaderOption::NoneWrongType), > } > } > > @@ -1155,8 +1160,8 @@ impl DataStore { > path.push(file); > > let index = match self.open_index_reader(&path)? { > - Some(index) => index, > - None => { > + IndexReaderOption::Some(index) => index, > + IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { > unprocessed_index_list.remove(&path); > continue; > } > @@ -1191,8 +1196,8 @@ impl DataStore { > let mut strange_paths_count = unprocessed_index_list.len(); > for path in unprocessed_index_list { > let index = match self.open_index_reader(&path)? { > - Some(index) => index, > - None => { > + IndexReaderOption::Some(index) => index, > + IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { > // do not count vanished (pruned) backup snapshots as strange paths. > strange_paths_count -= 1; > continue; > @@ -1695,3 +1700,9 @@ impl DataStore { > *OLD_LOCKING > } > } > + > +enum IndexReaderOption<T> { > + Some(T), > + NoneWrongType, > + NoneNotFound, > +} this is a NACK from me anyway, either this is an enum, then it should be Index(T) / Blob / Vanished, or it is a custom error, then the Option should be dropped and we have Result<T, GCIndexError> where GCIndexError has WrongType / NotFound / Other or something like that, and the call site can choose to ignore NotFound and/or WrongType. but I am not sure that is worth it for such an internal helper in any case, we can just make blobs a hard error in the helper, and filter the blobs out in the single code path where they might be included.. > -- > 2.39.5 > > > > _______________________________________________ > pbs-devel mailing list > pbs-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel > > > _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase 2025-04-16 9:17 [pbs-devel] [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader Christian Ebner @ 2025-04-16 9:17 ` Christian Ebner 2025-04-16 10:00 ` Fabian Grünbichler 2025-04-16 10:51 ` [pbs-devel] superseded: [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner 2 siblings, 1 reply; 6+ messages in thread From: Christian Ebner @ 2025-04-16 9:17 UTC (permalink / raw) To: pbs-devel During phase 1 of garbage collection referenced chunks are marked as in use by iterating over all index files and updating the atime on the chunks referenced by these. In an edge case for long running garbage collection jobs, where a newly added snapshot (created after the start of GC) reused known chunks from a previous snapshot, but the previous snapshot index referencing them disappeared before the marking phase could reach that index (e.g. pruned because only 1 snapshot to be kept by retention setting), known chunks from that previous index file might not be marked (given that by none of the other index files it was marked). Since commit 74361da8 ("garbage collection: generate index file list via datastore iterators") this is even less likely as now the iteration reads also index files added during phase 1, and therefore either the new or the previous index file will account for these chunks (the previous backup snapshot can only be prunded after the new one finished, since locked). There remains however a small race window between the reading of the snapshots in the backup group and the reading of the actual index files for marking. Fix this race by: 1. Checking if the last snapshot of a group disappeared and if so 2. generate the list again, looking for new index files previously not accounted for 3. To avoid possible endless looping, lock the group if the snapshot list changed even after the 10th time (which will lead to concurrent operations to this group failing). Signed-off-by: Christian Ebner <c.ebner@proxmox.com> --- pbs-datastore/src/datastore.rs | 112 ++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs index 4630b1b39..8a5075786 100644 --- a/pbs-datastore/src/datastore.rs +++ b/pbs-datastore/src/datastore.rs @@ -1148,47 +1148,87 @@ impl DataStore { let namespace = namespace.context("iterating namespaces failed")?; for group in arc_self.iter_backup_groups(namespace)? { let group = group.context("iterating backup groups failed")?; - let mut snapshots = group.list_backups().context("listing snapshots failed")?; - // Sort by snapshot timestamp to iterate over consecutive snapshots for each image. - BackupInfo::sort_list(&mut snapshots, true); - for snapshot in snapshots { - for file in snapshot.files { - worker.check_abort()?; - worker.fail_on_shutdown()?; - - let mut path = snapshot.backup_dir.full_path(); - path.push(file); - - let index = match self.open_index_reader(&path)? { - IndexReaderOption::Some(index) => index, - IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { - unprocessed_index_list.remove(&path); - continue; + + // Avoid race between listing/marking of snapshots by GC and pruning the last + // snapshot in the group, following a new snapshot creation. Otherwise known chunks + // might only be referenced by the new snapshot, so it must be read as well. + let mut retry_counter = 0; + 'retry: loop { + let _lock = match retry_counter { + 0..=9 => None, + 10 => Some( + group + .lock() + .context("exhausted retries and failed to lock group")?, + ), + _ => bail!("exhausted retires and unexpected counter overrun"), + }; + + let mut snapshots = match group.list_backups() { + Ok(snapshots) => snapshots, + Err(err) => { + if group.exists() { + return Err(err).context("listing snapshots failed")?; } - }; - self.index_mark_used_chunks( - index, - &path, - &mut chunk_lru_cache, - status, - worker, - )?; - - if !unprocessed_index_list.remove(&path) { - info!("Encountered new index file '{path:?}', increment total index file count"); - index_count += 1; + break 'retry; } + }; + + // Always start iteration with the last snapshot of the group to reduce race + // window with concurrent backup+prune previous last snapshot. Allows to retry + // without the need to keep track of already processed index files for the + // current group. + BackupInfo::sort_list(&mut snapshots, true); + for (count, snapshot) in snapshots.into_iter().rev().enumerate() { + for file in snapshot.files { + worker.check_abort()?; + worker.fail_on_shutdown()?; + + let mut path = snapshot.backup_dir.full_path(); + path.push(file); + + let index = match self.open_index_reader(&path)? { + IndexReaderOption::Some(index) => index, + IndexReaderOption::NoneWrongType => { + unprocessed_index_list.remove(&path); + continue; + } + IndexReaderOption::NoneNotFound => { + if count == 0 { + retry_counter += 1; + continue 'retry; + } + unprocessed_index_list.remove(&path); + continue; + } + }; + + self.index_mark_used_chunks( + index, + &path, + &mut chunk_lru_cache, + status, + worker, + )?; + + if !unprocessed_index_list.remove(&path) { + info!("Encountered new index file '{path:?}', increment total index file count"); + index_count += 1; + } - let percentage = (processed_index_files + 1) * 100 / index_count; - if percentage > last_percentage { - info!( - "marked {percentage}% ({} of {index_count} index files)", - processed_index_files + 1, - ); - last_percentage = percentage; + let percentage = (processed_index_files + 1) * 100 / index_count; + if percentage > last_percentage { + info!( + "marked {percentage}% ({} of {index_count} index files)", + processed_index_files + 1, + ); + last_percentage = percentage; + } + processed_index_files += 1; } - processed_index_files += 1; } + + break; } } } -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase Christian Ebner @ 2025-04-16 10:00 ` Fabian Grünbichler 0 siblings, 0 replies; 6+ messages in thread From: Fabian Grünbichler @ 2025-04-16 10:00 UTC (permalink / raw) To: Proxmox Backup Server development discussion On April 16, 2025 11:17 am, Christian Ebner wrote: > During phase 1 of garbage collection referenced chunks are marked as > in use by iterating over all index files and updating the atime on > the chunks referenced by these. > > In an edge case for long running garbage collection jobs, where a > newly added snapshot (created after the start of GC) reused known > chunks from a previous snapshot, but the previous snapshot index > referencing them disappeared before the marking phase could reach > that index (e.g. pruned because only 1 snapshot to be kept by > retention setting), known chunks from that previous index file might > not be marked (given that by none of the other index files it was > marked). > > Since commit 74361da8 ("garbage collection: generate index file list > via datastore iterators") this is even less likely as now the > iteration reads also index files added during phase 1, and > therefore either the new or the previous index file will account for > these chunks (the previous backup snapshot can only be prunded after > the new one finished, since locked). There remains however a small > race window between the reading of the snapshots in the backup group > and the reading of the actual index files for marking. > > Fix this race by: > 1. Checking if the last snapshot of a group disappeared and if so > 2. generate the list again, looking for new index files previously > not accounted for > 3. To avoid possible endless looping, lock the group if the snapshot > list changed even after the 10th time (which will lead to > concurrent operations to this group failing). > > Signed-off-by: Christian Ebner <c.ebner@proxmox.com> > --- > pbs-datastore/src/datastore.rs | 112 ++++++++++++++++++++++----------- > 1 file changed, 76 insertions(+), 36 deletions(-) > > diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs > index 4630b1b39..8a5075786 100644 > --- a/pbs-datastore/src/datastore.rs > +++ b/pbs-datastore/src/datastore.rs > @@ -1148,47 +1148,87 @@ impl DataStore { > let namespace = namespace.context("iterating namespaces failed")?; > for group in arc_self.iter_backup_groups(namespace)? { > let group = group.context("iterating backup groups failed")?; > - let mut snapshots = group.list_backups().context("listing snapshots failed")?; > - // Sort by snapshot timestamp to iterate over consecutive snapshots for each image. > - BackupInfo::sort_list(&mut snapshots, true); > - for snapshot in snapshots { > - for file in snapshot.files { > - worker.check_abort()?; > - worker.fail_on_shutdown()?; > - > - let mut path = snapshot.backup_dir.full_path(); > - path.push(file); > - > - let index = match self.open_index_reader(&path)? { > - IndexReaderOption::Some(index) => index, > - IndexReaderOption::NoneWrongType | IndexReaderOption::NoneNotFound => { > - unprocessed_index_list.remove(&path); > - continue; > + > + // Avoid race between listing/marking of snapshots by GC and pruning the last > + // snapshot in the group, following a new snapshot creation. Otherwise known chunks > + // might only be referenced by the new snapshot, so it must be read as well. > + let mut retry_counter = 0; > + 'retry: loop { > + let _lock = match retry_counter { > + 0..=9 => None, > + 10 => Some( > + group > + .lock() > + .context("exhausted retries and failed to lock group")?, > + ), > + _ => bail!("exhausted retires and unexpected counter overrun"), typo 'retires' other than that and the comments on patch#1, this looks good to me, so consider this Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> > + }; > + > + let mut snapshots = match group.list_backups() { > + Ok(snapshots) => snapshots, > + Err(err) => { > + if group.exists() { > + return Err(err).context("listing snapshots failed")?; > } > - }; > - self.index_mark_used_chunks( > - index, > - &path, > - &mut chunk_lru_cache, > - status, > - worker, > - )?; > - > - if !unprocessed_index_list.remove(&path) { > - info!("Encountered new index file '{path:?}', increment total index file count"); > - index_count += 1; > + break 'retry; > } > + }; > + > + // Always start iteration with the last snapshot of the group to reduce race > + // window with concurrent backup+prune previous last snapshot. Allows to retry > + // without the need to keep track of already processed index files for the > + // current group. > + BackupInfo::sort_list(&mut snapshots, true); > + for (count, snapshot) in snapshots.into_iter().rev().enumerate() { > + for file in snapshot.files { > + worker.check_abort()?; > + worker.fail_on_shutdown()?; > + > + let mut path = snapshot.backup_dir.full_path(); > + path.push(file); > + > + let index = match self.open_index_reader(&path)? { > + IndexReaderOption::Some(index) => index, > + IndexReaderOption::NoneWrongType => { > + unprocessed_index_list.remove(&path); > + continue; > + } > + IndexReaderOption::NoneNotFound => { > + if count == 0 { > + retry_counter += 1; > + continue 'retry; > + } > + unprocessed_index_list.remove(&path); > + continue; > + } > + }; > + > + self.index_mark_used_chunks( > + index, > + &path, > + &mut chunk_lru_cache, > + status, > + worker, > + )?; > + > + if !unprocessed_index_list.remove(&path) { > + info!("Encountered new index file '{path:?}', increment total index file count"); > + index_count += 1; > + } > > - let percentage = (processed_index_files + 1) * 100 / index_count; > - if percentage > last_percentage { > - info!( > - "marked {percentage}% ({} of {index_count} index files)", > - processed_index_files + 1, > - ); > - last_percentage = percentage; > + let percentage = (processed_index_files + 1) * 100 / index_count; > + if percentage > last_percentage { > + info!( > + "marked {percentage}% ({} of {index_count} index files)", > + processed_index_files + 1, > + ); > + last_percentage = percentage; > + } > + processed_index_files += 1; > } > - processed_index_files += 1; > } > + > + break; > } > } > } > -- > 2.39.5 > > > > _______________________________________________ > pbs-devel mailing list > pbs-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel > > > _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [pbs-devel] superseded: [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection 2025-04-16 9:17 [pbs-devel] [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader Christian Ebner 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase Christian Ebner @ 2025-04-16 10:51 ` Christian Ebner 2 siblings, 0 replies; 6+ messages in thread From: Christian Ebner @ 2025-04-16 10:51 UTC (permalink / raw) To: pbs-devel superseded-by version 5: https://lore.proxmox.com/pbs-devel/20250416105000.270166-1-c.ebner@proxmox.com/T/ _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-04-16 10:52 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-04-16 9:17 [pbs-devel] [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 1/2] garbage collection: distinguish variants for failed open index reader Christian Ebner 2025-04-16 10:00 ` Fabian Grünbichler 2025-04-16 9:17 ` [pbs-devel] [PATCH v4 proxmox-backup 2/2] garbage collection: fix rare race in chunk marking phase Christian Ebner 2025-04-16 10:00 ` Fabian Grünbichler 2025-04-16 10:51 ` [pbs-devel] superseded: [PATCH v4 proxmox-backup 0/2] fix rare race in garbage collection Christian Ebner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inboxService provided by Proxmox Server Solutions GmbH | Privacy | Legal