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 }