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_txthreads 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
impl DataContractCache
Sourcepub fn new(
global_cache_max_capacity: u64,
block_cache_max_capacity: u64,
) -> Self
pub fn new( global_cache_max_capacity: u64, block_cache_max_capacity: u64, ) -> Self
Create a new DataContract cache instance
Sourcepub fn committed_generation(&self) -> CommittedGeneration
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.
Sourcepub fn insert_block(&self, fetch_info: Arc<DataContractFetchInfo>)
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.
Sourcepub fn insert_committed(
&self,
fetch_info: Arc<DataContractFetchInfo>,
observed: CommittedGeneration,
)
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.
Sourcepub fn insert_rewritten(
&self,
fetch_info: Arc<DataContractFetchInfo>,
in_block_transaction: bool,
)
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.
Sourcepub fn replace_committed(
&self,
contract_id: [u8; 32],
fetch_info: Option<Arc<DataContractFetchInfo>>,
)
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.
Sourcepub fn get(
&self,
contract_id: [u8; 32],
is_block_cache: bool,
) -> Option<Arc<DataContractFetchInfo>>
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.
Sourcepub fn mark_modified_in_block(&self, contract_id: [u8; 32])
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.
Sourcepub fn is_modified_in_block(&self, contract_id: [u8; 32]) -> bool
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.
Sourcepub fn drop_block_modified_entries(&self)
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.
Sourcepub fn merge_and_clear_block_cache(&self)
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.
Sourcepub fn clear_block_cache(&self)
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.
Sourcepub fn clear(&self)
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§
impl !Freeze for DataContractCache
impl !RefUnwindSafe for DataContractCache
impl !UnwindSafe for DataContractCache
impl Send for DataContractCache
impl Sync for DataContractCache
impl Unpin for DataContractCache
impl UnsafeUnpin for DataContractCache
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
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> CostsExt for T
impl<T> CostsExt for T
§fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>where
Self: Sized,
fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>where
Self: Sized,
CostContext object with provided costs.§fn wrap_fn_cost(
self,
f: impl FnOnce(&Self) -> OperationCost,
) -> CostContext<Self>where
Self: Sized,
fn wrap_fn_cost(
self,
f: impl FnOnce(&Self) -> OperationCost,
) -> CostContext<Self>where
Self: Sized,
CostContext object with costs computed using the
value getting wrapped.§impl<T> FmtForward for T
impl<T> FmtForward for T
§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.§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.§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.§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.§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.§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.§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.§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.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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 more§impl<T, U> IntoOnNetwork<U> for Twhere
U: FromOnNetwork<T>,
impl<T, U> IntoOnNetwork<U> for Twhere
U: FromOnNetwork<T>,
§fn into_on_network(self, network: Network) -> U
fn into_on_network(self, network: Network) -> U
Calls U::from_on_network(self).
§impl<T, U> IntoPlatformVersioned<U> for Twhere
U: FromPlatformVersioned<T>,
impl<T, U> IntoPlatformVersioned<U> for Twhere
U: FromPlatformVersioned<T>,
§fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U
fn into_platform_versioned(self, platform_version: &PlatformVersion) -> U
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§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 more§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 more§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
§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
§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.§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.§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.§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§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 more§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 more§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 more§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 more§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 more§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 more§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.§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.§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.§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.§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.§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.§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.