Skip to content

KCL: Move cache out of static, into owned struct - #14540

Merged
adamchalmers merged 5 commits into
mainfrom
achalmers/kcl-cache
Oct 14, 2026
Merged

adamchalmers merged 5 commits into
mainfrom
achalmers/kcl-cache

Conversation

@adamchalmers

@adamchalmers adamchalmers commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Background

KCL interpreter has a cache, where it stores "execution state", like the results of calculating variables, previous sketch mode executions, etc. This cache is really important for KCL's ux, because it lets the frontend add a new KCL function call to the end of the current KCL program without re-executing everything up to that point. It saves users a lot of time.

Problem

The cache is currently kept in a mutable global static. This works OK for now, but it's not good. Having one shared cache for everything in the process means you can't have two concurrent KCL executions -- even if they're on different engines! Mutable global state is bad even for single-threaded monolithic apps, but it's even worse in something like kcl-lib, where consumers might want to have several different executors.

Solution

Move the cache out of global statics, into a dedicated Arc<RwMutex<Cache>> which can be shared by different executors if desired, but is probably just being used on its own.

Reviewing

First, I suggest you review cache.rs to see the changed cache structure (globals -> owned structs).

Then view kcl-wasm-lib/src/context.rs which initializes this new cache, and shares it with every ExecutorContext it creates (so that every execution can use the cache).

For x86/arm KCL execution (e.g. in the engine, or CLI, or Synera node), look at how ExecutorContext gets initialized in kcl-lib/src/execution/mod.rs.

Most of the other changes are straightforward changes like this:

// before
cache::write_old_ast
// after
self.cache.write_old_ast

@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
modeling-app Ready Ready Preview Oct 14, 2026 5:58pm UTC

Request Review

@codspeed

codspeed Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 157 untouched benchmarks
⏩ 131 skipped benchmarks1


Comparing achalmers/kcl-cache (6db189d) with main (a9df074)

Open in CodSpeed

Footnotes

  1. 131 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@adamchalmers adamchalmers changed the title Achalmers/kcl cache KCL: Move cache out of static, into owned struct Oct 13, 2026
@adamchalmers
adamchalmers marked this pull request as ready for review October 13, 2026 19:35
@adamchalmers
adamchalmers requested review from a team as code owners October 13, 2026 19:35
@adamchalmers
adamchalmers force-pushed the achalmers/kcl-cache branch 2 times, most recently from c081fff to 962161c Compare October 13, 2026 20:03
let mut old_ast = OLD_AST.write().await;
*old_ast = Some(old_state);
// Hide the cache's concrete state graph from Send/Sync proofs in callers.
trait CacheStorage: std::fmt::Debug + Send + Sync {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really get why this exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a hack to make rustc do less work. This was originally a concrete type with no trait, but because the type is very deeply nested, it was marking rustc work hard to prove it was Sync + Send. By putting this trait here, we break the deep recursion into two parts: proving that everything that uses this is Send + Sync (easy) and then separately proving that the struct which implements it is Send + Sync (also easier). It does two smaller checks rather than one big check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I haven't seen this trick before.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was Codex's find, but it's the same approach I've used for different problems (like avoiding cyclic dependencies, by changing a nested field struct into a trait)

@adamchalmers
adamchalmers enabled auto-merge (squash) October 14, 2026 17:53
@adamchalmers
adamchalmers disabled auto-merge October 14, 2026 18:08
@adamchalmers
adamchalmers enabled auto-merge (squash) October 14, 2026 18:09
@adamchalmers
adamchalmers merged commit e898ec4 into main Oct 14, 2026
123 of 125 checks passed
@adamchalmers
adamchalmers deleted the achalmers/kcl-cache branch October 14, 2026 20:02

This branch was successfully deployed

1 active deployment
Preview — 6db189d4 Deployed Oct 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants