From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 1C53F1FF133 for ; Mon, 27 Apr 2026 14:02:47 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B889F1A9E3; Mon, 27 Apr 2026 14:02:45 +0200 (CEST) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup 1/4] move group: use human-readable snapshot timestamp in check Date: Mon, 27 Apr 2026 14:02:25 +0200 Message-ID: <20260427120234.634681-2-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260427120234.634681-1-f.gruenbichler@proxmox.com> References: <20260427120234.634681-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1777291262187 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.054 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: P5KEYN72UDSA7PH2CEFFZFUBHLUQRPJI X-Message-ID-Hash: P5KEYN72UDSA7PH2CEFFZFUBHLUQRPJI X-MailFrom: f.gruenbichler@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: before: 2026-04-27T13:39:44+02:00: TASK ERROR: cannot merge group 'ct/999' from 'foo' into 'bar': snapshot time overlap (oldest source: 1678867213, conflicting target: 1774348044) after: 2026-04-27T13:42:03+02:00: TASK ERROR: cannot merge group 'ct/999' from 'foo' into 'bar': snapshot time overlap (oldest source: 2023-03-15T08:00:13Z, conflicting target: 2026-03-24T10:27:24Z) Signed-off-by: Fabian Grünbichler --- pbs-datastore/src/backup_info.rs | 35 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs index 69949c511..5f61890ed 100644 --- a/pbs-datastore/src/backup_info.rs +++ b/pbs-datastore/src/backup_info.rs @@ -291,24 +291,35 @@ impl BackupGroup { ); } - let src_oldest = self - .iter_snapshots()? - .filter_map(Result::ok) - .map(|s| s.backup_time()) - .min(); + let (src_oldest, src_oldest_str) = self.iter_snapshots()?.filter_map(Result::ok).fold( + (i64::MAX, String::new()), + |(min, min_str), s| { + let curr = s.backup_time(); + if curr < min { + (curr, s.backup_time_string.clone()) + } else { + (min, min_str) + } + }, + ); - if let Some(src_oldest) = src_oldest { + if src_oldest != i64::MAX { // Any target snapshot with time >= src_oldest violates the // "source strictly newer than target" merge invariant. Short-circuit on the first hit. if let Some(overlap) = target .iter_snapshots()? .filter_map(Result::ok) - .map(|s| s.backup_time()) - .find(|t| *t >= src_oldest) + .find_map(|s| { + if s.backup_time() >= src_oldest { + Some(s.backup_time_string().to_owned()) + } else { + None + } + }) { bail!( "cannot merge group '{}/{}' from '{}' into '{}': snapshot time overlap \ - (oldest source: {src_oldest}, conflicting target: {overlap})", + (oldest source: {src_oldest_str}, conflicting target: {overlap})", self.group.ty, self.group.id, self.ns, @@ -957,6 +968,12 @@ impl BackupDir { crate::SnapshotReader::new_do(self.clone()) } + /// Returns whether a manifest file exists + pub fn has_manifest(&self) -> bool { + let manifest_path = self.full_path().join(MANIFEST_BLOB_NAME.as_ref()); + manifest_path.exists() + } + /// Load the manifest without a lock. Must not be written back. pub fn load_manifest(&self) -> Result<(BackupManifest, u64), Error> { let blob = self.load_blob(MANIFEST_BLOB_NAME.as_ref())?; -- 2.47.3