repository.rs
Overview
This file implements the management and lifecycle of BlockState instances through the BlockStateRepository. It provides facilities for loading, caching, saving, and accessing block state data identified by BlockIdentifier. The key responsibility is to maintain a weakly referenced cache of block states, preventing duplication and enabling efficient state sharing across the system. The file also defines the BlockState wrapper struct that encapsulates shared ownership and deferred saving logic for individual block states.
Core Structures
BlockState
A cloneable handle representing the state of a particular block in the system.
Fields:
block_identifier: BlockIdentifier
A duplicate of the identifier for the block this state corresponds to. Maintained for historic reasons to simplify the API.inner: Arc<BlockStateInner>
Shared ownership pointer to the inner block state data.save_sender: Arc<InstrumentedSender<Arc<BlockStateInner>>>
An instrumented channel sender used to asynchronously queue the block state for saving when dropped.
Key Methods:
block_identifier(&self) -> &BlockIdentifier
Returns a reference to the block identifier for this state.
Trait Implementations:
Drop
On dropping the last strong reference to the inner state, schedules it for saving by sending it throughsave_sender.Debug
Provides a debug representation showing the block identifier.Deref<Target=Arc>
Allows transparent access to the inner state via dereferencing.PartialEq, Eq,Hash
Equality and hashing are based on theblock_identifierfield.
Usage Example:
let block_state = repository.get(&block_id)?; println!("BlockState ID: {:?}", block_state.block_identifier()); // Access inner state via deref let inner = &*block_state;
BlockStateRepository
Manages a collection of BlockState objects and provides access, caching, and persistence support.
Fields:
block_state_repo_data_dir: PathBuf
Filesystem directory path where block state data files are stored.map: Arc<RwLock<WeakValueHashMap<BlockIdentifier, Weak<BlockStateInner>>>>
Thread-safe weak-reference map caching block states by their identifier.notifications: Notification
Notification mechanism to track and propagate updates related to block states.save_service_sender: Arc<InstrumentedSender<Arc<BlockStateInner>>>
Sender for dispatching block state data to the save service asynchronously.
Key Methods:
new(block_state_repo_data_dir: PathBuf, save_service_sender: Arc<InstrumentedSender<Arc<BlockStateInner>>>) -> Self
Creates a new repository instance with a specific directory and save service sender.get(&self, block_identifier: &BlockIdentifier) -> anyhow::Result<BlockState>
Retrieves aBlockStatefor the given identifier.Checks the weak cache first; if found, returns the cached state.
Otherwise, attempts to load the state from disk; if loading fails or no saved state exists, initializes a new
AckiNackiBlockState.Inserts the newly loaded or created state into the cache before returning.
notifications(&self) -> &Notification
Provides access to the notification system for subscribing to block state changes.touch(&mut self)
Updates the internal notification timestamp to signal activity.
Testing Utilities:
test(block_state_repo_data_dir: PathBuf) -> Self
Creates a test instance of the repository with an in-memory save service and spawn of a state save service thread.
Usage Example:
let repository = BlockStateRepository::new(data_dir, save_sender); let block_state = repository.get(&block_id)?; block_state.guarded(|state| { // Access or mutate the block state safely here });
Important Implementation Details
WeakValueHashMap for Caching:
The repository uses aWeakValueHashMapto store weak references toBlockStateInnerinstances keyed by theirBlockIdentifier. This allows automatic cleanup of states that are no longer strongly referenced elsewhere, preventing memory leaks and stale cache entries.Deferred Saving on Drop:
TheBlockStatestruct implements aDropmethod that sends the internal state to a save service via an instrumented channel when the last reference is dropped. This ensures that changes are eventually persisted without requiring explicit save calls.Concurrent Access and Synchronization:
The inner block state data (BlockStateInner) is wrapped in anArc<RwLock<_>>allowing multiple readers or a single writer, supporting concurrent safe access and mutation. The repository's cache map is also protected by anRwLock.Notification System Integration:
The repository holds aNotificationinstance used for signaling changes or subscriptions to block state events. NewAckiNackiBlockStateinstances subscribe to this notification system.File Loading and Persistence:
Block states are loaded from and saved to files named by the hexadecimal representation of theirBlockIdentifier. The loading is done outside of write locks to minimize contention.
Interaction With Other Modules
BlockStateInner(block_state_inner.rs):
Contains the detailed implementation of the block state data, including internal synchronization and state mutations.AckiNackiBlockState(state.rs):
Provides the actual state logic and data structures used inside each block state, including finalized flags and attestation interests.Notification(types::notification):
Used to propagate events and updates related to block states.InstrumentedSenderand MPSC Channels (telemetry_utils::mpsc):
Used for asynchronous communication with the state save service.State Persistence (
super::private::load_state):
Responsible for loading saved block states from disk by file path.Testing Utilities:
Includes test-only helpers to create test repositories and metrics, and to spawn a background service for state saving.
Diagram: Structure of repository.rs
classDiagram
class BlockState {
+block_identifier: BlockIdentifier
+inner: Arc<BlockStateInner>
+save_sender: Arc<InstrumentedSender>
+block_identifier()
+drop()
}
class BlockStateRepository {
+block_state_repo_data_dir: PathBuf
+map: Arc<RwLock<WeakValueHashMap<BlockIdentifier, Weak<BlockStateInner>>>>
+notifications: Notification
+save_service_sender: Arc<InstrumentedSender>
+new()
+get()
+notifications()
+touch()
}
BlockStateRepository "1" *-- "*" BlockState : manages >
BlockState "1" *-- "1" BlockStateInner : owns >
BlockStateRepository "1" o-- "1" Notification : holds >
BlockStateRepository "1" o-- "1" InstrumentedSender : holds >
BlockState "1" o-- "1" InstrumentedSender : holds >
This diagram shows the main entities and their relationships: the BlockStateRepository manages multiple BlockState instances, each wrapping a shared BlockStateInner. Both BlockState and BlockStateRepository hold references to a shared InstrumentedSender for saving state asynchronously. The repository also maintains a notification mechanism.
Note: For further details on the BlockIdentifier type and state internals, see the BlockIdentifier and BlockStateInner topics. For the notification mechanism, refer to Notification. The asynchronous saving mechanism is detailed under [InstrumentedSender and MPSC Channels](/instrumented-sender).