Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
## 2026-04-08 - [Performance: Defer Allocation during Traversal]
**Learning:** During DAG traversals, creating owned variants of identifiers (like `file.to_path_buf()`) *before* checking `visited` HashSets results in heap allocations (O(E)) for every edge instead of every visited node (O(V)). By moving the `&PathBuf` allocation strictly *after* all HashSet `contains` checks using the borrowed reference (`&Path`), we drastically reduce memory churn.
**Action:** Always check `HashSet::contains` with a borrowed reference *before* creating the owned version required by `HashSet::insert`, especially in performance-critical graph traversal paths.
## 2026-05-07 - [Performance: SQL Generation Pre-allocation]\n**Learning:** Generating SQL string with intermediate `vec![]` string arrays, pushing `format!` results, and calling `.join(",")` incurs unnecessary string and heap allocations, especially during bulk upsert or deletes.\n**Action:** Use pre-allocated Strings with `String::with_capacity` and leverage `std::fmt::Write` directly (`write!`) to assemble complex SQL queries with no intermediate array joining.
10 changes: 6 additions & 4 deletions crates/ast-engine/src/tree_sitter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,8 @@ impl ContentExt for String {
let mut bytes = std::mem::take(self).into_bytes();
let original_len = bytes.len();
bytes.splice(safe_start..safe_end, full_inserted);
*self = Self::from_utf8(bytes).unwrap_or_else(|e| {
Self::from_utf8_lossy(&e.into_bytes()).into_owned()
});
*self = Self::from_utf8(bytes)
.unwrap_or_else(|e| Self::from_utf8_lossy(&e.into_bytes()).into_owned());

// We calculate new_end_byte using the difference in the new overall string length
// to correctly align the end offset, taking any potential replacement bytes from
Expand Down Expand Up @@ -791,7 +790,10 @@ mod test {

let tree2 = parse_lang(|p| p.parse(&src, Some(&tree)), &Tsx.get_ts_language())?;
let fresh_tree = parse(&src)?;
assert_eq!(tree2.root_node().to_sexp(), fresh_tree.root_node().to_sexp());
assert_eq!(
tree2.root_node().to_sexp(),
fresh_tree.root_node().to_sexp()
);
Ok(())
}
}
77 changes: 51 additions & 26 deletions crates/flow/src/targets/d1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,40 +300,61 @@ impl D1ExportContext {
key: &KeyValue,
values: &FieldValues,
) -> Result<(String, Vec<serde_json::Value>), RecocoError> {
let mut columns = vec![];
let mut placeholders = vec![];
use std::fmt::Write;

let mut params = vec![];
let mut update_clauses = vec![];

// ⚡ Bolt: Defer String heap allocations using write! macro over `format!` inside intermediate vecs.
let mut sql = String::with_capacity(128);
let mut placeholders = String::with_capacity(64);
let mut update_clauses = String::with_capacity(128);

write!(&mut sql, "INSERT INTO {} (", self.table_name).unwrap();

let mut first_col = true;
// Extract key parts - KeyValue is a wrapper around Box<[KeyPart]>
for (idx, _key_field) in self.key_fields_schema.iter().enumerate() {
if let Some(key_part) = key.0.get(idx) {
columns.push(self.key_fields_schema[idx].name.clone());
placeholders.push("?".to_string());
if !first_col {
write!(&mut sql, ", ").unwrap();
write!(&mut placeholders, ", ").unwrap();
}
write!(&mut sql, "{}", self.key_fields_schema[idx].name).unwrap();
write!(&mut placeholders, "?").unwrap();
params.push(key_part_to_json(key_part)?);
first_col = false;
}
}

let mut first_update = true;
// Add value fields
for (idx, value) in values.fields.iter().enumerate() {
if let Some(value_field) = self.value_fields_schema.get(idx) {
columns.push(value_field.name.clone());
placeholders.push("?".to_string());
let name = &value_field.name;
if !first_col {
write!(&mut sql, ", ").unwrap();
write!(&mut placeholders, ", ").unwrap();
}
write!(&mut sql, "{}", name).unwrap();
write!(&mut placeholders, "?").unwrap();

if !first_update {
write!(&mut update_clauses, ", ").unwrap();
}
write!(&mut update_clauses, "{} = excluded.{}", name, name).unwrap();

params.push(value_to_json(value)?);
update_clauses.push(format!(
"{} = excluded.{}",
value_field.name, value_field.name
));
first_col = false;
first_update = false;
}
}

let sql = format!(
"INSERT INTO {} ({}) VALUES ({}) ON CONFLICT DO UPDATE SET {}",
self.table_name,
columns.join(", "),
placeholders.join(", "),
update_clauses.join(", ")
);
write!(
&mut sql,
") VALUES ({}) ON CONFLICT DO UPDATE SET {}",
placeholders, update_clauses
)
.unwrap();

Ok((sql, params))
}
Expand All @@ -342,22 +363,26 @@ impl D1ExportContext {
&self,
key: &KeyValue,
) -> Result<(String, Vec<serde_json::Value>), RecocoError> {
let mut where_clauses = vec![];
use std::fmt::Write;

let mut params = vec![];

// ⚡ Bolt: Defer String heap allocations using write! macro over `format!` inside intermediate vecs.
let mut sql = String::with_capacity(128);
write!(&mut sql, "DELETE FROM {} WHERE ", self.table_name).unwrap();

let mut first = true;
for (idx, _key_field) in self.key_fields_schema.iter().enumerate() {
if let Some(key_part) = key.0.get(idx) {
where_clauses.push(format!("{} = ?", self.key_fields_schema[idx].name));
if !first {
write!(&mut sql, " AND ").unwrap();
}
write!(&mut sql, "{} = ?", self.key_fields_schema[idx].name).unwrap();
params.push(key_part_to_json(key_part)?);
first = false;
}
}

let sql = format!(
"DELETE FROM {} WHERE {}",
self.table_name,
where_clauses.join(" AND ")
);

Ok((sql, params))
}

Expand Down
6 changes: 5 additions & 1 deletion crates/rule-engine/src/rule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ impl Rule {

pub fn defined_vars(&self) -> RapidSet<String> {
match self {
Rule::Pattern(p) => p.defined_vars().into_iter().map(|s| s.to_string()).collect(),
Rule::Pattern(p) => p
.defined_vars()
.into_iter()
.map(|s| s.to_string())
.collect(),
Rule::Kind(_) => RapidSet::default(),
Rule::Regex(_) => RapidSet::default(),
Rule::NthChild(n) => n.defined_vars(),
Expand Down
5 changes: 1 addition & 4 deletions crates/rule-engine/src/rule/referent_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ impl<R> Clone for Registration<R> {

impl<R> Registration<R> {
fn read(&self) -> Arc<RapidMap<String, R>> {
self.0
.read()
.unwrap_or_else(|e| e.into_inner())
.clone()
self.0.read().unwrap_or_else(|e| e.into_inner()).clone()
}
Comment on lines 28 to 31
pub(crate) fn contains_key(&self, key: &str) -> bool {
self.read().contains_key(key)
Expand Down
Loading