Skip to content

Conversation

@illia-malachyn
Copy link
Contributor

@illia-malachyn illia-malachyn commented Nov 27, 2025

Closes #8059, #7587, #8231, #8135

Changes:

  1. No more separate test for a streaming from a spork root block. Any test does it!
  2. Result info provider extended with new functionality: it recognizes execution forks now!
  3. Removed index from execution data tracker
  4. Removed tests for different backfill types as they are not needed.

Streamer streams all available blocks at the start, so either such tests should be there, or they should be removed, since they're basically useless - no matter how many blocks are available, all of them are streamed to the subscription.

  1. Introduce a data provider interface instead of a callback.
    Initially, I wanted to do it for testing, so I could easily mock this behavior and write good tests for the backend. However, the problem is that this logic is not injected into the endpoints. Therefore, it is not as useful for the backend tests, but they're useful for the tests in the subscription package (which might be extended/refactored in the [Data Availability] Introduction of a new subscription package #8131). Also, the interface makes it easier to reason about both the backend and subscription packages, as well as their interaction.

Please, review in the following order:
optimistic sync package (changes to result provider + its tests) -> subscription package (changes to streamer and response handler/data provider interface) -> backend -> tests for backend

Note, the tests for events and account statutes MUST fail in this PR, as the result provider and response handler func were significantly changed. We should merge PRs for the events/accounts into this one, then check if CI passes, and only after that we can merge everything into the feature/optimistic_sync

To be done in this PR:

  • enhance error handling

@illia-malachyn illia-malachyn changed the base branch from master to feature/optimistic-sync November 27, 2025 19:10
@illia-malachyn
Copy link
Contributor Author

@AndriiDiachuk

Refactored execution data tracker. I completely
removed the indexer dependency from it.

Response handler in execution data backend was
refactored in a way it supports criteria and
execution forks. More work to be done on this but
this is the first step forward.
@illia-malachyn illia-malachyn force-pushed the illia-malachyn/new-execution-data-tests branch from d0c8497 to 661a1d5 Compare November 28, 2025 11:19
@illia-malachyn illia-malachyn force-pushed the illia-malachyn/new-execution-data-tests branch 2 times, most recently from e0cbb11 to 5ecb6ab Compare November 28, 2025 14:26
@illia-malachyn illia-malachyn force-pushed the illia-malachyn/new-execution-data-tests branch from 5ecb6ab to 720d4c8 Compare December 1, 2025 13:15
@illia-malachyn illia-malachyn marked this pull request as ready for review December 1, 2025 13:17
@illia-malachyn illia-malachyn requested a review from a team as a code owner December 1, 2025 13:17
@illia-malachyn
Copy link
Contributor Author

@peterargue can u look into that ?

illia-malachyn and others added 6 commits December 3, 2025 09:47
- Removed tests for different types of backfill.
- Simplify tests by removing unecessary goroutines.
…handler-function-with-interface

Illia malachyn/replace response handler function with interface
@illia-malachyn
Copy link
Contributor Author

@peterargue @UlyanaAndrukhiv @AndriiDiachuk, guys, I guess this is ready for review. I'd really appreciate your comments/criticisms. If it feels too clumsy and complex, I can split it up into several smaller PRs.

Also, skip the tests for events and account statutes
as they depend on execution data test suite.
@illia-malachyn illia-malachyn changed the title Illia malachyn/new execution data tests [DataAvailability] Add new execution data tests Dec 8, 2025
@illia-malachyn illia-malachyn changed the title [DataAvailability] Add new execution data tests [DataAvailability] Implement new unit tests for execution data backend Dec 8, 2025
Copy link
Contributor

@UlyanaAndrukhiv UlyanaAndrukhiv left a comment

Choose a reason for hiding this comment

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

Overall, this looks good! I’ve left a few suggestions for improvements. I mostly skipped missing godocs for this round. I am also considering a refactoring of ExecutionDataBackend and executionDataProvider, but I’d like to try it first—if it works, I will provide a separate comment afterward.

Copy link
Contributor

@peterargue peterargue left a comment

Choose a reason for hiding this comment

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

thanks for the cleanup. I like your refactor of the provider.

