[pbs-devel] [PATCH proxmox-backup 3/3] pbs-config: add TTL window to token secret cache
Samuel Rufinatscha
s.rufinatscha at proxmox.com
Fri Dec 5 14:25:56 CET 2025
Verify_secret() currently calls refresh_cache_if_file_changed() on every
request, which performs a metadata() call on token.shadow each time.
Under load this adds unnecessary overhead, considering also the file
usually should rarely change.
This patch introduces a TTL boundary, controlled by
TOKEN_SECRET_CACHE_TTL_SECS. File metadata is only re-loaded once the
TTL has expired.
This patch partly fixes bug #6049 [1].
[1] https://bugzilla.proxmox.com/show_bug.cgi?id=7017
Signed-off-by: Samuel Rufinatscha <s.rufinatscha at proxmox.com>
---
pbs-config/src/token_shadow.rs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/pbs-config/src/token_shadow.rs b/pbs-config/src/token_shadow.rs
index ed54cdfa..23837c60 100644
--- a/pbs-config/src/token_shadow.rs
+++ b/pbs-config/src/token_shadow.rs
@@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use serde_json::{from_value, Value};
use proxmox_sys::fs::CreateOptions;
+use proxmox_time::epoch_i64;
use pbs_api_types::Authid;
//use crate::auth;
@@ -24,6 +25,8 @@ const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow");
/// subsequent authentications for the same token+secret combination, avoiding
/// recomputing the password hash on every request.
static TOKEN_SECRET_CACHE: OnceCell<RwLock<ApiTokenSecretCache>> = OnceCell::new();
+/// Max age in seconds of the token secret cache before checking for file changes.
+const TOKEN_SECRET_CACHE_TTL_SECS: i64 = 60;
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
@@ -63,6 +66,15 @@ fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
fn refresh_cache_if_file_changed() -> Result<(), Error> {
let mut cache = token_secret_cache().write().unwrap();
+ let now = epoch_i64();
+
+ // Fast path: Within TTL boundary
+ if let Some(last) = cache.last_checked {
+ if now - last < TOKEN_SECRET_CACHE_TTL_SECS {
+ return Ok(());
+ }
+ }
+
// Fetch the current token.shadow metadata
let (new_mtime, new_len) = match fs::metadata(CONF_FILE) {
Ok(meta) => (meta.modified().ok(), Some(meta.len())),
@@ -79,6 +91,7 @@ fn refresh_cache_if_file_changed() -> Result<(), Error> {
cache.secrets.clear();
cache.file_mtime = new_mtime;
cache.file_len = new_len;
+ cache.last_checked = Some(now);
Ok(())
}
@@ -169,6 +182,8 @@ struct ApiTokenSecretCache {
file_mtime: Option<SystemTime>,
// shadow file length to detect changes
file_len: Option<u64>,
+ // last time the file metadata was checked
+ last_checked: Option<i64>,
}
fn token_secret_cache() -> &'static RwLock<ApiTokenSecretCache> {
@@ -177,6 +192,7 @@ fn token_secret_cache() -> &'static RwLock<ApiTokenSecretCache> {
secrets: HashMap::new(),
file_mtime: None,
file_len: None,
+ last_checked: None,
})
})
}
--
2.47.3
More information about the pbs-devel
mailing list