Skip to content

Commit 1ffb32f

Browse files
authored
Initial persistent caching prototype (#967)
* persistent caching prototype * move serialization arguments under `persist` attribute * move `serde` dependency under `persistence` feature * avoid serializing provisional memos * use exhaustive checking for manual `Serialize` implementations * update tests * remove distinction between ingredient `entries` and `instances` * avoid enabling `shuttle` feature in CI * serialize ingredients by index
1 parent 940f9c0 commit 1ffb32f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2955
-122
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ jobs:
5353
run: cargo fmt -- --check
5454
- name: Clippy
5555
run: cargo clippy --workspace --all-targets -- -D warnings
56+
# TODO: Use something like cargo-hack for more robust feature configuration testing.
57+
- name: Clippy / all-features
58+
run: cargo clippy --workspace --all-targets --features persistence -- -D warnings
5659
- name: Test
57-
run: cargo nextest run --workspace --all-targets --no-fail-fast
60+
run: cargo nextest run --workspace --all-targets --features persistence --no-fail-fast
5861
- name: Test Manual Registration / no-default-features
5962
run: cargo nextest run --workspace --tests --no-fail-fast --no-default-features --features macros
6063
- name: Test docs

Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ parking_lot = "0.12"
2323
portable-atomic = "1"
2424
rustc-hash = "2"
2525
smallvec = "1"
26+
thin-vec = { version = "0.2.14", features = ["serde"] }
2627
tracing = { version = "0.1", default-features = false, features = ["std"] }
2728

2829
# Automatic ingredient registration.
@@ -33,18 +34,23 @@ rayon = { version = "1.10.0", optional = true }
3334

3435
# Stuff we want Update impls for by default
3536
compact_str = { version = "0.9", optional = true }
36-
thin-vec = "0.2.14"
3737

3838
shuttle = { version = "0.8.1", optional = true }
3939

40+
# Persistent caching
41+
erased-serde = { version = "0.4.6", optional = true }
42+
serde = { version = "1.0.219", features = ["derive"], optional = true }
43+
4044
[features]
4145
default = ["salsa_unstable", "rayon", "macros", "inventory", "accumulator"]
4246
inventory = ["dep:inventory"]
47+
persistence = ["dep:serde", "dep:erased-serde", "salsa-macros/persistence"]
4348
shuttle = ["dep:shuttle"]
4449
accumulator = ["salsa-macro-rules/accumulator"]
50+
macros = ["dep:salsa-macros"]
51+
4552
# FIXME: remove `salsa_unstable` before 1.0.
4653
salsa_unstable = []
47-
macros = ["dep:salsa-macros"]
4854

4955
# This interlocks the `salsa-macros` and `salsa` versions together
5056
# preventing scenarios where they could diverge in a given project
@@ -68,6 +74,7 @@ expect-test = "1.5.1"
6874
rustversion = "1.0"
6975
test-log = { version = "0.2.18", features = ["trace"] }
7076
trybuild = "1.0"
77+
serde_json = "1.0.140"
7178

7279
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dev-dependencies]
7380
tikv-jemallocator = "0.6.0"

components/salsa-macro-rules/src/setup_input_struct.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ macro_rules! setup_input_struct {
5353
// The function used to implement `C::heap_size`.
5454
heap_size_fn: $($heap_size_fn:path)?,
5555

56+
// If `true`, `serialize_fn` and `deserialize_fn` have been provided.
57+
persist: $persist:tt,
58+
59+
// The path to the `serialize` function for the value's fields.
60+
serialize_fn: $($serialize_fn:path)?,
61+
62+
// The path to the `serialize` function for the value's fields.
63+
deserialize_fn: $($deserialize_fn:path)?,
64+
5665
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
5766
// We have the procedural macro generate names for those items that are
5867
// not used elsewhere in the user's code.
@@ -93,6 +102,9 @@ macro_rules! setup_input_struct {
93102
};
94103
const DEBUG_NAME: &'static str = stringify!($Struct);
95104
const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
105+
106+
const PERSIST: bool = $persist;
107+
96108
type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}};
97109

98110
type Struct = $Struct;
@@ -107,6 +119,32 @@ macro_rules! setup_input_struct {
107119
Some($heap_size_fn(value))
108120
}
109121
)?
122+
123+
fn serialize<S: $zalsa::serde::Serializer>(
124+
fields: &Self::Fields,
125+
serializer: S,
126+
) -> Result<S::Ok, S::Error> {
127+
$zalsa::macro_if! {
128+
if $persist {
129+
$($serialize_fn(fields, serializer))?
130+
} else {
131+
panic!("attempted to serialize value not marked with `persist` attribute")
132+
}
133+
}
134+
}
135+
136+
fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>(
137+
deserializer: D,
138+
) -> Result<Self::Fields, D::Error> {
139+
$zalsa::macro_if! {
140+
if $persist {
141+
$($deserialize_fn(deserializer))?
142+
} else {
143+
panic!("attempted to deserialize value not marked with `persist` attribute")
144+
}
145+
}
146+
}
147+
110148
}
111149