Comment on lines +100 to +102
if startBlockID == b.state.Params().SporkRootBlock().ID() {
return b.rootBlockHeight, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

is this needed? shouldn't this also be returned by b.headers.ByBlockID(startBlockID)?

Copy link
Contributor Author

@illia-malachyn illia-malachyn Dec 15, 2025

Choose a reason for hiding this comment

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

IDK actually. This code existed for a long period of time so I'm afraid to remove it. I'm not even sure we need these trackers at all. I've created a task to investigate it and remove them in case we don't #8238

// ignorable errors correctly. It ensures that the provider retries or waits when encountering
// errors like missing required executors, rather than terminating the subscription immediately,
// until the context is canceled.
func (s *BackendExecutionDataSuite2) TestExecutionDataProviderIgnorableErrors() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think "ignorable" is the right framing. how about

Suggested change
func (s *BackendExecutionDataSuite2) TestExecutionDataProviderIgnorableErrors() {
func (s *BackendExecutionDataSuite2) TestExecutionDataProviderCriteriaNotMet() {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TestExecutionDataProviderErrors is also a spectre of errors where a criteria is not met. However, in one case, we stop streaming data, and in another we keep doing it.

// that we wouldn't consider anyway.
if criteria.ParentExecutionResultID != flow.ZeroID &&
executionResult.PreviousResultID != criteria.ParentExecutionResultID {
return optimistic_sync.ErrForkAbandoned
Copy link
Contributor

Choose a reason for hiding this comment

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

this doesn't mean the fork was abandoned, just that the provided result's fork does not include the parent result.

Copy link
Contributor Author

@illia-malachyn illia-malachyn Dec 23, 2025

Choose a reason for hiding this comment

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

Hmm, will sth like that work?

func (p *Provider) findResultAndExecutors(...) (flow.Identifier, flow.IdentifierList, error) {
    // ... (ByBlockID call)

    var (
        hasParentMismatch bool
        lastErr           error
    )

    for executionResultID, executionReceiptList := range allReceiptsForBlock.GroupByResultID() {
        // ...
        if err := p.isExecutorGroupMeetingCriteria(result, receipts, criteria); err != nil {
            if errors.Is(err, optimistic_sync.ErrParentResultMismatch) { // 1. We return a different error
                hasParentMismatch = true
            }
            lastErr = err
            continue
        }
        // ... (collect matchingResults)
    }

    if len(matchingResults) == 0 {
        // If we found results but they all had the wrong parent, 
        // this is where we might eventually conclude the fork we are following is gone.
        if hasParentMismatch {
             return flow.ZeroID, nil, optimistic_sync.ErrForkAbandoned // 2. We state that this is a fork-abandoned error
        }
        
        if lastErr != nil {
            return flow.ZeroID, nil, lastErr
        }
        return flow.ZeroID, nil, optimistic_sync.ErrNotEnoughAgreeingExecutors
    }
    // ...
}

Copy link
Contributor

Choose a reason for hiding this comment

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

When we talk about an abandoned result, we mean a result that conflicts with a sealed result for a given block. Given results form a chain, if an ancestor result is abandoned, you can deduce that all of its ancestors must be as well.

Checking if a result conflicts with a sealed result is simple. All you have to do is check if there was a match for the block, and if not, check if the block is sealed (i.e. has a sealed result). I think Uliana added functionality to check this already.

In the case where the result is not yet sealed, but its ancestor is abandoned, you could optimize by flagging the result abandoned early so the client could react sooner. I don't think this is required in the first version.

my preference is to keep this functionality simple and and focused. The job of the results provider is to find the result given the provided criteria. then given a result, the snapshot provider can indicate if the result is abandoned or not. I think in the optimized case, we could do something like check for the next result. if there is no match, check the status of the previous result to see if it is abandoned. I'm not 100% sure if this is the right approach though, so let's leave it for later and just use the simple sealed check for now.

Comment on lines +59 to +64
// - [optimistic_sync.ErrForkAbandoned]: If the execution fork of an execution node from which we were getting the
// execution results was abandoned.
// - [optimistic_sync.ErrNotEnoughAgreeingExecutors]: If there are not enough execution nodes that produced the
// execution result.
// - [optimistic_sync.ErrRequiredExecutorNotFound]: If the criteria's required executor is not in the group of
// execution nodes that produced the execution result.
Copy link
Contributor

Choose a reason for hiding this comment

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

these errors relate to a specific execution result, but this method queries a set of results for a block. Instead of returning these, I think this method should simply state that no result matched the requested criteria.

looking at the returned errors, it seems there are a few cases:

  • The block has not been executed yet (no receipts found)
  • The block is before the node's available history
  • No results found that matched the criteria (unsealed block)
  • No results found that matched the criteria (sealed block)
  • The criteria was invalid

Comment on lines +274 to +277
// Expected errors during normal operations:
// - [optimistic_sync.ErrForkAbandoned]: If the execution result is in a different fork than the one specified in the criteria.
// - [optimistic_sync.ErrNotEnoughAgreeingExecutors]: If the group does not have enough agreeing executors.
// - [optimistic_sync.ErrRequiredExecutorNotFound]: If the required executor is not in the group.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure it's useful to return the specific error here. we generally just want to know if this result matched the criteria.

@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch illia-malachyn/new-execution-data-tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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.

5 participants