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 5B1F51FF13A for ; Wed, 13 May 2026 15:55:43 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 5C5CEFF8C; Wed, 13 May 2026 15:55:42 +0200 (CEST) From: Lukas Wagner To: pdm-devel@lists.proxmox.com Subject: [PATCH datacenter-manager 4/4] remote-updates: switch over to new api_cache Date: Wed, 13 May 2026 15:54:57 +0200 Message-ID: <20260513135457.573414-5-l.wagner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260513135457.573414-1-l.wagner@proxmox.com> References: <20260513135457.573414-1-l.wagner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778680498409 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 URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [updates.rs,mod.rs] Message-ID-Hash: 7XQIGQYSRK7LOYJT72ECRJHDYLVWBG7W X-Message-ID-Hash: 7XQIGQYSRK7LOYJT72ECRJHDYLVWBG7W X-MailFrom: l.wagner@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 Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Use the new, centralized API cache for caching remote update summaries. Add some cleanup logic to remove the old cachefile, which can be removed at some point in the future. Signed-off-by: Lukas Wagner --- Notes: Changes since the RFC: - use new async cache interface - clean up old cachefile automatically server/src/api/pve/mod.rs | 4 +- server/src/api/remotes/updates.rs | 4 +- server/src/remote_updates.rs | 87 ++++++++++++++++--------------- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/server/src/api/pve/mod.rs b/server/src/api/pve/mod.rs index 20892f38..649ab624 100644 --- a/server/src/api/pve/mod.rs +++ b/server/src/api/pve/mod.rs @@ -588,8 +588,8 @@ pub async fn get_options(remote: String) -> Result { }, )] /// Return the cached update information about a remote. -pub fn get_updates(remote: String) -> Result { - let update_summary = get_available_updates_for_remote(&remote)?; +pub async fn get_updates(remote: String) -> Result { + let update_summary = get_available_updates_for_remote(&remote).await?; Ok(update_summary) } diff --git a/server/src/api/remotes/updates.rs b/server/src/api/remotes/updates.rs index 365ffc19..0ca78214 100644 --- a/server/src/api/remotes/updates.rs +++ b/server/src/api/remotes/updates.rs @@ -42,7 +42,7 @@ const SUBDIRS: SubdirMap = &sorted!([ returns: { type: UpdateSummary } )] /// Return available update summary for managed remote nodes. -pub fn update_summary(rpcenv: &mut dyn RpcEnvironment) -> Result { +pub async fn update_summary(rpcenv: &mut dyn RpcEnvironment) -> Result { let auth_id = rpcenv .get_auth_id() .context("no authid available")? @@ -53,7 +53,7 @@ pub fn update_summary(rpcenv: &mut dyn RpcEnvironment) -> Result Resu } /// Get update summary for all managed remotes. -pub fn get_available_updates_summary() -> Result { +pub async fn get_available_updates_summary() -> Result { let (config, _digest) = pdm_config::remotes::config()?; - let cache_content = get_cached_summary_or_default()?; + let cache_content = get_cached_summary_or_default().await?; let mut summary = UpdateSummary::default(); @@ -137,11 +135,11 @@ pub fn get_available_updates_summary() -> Result { } /// Return cached update information from specific remote -pub fn get_available_updates_for_remote(remote: &str) -> Result { +pub async fn get_available_updates_for_remote(remote: &str) -> Result { let (config, _digest) = pdm_config::remotes::config()?; if let Some(remote) = config.get(remote) { - let cache_content = get_cached_summary_or_default()?; + let cache_content = get_cached_summary_or_default().await?; Ok(cache_content .remotes .get(&remote.id) @@ -156,22 +154,12 @@ pub fn get_available_updates_for_remote(remote: &str) -> Result Result { - match File::open(UPDATE_CACHE) { - Ok(file) => { - let content = match serde_json::from_reader(file) { - Ok(cache_content) => cache_content, - Err(err) => { - log::error!("failed to deserialize remote update cache: {err:#}"); - Default::default() - } - }; - - Ok(content) - } - Err(err) if err.kind() == ErrorKind::NotFound => Ok(Default::default()), - Err(err) => Err(err.into()), - } +async fn get_cached_summary_or_default() -> Result { + Ok(api_cache::read_global() + .await? + .get::(UPDATE_SUMMARY_CACHE_KEY) + .await? + .unwrap_or_default()) } async fn update_cached_summary_for_node( @@ -179,10 +167,11 @@ async fn update_cached_summary_for_node( node: String, node_data: NodeUpdateSummary, ) -> Result<(), Error> { - let mut file = File::open(UPDATE_CACHE)?; - let mut cache_content: UpdateSummary = serde_json::from_reader(&mut file)?; - let remote_entry = - cache_content + let cache = api_cache::write_global().await?; + let cache_content = cache.get::(UPDATE_SUMMARY_CACHE_KEY).await?; + + if let Some(mut entry) = cache_content { + let remote_entry = entry .remotes .entry(remote.id) .or_insert_with(|| RemoteUpdateSummary { @@ -191,15 +180,9 @@ async fn update_cached_summary_for_node( status: RemoteUpdateStatus::Success, }); - remote_entry.nodes.insert(node, node_data); - - let options = proxmox_product_config::default_create_options(); - proxmox_sys::fs::replace_file( - UPDATE_CACHE, - &serde_json::to_vec(&cache_content)?, - options, - true, - )?; + remote_entry.nodes.insert(node, node_data); + cache.set(UPDATE_SUMMARY_CACHE_KEY, entry).await?; + } Ok(()) } @@ -212,7 +195,7 @@ pub async fn refresh_update_summary_cache(remotes: Vec) -> Result<(), Er .do_for_all_remote_nodes(remotes.clone().into_iter(), fetch_available_updates) .await; - let mut content = get_cached_summary_or_default()?; + let mut content = get_cached_summary_or_default().await?; // Clean out any remotes that might have been removed from the remote config in the meanwhile. content @@ -275,8 +258,28 @@ pub async fn refresh_update_summary_cache(remotes: Vec) -> Result<(), Er } } - let options = proxmox_product_config::default_create_options(); - proxmox_sys::fs::replace_file(UPDATE_CACHE, &serde_json::to_vec(&content)?, options, true)?; + cleanup_old_cachefile().await?; + + let cache = api_cache::write_global().await?; + cache.set(UPDATE_SUMMARY_CACHE_KEY, content).await?; + + Ok(()) +} + +// FIXME: We can remove this pretty soon. +async fn cleanup_old_cachefile() -> Result<(), Error> { + tokio::task::spawn_blocking(|| { + if let Err(err) = std::fs::remove_file(OLD_CACHEFILE) { + if err.kind() != std::io::ErrorKind::NotFound { + log::error!( + "could not clean up old remote update cache file {OLD_CACHEFILE}: {err}" + ); + } + } else { + log::info!("removed obsolete remote update cachefile {OLD_CACHEFILE}") + } + }) + .await?; Ok(()) } -- 2.47.3