112150
impl $Configuration {
@@ -174,6 +212,13 @@ macro_rules! setup_input_struct {
174212
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
175213
}
176214

215+
fn entries(
216+
zalsa: &$zalsa::Zalsa
217+
) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
218+
let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
219+
<$Configuration>::ingredient_(zalsa).entries(zalsa).map(|(key, _)| key)
220+
}
221+
177222
#[inline]
178223
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
179224
if type_id == $zalsa::TypeId::of::<$Struct>() {
@@ -194,6 +239,26 @@ macro_rules! setup_input_struct {
194239
}
195240
}
196241

242+
$zalsa::macro_if! { $persist =>
243+
impl $zalsa::serde::Serialize for $Struct {
244+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
245+
where
246+
S: $zalsa::serde::Serializer,
247+
{
248+
$zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
249+
}
250+
}
251+
252+
impl<'de> $zalsa::serde::Deserialize<'de> for $Struct {
253+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254+
where
255+
D: $zalsa::serde::Deserializer<'de>,
256+
{
257+
let id = $zalsa::Id::deserialize(deserializer)?;
258+
Ok($zalsa::FromId::from_id(id))
259+
}
260+
}
261+
}
197262
impl $Struct {
198263
#[inline]
199264
pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self

components/salsa-macro-rules/src/setup_interned_struct.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ macro_rules! setup_interned_struct {
6969
// The function used to implement `C::heap_size`.
7070
heap_size_fn: $($heap_size_fn:path)?,
7171

72+
// If `true`, `serialize_fn` and `deserialize_fn` have been provided.
73+
persist: $persist:tt,
74+
75+
// The path to the `serialize` function for the value's fields.
76+
serialize_fn: $($serialize_fn:path)?,
77+
78+
// The path to the `serialize` function for the value's fields.
79+
deserialize_fn: $($deserialize_fn:path)?,
80+
7281
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
7382
// We have the procedural macro generate names for those items that are
7483
// not used elsewhere in the user's code.
@@ -144,9 +153,12 @@ macro_rules! setup_interned_struct {
144153
line: line!(),
145154
};
146155
const DEBUG_NAME: &'static str = stringify!($Struct);
156+
const PERSIST: bool = $persist;
157+
147158
$(
148159
const REVISIONS: ::core::num::NonZeroUsize = ::core::num::NonZeroUsize::new($revisions).unwrap();
149160
)?
161+
150162
type Fields<'a> = $StructDataIdent<'a>;
151163
type Struct<'db> = $Struct< $($db_lt_arg)? >;
152164

@@ -155,11 +167,35 @@ macro_rules! setup_interned_struct {
155167
Some($heap_size_fn(value))
156168
}
157169
)?
170+
171+
fn serialize<S: $zalsa::serde::Serializer>(
172+
fields: &Self::Fields<'_>,
173+
serializer: S,
174+
) -> Result<S::Ok, S::Error> {
175+
$zalsa::macro_if! {
176+
if $persist {
177+
$($serialize_fn(fields, serializer))?
178+
} else {
179+
panic!("attempted to serialize value not marked with `persist` attribute")
180+
}
181+
}
182+
}
183+
184+
fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>(
185+
deserializer: D,
186+
) -> Result<Self::Fields<'static>, D::Error> {
187+
$zalsa::macro_if! {
188+
if $persist {
189+
$($deserialize_fn(deserializer))?
190+
} else {
191+
panic!("attempted to deserialize value not marked with `persist` attribute")
192+
}
193+
}
194+
}
158195
}
159196

160197
impl $Configuration {
161-
pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self>
162-
{
198+
pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
163199
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
164200
$zalsa::IngredientCache::new();
165201

@@ -204,6 +240,13 @@ macro_rules! setup_interned_struct {
204240
aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
205241
}
206242

243+
fn entries(
244+
zalsa: &$zalsa::Zalsa
245+
) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
246+
let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
247+
<$Configuration>::ingredient(zalsa).entries(zalsa).map(|(key, _)| key)
248+
}
249+
207250
#[inline]
208251
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
209252
if type_id == $zalsa::TypeId::of::<$Struct>() {
@@ -224,6 +267,28 @@ macro_rules! setup_interned_struct {
224267
}
225268
}
226269

270+
$zalsa::macro_if! { $persist =>
271+
impl<$($db_lt_arg)?> $zalsa::serde::Serialize for $Struct<$($db_lt_arg)?> {
272+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
273+
where
274+
S: $zalsa::serde::Serializer,
275+
{
276+
$zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
277+
}
278+
}
279+
280+
impl<'de, $($db_lt_arg)?> $zalsa::serde::Deserialize<'de> for $Struct<$($db_lt_arg)?> {
281+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282+
where
283+
D: $zalsa::serde::Deserializer<'de>,
284+
{
285+
let id = $zalsa::Id::deserialize(deserializer)?;
286+
Ok($zalsa::FromId::from_id(id))
287+
}
288+
}
289+
}
290+
291+
227292
unsafe impl< $($db_lt_arg)? > $zalsa::Update for $Struct< $($db_lt_arg)? > {
228293
unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
229294
if unsafe { *old_pointer } != new_value {

0 commit comments

Comments
 (0)