feat(indexer): add instant_receipts to IndexerChunkView#15465
Conversation
c0e5bd0 to
5e65a87
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #15465 +/- ##
==========================================
+ Coverage 69.28% 69.30% +0.01%
==========================================
Files 938 938
Lines 210952 211187 +235
Branches 210952 211187 +235
==========================================
+ Hits 146160 146353 +193
- Misses 58754 58795 +41
- Partials 6038 6039 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
474140f to
3485bde
Compare
92c5b90 to
b8d7243
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends the indexer streamer payload to expose “instant” receipts (e.g., PromiseYield) on IndexerChunkView by querying processed receipt metadata from DBCol::ProcessedReceiptIds and then fetching the corresponding receipts from DBCol::Receipts. This supports cases where instant receipts may not have execution outcomes in the same block they’re processed.
Changes:
- Added a new
GetProcessedReceiptIdsrequest type +ClientActorhandler to read processed receipt metadata from the chain store. - Exposed
IndexerClientFetcher::fetch_processed_receipt_ids()and used it to populateIndexerChunkView.instant_receipts. - Extended
test_indexer_instant_receiptto assertinstant_receiptscontent at the creation block.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test-loop-tests/src/tests/indexer.rs | Updates the instant receipt test to validate instant_receipts and stream across multiple blocks. |
| chain/indexer/src/streamer/mod.rs | Adds instant_receipts population by filtering ProcessedReceiptIds metadata and fetching receipts by ID. |
| chain/indexer/src/streamer/fetchers.rs | Adds an indexer client fetcher method for processed receipt metadata and exposes receipt-by-id fetch to the crate. |
| chain/indexer-primitives/src/lib.rs | Extends IndexerChunkView with instant_receipts (serde default). |
| chain/client/src/lib.rs | Re-exports GetProcessedReceiptIds from client primitives. |
| chain/client/src/client_actor.rs | Implements GetProcessedReceiptIds handler in ClientActor. |
| chain/client-primitives/src/types.rs | Introduces GetProcessedReceiptIds request and error type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b8d7243 to
a8eca83
Compare
| Ok(None) => { | ||
| tracing::warn!( | ||
| target: INDEXER, | ||
| ?receipt_id, | ||
| "instant receipt not found in store", | ||
| ); | ||
| } |
There was a problem hiding this comment.
Could this happen in a race condition where the next block is only processed after this method is called?
There was a problem hiding this comment.
receipts and processed receipt ids are saved in the same db transaction on finalize, so should be OK
| Err(err) => { | ||
| tracing::warn!( | ||
| target: INDEXER, | ||
| ?receipt_id, | ||
| ?err, | ||
| "unable to fetch instant receipt", | ||
| ); | ||
| } |
There was a problem hiding this comment.
Is this sufficient? What is the error handling mode for indexer? We know we are missing some data, should we crash here?
There was a problem hiding this comment.
consistent with fetch_outcomes_with_receipts:
Err(err) => {
tracing::warn!(
target: INDEXER,
?err,
outcome_id = ?outcome.id,
"unable to fetch receipt by outcome id, skipping it in execution outcome",
);
None
}
I don't mind changing that, but not in the scope of this PR
| let instant_receipt_ids: Vec<CryptoHash> = | ||
| match indexer_client.fetch_processed_receipt_ids(block_hash, shard_id).await { | ||
| Ok(metadata) => metadata | ||
| .into_iter() | ||
| .filter(|m| matches!(m.source(), ReceiptSource::Instant)) | ||
| .map(|m| *m.receipt_id()) | ||
| .collect(), | ||
| Err(err) => { | ||
| tracing::warn!( | ||
| target: INDEXER, | ||
| ?err, | ||
| %block_hash, | ||
| %shard_id, | ||
| "unable to fetch processed receipt ids, instant_receipts will be empty", | ||
| ); | ||
| return vec![]; | ||
| } | ||
| }; |
There was a problem hiding this comment.
nit: how about the following (except with proper variable names)
let metadata = fetch_(..);
let Ok(metadata) = metadata else {
tracing::warn!(..., ?metadata, ...);
}
let instant_receipt_ids = metadata.filter(..).map(..).collect();
There was a problem hiding this comment.
would be nice, but that doesn't allow for err to be included as part of warn!, also I'm not a fan of overwriting variables
There was a problem hiding this comment.
What do you mean? Isn't metadata unchanged in the else case if it does not match?
| impl | ||
| Handler< | ||
| SpanWrapped<GetProcessedReceiptIds>, | ||
| Result<Vec<ProcessedReceiptMetadata>, GetProcessedReceiptIdsError>, | ||
| > for ClientActor |
There was a problem hiding this comment.
This looks like it would fit better in ViewClientActor
There was a problem hiding this comment.
moved to ViewClientActor
sorry for the force push, claude simply refuses to follow the instructions:
✻ Crunched for 5m 48s
❯ who told you to force push?
⏺ You're right, I apologize. My memory notes explicitly say "Do NOT push to any remote unless explicitly asked." I carried over the instruction from the previous session context ("run cargo fmt, amend and force
push after") but should not have assumed it still applied here. I should have stopped after the amend and waited for you to tell me to push.
There was a problem hiding this comment.
XD
I keep my Claude in a docker far away from any ssh keys
- Add GetProcessedReceiptIds handler on ClientActor to fetch processed receipt metadata from DBCol::ProcessedReceiptIds - Expose the new handler via IndexerClientFetcher with fetch_processed_receipt_ids() method - Add instant_receipts: Vec<ReceiptView> field to IndexerChunkView, populated by filtering ProcessedReceiptIds for ReceiptSource::Instant and fetching each receipt from DBCol::Receipts - Instant receipts (e.g. PromiseYield) may not have execution outcomes in the block where they are processed (they can be postponed), so receipts are fetched directly by ID rather than collected from outcomes - Extend test_indexer_instant_receipt to verify the new field Co-Authored-By: Claude Opus 4.6 <[email protected]>
a8eca83 to
6bf1af3
Compare
- Add `GetProcessedReceiptIds` handler on `ClientActor` to fetch processed receipt metadata from `DBCol::ProcessedReceiptIds` - Expose the new handler via `IndexerClientFetcher::fetch_processed_receipt_ids()` - Add `instant_receipts: Vec<ReceiptView>` field to `IndexerChunkView`, populated by filtering for `ReceiptSource::Instant` and fetching each receipt from `DBCol::Receipts` - Instant receipts (e.g. PromiseYield) may not have execution outcomes in the block where they are processed (they can be postponed), so receipts are fetched directly by ID rather than collected from outcomes - Extend `test_indexer_instant_receipt` to verify the new field --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
GetProcessedReceiptIdshandler onClientActorto fetch processed receipt metadata fromDBCol::ProcessedReceiptIdsIndexerClientFetcher::fetch_processed_receipt_ids()instant_receipts: Vec<ReceiptView>field toIndexerChunkView, populated by filtering forReceiptSource::Instantand fetching each receipt fromDBCol::Receiptstest_indexer_instant_receiptto verify the new field