file_saving_service.rs
Overview
The file_saving_service.rs file implements the FileSavingService struct and its associated functionality. This service is responsible for asynchronously saving the state of blockchain-related data to the file system. Its primary role is to serialize an optimistic state snapshot along with other relevant block and repository data into a binary format and persist it to a file on disk. This allows for durable storage of intermediate blockchain state data, facilitating recovery, synchronization, or diagnostic workflows.
The file leverages multi-threading to offload the potentially blocking I/O operations from the main execution thread, using Rust's standard threading primitives alongside concurrency utilities like parking_lot::Mutex for thread-safe access to shared resources.
Struct: FileSavingService
#[derive(Clone, TypedBuilder)]
pub struct FileSavingService {
root_path: PathBuf,
threads: Arc<Mutex<Vec<JoinHandle<anyhow::Result<()>>>>>,
repository: RepositoryImpl,
block_state_repository: BlockStateRepository,
shared_services: SharedServices,
message_db: MessageDurableStorage,
}
Description
FileSavingService encapsulates all dependencies and state required to perform state saving operations. It maintains:
root_path: The base directory path where files will be saved.threads: A thread-safe vector of join handles for all currently active saving threads.repository: An instance to interact with the blockchain repository layer.block_state_repository: Repository access for blockchain block state data.shared_services: Shared services needed for cross-thread reference data retrieval.message_db: Durable storage for blockchain messages.
The struct is cloneable and uses the TypedBuilder pattern for ergonomic construction.
Method: save_object
pub fn save_object(
&self,
state: Arc<OptimisticStateImpl>,
path: PathBuf,
) -> anyhow::Result<()>
Purpose
This method initiates the process of saving a snapshot of the optimistic blockchain state, along with related metadata and repository data, to disk asynchronously in a separate thread.
Parameters
state: AnArc-wrapped optimistic blockchain state snapshot (OptimisticStateImpl) representing the state to persist.path: A relativePathBufrepresenting the file path (relative toroot_path) where the serialized state will be saved.
Returns
anyhow::Result<()>: ReturnsOk(())if the save operation is successfully started (note: completion happens asynchronously), otherwise returns an error if thread spawning or initial setup fails.
Usage Example
let file_saving_service = FileSavingService::builder()
.root_path(root_dir)
.repository(repo_impl)
.block_state_repository(block_repo)
.shared_services(shared)
.message_db(message_storage)
.build();
let optimistic_state = Arc::new(current_state);
file_saving_service.save_object(optimistic_state, PathBuf::from("state_snapshot.bin"))?;
Implementation Details
Constructs the full save file path by joining
root_pathwith the providedpath.Clones necessary shared data to move into the spawned thread.
Spawns a new thread named after the target file path to perform the save operation:
Extracts messages related to the current state from
message_db.Serializes the optimistic state using
bincode.Retrieves cross-thread reference data history from
shared_services.Fetches the finalized block and block state from their respective repositories.
Extracts multiple fields from the guarded
block_state(such as block sets, statistics, attestation targets, and checkpoints).Validates presence of parent block state and its checkpoints.
Constructs a
ThreadSnapshotinstance with the collected data.Serializes the
ThreadSnapshotto bytes.Writes the bytes to a temporary file then renames it atomically to the target path to ensure file consistency.
Adds the thread handle to the
threadscollection, cleaning up any finished threads.Returns immediately after spawning the thread, enabling non-blocking operation.
Algorithms and Patterns
Threaded Asynchronous Saving: The actual I/O and serialization work is offloaded to a dedicated thread to avoid blocking the main thread.
Atomic File Write: Uses a temporary file with rename to ensure that partial writes or failures do not corrupt the target file.
Data Guarding: Uses guarded access to mutable state for thread safety, leveraging
parking_lot::Mutexand custom guard utilities (Guarded,AllowGuardedMut).Bincode Serialization: Efficient binary serialization for compact storage of complex Rust structs.
Interactions with Other Modules
Repository Layer: Utilizes
RepositoryImplandBlockStateRepositoryto query finalized blocks and detailed block state data, crucial for capturing a consistent snapshot.Shared Services: Interacts with
SharedServicesto retrieve cross-thread reference data history that tracks dependencies between blocks.Message Durable Storage: Accesses
MessageDurableStorageto extract messages related to the optimistic state, ensuring message data persistence.Utilities: Uses helper functions such as
get_temp_file_pathfrom thehelpermodule for safe temporary file creation.Guarded Utilities: Employs concurrency guard utilities (
AllowGuardedMut,Guarded,GuardedMut) to handle mutable shared state safely in a multi-threaded context.ThreadSnapshot: Constructs a
ThreadSnapshotdata structure that encapsulates all state components to be saved.
Detailed Workflow Diagram
flowchart TD
A[save_object called] --> B[Construct full save path]
B --> C[Clone dependencies for thread]
C --> D[Spawn save thread]
D --> E[Fetch block messages from message_db]
E --> F[Serialize optimistic state]
F --> G[Get cross-thread ref data via shared_services]
G --> H[Retrieve finalized block]
H --> I[Retrieve block state and extract fields]
I --> J[Retrieve parent block state and checkpoints]
J --> K[Build ThreadSnapshot struct]
K --> L[Serialize ThreadSnapshot]
L --> M[Write to temp file]
M --> N[Rename temp file to target path]
N --> O[Thread completes]
D --> P[Add thread handle to threads Vec]
P --> Q[Return Ok]
Supporting Types and Traits
AllowGuardedMut for Vec<JoinHandle<anyhow::Result<()>>>: Enables mutable guarded access to the vector of thread handles, allowing safe concurrent modification when adding new threads or cleaning up finished ones.
ThreadSnapshot: A builder-pattern struct (defined elsewhere) used to aggregate all state data before serialization.
OptimisticStateImpl: Represents an optimistic blockchain state that may not yet be finalized.
Notes on Error Handling
Errors during serialization, repository access, or file I/O inside the spawned thread are propagated using
anyhow::Result.The method
save_objectitself returns immediately with an error only if thread spawning fails.Runtime errors inside threads do not panic but return error results from the thread handle.
This file is integral for the persistence layer of blockchain state snapshots, enabling asynchronous, thread-safe saving of complex state data to disk. It connects multiple repository and service modules to gather comprehensive state information for durable storage. For details on state representation, serialization, and cross-thread synchronization, refer to topics like Repository Pattern, State Serialization, and Threading and Concurrency.