From 46be39dfb7fbfb2b70e61819d6065b45810c41c9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 5 Jul 2026 22:34:58 +0200 Subject: [PATCH] fix(pipelines): order data_test relationships refs before the tested script in a cascade (#9934) * fix(pipelines): order data_test relationships refs before the tested script in a cascade Co-Authored-By: Claude Opus 4.8 (1M context) * fix(pipelines): key custom-test reads by (usage_kind, path) to avoid same-path flow collisions Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- backend/windmill-api-assets/src/lib.rs | 118 ++++++++++++++++++ backend/windmill-api/openapi.yaml | 32 +++++ docs/ducklake-materialization.md | 34 +++++ .../assets/AssetGraph/AssetGraphCanvas.svelte | 27 ++++ .../assets/AssetGraph/boundedCascade.test.ts | 71 ++++++++++- .../assets/AssetGraph/boundedCascade.ts | 14 +++ .../lib/components/assets/AssetGraph/types.ts | 17 +++ 7 files changed, 311 insertions(+), 2 deletions(-) diff --git a/backend/windmill-api-assets/src/lib.rs b/backend/windmill-api-assets/src/lib.rs index 2cebf9ed13..c0a93c039b 100644 --- a/backend/windmill-api-assets/src/lib.rs +++ b/backend/windmill-api-assets/src/lib.rs @@ -797,6 +797,25 @@ enum TriggerEdge { }, } +// Ordering-only "must-run-after" edge: `runnable_path`'s data test reads +// `asset` (a `// data_test relationships` ref, or a custom test whose body +// reads a known pipeline asset), so the asset's in-pipeline producer must +// materialize before `runnable_path` runs. NOT a data-consumption edge — the +// tested script doesn't ingest the asset's rows, it only needs the table to +// exist at test time. Rendered dashed (like macro edges) and fed into the +// cascade topo-sort so a cold cascade orders the referenced dimension first. +// Only emitted when the referenced asset has a producer in the graph; an +// external table (no producer) adds no edge — the runtime error stands. +#[derive(Serialize, Debug)] +struct TestEdge { + producer_kind: AssetUsageKind, + producer_path: String, + runnable_kind: AssetUsageKind, + runnable_path: String, + asset_kind: AssetKind, + asset_path: String, +} + #[derive(Serialize, Debug)] struct AssetGraphResponse { assets: Vec, @@ -805,6 +824,8 @@ struct AssetGraphResponse { triggers: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] macro_edges: Vec, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + test_edges: Vec, } async fn asset_graph( @@ -1257,6 +1278,102 @@ async fn asset_graph( runnable_set.insert((AssetUsageKind::Script, e.consumer_path.clone())); } + // Data-test ordering edges. A `// data_test relationships -> ` + // (and, best-effort, a custom `// data_test