diff --git a/components/salsa-2022/src/database.rs b/components/salsa-2022/src/database.rs index c6077f45..15104844 100644 --- a/components/salsa-2022/src/database.rs +++ b/components/salsa-2022/src/database.rs @@ -7,8 +7,8 @@ pub trait Database: HasJarsDyn + AsSalsaDatabase { /// /// By default, the event is logged at level debug using /// the standard `log` facade. - fn salsa_event(&self, event_fn: Event) { - log::debug!("salsa_event: {:?}", event_fn.debug(self)); + fn salsa_event(&self, event: Event) { + log::debug!("salsa_event: {:?}", event.debug(self)); } fn salsa_runtime(&self) -> &Runtime; diff --git a/components/salsa-2022/src/event.rs b/components/salsa-2022/src/event.rs index c328df97..e35bfd43 100644 --- a/components/salsa-2022/src/event.rs +++ b/components/salsa-2022/src/event.rs @@ -1,4 +1,6 @@ -use crate::{debug::DebugWithDb, runtime::RuntimeId, Database, DatabaseKeyIndex}; +use crate::{ + debug::DebugWithDb, key::DependencyIndex, runtime::RuntimeId, Database, DatabaseKeyIndex, +}; use std::fmt; /// The `Event` struct identifies various notable things that can @@ -74,6 +76,15 @@ pub enum EventKind { /// Indicates that `unwind_if_cancelled` was called and salsa will check if /// the current revision has been cancelled. WillCheckCancellation, + + /// Discovered that a query used to output a given output but no longer does. + WillDiscardStaleOutput { + /// Key for the query that is executing and which no longer outputs the given value. + execute_key: DatabaseKeyIndex, + + /// Key for the query that is no longer output + output_key: DependencyIndex, + }, } impl fmt::Debug for EventKind { @@ -96,6 +107,14 @@ impl fmt::Debug for EventKind { .field("database_key", database_key) .finish(), EventKind::WillCheckCancellation => fmt.debug_struct("WillCheckCancellation").finish(), + EventKind::WillDiscardStaleOutput { + execute_key, + output_key, + } => fmt + .debug_struct("WillDiscardStaleOutput") + .field("execute_key", &execute_key) + .field("output_key", &output_key) + .finish(), } } } @@ -123,6 +142,14 @@ where .field("database_key", &database_key.debug(db)) .finish(), EventKind::WillCheckCancellation => fmt.debug_struct("WillCheckCancellation").finish(), + EventKind::WillDiscardStaleOutput { + execute_key, + output_key, + } => fmt + .debug_struct("WillDiscardStaleOutput") + .field("execute_key", &execute_key.debug(db)) + .field("output_key", &output_key.debug(db)) + .finish(), } } } diff --git a/components/salsa-2022/src/function.rs b/components/salsa-2022/src/function.rs index 1ac8793a..8d543fbb 100644 --- a/components/salsa-2022/src/function.rs +++ b/components/salsa-2022/src/function.rs @@ -17,6 +17,7 @@ use super::{ingredient::Ingredient, routes::IngredientIndex, AsId}; mod accumulated; mod backdate; +mod diff_outputs; mod execute; mod fetch; mod inputs; diff --git a/components/salsa-2022/src/function/diff_outputs.rs b/components/salsa-2022/src/function/diff_outputs.rs new file mode 100644 index 00000000..24398746 --- /dev/null +++ b/components/salsa-2022/src/function/diff_outputs.rs @@ -0,0 +1,63 @@ +use crate::{ + key::DependencyIndex, runtime::local_state::QueryRevisions, Database, DatabaseKeyIndex, Event, + EventKind, +}; + +use super::{memo::Memo, Configuration, DynDb, FunctionIngredient}; + +impl FunctionIngredient +where + C: Configuration, +{ + /// Compute the old and new outputs and invoke the `clear_stale_output` callback + /// for each output that was generated before but is not generated now. + pub(super) fn diff_outputs( + &self, + db: &DynDb<'_, C>, + key: DatabaseKeyIndex, + old_memo: &Memo, + revisions: &QueryRevisions, + ) { + let mut old_outputs = old_memo + .revisions + .edges + .outputs() + .iter() + .copied() + .peekable(); + let mut new_outputs = revisions.edges.outputs().iter().copied().peekable(); + + // two list are in sorted order, we can merge them in linear time. + while let (Some(&old_output), Some(&new_output)) = (old_outputs.peek(), new_outputs.peek()) + { + if old_output < new_output { + // Output that was generated but is no longer. + Self::report_stale_output(db, key, old_output); + old_outputs.next(); + } else if new_output < old_output { + // This is a new output that was not generated before. + // No action needed. + new_outputs.next(); + } else { + // Output generated both times. + old_outputs.next(); + new_outputs.next(); + } + } + + for old_output in old_outputs { + Self::report_stale_output(db, key, old_output); + } + } + + fn report_stale_output(db: &DynDb<'_, C>, key: DatabaseKeyIndex, output: DependencyIndex) { + let runtime_id = db.salsa_runtime().id(); + db.salsa_event(Event { + runtime_id, + kind: EventKind::WillDiscardStaleOutput { + execute_key: key, + output_key: output, + }, + }); + } +} diff --git a/components/salsa-2022/src/function/execute.rs b/components/salsa-2022/src/function/execute.rs index 12a45ecb..51e3867e 100644 --- a/components/salsa-2022/src/function/execute.rs +++ b/components/salsa-2022/src/function/execute.rs @@ -87,6 +87,7 @@ where // old value. if let Some(old_memo) = &opt_old_memo { self.backdate_if_appropriate(old_memo, &mut revisions, &value); + self.diff_outputs(db, database_key_index, &old_memo, &revisions); } let value = self diff --git a/components/salsa-2022/src/function/specify.rs b/components/salsa-2022/src/function/specify.rs index 6c879707..49e9fd1b 100644 --- a/components/salsa-2022/src/function/specify.rs +++ b/components/salsa-2022/src/function/specify.rs @@ -26,8 +26,8 @@ where None => panic!("can only use `set` with an active query"), }; - let entity_index = key.database_key_index(db); - if !runtime.is_output_of_active_query(entity_index) { + let database_key_index = key.database_key_index(db); + if !runtime.is_output_of_active_query(database_key_index) { panic!("can only use `set` on entities created during current query"); } @@ -64,6 +64,7 @@ where if let Some(old_memo) = self.memo_map.get(key) { self.backdate_if_appropriate(&old_memo, &mut revisions, &value); + self.diff_outputs(db, database_key_index, &old_memo, &revisions); } let memo = Memo { diff --git a/salsa-2022-tests/tests/specify_tracked_fn_in_rev_1_but_not_2.rs b/salsa-2022-tests/tests/specify_tracked_fn_in_rev_1_but_not_2.rs index 0a8e9138..c955b9d8 100644 --- a/salsa-2022-tests/tests/specify_tracked_fn_in_rev_1_but_not_2.rs +++ b/salsa-2022-tests/tests/specify_tracked_fn_in_rev_1_but_not_2.rs @@ -2,6 +2,7 @@ //! compiles and executes successfully. use expect_test::expect; +use salsa::DebugWithDb; use salsa_2022_tests::{HasLogger, Logger}; use test_log::test; @@ -74,6 +75,10 @@ impl salsa::Database for Database { fn salsa_runtime(&self) -> &salsa::Runtime { self.storage.runtime() } + + fn salsa_event(&self, event: salsa::Event) { + self.push_log(format!("{:?}", event.debug(self))); + } } impl Db for Database {} @@ -92,9 +97,17 @@ fn test_run_0() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); } @@ -106,9 +119,17 @@ fn test_run_5() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); } @@ -120,10 +141,21 @@ fn test_run_10() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", "maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); } @@ -135,10 +167,21 @@ fn test_run_20() { assert_eq!(final_result(&db, input), 200); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", "maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); } @@ -150,9 +193,17 @@ fn test_run_0_then_5_then_20() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); // FIXME: read_maybe_specified should not re-execute @@ -160,16 +211,35 @@ fn test_run_0_then_5_then_20() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 2 }) } } }", "final_result(MyInput(Id { value: 2 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 2 }) } } }", "create_tracked(MyInput(Id { value: 2 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 2 }) } } }", "read_maybe_specified(MyTracked(Id { value: 2 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); input.set_field(&mut db, 20); assert_eq!(final_result(&db, input), 100); // FIXME: Should be 20. db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 2 }) } } }", "create_tracked(MyInput(Id { value: 2 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 2 }) }, output_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 2 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 2 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 2 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 2 }) } } }", ]"#]]); // FIXME: should invoke maybe_specified } @@ -181,41 +251,71 @@ fn test_run_0_then_5_then_10_then_20() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); // FIXME: `read_maybe_specified` should not re-execute - let input = MyInput::new(&mut db, 5); + input.set_field(&mut db, 5); assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ - "final_result(MyInput(Id { value: 2 }))", - "create_tracked(MyInput(Id { value: 2 }))", - "read_maybe_specified(MyTracked(Id { value: 2 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", + "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", ]"#]]); // FIXME: should execute `maybe_specified` but not `read_maybe_specified` - let input = MyInput::new(&mut db, 10); + input.set_field(&mut db, 10); assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ - "final_result(MyInput(Id { value: 3 }))", - "create_tracked(MyInput(Id { value: 3 }))", - "read_maybe_specified(MyTracked(Id { value: 3 }))", - "maybe_specified(MyTracked(Id { value: 3 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", + "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) }, output_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", ]"#]]); // FIXME: should execute `maybe_specified` but not `read_maybe_specified` input.set_field(&mut db, 20); - assert_eq!(final_result(&db, input), 200); + assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ - "create_tracked(MyInput(Id { value: 3 }))", - "maybe_specified(MyTracked(Id { value: 3 }))", - "read_maybe_specified(MyTracked(Id { value: 3 }))", - "final_result(MyInput(Id { value: 3 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", + "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", ]"#]]); } @@ -227,15 +327,34 @@ fn test_run_5_then_20() { assert_eq!(final_result(&db, input), 100); db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", "final_result(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", "read_maybe_specified(MyTracked(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", ]"#]]); input.set_field(&mut db, 20); assert_eq!(final_result(&db, input), 100); // FIXME: Should be 20. db.assert_logs(expect![[r#" [ + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) } } }", "create_tracked(MyInput(Id { value: 1 }))", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: DependencyIndex { ingredient_index: IngredientIndex(6), key_index: Some(Id { value: 1 }) }, output_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(4), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(5), key_index: Some(Id { value: 1 }) } } }", + "Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: DependencyIndex { ingredient_index: IngredientIndex(7), key_index: Some(Id { value: 1 }) } } }", ]"#]]); // FIXME: should invoke maybe_specified }