# Salsa2022
This note describes how salsa works internally for Fe-devs to work with salsa2022 better and more confidently.
## Resources
Before reading this note, it's highly recommended to read [Salsa Book](https://salsa-rs.github.io/salsa/) and watch [Salsa Architecture Walkthrough](https://www.youtube.com/watch?v=vrnNvAAoQFk).
Plus, if you are interested in cycle handling in salsa 2022, [Salsa cycle-handling walkthrough](https://www.youtube.com/watch?v=ES4H818jPus&t=1412) is a nice starter.
**NOTE:** Niko talks about the entity system in [Salsa cycle-handling walkthrough @1:01:38](https://youtu.be/ES4H818jPus?t=3698), but this might be misleading about an entity data representation inside salsa; please see [Tracked Struct](#Tracked-Struct) section for details.
## Revision
A [`Revision`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/src/revision.rs#L13-L16) is just an integer that is incremented by one when an input changes. The integer indicates the version of the database and mainly works to verify cache(memo).
So what does an input change mean?
From a user perspective, input change is nearly equal to invoking a query that takes `&mut Db`. This means `revision` is incremented by one whenever you invoke such queries.
From a salsa perspective, `revision` is always incremented when [`Storage::jars_mut`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/storage.rs#L100-L126) is called.
We'll also see how revision changes affect other working threads in the [`Threads`](#Threads) section.
## Memo
[`Memo`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/function/memo.rs#L79-L90) is a cache of each query result, which is a small type that have three fields.
1. Exact data resulting from a query with specific arguments.
2. The last revision that the memo is verified at, we will look in depth at memo verification in [Verification](#Verification) section.
3. The information about how the memo was generated, this includes information about query dependencies.
Memos are stored in [`MemoMap`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/function/memo.rs#L14-L16), which is a concurrency optimized hashmap from a `Key` to a memo. A `Key` is either a tracked struct or interned arguments.
MemoMap is stored in [`FunctionIngredient`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/function.rs#L42-L76) that corresponds to each tracked function. An important observation is that memos are not stored directly in a database(storage); instead, a query is responsible for its memos.
About storage details, please refer to [`Storage`](#Storage) section.
## Tracked Function
TBW...
## Input
TBW...
## Tracked Struct
As the salsa book describes, an instance of a tracked struct is just `salsa::Id`, which is just an integer. But these integers are **NOT** interned id for all fields of a tracked struct; rather, they can be regarded as a descriptor for each instance with ``#[id]`` fields data.
*TIP: The internal type of `salsa::Id` is `NonzeroU32`, so some types containing the Id can be optimized. e.g., the size of `Option<salsa::Id>` is still 4 bytes.*
A constructor generated by ``#[salsa::tracked]`` conceptually performs two different calls.
1. Call [`TrackedStructIngredient::new_struct`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/tracked_struct.rs#L81-L96) with `#[id]` fields as arguments, then the `new_struct` method interns fields with [`Disambiguator`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/tracked_struct.rs#L58-L59) to distinguish different instances with the same fields.
**NOTE:** `Disambiguator` is also just an integer created by [`ActiveQuery::disambiguate`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/runtime/active_query.rs#L138-L147).
2. Call [`TrackedStructIngredient::specify_and_record`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/function/specify.rs#L97-L108) with each remaining field.
**NOTE:** `specify_and_record` is called multiple times because `TrackedStructIngredient` has a one-to-one correspondence with the remaining fields.
The macro also generates different kinds of getter for each field depending on whether the field is `#[id]` attributed.
1. If the field is `#[id]` attributed: the generated getter calls [`TrackedStructIngredient::tracked_struct_data`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/tracked_struct.rs#L98-L10) to obtain the data stored inside the ingredient.
2. Otherwise: the generated getter invokes [`FunctionIngredient::fetch`](https://github.com/salsa-rs/salsa/blob/ef7c0f12c8159e7025316e959c26f6278a576fa5/components/salsa-2022/src/function/fetch.rs#L11-L33) to obtain registered result.
### Example
```rust!
#[salsa::tracked]
pub struct Function {
#[id]
pub name: IdentId,
pub params: FnParamListId,
}
// The abobe `Function` struct will be expanded into the below.
// *----------------------------------------------------------*
pub struct __FunctionParams(std::convert::Infallible);
pub struct Function(salsa::Id);
impl Function {
pub fn new(
__db: &<crate::Jar as salsa::jar::Jar<'_>>::DynDb,
name: IdentId,
params: FnParamListId,
) -> Self {
// Obtains `Function` ingredients.
// `__ingredients.0` is a `FunctionIngredient` for `params` field.
// `__ingredients.1` is a `TrackedStructIngredient` for
// `Function`, which holds `name` field as an interned data.
let (__jar, __runtime) = <_ as salsa::storage::HasJar<crate::Jar>>::jar(__db);
let __ingredients =
<crate::Jar as salsa::storage::HasIngredientsFor<Function>>::ingredient(__jar);
// Construct an instance of `Function`.
// The `__id` is guaranteed to be unique for each
// instance even if the `name` is the same.
let __id = __ingredients.1.new_struct(__runtime, (name,));
// Register `params` as an result of the getter query.
__ingredients.0.specify_and_record(__db, __id, params);
__id
}
pub fn name<'db>(self, __db: &'db <crate::Jar as salsa::jar::Jar<'_>>::DynDb) -> IdentId {
let (__jar, __runtime) = <_ as salsa::storage::HasJar<crate::Jar>>::jar(__db);
let __ingredients =
<crate::Jar as salsa::storage::HasIngredientsFor<Function>>::ingredient(__jar);
// Obtain the `name` from the interner inside
// the `TrackedStructIngredient`.
__ingredients
.1
.tracked_struct_data(__runtime, self)
.0
.clone()
}
// This is a getter for `params`.
pub fn params<'db>(
self,
__db: &'db <crate::Jar as salsa::jar::Jar<'_>>::DynDb,
) -> FnParamListId {
// ...
let (__jar, __runtime) = <_ as salsa::storage::HasJar<crate::Jar>>::jar(__db);
let __ingredients =
<crate::Jar as salsa::storage::HasIngredientsFor<Function>>::ingredient(__jar);
// Invokes the query to obtain the `params`, the query must find the
// valid memo corresponding to the `self` becauase
// it's already registered in `Self::new()`
__ingredients.0.fetch(__db, self).clone()
}
}
// Trait implementations of ingrediants for `Function` and `params`.
...
```
**NOTE:** The design described above might be changed in the future, [Salsa cycle-handling walkthrough @1:12:23](https://www.youtube.com/watch?v=ES4H818jPus&t=4343s).
## Input
TBW...
## Storage
TBW...
## Verification
TBW...
## Durability
TBW...
## Runtime
TBW...
## Threads
TBW...
## Cycle Handling
TBW...