Skip to main content

DataContractCache

Struct DataContractCache 

Source
pub struct DataContractCache { /* private fields */ }
Expand description

DataContract cache that handles both global and block data.

Two kinds of reader share this cache, and neither may be served the other’s view of a contract:

  • Block execution reads through the block transaction, on the single consensus thread. Its reads and rewrites land in the block cache, which is cleared when a block starts (Self::clear_block_cache) and promoted into the global cache once the block is committed (Self::merge_and_clear_block_cache).
  • Committed-state readers (the query threads) read with no transaction, concurrently with block execution, and populate the global cache with what they read. check_tx threads read through a fresh transaction of their own and never write to the cache.

CONSENSUS-CRITICAL. Block execution serializes documents and validates transitions against whatever contract definition this cache hands it. If two honest validators resolve the same contract differently while executing the same block, they write different bytes and produce different app hashes. Two mechanisms keep a transactional read from ever resolving to a definition older than the one its transaction holds:

  • Self::mark_modified_in_block: once the block transaction rewrites a contract, a transactional read of it never falls back to the global cache. The global copy is committed state, which a committed-state reader may legitimately have (re)inserted after the rewrite, and which the transaction has moved past. A block-cache miss for such a contract goes to state through the transaction instead.
  • CommittedGeneration: a committed-state copy enters the global cache only if no block was committed between the read that produced it and the insert. Without this, a query thread that read a contract, was descheduled across the block’s commit and promotion, and inserted afterwards would clobber the promoted definition with the pre-block one.

Implementations§

Source§

impl DataContractCache

Source

pub fn new( global_cache_max_capacity: u64, block_cache_max_capacity: u64, ) -> Self

Create a new DataContract cache instance

Source

pub fn committed_generation(&self) -> CommittedGeneration

The snapshot a committed-state reader must take before reading state, to pass to Self::insert_committed with what it read.

Source

pub fn insert_block(&self, fetch_info: Arc<DataContractFetchInfo>)

Inserts a contract read or rewritten through the block transaction into the block cache.

Only the consensus thread writes the block cache, so its inserts are sequential; the insert is nevertheless skipped if the block cache already holds the same contract at a strictly higher version, as defense in depth. Contract versions increase strictly monotonically (the update transition enforces new == old + 1, token configuration updates and system contract migrations bump the version), so a lower version is never fresh information. Same-version inserts overwrite: re-inserting an identical contract with a freshly calculated fee is the normal cache-hit fee path.

Source

pub fn insert_committed( &self, fetch_info: Arc<DataContractFetchInfo>, observed: CommittedGeneration, )

Inserts a contract read from committed state into the global cache, unless a block was committed since observed was taken.

The generation is compared inside moka’s per-key compute closure, which runs and applies its write under the key-level lock, and every write that must be ordered against this insert (Self::merge_and_clear_block_cache, Self::replace_committed) goes through that same lock. A promotion therefore either runs before this insert reaches the lock, in which case the insert sees the new generation and is dropped, or after it, in which case the promotion overwrites what was inserted. There is no interleaving in which an insert approved under the old generation lands after the promotion. The same-contract version guard of Self::insert_block applies as well.

Source

pub fn insert_rewritten( &self, fetch_info: Arc<DataContractFetchInfo>, in_block_transaction: bool, )

Seeds the copy a rewrite of the contract in state produced.

When the rewrite went through the block transaction (in_block_transaction), the contract is marked as modified in the block and the copy goes to the block cache, see Self::mark_modified_in_block. Otherwise the rewrite is committed already and the copy replaces the global entry through Self::replace_committed.

Source

pub fn replace_committed( &self, contract_id: [u8; 32], fetch_info: Option<Arc<DataContractFetchInfo>>, )

Records a rewrite of the contract in committed state, made outside any block transaction, and replaces the global entry with fetch_info, or removes it when None.

Such a rewrite is a commit like any other: a committed-state reader that took its snapshot before it may hold the pre-write copy. The generation is advanced first, so that reader inserts nothing, and the replacement goes through the per-key compute lock that Self::insert_committed evaluates its check under, so a reader whose insert was already approved lands it before, not after, the replacement. Block execution never takes this path; it exists for direct callers that write outside a block.

Source

pub fn get( &self, contract_id: [u8; 32], is_block_cache: bool, ) -> Option<Arc<DataContractFetchInfo>>

Tries to get a data contract from the block cache if the read is transactional, then from the global cache.

A transactional read of a contract the block transaction rewrote (Self::mark_modified_in_block) does not fall back to the global cache: on a block-cache miss it returns None, and the caller reads state through the transaction. A read with no transaction only consults the global cache.

Source

pub fn remove(&self, contract_id: [u8; 32])

Remove contract from both block and global cache

Source

