| name | salsa |
| description | Mental-model reset for Salsa, the incremental computation framework for Rust. Use when building or reviewing Salsa databases, tracked functions, input/ tracked/interned structs, query pipelines, accumulators, cancellation, durability, LSP integration, memory management, cycles, or production Salsa architecture. Triggers on #[salsa::db], #[salsa::input], #[salsa::tracked], #[salsa::interned], #[salsa::accumulator], salsa::Storage, memoization, revisions, backdating, red-green algorithm, WillExecute, DidValidateMemoizedValue, Cancelled, returns(ref), no_eq, lru, cycle_fn, cycle_result, durability, or salsa::Event.
|
Salsa: Incremental Computation for Rust
Salsa is a framework for incremental recomputation. You define inputs and pure functions over them. Salsa memoizes every function call. When inputs change, it re-executes only the functions whose dependencies actually changed — skipping everything else.
Salsa powers rust-analyzer (Rust IDE), ty (Python type checker), and Cairo, all of which need sub-second response times on large codebases after small edits.
The Mental Model
Salsa Database
┌─────────────────────────────────────┐
External world │ │
(editor, CLI, │ Inputs ──→ Tracked Fns ──→ Output │
filesystem) │ │ │ │
│ │ └── memoized ┘ │
│ │ dependencies tracked automatically│
▼ └─────────────────────────────────────┘
Mutate inputs │
(new revision) ▼
Only re-run what changed
The core loop:
let db = MyDatabase::default();
let file = SourceFile::new(&db, "fn main() {}".into(), path);
let result = analyze(&db, file);
file.set_text(&mut db).to("fn main() { 42 }".into());
let result = analyze(&db, file);
Core Concepts
The Database
A struct with #[salsa::db] that stores all cached data. It's the single source of truth — every Salsa operation takes a &db or &mut db.
#[salsa::db]
#[derive(Default, Clone)]
pub struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
→ Deep dive: database-architecture.md for layered traits, test vs production, and side tables.
Inputs — The Roots
External data entering the system. The only mutable Salsa structs. Just newtypes around integer IDs (Copy, no lifetime).
#[salsa::input]
pub struct SourceFile {
#[returns(ref)]
pub text: String,
pub path: PathBuf,
}
Tracked Functions — The Computation
Pure functions whose results are memoized. Salsa records which inputs/fields each call reads. On re-execution, it checks if those dependencies changed.
#[salsa::tracked]
fn parse(db: &dyn Db, file: SourceFile) -> Ast<'_> {
let text = file.text(db);
Ast::new(db, statements)
}
→ Deep dive: query-pipeline.md for return modes, LRU, no_eq, specify, and granularity.
Tracked Structs — Intermediate Entities
Created inside tracked functions. Have per-field change tracking. Carry a 'db lifetime.
#[salsa::tracked]
pub struct Function<'db> {
#[id]
pub name: Word<'db>,
#[tracked]
#[returns(ref)]
pub body: Expression<'db>,
}
Interned Structs — Cheap Equality
Same data → same integer ID. Used for identifiers, type representations, module paths. Carry a 'db lifetime.
#[salsa::interned]
pub struct Word<'db> {
#[returns(ref)]
pub text: String,
}
→ Deep dive: struct-selection.md for the decision framework, including ty's "no tracked structs" and rust-analyzer's "intern every definition" styles.
Accumulators — Side-Channel Output
Report diagnostics from tracked functions without affecting the return value.
#[salsa::accumulator]
pub struct Diagnostics(Diagnostic);
Diagnostics::push(db, Diagnostic { message: "type error".into(), .. });
→ Deep dive: accumulators.md.
Revisions and the Red-Green Algorithm
Every input mutation increments a revision counter. When you call a tracked function, Salsa checks its dependencies since the last cached result. If they changed, it re-executes; if the new result equals the old one (backdating), it stops propagation.
Durability — Optimization Hint
Inputs can be tagged with LOW, MEDIUM, or HIGH durability. When only LOW-durability inputs change, Salsa skips validating stable subgraphs.
→ Deep dive: durability.md.
Where to Go Deeper
Key Vocabulary
- Revision: A version of the database between input mutations.
- Ingredient: Any Salsa-managed item (input, tracked struct, interned struct, tracked fn, accumulator).
- Backdating: If a re-executed function produces the same result, it's marked unchanged to prevent downstream re-execution.
- Red-green algorithm: Salsa's strategy for deciding what to re-execute.
- Durability: A promise about how often an input changes.
- LRU: Least Recently Used eviction for bounding cache size.
no_eq: Skip equality comparison on result — always propagate changes.
- Cycle: When tracked functions form a dependency loop.
Real-World Scale
- ty: 2 inputs, 0 tracked structs for types (all interned), 60+ cycle sites.
- rust-analyzer: 5+ inputs, tracked structs for collection caches, 6-layer DB hierarchy.
- Cairo: 4 singleton inputs, 127+ tracked functions, 29 cycle sites, parallel compilation via
CloneableDatabase.
- django-language-server: 1 input, 2 accumulators, 5-layer DB (~78 Rust files — very approachable).
- BAML: 15 tracked structs, 6-layer DB, documented early cutoff strategies.
- Fe: marker traits for compilation phase enforcement, hybrid accumulator/return-value diagnostics.
- stc [Legacy API]: 7 tracked functions wrapping the SWC TypeScript ecosystem — the "thin Salsa shell" approach to incrementalizing an existing tool.
- wgsl-analyzer [Legacy API]: ~30 queries across 4 layers (~110 Rust files) — validates the rust-analyzer LSP architecture in a GPU shader language.
- Mun [Legacy API]: ~40 queries across 6 query groups (~150 Rust files) — the only project extending Salsa through LLVM codegen to shared library output, with a hot-reloading compiler daemon.
The hardest part of Salsa is the design decisions about granularity, identity, and what to track. django-language-server and BAML are the best starting points for learning. stc [Legacy API] is the best example of wrapping an existing non-Salsa library with minimal Salsa code. Mun [Legacy API] is the best example of extending Salsa queries all the way to machine code generation and hot reloading.
Further Reading