diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 029aad5..4b4f0f9 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -42,6 +42,7 @@ jobs: label_trigger: claude base_branch: staging max_turns: "30" + allowed_bots: "Copilot" allowed_tools: &allowed_tools | mcp__context7__resolve-library-id mcp__context7__get-library-docs @@ -111,6 +112,7 @@ jobs: label_trigger: claude base_branch: staging max_turns: "30" + allowed_bots: "Copilot" allowed_tools: *allowed_tools mcp_config: *mcp_config direct_prompt: | @@ -134,6 +136,7 @@ jobs: mode: agent base_branch: staging max_turns: "30" + allowed_bots: "Copilot" allowed_tools: *allowed_tools mcp_config: *mcp_config direct_prompt: | @@ -157,6 +160,7 @@ jobs: label_trigger: claude base_branch: staging max_turns: "30" + allowed_bots: "Copilot" allowed_tools: *allowed_tools mcp_config: *mcp_config direct_prompt: | diff --git a/Cargo.lock b/Cargo.lock index a3a3907..ae5c146 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,9 +210,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.0" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "zeroize", @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" dependencies = [ "cc", "cmake", @@ -3230,9 +3230,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "aws-lc-rs", "ring", diff --git a/crates/flow/Cargo.toml b/crates/flow/Cargo.toml index 1739f07..556b231 100644 --- a/crates/flow/Cargo.toml +++ b/crates/flow/Cargo.toml @@ -43,6 +43,10 @@ name = "d1_profiling" harness = false name = "load_test" +[[bench]] +harness = false +name = "bench_graph_traversal" + [dependencies] async-trait = { workspace = true } base64 = "0.22" @@ -103,6 +107,7 @@ rusqlite = { version = "0.32.1", features = ["bundled"] } tempfile = "3.13" testcontainers = "0.27.1" testcontainers-modules = { version = "0.15.0", features = ["postgres"] } +thread-utilities.workspace = true tokio-postgres = "0.7" [features] diff --git a/crates/flow/benches/bench_graph_traversal.rs b/crates/flow/benches/bench_graph_traversal.rs new file mode 100644 index 0000000..d52c4fb --- /dev/null +++ b/crates/flow/benches/bench_graph_traversal.rs @@ -0,0 +1,43 @@ +use std::path::PathBuf; +use thread_flow::incremental::graph::DependencyGraph; +use thread_flow::incremental::types::{DependencyEdge, DependencyType}; +use thread_utilities::RapidSet; + +use std::hint::black_box; +use criterion::{Criterion, criterion_group, criterion_main}; + +fn bench_find_affected_files(c: &mut Criterion) { + let mut graph = DependencyGraph::new(); + let num_files = 10000; + let deps_per_file = 10; + + // Create nodes + for i in 0..num_files { + graph.add_node(&PathBuf::from(format!("file_{}.rs", i))); + } + + // Create edges (linear chain with some random deps) + for i in 0..num_files { + for j in 1..=deps_per_file { + let dep_idx = (i + j) % num_files; + graph.add_edge(DependencyEdge::new( + PathBuf::from(format!("file_{}.rs", i)), + PathBuf::from(format!("file_{}.rs", dep_idx)), + DependencyType::Import, + )); + } + } + + let changed_files: RapidSet = (0..10) + .map(|i| PathBuf::from(format!("file_{}.rs", i))) + .collect(); + + c.bench_function("find_affected_files_10000_nodes", |b| { + b.iter(|| { + let _affected = graph.find_affected_files(black_box(&changed_files)); + }) + }); +} + +criterion_group!(benches, bench_find_affected_files); +criterion_main!(benches); diff --git a/crates/flow/src/incremental/graph.rs b/crates/flow/src/incremental/graph.rs index 77a1c82..d2af4b5 100644 --- a/crates/flow/src/incremental/graph.rs +++ b/crates/flow/src/incremental/graph.rs @@ -267,21 +267,17 @@ impl DependencyGraph { /// assert!(affected.contains(&PathBuf::from("C"))); /// ``` pub fn find_affected_files(&self, changed_files: &RapidSet) -> RapidSet { - let mut affected = thread_utilities::get_set(); - let mut visited = thread_utilities::get_set(); - let mut queue: VecDeque = changed_files.iter().cloned().collect(); + let mut affected = changed_files.clone(); + let mut queue: VecDeque<&PathBuf> = changed_files.iter().collect(); while let Some(file) = queue.pop_front() { - if !visited.insert(file.clone()) { - continue; - } - - affected.insert(file.clone()); - // Follow reverse edges (files that depend on this file) - for edge in self.get_dependents(&file) { - if edge.effective_strength() == DependencyStrength::Strong { - queue.push_back(edge.from.clone()); + for edge in self.get_dependents(file) { + if edge.effective_strength() == DependencyStrength::Strong + && !affected.contains(&edge.from) + { + affected.insert(edge.from.clone()); + queue.push_back(&edge.from); } } } diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 721ddd6..7709c0e 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -1721,17 +1721,17 @@ pub fn from_extension(path: &Path) -> Option { } // Handle extensionless files or files with unknown extensions - if let Some(_file_name) = path.file_name().and_then(|n| n.to_str()) { + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { // 1. Check if the full filename matches a known extension (e.g. .bashrc) #[cfg(any(feature = "bash", feature = "all-parsers"))] - if constants::BASH_EXTS.contains(&_file_name) { + if constants::BASH_EXTS.contains(&file_name) { return Some(SupportLang::Bash); } // 2. Check known extensionless file names #[cfg(any(feature = "bash", feature = "all-parsers", feature = "ruby"))] for (name, lang) in constants::LANG_RELATIONSHIPS_WITH_NO_EXTENSION { - if *name == _file_name { + if *name == file_name { return Some(*lang); } }