-
Notifications
You must be signed in to change notification settings - Fork 9
Scheduled Transactions indexing #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAlways fetch the first system-collection transaction; for spork.Version >= 8, inspect its events for PendingExecution and iteratively fetch scheduled and final system transactions with per-transaction retries/backoff and context-cancellation checks, processing system-collection events in the normal flow without a special-case skip. Changes
Sequence Diagram(s)sequenceDiagram
participant Flow as Process Flow
participant Fetcher as Transaction Fetcher
participant Inspector as Event Inspector
participant Collector as Data Collector
rect rgb(240,248,255)
Flow->>Fetcher: Fetch first system-collection transaction
Fetcher-->>Flow: Return first system transaction
Flow->>Inspector: Inspect events for PendingExecution
end
alt spork.Version >= 8 AND PendingExecution found
rect rgb(245,255,240)
loop while PendingExecution present
Flow->>Fetcher: Fetch next scheduled/final system transaction (retries/backoff, context-aware)
Fetcher-->>Flow: Return transaction or NotFound
alt NotFound
Flow-->>Flow: Break fetch loop
else Returned
Flow->>Inspector: Inspect events for more PendingExecution
end
end
end
end
rect rgb(255,250,240)
Flow->>Collector: Fetch transaction results and full infos (with per-fetch backoff and cancellation checks)
Collector-->>Flow: Complete
Flow->>Collector: Process/index system-collection events (normal flow)
Collector-->>Flow: Done
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
UlyanaAndrukhiv
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
state/process.go
Outdated
| select { | ||
| case <-ctx.Done(): | ||
| return | ||
| default: | ||
| } | ||
| client := spork.AccessNodes.Client() | ||
| txnResult, err := client.TransactionResult(ctx, hash, uint32(txnIndex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| select { | |
| case <-ctx.Done(): | |
| return | |
| default: | |
| } | |
| client := spork.AccessNodes.Client() | |
| txnResult, err := client.TransactionResult(ctx, hash, uint32(txnIndex)) | |
| client := spork.AccessNodes.Client() | |
| txnResult, err := client.TransactionResult(ctx, hash, uint32(txnIndex)) |
Do we need the explicit context check if we are passing it to a blocking network call right away? My expectation would be that client.TransactionResult will exit if the context is canceled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My expectation would be that client.TransactionResult will exit if the context is canceled.
it will
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just copying code here; it does seem client.TransactionResult will return with the context's status if the context is canceled. So it would be something like
| select { | |
| case <-ctx.Done(): | |
| return | |
| default: | |
| } | |
| client := spork.AccessNodes.Client() | |
| txnResult, err := client.TransactionResult(ctx, hash, uint32(txnIndex)) | |
| client := spork.AccessNodes.Client() | |
| txnResult, err := client.TransactionResult(ctx, hash, uint32(txnIndex)) | |
| if errors.Is(err, context.Canceled) { | |
| return | |
| } |
which IMO is not really much better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to handle the case precisely, you would need this anyway since it's possible the context was canceled during the request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated these instances to check errors for cancellation in 3e83242
Co-authored-by: Jordan Schalm <jordan@dapperlabs.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
state/process.go (1)
405-413: Critical: System collection transactions will cause panic here.This code accesses
col.txns[idx].Payerfor all collections, including system collections. In the slow-path processing (lines 188-253), system collections havecol.txnResultspopulated butcol.txnsremains empty. When this loop processes system collection transactions, accessingcol.txns[idx]will panic with an index out of range error.This issue stems from the incomplete system transaction fetching in lines 216-252 (see my previous comment). The fix requires ensuring full transaction objects are fetched for system transactions, not just transaction results.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
state/process.go(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
state/process.go (1)
log/log.go (1)
Errorf(72-74)
🔇 Additional comments (5)
state/process.go (5)
7-7: LGTM!The
errorsimport is necessary for the newerrors.Is(err, context.Canceled)checks added throughout the slow-path processing logic.
139-177: LGTM - Context cancellation handling is correct.The explicit context cancellation checks after fetch operations properly end the span and return gracefully. While the client calls will return with context status, these checks ensure cleanup happens immediately after detecting cancellation.
206-209: LGTM - Consistent context cancellation handling.
237-240: LGTM - Context cancellation check is correct.
196-215: LGTM - First system transaction fetch is correct.The logic correctly retrieves the first transaction of the system collection with appropriate retry and cancellation handling.
| if initialTxn { | ||
| initialTxn = false | ||
| } else { | ||
| time.Sleep(time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is different. we're no longer sleeping for each request, just failing requests. I think that's ok since we will receive an error if the request is rate limited, which will cause it to sleep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In each of these cases there's only one request, and the sleep was only occurring on retries - notice that initialTxn := true was run for every transactionID (so every unique request that is made). The updated code should have the same behaviour as far as I can tell - no sleep on the first attempt, and sleep before retrying.
state/process.go
Outdated
| txnIndex, hash, height, err, | ||
| ) | ||
| if errors.Is(err, context.Canceled) { | ||
| span.End() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handling a canceled context previously didn't call end. is it OK to add it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of a canceled context, we will be returning anyways and abandoning the processing of the block (this was already the case). I think the previous code was incorrect in not calling end or deferring a call to it, as the span will be going out of scope.
state/process.go
Outdated
| col.CollectionId, hash, height, err, | ||
| ) | ||
| if errors.Is(err, context.Canceled) { | ||
| span.End() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this something that could be called in a defer, or are there cases where we return without calling it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I avoided using a defer because of the trace.EndSpanOk() being called when the function is successful, but it seems that span.End should be idempotent (so ok to be defer). I don't know if there is a case where we would want to return without calling it, and I assumed not.
There was a problem hiding this comment.
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)
state/process.go (1)
44-75: Defer onspan.End()should defensively handle zero-value span and double-endDeferring
span.End()before any call totrace.NewSpanassumes the zero value oftrace.Spanis safe to callEnd()on. Iftrace.Spanis an interface whose zero value isnil, this will panic on early returns (for example ifrctxis already canceled before the first span is created). In addition, you now both calltrace.EndSpanOk(span)andspan.End()on the same span, which may be redundant ifEndSpanOkalso ends the span.To make this robust regardless of the concrete
trace.Spantype, consider guarding the deferred call and relying on it as the single place that ends the span:var span trace.Span defer func() { if span != nil { span.End() } }()and, if
EndSpanOkinternally callsEnd, consider adjusting it (or dropping the explicitspan.End()here) so the span is only ended in one path.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
state/process.go(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
state/process.go (2)
access/access.go (1)
Client(66-69)log/log.go (1)
Errorf(72-74)
🔇 Additional comments (2)
state/process.go (2)
139-143: Cancellation-aware retries in slow-path fetch loops look goodThe new
codes.Canceledchecks in the slow-path collection/transaction/transaction-result loops correctly bail out on cancellation and only sleep/retry on other errors. This prevents spinning when the context is canceled while preserving the previous retry-with-backoff behavior for transient failures.Also applies to: 157-161, 174-178
196-271: Syntaxfor range scheduledTxs + 1is valid in Go 1.22+ and does not require changesGo 1.22 introduced support for ranging over integers with the syntax
for range N. The repository uses Go 1.25.0 (per go.mod), which fully supports this feature. The code at line 227 in state/process.go—for range scheduledTxs + 1 {—is valid Go syntax and will compile without error. When ranging over an integer, the loop iterates the specified number of times (0 through N-1), so this loop correctly executesscheduledTxs + 1iterations.The suggested refactor to replace it with a traditional 3-clause
for i := 0; i < scheduledTxs+1; i++loop is unnecessary.Likely an incorrect or invalid review comment.
Enable processing of transactions and events in the system collection.
Summary by CodeRabbit
Bug Fixes
Improvements