github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/executiondatasync/execution_data/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/model/flow"
     8  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
     9  	"github.com/onflow/flow-go/module/mempool"
    10  	"github.com/onflow/flow-go/storage"
    11  )
    12  
    13  // ExecutionDataCache is a read-through cache for ExecutionData.
    14  type ExecutionDataCache struct {
    15  	backend execution_data.ExecutionDataGetter
    16  
    17  	headers storage.Headers
    18  	seals   storage.Seals
    19  	results storage.ExecutionResults
    20  	cache   mempool.ExecutionData
    21  }
    22  
    23  // NewExecutionDataCache returns a new ExecutionDataCache.
    24  func NewExecutionDataCache(
    25  	backend execution_data.ExecutionDataGetter,
    26  	headers storage.Headers,
    27  	seals storage.Seals,
    28  	results storage.ExecutionResults,
    29  	cache mempool.ExecutionData,
    30  ) *ExecutionDataCache {
    31  	return &ExecutionDataCache{
    32  		backend: backend,
    33  
    34  		headers: headers,
    35  		seals:   seals,
    36  		results: results,
    37  		cache:   cache,
    38  	}
    39  }
    40  
    41  // ByID returns the execution data for the given ExecutionDataID.
    42  //
    43  // Expected errors during normal operations:
    44  // - BlobNotFoundError if some CID in the blob tree could not be found from the blobstore
    45  // - MalformedDataError if some level of the blob tree cannot be properly deserialized
    46  // - BlobSizeLimitExceededError if some blob in the blob tree exceeds the maximum allowed size
    47  func (c *ExecutionDataCache) ByID(ctx context.Context, executionDataID flow.Identifier) (*execution_data.BlockExecutionDataEntity, error) {
    48  	execData, err := c.backend.Get(ctx, executionDataID)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return execution_data.NewBlockExecutionDataEntity(executionDataID, execData), nil
    54  }
    55  
    56  // ByBlockID returns the execution data for the given block ID.
    57  //
    58  // Expected errors during normal operations:
    59  // - storage.ErrNotFound if a seal or execution result is not available for the block
    60  // - BlobNotFoundError if some CID in the blob tree could not be found from the blobstore
    61  // - MalformedDataError if some level of the blob tree cannot be properly deserialized
    62  // - BlobSizeLimitExceededError if some blob in the blob tree exceeds the maximum allowed size
    63  func (c *ExecutionDataCache) ByBlockID(ctx context.Context, blockID flow.Identifier) (*execution_data.BlockExecutionDataEntity, error) {
    64  	if execData, ok := c.cache.ByID(blockID); ok {
    65  		return execData, nil
    66  	}
    67  
    68  	executionDataID, err := c.LookupID(blockID)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	execData, err := c.backend.Get(ctx, executionDataID)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	execDataEntity := execution_data.NewBlockExecutionDataEntity(executionDataID, execData)
    79  
    80  	_ = c.cache.Add(execDataEntity)
    81  
    82  	return execDataEntity, nil
    83  }
    84  
    85  // ByHeight returns the execution data for the given block height.
    86  //
    87  // Expected errors during normal operations:
    88  // - storage.ErrNotFound if a seal or execution result is not available for the block
    89  // - BlobNotFoundError if some CID in the blob tree could not be found from the blobstore
    90  // - MalformedDataError if some level of the blob tree cannot be properly deserialized
    91  // - BlobSizeLimitExceededError if some blob in the blob tree exceeds the maximum allowed size
    92  func (c *ExecutionDataCache) ByHeight(ctx context.Context, height uint64) (*execution_data.BlockExecutionDataEntity, error) {
    93  	blockID, err := c.headers.BlockIDByHeight(height)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	return c.ByBlockID(ctx, blockID)
    99  }
   100  
   101  // LookupID returns the ExecutionDataID for the given block ID.
   102  //
   103  // Expected errors during normal operations:
   104  // - storage.ErrNotFound if a seal or execution result is not available for the block
   105  func (c *ExecutionDataCache) LookupID(blockID flow.Identifier) (flow.Identifier, error) {
   106  	seal, err := c.seals.FinalizedSealForBlock(blockID)
   107  	if err != nil {
   108  		return flow.ZeroID, fmt.Errorf("failed to lookup seal for block %s: %w", blockID, err)
   109  	}
   110  
   111  	result, err := c.results.ByID(seal.ResultID)
   112  	if err != nil {
   113  		return flow.ZeroID, fmt.Errorf("failed to lookup execution result for block %s: %w", blockID, err)
   114  	}
   115  
   116  	return result.ExecutionDataID, nil
   117  }