mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-27 16:02:43 +00:00
* refactor: extract windmill-dep-map crate for parallel api/worker compilation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve WebhookShared type mismatch and missing enterprise propagation - Make windmill-api webhook_util re-export from windmill-common instead of duplicating types, fixing Extension<WebhookShared> mismatch between windmill-store and windmill-api - Add windmill-api-jobs/enterprise to windmill-trigger enterprise feature so check_license_key_valid is available when trigger subcrates enable enterprise on windmill-trigger Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stop trigger features from unconditionally enabling enterprise Move enterprise propagation for all trigger subcrates from individual trigger feature definitions to the enterprise feature itself, so enterprise is only enabled when explicitly requested. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove unused pub use re-exports and disable CI cargo cache - Remove unused re-exports from windmill-worker/src/lib.rs: trigger_dependents_to_recompute_dependencies, handle_job_error, and unused bun/otel items - Fix callers to use direct module paths instead - Add windmill-dep-map as dev-dependency for tests - Disable cargo cache in backend-check CI (faster from-scratch builds) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restore bun re-exports used by tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * all * chore: re-enable cargo cache for check_ee_full CI job Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
7.5 KiB
Rust
221 lines
7.5 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use chrono::{Duration, Utc};
|
|
use itertools::Itertools;
|
|
use serde_json::value::RawValue;
|
|
use windmill_common::error;
|
|
use windmill_common::jobs::JobPayload;
|
|
use windmill_common::runnable_settings::DebouncingSettings;
|
|
use windmill_common::scripts::ScriptHash;
|
|
use windmill_common::worker::to_raw_value;
|
|
use windmill_queue::PushIsolationLevel;
|
|
|
|
use crate::scoped_dependency_map::{DependencyDependent, ScopedDependencyMap};
|
|
|
|
lazy_static::lazy_static! {
|
|
static ref DEPENDENCY_JOB_DEBOUNCE_DELAY: usize = std::env::var("DEPENDENCY_JOB_DEBOUNCE_DELAY").ok().and_then(|flag| flag.parse().ok()).unwrap_or(
|
|
if cfg!(test) { 15 } else { 5 }
|
|
);
|
|
}
|
|
|
|
pub async fn trigger_dependents_to_recompute_dependencies(
|
|
w_id: &str,
|
|
importers: Vec<DependencyDependent>,
|
|
deployment_message: Option<String>,
|
|
parent_path: Option<String>,
|
|
email: &str,
|
|
created_by: &str,
|
|
permissioned_as: &str,
|
|
db: &sqlx::Pool<sqlx::Postgres>,
|
|
already_visited: Vec<String>,
|
|
) -> error::Result<()> {
|
|
tracing::debug!(
|
|
"Triggering dependents to recompute dependencies: {}",
|
|
importers.iter().map(|dd| &dd.importer_path).join(",")
|
|
);
|
|
for DependencyDependent { importer_path, importer_kind, importer_node_ids } in importers.iter()
|
|
{
|
|
tracing::trace!("Processing dependency: {:?}", importer_path);
|
|
if already_visited.contains(importer_path) {
|
|
tracing::trace!("Skipping already visited dependency");
|
|
continue;
|
|
}
|
|
|
|
let mut tx = db.clone().begin().await?;
|
|
let mut args: HashMap<String, Box<RawValue>> = HashMap::new();
|
|
if let Some(ref dm) = deployment_message {
|
|
args.insert("deployment_message".to_string(), to_raw_value(&dm));
|
|
}
|
|
if let Some(ref p_path) = parent_path {
|
|
args.insert("common_dependency_path".to_string(), to_raw_value(&p_path));
|
|
}
|
|
|
|
args.insert(
|
|
"already_visited".to_string(),
|
|
to_raw_value(&already_visited),
|
|
);
|
|
|
|
args.insert(
|
|
"triggered_by_relative_import".to_string(),
|
|
to_raw_value(&true),
|
|
);
|
|
|
|
let mut debouncing_settings = DebouncingSettings {
|
|
debounce_key: Some(format!("{w_id}:{importer_path}:dependency")),
|
|
debounce_delay_s: Some(5),
|
|
..Default::default()
|
|
};
|
|
|
|
let job_payload = match importer_kind.as_str() {
|
|
"script" => match sqlx::query_scalar!(
|
|
"SELECT hash FROM script WHERE path = $1 AND workspace_id = $2 AND deleted = false ORDER BY created_at DESC LIMIT 1",
|
|
importer_path,
|
|
w_id
|
|
)
|
|
.fetch_optional(&mut *tx)
|
|
.await?
|
|
{
|
|
Some(hash) => {
|
|
tracing::debug!("newest hash for {} is: {hash}", importer_path);
|
|
|
|
let info =
|
|
windmill_common::get_script_info_for_hash(None, db, w_id, hash).await?;
|
|
|
|
JobPayload::Dependencies {
|
|
path: importer_path.clone(),
|
|
hash: ScriptHash(hash),
|
|
language: info.language,
|
|
dedicated_worker: info.dedicated_worker,
|
|
debouncing_settings,
|
|
}
|
|
}
|
|
None => {
|
|
ScopedDependencyMap::clear_map_for_item(
|
|
importer_path,
|
|
w_id,
|
|
"script",
|
|
tx,
|
|
&None,
|
|
)
|
|
.await
|
|
.commit()
|
|
.await?;
|
|
continue;
|
|
}
|
|
},
|
|
|
|
"flow" => match sqlx::query_scalar!(
|
|
"SELECT id FROM flow_version WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1",
|
|
importer_path,
|
|
w_id
|
|
)
|
|
.fetch_optional(&mut *tx)
|
|
.await?
|
|
{
|
|
Some(version) => {
|
|
tracing::debug!("Handling flow dependency update for: {}", importer_path);
|
|
|
|
args.insert(
|
|
"nodes_to_relock".to_string(),
|
|
to_raw_value(&importer_node_ids),
|
|
);
|
|
|
|
debouncing_settings.debounce_args_to_accumulate = Some(vec!["nodes_to_relock".into()]);
|
|
|
|
JobPayload::FlowDependencies {
|
|
path: importer_path.clone(),
|
|
version,
|
|
dedicated_worker: None,
|
|
debouncing_settings,
|
|
}
|
|
}
|
|
None => {
|
|
ScopedDependencyMap::clear_map_for_item(importer_path, w_id, "flow", tx, &None)
|
|
.await
|
|
.commit()
|
|
.await?;
|
|
continue;
|
|
}
|
|
},
|
|
|
|
"app" => match sqlx::query_scalar!(
|
|
"SELECT id FROM app_version WHERE app_id = (SELECT id FROM app WHERE path = $1 AND workspace_id = $2) ORDER BY created_at DESC LIMIT 1",
|
|
importer_path,
|
|
w_id
|
|
)
|
|
.fetch_optional(&mut *tx)
|
|
.await?
|
|
{
|
|
Some(version) => {
|
|
tracing::debug!("Handling app dependency update for: {}", importer_path);
|
|
|
|
args.insert(
|
|
"components_to_relock".to_string(),
|
|
to_raw_value(importer_node_ids),
|
|
);
|
|
|
|
debouncing_settings.debounce_args_to_accumulate = Some(vec!["components_to_relock".into()]);
|
|
|
|
JobPayload::AppDependencies { path: importer_path.clone(), version, debouncing_settings }
|
|
}
|
|
None => {
|
|
ScopedDependencyMap::clear_map_for_item(importer_path, w_id, "app", tx, &None)
|
|
.await
|
|
.commit()
|
|
.await?;
|
|
continue;
|
|
}
|
|
},
|
|
|
|
_ => {
|
|
tracing::error!(
|
|
"unexpected importer kind: {kind:?} for path {path}",
|
|
kind = importer_kind,
|
|
path = importer_path
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
tracing::debug!("Pushing dependency job for: {}", importer_path);
|
|
let (job_uuid, new_tx) = windmill_queue::push(
|
|
db,
|
|
PushIsolationLevel::Transaction(tx),
|
|
&w_id,
|
|
job_payload,
|
|
windmill_queue::PushArgs { args: &args, extra: None },
|
|
&created_by,
|
|
email,
|
|
permissioned_as.to_string(),
|
|
Some("trigger.dependents.to.recompute.dependencies"),
|
|
Some(Utc::now() + Duration::seconds(*DEPENDENCY_JOB_DEBOUNCE_DELAY as i64)),
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
false,
|
|
false,
|
|
None,
|
|
true,
|
|
Some("dependency".into()),
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
false,
|
|
None,
|
|
None,
|
|
None,
|
|
)
|
|
.await?;
|
|
|
|
tracing::info!(
|
|
"pushed dependency job due to common python path: {job_uuid} for path {path}",
|
|
path = importer_path,
|
|
);
|
|
new_tx.commit().await?;
|
|
}
|
|
Ok(())
|
|
}
|