github.com/koko1123/flow-go-1@v0.29.6/storage/badger/blocks.go (about)

     1  // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED
     2  
     3  package badger
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/dgraph-io/badger/v3"
    10  
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/storage"
    13  	"github.com/koko1123/flow-go-1/storage/badger/operation"
    14  	"github.com/koko1123/flow-go-1/storage/badger/transaction"
    15  )
    16  
    17  // Blocks implements a simple block storage around a badger DB.
    18  type Blocks struct {
    19  	db       *badger.DB
    20  	headers  *Headers
    21  	payloads *Payloads
    22  }
    23  
    24  // NewBlocks ...
    25  func NewBlocks(db *badger.DB, headers *Headers, payloads *Payloads) *Blocks {
    26  	b := &Blocks{
    27  		db:       db,
    28  		headers:  headers,
    29  		payloads: payloads,
    30  	}
    31  	return b
    32  }
    33  
    34  func (b *Blocks) StoreTx(block *flow.Block) func(*transaction.Tx) error {
    35  	return func(tx *transaction.Tx) error {
    36  		err := b.headers.storeTx(block.Header)(tx)
    37  		if err != nil {
    38  			return fmt.Errorf("could not store header: %w", err)
    39  		}
    40  		err = b.payloads.storeTx(block.ID(), block.Payload)(tx)
    41  		if err != nil {
    42  			return fmt.Errorf("could not store payload: %w", err)
    43  		}
    44  		return nil
    45  	}
    46  }
    47  
    48  func (b *Blocks) retrieveTx(blockID flow.Identifier) func(*badger.Txn) (*flow.Block, error) {
    49  	return func(tx *badger.Txn) (*flow.Block, error) {
    50  		header, err := b.headers.retrieveTx(blockID)(tx)
    51  		if err != nil {
    52  			return nil, fmt.Errorf("could not retrieve header: %w", err)
    53  		}
    54  		payload, err := b.payloads.retrieveTx(blockID)(tx)
    55  		if err != nil {
    56  			return nil, fmt.Errorf("could not retrieve payload: %w", err)
    57  		}
    58  		block := &flow.Block{
    59  			Header:  header,
    60  			Payload: payload,
    61  		}
    62  		return block, nil
    63  	}
    64  }
    65  
    66  // Store ...
    67  func (b *Blocks) Store(block *flow.Block) error {
    68  	return operation.RetryOnConflictTx(b.db, transaction.Update, b.StoreTx(block))
    69  }
    70  
    71  // ByID ...
    72  func (b *Blocks) ByID(blockID flow.Identifier) (*flow.Block, error) {
    73  	tx := b.db.NewTransaction(false)
    74  	defer tx.Discard()
    75  	return b.retrieveTx(blockID)(tx)
    76  }
    77  
    78  // ByHeight ...
    79  func (b *Blocks) ByHeight(height uint64) (*flow.Block, error) {
    80  	tx := b.db.NewTransaction(false)
    81  	defer tx.Discard()
    82  
    83  	blockID, err := b.headers.retrieveIdByHeightTx(height)(tx)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	return b.retrieveTx(blockID)(tx)
    88  }
    89  
    90  // ByCollectionID ...
    91  func (b *Blocks) ByCollectionID(collID flow.Identifier) (*flow.Block, error) {
    92  	var blockID flow.Identifier
    93  	err := b.db.View(operation.LookupCollectionBlock(collID, &blockID))
    94  	if err != nil {
    95  		return nil, fmt.Errorf("could not look up block: %w", err)
    96  	}
    97  	return b.ByID(blockID)
    98  }
    99  
   100  // IndexBlockForCollections ...
   101  func (b *Blocks) IndexBlockForCollections(blockID flow.Identifier, collIDs []flow.Identifier) error {
   102  	for _, collID := range collIDs {
   103  		err := operation.RetryOnConflict(b.db.Update, operation.SkipDuplicates(operation.IndexCollectionBlock(collID, blockID)))
   104  		if err != nil {
   105  			return fmt.Errorf("could not index collection block (%x): %w", collID, err)
   106  		}
   107  	}
   108  	return nil
   109  }
   110  
   111  // InsertLastFullBlockHeightIfNotExists inserts the last full block height
   112  func (b *Blocks) InsertLastFullBlockHeightIfNotExists(height uint64) error {
   113  	return operation.RetryOnConflict(b.db.Update, func(tx *badger.Txn) error {
   114  		err := operation.InsertLastCompleteBlockHeightIfNotExists(height)(tx)
   115  		if err != nil {
   116  			return fmt.Errorf("could not set LastFullBlockHeight: %w", err)
   117  		}
   118  		return nil
   119  	})
   120  }
   121  
   122  // UpdateLastFullBlockHeight upsert (update or insert) the last full block height
   123  func (b *Blocks) UpdateLastFullBlockHeight(height uint64) error {
   124  	return operation.RetryOnConflict(b.db.Update, func(tx *badger.Txn) error {
   125  
   126  		// try to update
   127  		err := operation.UpdateLastCompleteBlockHeight(height)(tx)
   128  		if err == nil {
   129  			return nil
   130  		}
   131  
   132  		if !errors.Is(err, storage.ErrNotFound) {
   133  			return fmt.Errorf("could not update LastFullBlockHeight: %w", err)
   134  		}
   135  
   136  		// if key does not exist, try insert.
   137  		err = operation.InsertLastCompleteBlockHeight(height)(tx)
   138  		if err != nil {
   139  			return fmt.Errorf("could not insert LastFullBlockHeight: %w", err)
   140  		}
   141  
   142  		return nil
   143  	})
   144  }
   145  
   146  // GetLastFullBlockHeight ...
   147  func (b *Blocks) GetLastFullBlockHeight() (uint64, error) {
   148  	var h uint64
   149  	err := b.db.View(operation.RetrieveLastCompleteBlockHeight(&h))
   150  	if err != nil {
   151  		return 0, fmt.Errorf("failed to retrieve LastFullBlockHeight: %w", err)
   152  	}
   153  	return h, nil
   154  }