test: de-flake asset-dispatch by bypassing cross-DB script-hash caches (#9820)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-06-26 18:35:52 +02:00
committed by GitHub
parent 3cda447621
commit 1c4bb8af14
2 changed files with 37 additions and 10 deletions
+8
View File
@@ -54,6 +54,14 @@ async fn seed_script(
.bind(language)
.execute(db)
.await?;
// These tests use #[sqlx::test] isolated DBs that share one workspace id and
// reuse script paths, while the same path is seeded with different content
// (hence different hashes) across tests. The process-global deployed-script
// caches are keyed by (workspace, path)/(workspace, hash), so a concurrent
// test resolves a path to a hash that lives in another test's DB and the
// dispatch 404s. Disable them so every resolution reads the test's own DB.
windmill_common::DEPLOYED_SCRIPT_CACHE_DISABLED
.store(true, std::sync::atomic::Ordering::Relaxed);
Ok(h)
}
+29 -10
View File
@@ -285,6 +285,16 @@ lazy_static::lazy_static! {
const LATEST_VERSION_ID_CACHE_TTL: std::time::Duration = std::time::Duration::from_secs(60);
/// Test hook: disables the process-global deployed-script hash/info caches so
/// every resolution reads the current DB. Integration tests use `#[sqlx::test]`
/// isolated DBs that share one workspace id and reuse script paths, so a cache
/// keyed by `(workspace, path)`/`(workspace, hash)` resolves a path to a hash
/// that lives in a *different* test's DB — and when the info cache misses for
/// that foreign hash the lookup 404s in the wrong DB. Always `false` in
/// production (the caches are TTL/LRU-bounded against real deploys).
pub static DEPLOYED_SCRIPT_CACHE_DISABLED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub async fn shutdown_signal(
tx: KillpillSender,
mut rx: tokio::sync::broadcast::Receiver<()>,
@@ -1305,8 +1315,12 @@ pub fn get_latest_deployed_hash_for_path<'e>(
) -> impl Future<Output = error::Result<ScriptHashInfo<ScriptRunnableSettingsHandle>>> + Send + 'e {
async move {
let cache_key = (w_id.to_string(), script_path.to_string());
let use_cache = !DEPLOYED_SCRIPT_CACHE_DISABLED.load(std::sync::atomic::Ordering::Relaxed);
let mut computed_hash = None;
let hash = match DEPLOYED_SCRIPT_HASH_CACHE.get(&cache_key) {
let hash = match DEPLOYED_SCRIPT_HASH_CACHE
.get(&cache_key)
.filter(|_| use_cache)
{
Some(cached_hash)
if cached_hash.expires_at > std::time::Instant::now()
&& db.as_ref().is_none_or(|x| {
@@ -1349,13 +1363,15 @@ pub fn get_latest_deployed_hash_for_path<'e>(
};
let hash = utils::not_found_if_none(hash, "script", script_path)?;
DEPLOYED_SCRIPT_HASH_CACHE.insert(
cache_key,
ExpiringLatestVersionId {
id: hash,
expires_at: std::time::Instant::now() + LATEST_VERSION_ID_CACHE_TTL,
},
);
if use_cache {
DEPLOYED_SCRIPT_HASH_CACHE.insert(
cache_key,
ExpiringLatestVersionId {
id: hash,
expires_at: std::time::Instant::now() + LATEST_VERSION_ID_CACHE_TTL,
},
);
}
hash
}
@@ -1387,9 +1403,10 @@ pub async fn get_script_info_for_hash<'e, E: sqlx::PgExecutor<'e>>(
hash: i64,
) -> error::Result<ScriptHashInfo<ScriptRunnableSettingsHandle>> {
let key = (w_id.to_string(), hash);
let use_cache = !DEPLOYED_SCRIPT_CACHE_DISABLED.load(std::sync::atomic::Ordering::Relaxed);
let mut computed_hash = None;
match DEPLOYED_SCRIPT_INFO_CACHE.get(&key) {
match DEPLOYED_SCRIPT_INFO_CACHE.get(&key).filter(|_| use_cache) {
Some(info)
if db_authed.as_ref().is_none_or(|x| {
let r = HASH_PERMS_CACHE.check_perms_in_cache(x.authed, scripts::ScriptHash(hash));
@@ -1418,7 +1435,9 @@ pub async fn get_script_info_for_hash<'e, E: sqlx::PgExecutor<'e>>(
let info = utils::not_found_if_none(info, "script", &hash.to_string())?;
DEPLOYED_SCRIPT_INFO_CACHE.insert(key, info.clone());
if use_cache {
DEPLOYED_SCRIPT_INFO_CACHE.insert(key, info.clone());
}
Ok(info)
}