github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/backend/retry.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync"
     7  
     8  	"github.com/koko1123/flow-go-1/model/flow"
     9  	"github.com/koko1123/flow-go-1/storage"
    10  )
    11  
    12  // retryFrequency has to be less than TransactionExpiry or else this module does nothing
    13  const retryFrequency uint64 = 120 // blocks
    14  
    15  // Retry implements a simple retry mechanism for transaction submission.
    16  type Retry struct {
    17  	mu sync.RWMutex
    18  	// pending transactions
    19  	transactionByReferencBlockHeight map[uint64]map[flow.Identifier]*flow.TransactionBody
    20  	backend                          *Backend
    21  	active                           bool
    22  }
    23  
    24  func newRetry() *Retry {
    25  	return &Retry{
    26  		transactionByReferencBlockHeight: map[uint64]map[flow.Identifier]*flow.TransactionBody{},
    27  	}
    28  }
    29  
    30  func (r *Retry) Activate() *Retry {
    31  	r.active = true
    32  	return r
    33  }
    34  
    35  func (r *Retry) IsActive() bool {
    36  	return r.active
    37  }
    38  
    39  func (r *Retry) SetBackend(b *Backend) *Retry {
    40  	r.backend = b
    41  	return r
    42  }
    43  
    44  func (r *Retry) Retry(height uint64) {
    45  	// No need to retry if height is lower than DefaultTransactionExpiry
    46  	if height < flow.DefaultTransactionExpiry {
    47  		return
    48  	}
    49  
    50  	// naive cleanup for now, prune every 120 blocks
    51  	if height%retryFrequency == 0 {
    52  		r.prune(height)
    53  	}
    54  
    55  	heightToRetry := height - flow.DefaultTransactionExpiry + retryFrequency
    56  
    57  	for heightToRetry < height {
    58  		r.retryTxsAtHeight(heightToRetry)
    59  
    60  		heightToRetry = heightToRetry + retryFrequency
    61  	}
    62  
    63  }
    64  
    65  func (b *Retry) Notify(signal interface{}) bool {
    66  	height, ok := signal.(uint64)
    67  	if !ok {
    68  		return false
    69  	}
    70  	b.Retry(height)
    71  	return true
    72  }
    73  
    74  // RegisterTransaction adds a transaction that could possibly be retried
    75  func (r *Retry) RegisterTransaction(height uint64, tx *flow.TransactionBody) {
    76  	r.mu.Lock()
    77  	defer r.mu.Unlock()
    78  	if r.transactionByReferencBlockHeight[height] == nil {
    79  		r.transactionByReferencBlockHeight[height] = make(map[flow.Identifier]*flow.TransactionBody)
    80  	}
    81  	r.transactionByReferencBlockHeight[height][tx.ID()] = tx
    82  }
    83  
    84  func (r *Retry) prune(height uint64) {
    85  	r.mu.Lock()
    86  	defer r.mu.Unlock()
    87  	// If height is less than the default, there will be no expired transactions
    88  	if height < flow.DefaultTransactionExpiry {
    89  		return
    90  	}
    91  	for h := range r.transactionByReferencBlockHeight {
    92  		if h < height-flow.DefaultTransactionExpiry {
    93  			delete(r.transactionByReferencBlockHeight, h)
    94  		}
    95  	}
    96  }
    97  
    98  func (r *Retry) retryTxsAtHeight(heightToRetry uint64) {
    99  	r.mu.Lock()
   100  	defer r.mu.Unlock()
   101  	txsAtHeight := r.transactionByReferencBlockHeight[heightToRetry]
   102  	for txID, tx := range txsAtHeight {
   103  		// find the block for the transaction
   104  		block, err := r.backend.lookupBlock(txID)
   105  		if err != nil {
   106  			if !errors.Is(err, storage.ErrNotFound) {
   107  				continue
   108  			}
   109  			block = nil
   110  		}
   111  
   112  		// find the transaction status
   113  		status, err := r.backend.deriveTransactionStatus(tx, false, block)
   114  		if err != nil {
   115  			continue
   116  		}
   117  		if status == flow.TransactionStatusPending {
   118  			_ = r.backend.SendRawTransaction(context.Background(), tx)
   119  		} else if status != flow.TransactionStatusUnknown {
   120  			// not pending or unknown, don't need to retry anymore
   121  			delete(txsAtHeight, txID)
   122  		}
   123  	}
   124  }