Skip to content

opt(torii-indexer): batching requests for ercs - #3132

Merged
Larkooo merged 4 commits into
dojoengine:mainfrom
Larkooo:batch-requests-erc
Mar 31, 2025
Merged

opt(torii-indexer): batching requests for ercs#3132
Larkooo merged 4 commits into
dojoengine:mainfrom
Larkooo:batch-requests-erc

Conversation

@Larkooo

@Larkooo Larkooo commented Mar 27, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Optimized token information retrieval by consolidating multiple data requests into a single, efficient operation.
    • Enhanced efficiency in fetching NFT metadata through batch requests, reducing the number of asynchronous calls.
    • Improved logging capabilities for NFT token registration, providing better context on registered tokens.
  • Bug Fixes
    • Enhanced error handling for unexpected response types during token metadata retrieval.

@coderabbitai

coderabbitai Bot commented Mar 27, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Ohayo, sensei! This change implements a batch request mechanism within the register_erc20_token_metadata function to aggregate fetching token metadata (name, symbol, decimals) into a single call. The implementation now creates multiple CallRequest objects that are sent together via a batch request. The response parsing logic has been updated to index into the returned array and validate each value's type, with improved error handling for unexpected responses. Additionally, related type imports have been updated to support the new functionality.

Changes

File Change Summary
crates/torii/sqlite/src/erc.rs Implemented batch request in register_erc20_token_metadata; replaced individual calls with a batch request; updated response parsing and error handling; added new type imports (CallRequest, ProviderRequestData, ProviderResponseData).
crates/torii/sqlite/src/executor/mod.rs Introduced batch request for NFT metadata in register_nft_token; consolidated name and symbol fetching into a single request; updated imports and parsing logic.
crates/torii/sqlite/src/executor/erc.rs Enhanced logging in handle_nft_token_metadata method; added info level logging for NFT registration; minor reordering of import statements.

Sequence Diagram(s)

sequenceDiagram
    participant F as register_erc20_token_metadata
    participant P as Provider
    F->>P: Send batch request with [CallRequest(Name), CallRequest(Symbol), CallRequest(Decimals)]
    P-->>F: Return results array
    F->>F: Parse results by index and validate response types
Loading

Possibly related PRs

  • fix(torii): handle failed metadata fetch and empty metadata in db #2755: The changes in the main PR and the retrieved PR are related as both involve modifications to the handling of metadata fetching, specifically utilizing batch requests and enhancing error handling in the context of token metadata retrieval.
  • fix(torii-sqlite): erc721 tokens upsert do nothing #2986: The changes in the main PR and the retrieved PR are related as both involve modifications to the handling of token metadata retrieval, specifically utilizing batch requests and SQL queries to optimize the fetching of token details.
  • test(torii): add metadata to erc1155 and update #3054: The changes in the main PR and the retrieved PR are related as both involve the implementation of batch requests for fetching token metadata, specifically utilizing the CallRequest structure and ProviderRequestData, indicating a shared approach to handling metadata retrieval.

Suggested reviewers

  • glihm (sensei)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/torii/sqlite/src/executor/erc.rs (1)

369-372: Consider adding specific error handling for each entry point

Ohayo! While the current implementation works well, you might want to add more specific error handling to identify which entry points failed specifically. This could help with debugging issues with specific token standards.

- let token_uri = results.into_iter().find_map(|result| match result {
-     ProviderResponseData::Call(data) if !data.is_empty() => Some(data),
-     _ => None,
- });
+ let endpoints = ["token_uri", "tokenURI", "uri"];
+ let mut errors = Vec::new();
+ 
+ let token_uri = results.into_iter().enumerate().find_map(|(i, result)| match result {
+     ProviderResponseData::Call(data) if !data.is_empty() => {
+         debug!(
+             endpoint = %endpoints[i],
+             "Successfully retrieved token URI from endpoint"
+         );
+         Some(data)
+     },
+     _ => {
+         errors.push(endpoints[i]);
+         None
+     },
+ });
+ 
+ if !errors.is_empty() {
+     debug!(
+         failed_endpoints = ?errors,
+         "Failed to retrieve token URI from some endpoints"
+     );
+ }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ace89 and a578531.

