@@ -25,8 +25,11 @@ use allocative::Allocative;
2525use dupe:: Dupe ;
2626use gazebo:: variants:: UnpackVariants ;
2727use itertools:: Itertools ;
28+ use pagable:: DataKey ;
2829use sorted_vector_map:: SortedVectorMap ;
2930
31+ use super :: types:: PagedOutMatch ;
32+ use super :: types:: PagedOutMismatch ;
3033use super :: types:: VersionedGraphResult ;
3134use crate :: HashSet ;
3235use crate :: api:: key:: InvalidationSourcePriority ;
@@ -162,7 +165,10 @@ impl VersionedGraphNode {
162165 VersionedGraphNode :: Occupied ( entry) if reusable. is_reusable ( & value, & deps, entry) => {
163166 debug ! ( "marking graph entry as unchanged" ) ;
164167 entry. mark_unchanged ( key. v , valid_deps_versions, invalidation_paths) ;
165- let ret = entry. computed_val ( key. v ) ;
168+ let ret = entry. computed_val (
169+ key. v ,
170+ "is_reusable returned true, which only happens for hydrated entries" ,
171+ ) ;
166172 return ( ret, false ) ;
167173 }
168174 VersionedGraphNode :: Occupied ( entry) => {
@@ -214,7 +220,10 @@ impl VersionedGraphNode {
214220 dirtied_history. clone ( ) ,
215221 invalidation_paths,
216222 ) ;
217- let ret = new. computed_val ( key. v ) ;
223+ let ret = new. computed_val (
224+ key. v ,
225+ "newly-constructed OccupiedGraphNode is always hydrated" ,
226+ ) ;
218227 * self = VersionedGraphNode :: Occupied ( new) ;
219228
220229 ( ret, true )
@@ -298,11 +307,52 @@ pub(crate) enum InvalidateResult<'a> {
298307 Changed ( Option < std:: vec:: Drain < ' a , DiceKey > > ) ,
299308}
300309
310+ /// The stored value for an `OccupiedGraphNode`. At least one of `value` (the
311+ /// in-memory hydrated form) and `data_key` (the on-disk content-addressable
312+ /// reference) must be set. When both are set, the value is resident in memory
313+ /// AND known to be persisted on disk, so the next page-out can skip
314+ /// re-serialization.
315+ #[ derive( Allocative , Debug ) ]
316+ pub ( crate ) struct PagableNodeValue {
317+ value : Option < DiceValidValue > ,
318+ data_key : Option < DataKey > ,
319+ }
320+
321+ impl PagableNodeValue {
322+ /// Constructs a hydrated-only value (no on-disk copy yet).
323+ pub ( crate ) fn hydrated ( value : DiceValidValue ) -> Self {
324+ Self {
325+ value : Some ( value) ,
326+ data_key : None ,
327+ }
328+ }
329+
330+ /// Returns the hydrated value, panicking with `msg` if the value is not currently
331+ /// resident in memory. `msg` should explain why the caller knows the value is
332+ /// hydrated (analogous to `Option::expect`).
333+ pub ( crate ) fn expect_hydrated ( & self , msg : & str ) -> & DiceValidValue {
334+ self . value . as_ref ( ) . unwrap_or_else ( || {
335+ panic ! (
336+ "PagableNodeValue::expect_hydrated called on a paged-out value: {}" ,
337+ msg
338+ )
339+ } )
340+ }
341+
342+ #[ expect(
343+ dead_code,
344+ reason = "used by the page-out flow; D101759759 will remove this suppression"
345+ ) ]
346+ pub ( crate ) fn as_hydrated ( & self ) -> Option < & DiceValidValue > {
347+ self . value . as_ref ( )
348+ }
349+ }
350+
301351/// The stored entry of the cache
302352#[ derive( Allocative , Debug ) ]
303353pub ( crate ) struct OccupiedGraphNode {
304354 key : DiceKey ,
305- res : DiceValidValue ,
355+ res : PagableNodeValue ,
306356 metadata : NodeMetadata ,
307357 invalidation_paths : TrackedInvalidationPaths ,
308358}
@@ -436,7 +486,7 @@ impl OccupiedGraphNode {
436486 ) -> Self {
437487 Self {
438488 key,
439- res,
489+ res : PagableNodeValue :: hydrated ( res ) ,
440490 metadata : NodeMetadata {
441491 deps,
442492 rdeps : LazyDepsSet :: new ( ) ,
@@ -467,13 +517,33 @@ impl OccupiedGraphNode {
467517 self . invalidation_paths . update ( new_invalidation_paths)
468518 }
469519
470- pub ( crate ) fn val ( & self ) -> & DiceValidValue {
520+ /// Returns this node's stored value (which may be hydrated or paged out).
521+ /// Callers that need a hydrated `DiceValidValue` should call `.expect_hydrated(msg)`
522+ /// with a message explaining why the caller knows the value is hydrated.
523+ pub ( crate ) fn val ( & self ) -> & PagableNodeValue {
471524 & self . res
472525 }
473526
474- pub ( crate ) fn computed_val ( & self , for_version : VersionNumber ) -> DiceComputedValue {
527+ /// Restores the in-memory hydrated value (typically after deserializing from
528+ /// disk). Keeps any existing `data_key` so the next page-out skips
529+ /// re-serialization.
530+ #[ expect(
531+ dead_code,
532+ reason = "used by the rehydrate request; D101759759 will remove this suppression"
533+ ) ]
534+ pub ( crate ) fn rehydrate ( & mut self , value : DiceValidValue ) {
535+ self . res . value = Some ( value) ;
536+ }
537+
538+ /// `expect_hydrated_msg` is forwarded to `expect_hydrated` and should explain why the
539+ /// caller knows the entry is hydrated.
540+ pub ( crate ) fn computed_val (
541+ & self ,
542+ for_version : VersionNumber ,
543+ expect_hydrated_msg : & str ,
544+ ) -> DiceComputedValue {
475545 DiceComputedValue :: new (
476- MaybeValidDiceValue :: valid ( self . res . dupe ( ) ) ,
546+ MaybeValidDiceValue :: valid ( self . val ( ) . expect_hydrated ( expect_hydrated_msg ) . dupe ( ) ) ,
477547 self . metadata . verified_ranges . dupe ( ) ,
478548 self . invalidation_paths . at_version ( for_version) ,
479549 )
@@ -487,13 +557,20 @@ impl OccupiedGraphNode {
487557 ) -> InvalidateResult < ' _ > {
488558 // TODO(cjhopman): accepting injections only for InjectedKey would make the VersionedGraph simpler. Currently, this is used
489559 // for "mocking" dice keys in tests via DiceBuilder::mock_and_return().
490- if self . val ( ) . equality ( & value) {
560+ if self
561+ . val ( )
562+ . expect_hydrated (
563+ "on_injected does not yet handle paged-out entries; this is a known \
564+ footgun fixed in a follow-up commit",
565+ )
566+ . equality ( & value)
567+ {
491568 // TODO(cjhopman): This is wrong. The node could currently be in a dirtied state and we
492569 // aren't recording that the value is verified at this version.
493570 return InvalidateResult :: NoChange ;
494571 }
495572
496- self . res = value;
573+ self . res = PagableNodeValue :: hydrated ( value) ;
497574 self . metadata . deps = Arc :: new ( SeriesParallelDeps :: None ) ;
498575 self . metadata . verified_ranges = Arc :: new ( VersionRange :: begins_with ( version) . into_ranges ( ) ) ;
499576 self . invalidation_paths
@@ -508,19 +585,43 @@ impl OccupiedGraphNode {
508585
509586 fn at_version ( & self , v : VersionNumber ) -> VersionedGraphResult {
510587 match self . metadata . verified_ranges . find_value_upper_bound ( v) {
511- Some ( found) if found == v => VersionedGraphResult :: Match ( self . computed_val ( v) ) ,
588+ Some ( found) if found == v => {
589+ if self . res . value . is_some ( ) {
590+ VersionedGraphResult :: Match (
591+ self . computed_val ( v, "the Hydrated branch checked value.is_some()" ) ,
592+ )
593+ } else {
594+ VersionedGraphResult :: MatchPagedOut ( PagedOutMatch {
595+ data_key : self . res . data_key . expect (
596+ "PagableNodeValue invariant: at least one of value or data_key is set" ,
597+ ) ,
598+ valid : self . metadata . verified_ranges . dupe ( ) ,
599+ invalidation_paths : self . invalidation_paths . at_version ( v) ,
600+ } )
601+ }
602+ }
512603 Some ( prev_verified_version) => {
513604 if self
514605 . metadata
515606 . dirtied_history
516607 . restricted_range ( v)
517608 . contains ( & prev_verified_version)
518609 {
519- VersionedGraphResult :: CheckDeps ( VersionedGraphResultMismatch {
520- entry : self . val ( ) . dupe ( ) ,
521- prev_verified_version,
522- deps_to_validate : self . metadata . deps . dupe ( ) ,
523- } )
610+ if let Some ( value) = & self . res . value {
611+ VersionedGraphResult :: CheckDeps ( VersionedGraphResultMismatch {
612+ entry : value. dupe ( ) ,
613+ prev_verified_version,
614+ deps_to_validate : self . metadata . deps . dupe ( ) ,
615+ } )
616+ } else {
617+ VersionedGraphResult :: CheckDepsPagedOut ( PagedOutMismatch {
618+ data_key : self . res . data_key . expect (
619+ "PagableNodeValue invariant: at least one of value or data_key is set" ,
620+ ) ,
621+ prev_verified_version,
622+ deps_to_validate : self . metadata . deps . dupe ( ) ,
623+ } )
624+ }
524625 } else {
525626 VersionedGraphResult :: Compute
526627 }
0 commit comments