pub struct SlotObservationTracker { /* private fields */ }Expand description
Tracks per-slot change frequency to drive intelligent purge decisions.
Before purging a slot, check should_refetch(). Slots that rarely change
are kept in the EVM cache, avoiding unnecessary RPC calls. On simulation
revert, take_skipped() returns all slots that were kept, allowing a
full-refresh fallback.
Implementations§
Source§impl SlotObservationTracker
impl SlotObservationTracker
Sourcepub fn load(path: &Path) -> Self
pub fn load(path: &Path) -> Self
Load persisted observations from disk (versioned binary format).
Returns a fresh tracker if the file doesn’t exist, has an unrecognized magic/version header, or can’t be decoded. Legacy unversioned bincode is treated as a cache miss.
Sourcepub fn save(&mut self, path: &Path) -> Result<(), PersistenceError>
pub fn save(&mut self, path: &Path) -> Result<(), PersistenceError>
Persist observations to disk using the versioned binary format. Called at end of cycle or on shutdown.
Sourcepub fn should_refetch(
&self,
addr: Address,
slot: U256,
now: u64,
params: &FreshnessParams,
) -> bool
pub fn should_refetch( &self, addr: Address, slot: U256, now: u64, params: &FreshnessParams, ) -> bool
Core decision: should we re-fetch this slot from RPC?
Returns true if the slot should be purged and re-fetched.
Returns false if the cached value is likely still valid.
now is the current clock value (block number or unix seconds) and
params carries the (clock-unit) thresholds — see
crate::freshness::FreshnessParams.
The heuristic is fully deterministic (no randomness): a never-observed slot
always refetches, as does one with fewer than
min_observations;
once enough observations accrue, a never-changed slot is reused until the
max_reuse window elapses,
while changing slots refetch once the probabilistic expected-change estimate
crosses staleness_threshold.
§Examples
The deterministic threshold edges around a stable (never-changed) slot:
use alloy_primitives::{Address, U256};
use evm_fork_cache::cache::SlotObservationTracker;
use evm_fork_cache::freshness::FreshnessParams;
let params = FreshnessParams::default();
let mut tracker = SlotObservationTracker::new();
let addr = Address::repeat_byte(0x01);
let slot = U256::from(0);
// An unobserved slot must always be fetched.
assert!(tracker.should_refetch(addr, slot, 0, ¶ms));
// Record fewer than `min_observations` of the same value: still refetches
// because there is not enough data to trust the change frequency.
for now in 0..(params.min_observations - 1) {
tracker.observe(addr, slot, U256::from(42), now as u64);
}
assert!(tracker.should_refetch(addr, slot, params.min_observations as u64, ¶ms));
// One more identical observation reaches `min_observations`; the slot has
// never changed, so within the reuse window it is now reused (no refetch).
let last = params.min_observations as u64 - 1;
tracker.observe(addr, slot, U256::from(42), last);
assert!(!tracker.should_refetch(addr, slot, last, ¶ms));Sourcepub fn observe(
&mut self,
addr: Address,
slot: U256,
value: U256,
now: u64,
) -> bool
pub fn observe( &mut self, addr: Address, slot: U256, value: U256, now: u64, ) -> bool
Record a fresh observation after re-fetch or injection.
now is the current clock value (block number or unix seconds).
Returns true if the value changed from the last observation.
Sourcepub fn record_skip(&mut self, addr: Address, slot: U256)
pub fn record_skip(&mut self, addr: Address, slot: U256)
Record that a slot was skipped (not purged) this cycle. Used for revert fallback: if simulation fails, these slots need re-fetching.
Sourcepub fn take_skipped(&mut self) -> Vec<(Address, U256)>
pub fn take_skipped(&mut self) -> Vec<(Address, U256)>
Take all slots skipped this cycle (for revert recovery). Clears the internal list.
Sourcepub fn reset_contract(&mut self, addr: Address)
pub fn reset_contract(&mut self, addr: Address)
Reset all observations for a contract address. Called after a simulation revert to force full refresh next cycle.
Sourcepub fn begin_cycle(&mut self)
pub fn begin_cycle(&mut self)
Clear cycle-specific state. Call at the start of each cycle.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for SlotObservationTracker
impl RefUnwindSafe for SlotObservationTracker
impl Send for SlotObservationTracker
impl Sync for SlotObservationTracker
impl Unpin for SlotObservationTracker
impl UnsafeUnpin for SlotObservationTracker
impl UnwindSafe for SlotObservationTracker
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.