pub fn mark_modified_in_block(&self, contract_id: [u8; 32])

Records that the block transaction rewrote contract_id.

From now until the block cache is cleared or promoted, a transactional read of this contract is served from the block cache or from state through the transaction, never from the global cache. Callers seed the post-write copy with Self::insert_block so that the next read is a hit; the mark is what makes an eviction or a rollback safe.

Source

pub fn is_modified_in_block(&self, contract_id: [u8; 32]) -> bool

Whether the block transaction rewrote contract_id since the block cache was cleared.

Source

pub fn drop_block_modified_entries(&self)

Drops from the block cache every contract the block transaction rewrote, keeping the contracts marked as modified.

For after a savepoint rollback: the rollback reverted the rewrites in state, but the block cache still holds the post-write copies seeded when they were applied. Dropping them makes the next transactional read go to state, which now holds whatever the rollback restored. Entries the block only read stay: nothing rewrote what they hold.

Source

pub fn merge_and_clear_block_cache(&self)

Promotes the block cache into the global cache and clears it.

Call this once the block transaction is committed, never before: from this call on, committed-state readers are served the block’s definitions, and a reader that took its CommittedGeneration snapshot before this call can no longer insert. Both are only correct once state itself holds what the block wrote.

Promotion is unconditional: everything the block read or rewrote through its transaction is committed state now, and nothing else could have written state in the meantime. A rewritten contract that is no longer in the block cache (evicted, or dropped by a rollback and not read again) is removed from the global cache instead, because a committed-state reader may have inserted the pre-block definition there while the block was executing.

Every global-cache write here goes through moka’s per-key compute lock, the lock Self::insert_committed evaluates its generation check under. A reader whose closure already approved its insert holds that lock until the insert is applied, so the promotion waits for it and then overwrites (or removes) it; a reader that reaches the lock afterwards sees the new generation and inserts nothing. moka’s plain insert and remove do not take that lock and would let an approved insert of the pre-block definition land after the promotion.

Source

pub fn clear_block_cache(&self)

Clears the block cache and the record of what the block transaction rewrote.

For the start of a block: a fresh transaction sees exactly committed state, which is what the global cache mirrors, so nothing needs protecting yet.

Source

pub fn clear(&self)

Drops every cached entry from both caches.

The record of what the block transaction rewrote is kept: it is not a cache, and a clear in the middle of a block (a migration that rewrote contracts in state and wants every reader to reload them) must not let transactional reads fall back to the global cache again.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Conv for T

§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
§

impl<T> CostsExt for T

§

fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>
where Self: Sized,

Wraps any value into a CostContext object with provided costs.
§

fn wrap_fn_cost( self, f: impl FnOnce(&Self) -> OperationCost, ) -> CostContext<Self>
where Self: Sized,

Wraps any value into CostContext object with costs computed using the value getting wrapped.
§

impl<T> FmtForward for T

§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
§

impl<T, U> IntoOnNetwork<U> for T
where U: FromOnNetwork<T>,

§

fn into_on_network(self, network: Network) -> U

Calls U::from_on_network(self).

§

impl<T, U> IntoPlatformVersioned<U> for T
where U: FromPlatformVersioned<T>,

§

fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U

Performs the conversion.
§

impl<T> Pipe for T
where T: ?Sized,

§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> Tap for T

§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
§

impl<T> TryConv for T

§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryFromVersioned<U> for T
where T: TryFrom<U>,

§

type Error = <T as TryFrom<U>>::Error

The type returned in the event of a conversion error.
§

fn try_from_versioned( value: U, _grove_version: &GroveVersion, ) -> Result<T, <T as TryFromVersioned<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T, U> TryIntoPlatformVersioned<U> for T
where U: TryFromPlatformVersioned<T>,

§

type Error = <U as TryFromPlatformVersioned<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into_platform_versioned( self, platform_version: &PlatformVersion, ) -> Result<U, <U as TryFromPlatformVersioned<T>>::Error>

Performs the conversion.
§

impl<T, U> TryIntoVersioned<U> for T
where U: TryFromVersioned<T>,

§

type Error = <U as TryFromVersioned<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into_versioned( self, grove_version: &GroveVersion, ) -> Result<U, <U as TryFromVersioned<T>>::Error>

Performs the conversion.
§

impl<T, U> TryIntoWithBlockHashLookup<U> for T
where U: TryFromWithBlockHashLookup<T>,

§

type Error = <U as TryFromWithBlockHashLookup<T>>::Error

§

fn try_into_with_block_hash_lookup<F>( self, block_hash_lookup: F, network: Network, ) -> Result<U, <T as TryIntoWithBlockHashLookup<U>>::Error>
where F: Fn(&BlockHash) -> Option<u32>,

Converts self into T, using a block hash lookup function.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more