📒 Files selected for processing (2)
  • crates/torii/sqlite/src/erc.rs (2 hunks)
  • crates/torii/sqlite/src/executor/erc.rs (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
crates/torii/sqlite/src/erc.rs (2)
crates/torii/sqlite/src/utils.rs (1)
  • u256_to_sql_string (48-50)
crates/torii/sqlite/src/model.rs (2)
  • contract_address (92-94)
  • name (80-82)
🪛 GitHub Actions: ci
crates/torii/sqlite/src/erc.rs

[error] 8-8: Rust formatting check failed. Please run 'rustfmt' to format the code.

🔇 Additional comments (7)
crates/torii/sqlite/src/erc.rs (4)

6-6: Added necessary imports for batch request functionality

Ohayo! These imports are essential for implementing the batching mechanism for provider requests. Good choice to use the official Starknet types for this optimization.

Also applies to: 9-9


164-191: Excellent batching mechanism implementation

Ohayo, sensei! This is a great optimization to batch all three metadata requests (name, symbol, decimals) into a single provider call. The code is well-structured with clear separation between request preparation and execution.


193-193: Efficient use of batch request API

The batch approach will significantly reduce network overhead compared to individual calls, especially for high-volume operations. Nice implementation, sensei!


195-225: Well-structured response parsing logic

Ohayo! The response parsing logic is well-implemented with proper error handling. The use of pattern matching for different response formats is elegant, and the unreachable! macros provide good defensive programming.

crates/torii/sqlite/src/executor/erc.rs (3)

9-9: Added necessary imports for batch request functionality

Ohayo, sensei! These imports are essential for implementing the batching mechanism for token URI retrieval. Consistent with the approach used in the ERC module.

Also applies to: 12-12


337-364: Excellent batching strategy for token URI retrieval

Ohayo! This is a smart approach to try all three possible token URI entry points (token_uri, tokenURI, and uri) in a single batch request. The code is well-organized and follows the same pattern as the ERC20 metadata implementation.


366-383: Efficient response processing with find_map

Nice use of functional programming with find_map to extract the first successful response, sensei! The error handling with appropriate warning logs is also well-implemented.

use cainome::cairo_serde::{ByteArray, CairoSerde};
use starknet::core::types::requests::CallRequest;
use starknet::core::types::{BlockId, BlockTag, Felt, FunctionCall, U256};
use starknet::core::utils::{get_selector_from_name, parse_cairo_short_string};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix rustfmt error

Ohayo sensei! CI pipeline indicates a formatting error on this line. Please run rustfmt to fix the formatting issue.

rustfmt crates/torii/sqlite/src/erc.rs
🧰 Tools
🪛 GitHub Actions: ci

[error] 8-8: Rust formatting check failed. Please run 'rustfmt' to format the code.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/torii/sqlite/src/erc.rs (1)

195-205: Consider adding a length check for results array

Ohayo sensei! While the batching implementation looks solid, it would be good to verify that the results array has at least 3 elements before accessing them by index to prevent potential index out of bounds panics.

let results = provider.batch_requests(requests).await?;

+ // Ensure we have all expected results
+ if results.len() < 3 {
+     return Err(anyhow::anyhow!("Expected 3 results from batch request, got {}", results.len()));
+ }

// Parse name
let name = match &results[0] {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a578531 and d681fc7.

📒 Files selected for processing (1)
  • crates/torii/sqlite/src/erc.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (5)
crates/torii/sqlite/src/erc.rs (5)

6-6: Good job adding the necessary imports for batch requests!

Ohayo sensei! The additions of CallRequest, ProviderRequestData, and ProviderResponseData are essential for implementing the batch request approach. This optimization will help reduce network calls and improve performance.

Also applies to: 9-9


164-191: Nice implementation of batch requests!

Ohayo sensei! This is a great optimization for fetching token metadata. By preparing all three requests (name, symbol, decimals) in a single batch, you're reducing multiple network calls to just one, which should significantly improve performance.


193-193: Proper batch execution

The batch request is correctly executed and the result is properly awaited with error propagation.


196-217: Well-structured parsing logic for name and symbol

The pattern matching for both name and symbol is correctly implemented. I appreciate how you handle both short strings (single felt) and byte arrays consistently.


219-225: Clean parsing for decimals

Ohayo sensei! The parsing for decimals is concise and correctly implemented. The error handling with expect and unreachable! is appropriate for this case.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
crates/torii/sqlite/src/executor/mod.rs (3)

149-150: Consider making batch_size more configurable.

Hardcoding 100 might be limiting if usage patterns vary significantly. A configurable value could improve flexibility.


244-246: Unify or reference capacities in constants.

Ohayo sensei, repeated capacities (64 and 100) could be declared as constants or read from config for consistent clarity.


800-814: Tidy and safe bulk registration approach.

process_pending_nft_tokens is well-structured, but consider chunk processing for extremely large sets or to avoid memory spikes.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between d681fc7 and 45fd677.

📒 Files selected for processing (2)
  • crates/torii/sqlite/src/executor/erc.rs (3 hunks)
  • crates/torii/sqlite/src/executor/mod.rs (10 hunks)
🔇 Additional comments (9)
crates/torii/sqlite/src/executor/mod.rs (7)

9-9: Ohayo sensei! Imports look neat.

Everything here seems in order for pulling in new references to UpdateNftMetadataQuery, Felt, and the required tokio modules. No issues spotted.

Also applies to: 11-11, 14-15, 25-25


142-143: Ohayo sensei! Good addition for NFT token batching.

pending_nft_tokens is a helpful approach to accumulate and process NFT tokens in bulk, improving throughput.


282-286: Bulk flush triggers look good!

Ohayo sensei, automatically flushing pending tokens when the batch is full is a straightforward and effective strategy.


653-660: Ensure no unbounded backlog.

Ohayo sensei, adding tokens to pending_nft_tokens until hitting batch_size is efficient, but do confirm that under heavy load, we don’t accumulate too many queries if batch_size isn’t reached quickly.


753-754: Pre-execution token flush is consistent.

Processing pending tokens before execute ensures data consistency.


793-798: Rollback clears pending_nft_tokens.

Ohayo sensei, wiping pending tokens on rollback might discard tokens that could be retried. Confirm if this is desired behavior or if we need a re-queue.


270-271:

Details

❓ Verification inconclusive

Verify error handling when shutting down.

We’re processing any remaining tokens before shutting down, but if an error arises, ensure it won't block or degrade the shutdown sequence.

Would you like to confirm error-handling behavior (e.g., partial commits) during shutdown steps with a quick codebase check?


🏁 Script executed:

#!/bin/bash
# Searching for any explicit shutdown handling or error conditions that might block
rg "shutdown" -A 10

Length of output: 59516


Ohayo, sensei – Confirming Error Handling on Shutdown

In reviewing crates/torii/sqlite/src/executor/mod.rs (lines 270–271), we see that upon receiving the shutdown signal the executor logs the shutdown intent and then calls:

// Process any remaining tokens before shutting down
self.process_pending_nft_tokens().await?;

Since the code uses the ? operator, any error from process_pending_nft_tokens() will be propagated immediately. Given similar shutdown patterns elsewhere in the codebase, please double-check that such error propagation won’t block or degrade the shutdown sequence (e.g., resulting in partial commits or a hanging shutdown). If non-critical errors during token processing should not interrupt shutdown, consider handling them by logging the error and continuing with shutdown instead of propagating the error.

crates/torii/sqlite/src/executor/erc.rs (2)

9-9: Ohayo sensei! Newly introduced imports make sense.

These imports from CallRequest, ProviderRequestData, and ProviderResponseData align well with your batch request logic. No concerns spotted.

Also applies to: 12-12


473-545: Batch-based fetch_token_uri approach looks solid.

Ohayo sensei, the new method for trying multiple entry points in one shot is neat. This should reduce RPC round trips and speed up retrieval. Nicely done!

Comment thread crates/torii/sqlite/src/executor/erc.rs Outdated
Comment on lines 207 to 227
pub async fn process_register_nft_tokens_batch(
tokens: Vec<RegisterNftTokenQuery>,
provider: Arc<P>,
name: String,
symbol: String,
) -> Result<RegisterNftTokenMetadata> {
let token_uri = Self::fetch_token_uri(
&provider,
register_nft_token.contract_address,
register_nft_token.token_id,
)
.await?;
) -> Result<Vec<RegisterNftTokenMetadata>> {
// First batch: Get name and symbol for all contracts (deduplicated)
let mut unique_contracts = std::collections::HashSet::new();
for token in &tokens {
unique_contracts.insert(token.contract_address);
}

let metadata = Self::fetch_token_metadata(
register_nft_token.contract_address,
register_nft_token.token_id,
&token_uri,
)
.await?;
let mut name_symbol_requests = Vec::new();
let block_id = BlockId::Tag(BlockTag::Pending);

for &contract_address in &unique_contracts {
// Add name request
name_symbol_requests.push(ProviderRequestData::Call(CallRequest {
request: FunctionCall {
contract_address,
entry_point_selector: get_selector_from_name("name").unwrap(),
calldata: vec![],
},
block_id,
}));
// Add symbol request
name_symbol_requests.push(ProviderRequestData::Call(CallRequest {
request: FunctionCall {
contract_address,
entry_point_selector: get_selector_from_name("symbol").unwrap(),
calldata: vec![],
},
block_id,
}));
}

let name_symbol_results = provider.batch_requests(name_symbol_requests).await?;

// Create a map of contract_address -> (name, symbol)
let mut contract_metadata = std::collections::HashMap::new();
for (i, contract_address) in unique_contracts.iter().enumerate() {
let name_result = &name_symbol_results[i * 2];
let symbol_result = &name_symbol_results[i * 2 + 1];

let name = match name_result {
ProviderResponseData::Call(name) if name.len() == 1 => {
parse_cairo_short_string(&name[0]).unwrap()
}
ProviderResponseData::Call(name) => {
ByteArray::cairo_deserialize(name, 0)
.expect("Return value not ByteArray")
.to_string()
.expect("Return value not String")
}
_ => "".to_string(),
};

let symbol = match symbol_result {
ProviderResponseData::Call(symbol) if symbol.len() == 1 => {
parse_cairo_short_string(&symbol[0]).unwrap()
}
ProviderResponseData::Call(symbol) => {
ByteArray::cairo_deserialize(symbol, 0)
.expect("Return value not ByteArray")
.to_string()
.expect("Return value not String")
}
_ => "".to_string(),
};

Ok(RegisterNftTokenMetadata { query: register_nft_token, metadata, name, symbol })
contract_metadata.insert(*contract_address, (name, symbol));
}

// Second batch: Get token URIs for all tokens
let mut token_uri_requests = Vec::new();
for token in &tokens {
// Try all possible token URI functions for each token
token_uri_requests.push(ProviderRequestData::Call(CallRequest {
request: FunctionCall {
contract_address: token.contract_address,
entry_point_selector: get_selector_from_name("token_uri").unwrap(),
calldata: vec![token.token_id.low().into(), token.token_id.high().into()],
},
block_id,
}));
token_uri_requests.push(ProviderRequestData::Call(CallRequest {
request: FunctionCall {
contract_address: token.contract_address,
entry_point_selector: get_selector_from_name("tokenURI").unwrap(),
calldata: vec![token.token_id.low().into(), token.token_id.high().into()],
},
block_id,
}));
token_uri_requests.push(ProviderRequestData::Call(CallRequest {
request: FunctionCall {
contract_address: token.contract_address,
entry_point_selector: get_selector_from_name("uri").unwrap(),
calldata: vec![token.token_id.low().into(), token.token_id.high().into()],
},
block_id,
}));
}

let token_uri_results = provider.batch_requests(token_uri_requests).await?;

// Process results and fetch metadata
let mut results = Vec::new();
for (i, token) in tokens.into_iter().enumerate() {
let (name, symbol) = contract_metadata.get(&token.contract_address).unwrap().clone();

// Find first successful URI response for this token
let token_uri_base = i * 3;
let token_uri = token_uri_results[token_uri_base..token_uri_base + 3]
.iter()
.find_map(|result| match result {
ProviderResponseData::Call(data) if !data.is_empty() => {
let uri = if let Ok(byte_array) = ByteArray::cairo_deserialize(data, 0) {
byte_array.to_string().expect("Return value not String")
} else if let Ok(felt_array) = Vec::<Felt>::cairo_deserialize(data, 0) {
felt_array
.iter()
.map(parse_cairo_short_string)
.collect::<Result<Vec<String>, _>>()
.map(|strings| strings.join(""))
.unwrap_or_default()
} else {
"".to_string()
};

if uri.is_empty() {
None
} else {
Some(uri)
}
}
_ => None,
})
.unwrap_or_default();

// Replace {id} in token URI if needed
let token_uri = if token_uri.contains("{id}") {
let token_id_hex = format!("{:064x}", token.token_id);
token_uri.replace("{id}", &token_id_hex)
} else {
token_uri
};

// Fetch metadata
let metadata = Self::fetch_token_metadata(token.contract_address, token.token_id, &token_uri).await?;

results.push(RegisterNftTokenMetadata {
query: token,
name,
symbol,
metadata,
});
}

Ok(results)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential iteration-order mismatch with HashSet.

Ohayo sensei, enumerating a HashSet while indexing responses by i * 2 or i * 2 + 1 may cause mismatch if iteration order diverges from request order. Usually it’s fine within a single iteration block, but not guaranteed by HashSet docs. Consider using an ordered structure or a linked mapping of contracts to responses. That ensures the correct name/symbol pairs map consistently to each contract.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/torii/sqlite/src/executor/erc.rs (1)

245-272: ⚠️ Potential issue

Potential iteration-order mismatch with HashSet.

Ohayo sensei, enumerating a HashSet while indexing responses by i * 2 or i * 2 + 1 may cause mismatch if iteration order diverges from request order. Usually it's fine within a single iteration block, but not guaranteed by HashSet docs. Consider using an ordered structure or a linked mapping of contracts to responses. That ensures the correct name/symbol pairs map consistently to each contract.

- let mut unique_contracts = std::collections::HashSet::new();
+ let mut unique_contracts = std::collections::LinkedHashSet::new();

Or use a Vec with deduplication:

- let mut unique_contracts = std::collections::HashSet::new();
+ let mut unique_contracts = Vec::new();
+ for token in &tokens {
+    if !unique_contracts.contains(&token.contract_address) {
+        unique_contracts.push(token.contract_address);
+    }
+ }
🧹 Nitpick comments (2)
crates/torii/sqlite/src/executor/mod.rs (1)

245-246: Consider making the batch size configurable.

The hardcoded batch size of 100 works well for most cases, but for flexibility, consider making this configurable through settings. This would allow tuning based on network conditions or load patterns.

- batch_size: 100,
+ batch_size: config.nft_batch_size.unwrap_or(100),

Also applies to: 259-260

crates/torii/sqlite/src/executor/erc.rs (1)

494-497: Consider adding logging for failed token URI requests.

When a token URI request fails, adding debug logging could help with troubleshooting. This would provide visibility into which specific request format failed.

- let token_uri = results.into_iter().find_map(|result| match result {
-     ProviderResponseData::Call(data) if !data.is_empty() => Some(data),
-     _ => None,
- });
+ let token_uri = results.into_iter().enumerate().find_map(|(i, result)| match result {
+     ProviderResponseData::Call(data) if !data.is_empty() => Some(data),
+     _ => {
+         let method = match i {
+             0 => "token_uri",
+             1 => "tokenURI",
+             2 => "uri",
+             _ => "unknown",
+         };
+         debug!(
+             contract_address = format!("{:#x}", contract_address),
+             token_id = %token_id,
+             method = %method,
+             "Token URI request failed"
+         );
+         None
+     },
+ });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 45fd677 and c004507.

📒 Files selected for processing (3)
  • crates/torii/sqlite/src/executor/erc.rs (3 hunks)
  • crates/torii/sqlite/src/executor/mod.rs (10 hunks)
  • examples/spawn-and-move/pistols.toml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • examples/spawn-and-move/pistols.toml
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (12)
crates/torii/sqlite/src/executor/mod.rs (7)

143-150: Solid architectural change from concurrent tasks to batch processing!

Ohayo sensei! Replacing the JoinSet with a batch collection approach is a great optimization. This should reduce task spawning overhead and improve throughput for NFT token processing.


271-272: Excellent cleanup handling before shutdown!

Ohayo sensei, processing remaining tokens before shutdown ensures no work is lost. This is a valuable reliability improvement.


283-287: Well-implemented batch processing trigger.

Nice implementation of the batch size check! This ensures tokens are processed in appropriately sized batches during normal operation for optimal performance.


654-661: Good addition of batching logic for RegisterNftToken.

The query handling now adds tokens to the pending batch instead of processing them immediately. This aligns perfectly with the new batching architecture.


753-754: Important pre-execution cleanup.

Processing pending tokens before executing a transaction ensures consistent state. Good attention to detail here!


796-797: Proper state cleanup on rollback.

Clearing pending tokens on rollback maintains consistency with the transaction state. This prevents orphaned tokens from being processed after a rollback.


800-814: Well-structured batch processing implementation.

The new process_pending_nft_tokens method efficiently implements the batch approach with good error handling. This is the core of the optimization and it looks solid.

crates/torii/sqlite/src/executor/erc.rs (5)

212-215: Good contract deduplication optimization.

Deduplicating contract addresses before making RPC calls is an excellent optimization. This reduces network overhead and improves throughput.


217-239: Well-structured batch request building.

The request construction for names and symbols is clear and efficient. Good job utilizing the batch request capability of the provider.


275-302: Comprehensive token URI fetching approach.

Trying multiple URI function signatures (token_uri, tokenURI, uri) increases compatibility with different NFT standards. This is a thoughtful approach to handle variations in contract implementations.


308-349: Good result processing with error handling.

The processing of batched results is well-structured with good error handling. The token ID replacement in URIs is handled properly, and metadata fetching is comprehensive.


463-489: Efficient batch request implementation for fetch_token_uri.

Ohayo sensei! Converting the token URI fetching to use batch requests reduces network round-trips and improves performance. This is an excellent optimization.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
crates/torii/sqlite/src/executor/mod.rs (3)

143-143: Consider making batch_size configurable

Ohayo, sensei! The hardcoded batch size of 100 for NFT tokens looks good for now, but consider making it configurable through constructor parameters or environment variables for better flexibility in different deployment scenarios.

 struct Executor<'c, P: Provider + Sync + Send + 'static> {
     // ... other fields
     // Used to limit number of tokens processed in a single batch
-    batch_size: usize,
+    batch_size: usize,
 }

 impl<'c, P: Provider + Sync + Send + 'static> Executor<'c, P> {
     pub async fn new(
         pool: Pool<Sqlite>,
         shutdown_tx: Sender<()>,
         provider: Arc<P>,
+        batch_size: Option<usize>,
     ) -> Result<(Self, UnboundedSender<QueryMessage>)> {
         // ... existing code
         Ok((
             Executor {
                 // ... other fields
-                batch_size: 100,
+                batch_size: batch_size.unwrap_or(100),
             },
             tx,
         ))
     }
 }

Also applies to: 150-150


798-799: Consider adding error handling for individual tokens

The current implementation processes tokens in a batch, but if one token fails, it might affect the entire batch. Consider enhancing error handling to isolate failures.

 async fn process_pending_nft_tokens(&mut self) -> Result<()> {
     // ... existing code
     let tokens = mem::take(&mut self.pending_nft_tokens);
-    let results =
-        Self::process_register_nft_tokens_batch(tokens, self.provider.clone()).await?;
+    let batch_result = Self::process_register_nft_tokens_batch(tokens.clone(), self.provider.clone()).await;
+    
+    let results = match batch_result {
+        Ok(results) => results,
+        Err(e) => {
+            error!(target: LOG_TARGET, error = %e, "Failed to process batch. Falling back to individual processing.");
+            // Optionally implement fallback to individual processing
+            // or better error recovery strategy
+            return Err(e);
+        }
+    };
     
     for result in results {
         self.handle_nft_token_metadata(result).await?;
     }
     // ... rest of the method
 }

143-150: Add performance metrics for batch processing

Ohayo again! Consider adding performance metrics or logging to track the efficiency gains from batch processing. This would help with monitoring and future optimizations.

 async fn process_pending_nft_tokens(&mut self) -> Result<()> {
     if self.pending_nft_tokens.is_empty() {
         return Ok(());
     }

+    let batch_size = self.pending_nft_tokens.len();
+    let start_time = Instant::now();
     let tokens = mem::take(&mut self.pending_nft_tokens);
     let results =
         Self::process_register_nft_tokens_batch(tokens, self.provider.clone()).await?;

     for result in results {
         self.handle_nft_token_metadata(result).await?;
     }
+    debug!(
+        target: LOG_TARGET,
+        batch_size = batch_size,
+        duration = ?start_time.elapsed(),
+        "Processed NFT tokens batch"
+    );

     Ok(())
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between c004507 and 02078a9.

📒 Files selected for processing (2)
  • crates/torii/runner/src/lib.rs (0 hunks)
  • crates/torii/sqlite/src/executor/mod.rs (8 hunks)
💤 Files with no reviewable changes (1)
  • crates/torii/runner/src/lib.rs
🔇 Additional comments (4)
crates/torii/sqlite/src/executor/mod.rs (4)

647-653: Batching implementation looks good!

The token batching implementation is clean and efficient. Adding tokens to the batch and processing them when reaching the batch size is a good approach for optimizing performance.


791-805: Properly handles remaining tokens that don't reach batch size

Good job, sensei! The implementation ensures that all pending tokens are processed through calls in the execute method even if they don't reach the batch size, preventing tokens from being stuck in the pending state.


743-744: Nice handling of pending tokens before transaction execution

Ensuring all pending tokens are processed before executing a transaction is crucial for data consistency. This implementation handles that well.


785-788: Good cleanup in rollback

Properly clearing all pending state, including the pending_nft_tokens, during rollback ensures a clean slate for subsequent operations.

@Larkooo
Larkooo force-pushed the batch-requests-erc branch from 02078a9 to 3149ab5 Compare March 28, 2025 05:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/torii/sqlite/src/erc.rs (2)

164-229: Excellent performance optimization through batching!

Ohayo sensei! Your implementation of batch requests for fetching token metadata is a great optimization. By reducing three network calls to a single batch request, you've improved the performance of token metadata retrieval, which is especially valuable for high-throughput applications.

The code is well-structured with clear separation between request creation, execution, and response parsing. The error handling is robust with specific error messages for different types of invalid responses.

Consider adding a comment explaining the expected response formats for each request type to help future developers understand the parsing logic more easily. For example:

 // Prepare batch requests for name, symbol, and decimals
 let block_id = BlockId::Tag(BlockTag::Pending);
+// For ERC20 tokens, we expect:
+// - name: Either a short string (single felt) or a ByteArray
+// - symbol: Either a short string (single felt) or a ByteArray
+// - decimals: A u8 value
 let requests = vec![
🧰 Tools
🪛 GitHub Actions: ci

[error] 197-197: Formatting check failed. Code changes detected that do not adhere to the expected style.


[error] 211-211: Formatting check failed. Code changes detected that do not adhere to the expected style.


164-229: Consider adding exponential backoff for batch requests

Ohayo sensei! While the batch request implementation is excellent, consider adding error handling with exponential backoff for network-related failures. This can improve reliability when the provider service is experiencing temporary issues.

-        let results = provider.batch_requests(requests).await?;
+        let results = match provider.batch_requests(requests).await {
+            Ok(results) => results,
+            Err(err) if err.is_network_related() => {
+                // Retry with exponential backoff
+                // This is a simplified example - you'd want to implement proper retry logic
+                tokio::time::sleep(std::time::Duration::from_millis(500)).await;
+                provider.batch_requests(requests).await?
+            }
+            Err(err) => return Err(err.into()),
+        };

This is optional but could improve resilience in production environments.

🧰 Tools
🪛 GitHub Actions: ci

[error] 197-197: Formatting check failed. Code changes detected that do not adhere to the expected style.


[error] 211-211: Formatting check failed. Code changes detected that do not adhere to the expected style.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 02078a9 and 3149ab5.

📒 Files selected for processing (1)
  • crates/torii/sqlite/src/erc.rs (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
crates/torii/sqlite/src/erc.rs (2)
crates/torii/indexer/src/engine.rs (1)
  • block_id (196-203)
crates/torii/sqlite/src/model.rs (2)
  • contract_address (92-94)
  • name (80-82)
🪛 GitHub Actions: ci
crates/torii/sqlite/src/erc.rs

[error] 197-197: Formatting check failed. Code changes detected that do not adhere to the expected style.


[error] 211-211: Formatting check failed. Code changes detected that do not adhere to the expected style.

🔇 Additional comments (8)
crates/torii/sqlite/src/erc.rs (8)

6-9: Ohayo, sensei! Great job updating the imports for the new batch request functionality.

The new imports for CallRequest, ProviderRequestData, and ProviderResponseData align perfectly with the changes in the implementation. This is a clean way to support the new batching functionality.


164-191: Nice implementation of batch requests to optimize token metadata fetching!

Ohayo, sensei! You've done a great job setting up the batch requests for fetching token metadata. Combining three separate calls into a single batch request will definitely improve performance by reducing network round-trips.

The code creates three separate CallRequest objects for name, symbol, and decimals, all using the same block_id. This is a well-structured approach that makes the code clearer to understand.


193-193: Efficient use of batch requests API!

Using provider.batch_requests(requests).await? is a clean way to execute all three requests in a single call, reducing network overhead.


196-207: Fix formatting issues flagged by CI

Ohayo sensei! The CI pipeline has flagged formatting issues on this line. Please run rustfmt to fix the formatting issue.

rustfmt crates/torii/sqlite/src/erc.rs
🧰 Tools
🪛 GitHub Actions: ci

[error] 197-197: Formatting check failed. Code changes detected that do not adhere to the expected style.


196-207: Good error handling for name parsing

The name parsing logic handles different response formats effectively. You correctly handle both the short string format (with length 1) and the ByteArray format, with proper error handling for invalid responses.

🧰 Tools
🪛 GitHub Actions: ci

[error] 197-197: Formatting check failed. Code changes detected that do not adhere to the expected style.


210-221: Fix formatting issues flagged by CI

Ohayo sensei! The CI pipeline has flagged formatting issues on this line. Please run rustfmt to fix the formatting issue.

rustfmt crates/torii/sqlite/src/erc.rs
🧰 Tools
🪛 GitHub Actions: ci

[error] 211-211: Formatting check failed. Code changes detected that do not adhere to the expected style.


210-221: Well-structured symbol parsing logic

The symbol parsing logic follows the same pattern as the name parsing, maintaining consistency in the codebase. The error handling is appropriate with a clear error message for invalid responses.

🧰 Tools
🪛 GitHub Actions: ci

[error] 211-211: Formatting check failed. Code changes detected that do not adhere to the expected style.


224-229: Clean implementation of decimals parsing

The decimals parsing is appropriately simpler since it expects a specific type (u8). The error handling is still robust with a clear error message if the response is invalid.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 3149ab5 and 48d095c.

📒 Files selected for processing (1)
  • crates/torii/sqlite/src/executor/mod.rs (4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
crates/torii/sqlite/src/executor/mod.rs (1)
crates/torii/sqlite/src/utils.rs (2)
  • felt_to_sql_string (40-42)
  • felts_to_sql_string (35-38)
🪛 GitHub Actions: ci
crates/torii/sqlite/src/executor/mod.rs

[error] 14-14: Code formatting issue: Incorrect order of imports in the file.


[error] 27-27: Code formatting issue: Incorrect order of imports in the file.

🔇 Additional comments (5)
crates/torii/sqlite/src/executor/mod.rs (5)

12-12: Ohayo! New import for CallRequest is properly added.

The addition of this import enables the usage of CallRequest which is essential for the batch request implementation.


15-15: Import expanded to include Provider and batch request data types.

The import now includes ProviderRequestData and ProviderResponseData which are necessary for implementing the batch request functionality.


17-18: Updated tokio imports for better organization.

The tokio imports have been properly organized to include all necessary components for async communication.


682-701: Well implemented batch request setup for token metadata, sensei!

The implementation efficiently creates a batch request for fetching both the name and symbol of an NFT token in a single network call. This is a significant improvement over making separate calls.

Key improvements:

  1. Using a consistent block ID for both requests
  2. Properly structuring the call requests with the appropriate function selectors
  3. Creating a vector of requests for batching

702-730: Excellent implementation of batch request processing and response handling.

The code properly:

  1. Sends the batch request
  2. Handles the response with appropriate error checking
  3. Parses the results for both name and symbol
  4. Provides fallbacks when responses don't match expected formats

This approach reduces network overhead and improves performance by combining multiple calls into one request.

Comment thread crates/torii/sqlite/src/executor/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/torii/sqlite/src/executor/erc.rs (1)

322-322: Great addition of informative logging!

Ohayo sensei! This new logging statement provides valuable context about successfully registered NFT tokens. The info level is appropriate for this operational event, and including token details (name, symbol, contract address, and token ID) will be helpful for troubleshooting.

Consider breaking this very long line into multiple lines for better readability while maintaining the same information.

-            info!(target: LOG_TARGET, name = %result.name, symbol = %result.symbol, contract_address = %token.contract_address, token_id = %result.query.token_id, "NFT token registered.");
+            info!(
+                target: LOG_TARGET,
+                name = %result.name,
+                symbol = %result.symbol,
+                contract_address = %token.contract_address,
+                token_id = %result.query.token_id,
+                "NFT token registered."
+            );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 48d095c and e0ef268.

📒 Files selected for processing (2)
  • crates/torii/sqlite/src/executor/erc.rs (2 hunks)
  • crates/torii/sqlite/src/executor/mod.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/torii/sqlite/src/executor/mod.rs
🧰 Additional context used
🧬 Code Definitions (1)
crates/torii/sqlite/src/executor/erc.rs (1)
crates/torii/sqlite/src/utils.rs (4)
  • felt_and_u256_to_sql_string (44-46)
  • felt_to_sql_string (40-42)
  • sql_string_to_u256 (52-55)
  • u256_to_sql_string (48-50)
🪛 GitHub Actions: ci
crates/torii/sqlite/src/executor/erc.rs

[error] 3-3: Formatting issue detected: Code does not adhere to formatting standards.


[error] 18-18: Formatting issue detected: Code does not adhere to formatting standards.

🔇 Additional comments (3)
crates/torii/sqlite/src/executor/erc.rs (3)

7-7: Well done adding the explicit Mime type import!

Ohayo sensei! This is a good explicit import for the Mime type that's used on line 276. Making dependencies explicit improves code clarity.


13-13: Nice addition of info-level logging!

Ohayo sensei! Adding the info import to the tracing module is a good preparation for the new logging statement you've added. This will help with observability of token registration events.


21-22: Clean import organization!

The reordering of imports follows a logical pattern - keeping the alphabetical ordering while making sure related utilities are grouped together.

use cainome::cairo_serde::{ByteArray, CairoSerde};
use data_url::mime::Mime;
use data_url::DataUrl;
use data_url::mime::Mime;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Let's address the pipeline failure

Ohayo sensei! The CI pipeline is reporting formatting issues on lines 3 and 18, but these don't match with your changes. Before merging, please run cargo fmt on the entire file to ensure it meets formatting standards.

Also applies to: 13-13, 21-22, 322-322


🏁 Script executed:

#!/bin/bash
# Check rust formatting errors in the file
cd crates/torii/sqlite/src/executor/
rustfmt --check erc.rs || echo "Formatting issues detected"

Length of output: 224


Ohayo sensei! Please note there's an issue with the CI pipeline's formatting check.

It seems that the error ("rustfmt: command not found") indicates that our CI environment isn’t set up with rustfmt. This is causing the reported formatting issues on lines 3, 18, 13, 13, 21-22, and 322—even though your changes themselves don’t introduce formatting errors.

Action items:

  • Install rustfmt in the CI environment so that formatting checks can run properly.
  • Once rustfmt is available, run cargo fmt on the entire file (i.e., crates/torii/sqlite/src/executor/erc.rs) to ensure all formatting is up to standard.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/torii/sqlite/src/erc.rs (2)

207-225: Consider extracting the parse logic to reduce duplication.

Ohayo sensei! The parsing logic for name, symbol, and decimals follows similar patterns. To make the code more maintainable, consider extracting these into helper functions - especially for the name and symbol which share identical parsing logic.

- // Parse symbol
- let symbol = match &results[1] {
-     ProviderResponseData::Call(symbol) if symbol.len() == 1 => {
-         parse_cairo_short_string(&symbol[0]).unwrap()
-     }
-     ProviderResponseData::Call(symbol) => ByteArray::cairo_deserialize(symbol, 0)
-         .expect("Return value not ByteArray")
-         .to_string()
-         .expect("Return value not String"),
-     _ => return Err(anyhow::anyhow!("Invalid response for symbol")),
- };

+ // Helper function to parse short strings or byte arrays
+ fn parse_string_response(response: &ProviderResponseData, field_name: &str) -> Result<String> {
+     match response {
+         ProviderResponseData::Call(data) if data.len() == 1 => {
+             Ok(parse_cairo_short_string(&data[0]).unwrap())
+         }
+         ProviderResponseData::Call(data) => {
+             Ok(ByteArray::cairo_deserialize(data, 0)
+                 .expect("Return value not ByteArray")
+                 .to_string()
+                 .expect("Return value not String"))
+         }
+         _ => Err(anyhow::anyhow!("Invalid response for {}", field_name)),
+     }
+ }

+ // Parse name and symbol
+ let name = parse_string_response(&results[0], "name")?;
+ let symbol = parse_string_response(&results[1], "symbol")?;

220-225: Consider more descriptive error messages.

When handling errors for decimals, providing more context in the error message would make debugging easier.

- _ => return Err(anyhow::anyhow!("Invalid response for decimals")),
+ _ => return Err(anyhow::anyhow!("Invalid response type for decimals from contract {}", contract_address)),
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between e0ef268 and c6f8537.

📒 Files selected for processing (3)
  • crates/torii/sqlite/src/erc.rs (2 hunks)
  • crates/torii/sqlite/src/executor/erc.rs (2 hunks)
  • crates/torii/sqlite/src/executor/mod.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/torii/sqlite/src/executor/erc.rs
  • crates/torii/sqlite/src/executor/mod.rs
🧰 Additional context used
🧬 Code Definitions (1)
crates/torii/sqlite/src/erc.rs (2)
crates/torii/indexer/src/engine.rs (1)
  • block_id (196-203)
crates/torii/sqlite/src/model.rs (1)
  • contract_address (92-94)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
crates/torii/sqlite/src/erc.rs (3)

6-6: Ohayo sensei! Nice import additions for batch requests.

The addition of CallRequest and ProviderResponseData/ProviderRequestData imports sets the foundation for the batching optimization.

Also applies to: 9-9


164-193: Great implementation of batch requests!

Ohayo sensei! You've successfully created a batch request mechanism that consolidates three separate API calls into a single request. This is an excellent optimization that will reduce network overhead and improve indexer performance.

The structure with separate CallRequest objects for name, symbol, and decimals is clean and maintainable.


195-205: Solid parsing logic for the name response.

The pattern matching approach correctly handles both short string format and ByteArray deserialization cases. The error handling for invalid responses is also well implemented.

@codecov

codecov Bot commented Mar 28, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 86.95652% with 9 lines in your changes missing coverage. Please review.

Project coverage is 55.73%. Comparing base (ecf6889) to head (c6f8537).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
crates/torii/sqlite/src/erc.rs 87.17% 5 Missing ⚠️
crates/torii/sqlite/src/executor/mod.rs 86.20% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3132      +/-   ##
==========================================
- Coverage   55.74%   55.73%   -0.02%     
==========================================
  Files         443      443              
  Lines       62750    62760      +10     
==========================================
- Hits        34983    34980       -3     
- Misses      27767    27780      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Larkooo
Larkooo enabled auto-merge (squash) March 28, 2025 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants