github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/state_synchronization/requester/jobs/execution_data_reader.go (about)

     1  package jobs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    11  	"github.com/onflow/flow-go/module/executiondatasync/execution_data/cache"
    12  	"github.com/onflow/flow-go/module/irrecoverable"
    13  	"github.com/onflow/flow-go/storage"
    14  )
    15  
    16  // BlockEntry represents a block that's tracked by the ExecutionDataRequester
    17  type BlockEntry struct {
    18  	BlockID       flow.Identifier
    19  	Height        uint64
    20  	ExecutionData *execution_data.BlockExecutionDataEntity
    21  }
    22  
    23  var _ module.Jobs = (*ExecutionDataReader)(nil)
    24  
    25  // ExecutionDataReader provides an abstraction for consumers to read blocks as job.
    26  type ExecutionDataReader struct {
    27  	store *cache.ExecutionDataCache
    28  
    29  	fetchTimeout             time.Duration
    30  	highestConsecutiveHeight func() (uint64, error)
    31  
    32  	// TODO: refactor this to accept a context in AtIndex instead of storing it on the struct.
    33  	// This requires also refactoring jobqueue.Consumer
    34  	ctx irrecoverable.SignalerContext
    35  }
    36  
    37  // NewExecutionDataReader creates and returns a ExecutionDataReader.
    38  func NewExecutionDataReader(
    39  	store *cache.ExecutionDataCache,
    40  	fetchTimeout time.Duration,
    41  	highestConsecutiveHeight func() (uint64, error),
    42  ) *ExecutionDataReader {
    43  	return &ExecutionDataReader{
    44  		store:                    store,
    45  		fetchTimeout:             fetchTimeout,
    46  		highestConsecutiveHeight: highestConsecutiveHeight,
    47  	}
    48  }
    49  
    50  // AddContext adds a context to the execution data reader
    51  // TODO: this is an anti-pattern, refactor this to accept a context in AtIndex instead of storing
    52  // it on the struct.
    53  func (r *ExecutionDataReader) AddContext(ctx irrecoverable.SignalerContext) {
    54  	r.ctx = ctx
    55  }
    56  
    57  // AtIndex returns the block entry job at the given height, or storage.ErrNotFound.
    58  // Any other error is unexpected
    59  func (r *ExecutionDataReader) AtIndex(height uint64) (module.Job, error) {
    60  	if r.ctx == nil {
    61  		return nil, fmt.Errorf("execution data reader is not initialized")
    62  	}
    63  
    64  	// data for the requested height or a lower height, has not been downloaded yet.
    65  	highestHeight, err := r.highestConsecutiveHeight()
    66  	if err != nil {
    67  		return nil, fmt.Errorf("failed to get highest height: %w", err)
    68  	}
    69  
    70  	if height > highestHeight {
    71  		return nil, storage.ErrNotFound
    72  	}
    73  
    74  	ctx, cancel := context.WithTimeout(r.ctx, r.fetchTimeout)
    75  	defer cancel()
    76  
    77  	executionData, err := r.store.ByHeight(ctx, height)
    78  	if err != nil {
    79  		return nil, fmt.Errorf("failed to get execution data for height %d: %w", height, err)
    80  	}
    81  
    82  	return BlockEntryToJob(&BlockEntry{
    83  		BlockID:       executionData.BlockID,
    84  		Height:        height,
    85  		ExecutionData: executionData,
    86  	}), nil
    87  }
    88  
    89  // Head returns the highest consecutive block height with downloaded execution data
    90  func (r *ExecutionDataReader) Head() (uint64, error) {
    91  	return r.highestConsecutiveHeight()
    92  }