resolver.rs
Overview
This file defines a data loader utility for efficiently retrieving blockchain block data from a SQLite database. The primary purpose is to batch load multiple blocks in a single SQL query to optimize database access, reducing the number of queries and improving performance when resolving blocks by their IDs. It interfaces with the database schema defined elsewhere and converts raw database records into application-level block objects.
BlockLoader Struct
pub struct BlockLoader {
pub pool: SqlitePool,
}
Purpose: Holds a connection pool (
SqlitePool) to the SQLite database, enabling asynchronous database queries.Field:
pool: ASqlitePoolinstance representing the connection pool to the SQLite database.
Implementation of Loader Trait for BlockLoader
BlockLoader implements the Loader<String> trait from the async_graphql::dataloader module, which allows it to asynchronously batch load data based on string keys.
Associated Types
type Error = Error
The error type used for handling errors during data loading, imported fromasync_graphql::Error.type Value = super::Block
The type of values returned by the loader, representing blockchain blocks. This refers to theBlocktype defined in the parent module.
load Method
async fn load(
&self,
keys: &[String],
) -> anyhow::Result<HashMap<String, Self::Value>, Self::Error>
Parameters:
&self: Reference to theBlockLoaderinstance.keys: A slice of block IDs (&[String]) that should be loaded from the database.
Returns:
An asynchronous result containing aHashMapthat maps block IDs (String) to their correspondingBlockobjects (Self::Value). On failure, it returns an error of typeSelf::Error.Functionality:
Converts the list of block ID keys into a comma-separated string formatted for SQL
INclause.Constructs a SQL query string to select all blocks where their
idis in the provided list.Uses
sqlx::QueryBuilderto prepare the query.Executes the query asynchronously against the SQLite pool.
Maps each database row (
db::Block) into the applicationBlocktype using theFromtrait (block.into()).Collects the results into a
HashMapwith the block ID as the key.Returns the
HashMapon success.
Logging:
Usestracing::trace!to log the generated SQL query under thedata_loadertarget for debugging.Error Handling:
Utilizesanyhow::Resultcombined withasync_graphql::Errorto propagate errors encountered during query execution or data conversion.
Usage Example
let loader = BlockLoader { pool: sqlite_pool };
let block_ids = vec!["block1".to_string(), "block2".to_string()];
let blocks_map = loader.load(&block_ids).await?;
if let Some(block) = blocks_map.get("block1") {
// Use block object here
}
Implementation Details and Algorithms
Batch Loading:
Theloadmethod batches multiple block ID requests into a single SQL query using theINclause, optimizing database roundtrips.Async/Await with SQLx:
Uses asynchronous database querying withsqlxand Rust'sasync/awaitsyntax, improving scalability and non-blocking execution.Data Transformation:
Converts database records into domain-specificBlockobjects, enabling separation between raw data and business logic representations.Error Propagation:
Combines SQL query errors and GraphQL-related errors in a unified error handling approach.
Interactions with Other Components
Database Schema (
crate::schema::db):
Relies on thedb::Blockstruct representing the database table schema forblocks. The conversion fromdb::Blockto the applicationBlocktype ensures data consistency.GraphQL DataLoader (
async_graphql::dataloader::Loader):
Implements theLoadertrait to integrate with the GraphQL server's data loading mechanism, enabling efficient batch fetching of blocks during query resolution.SQLx and SQLite Connection Pool:
Utilizessqlx::SqlitePoolfor managing connections to the SQLite database, ensuring thread-safe and performant database access.
Mermaid Diagram
classDiagram
class BlockLoader {
+pool: SqlitePool
+load(keys: &[String]) async -> Result<HashMap<String, Block>, Error>
}
class Loader~String~ {
<<trait>>
+load(keys: &[String]) async -> Result<HashMap<String, Value>, Error>
}
class Block {
<<struct>>
}
class db::Block {
<<struct>>
}
BlockLoader ..|> Loader~String~
BlockLoader --> SqlitePool
BlockLoader --> db::Block
BlockLoader --> Block