github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/backend/retry_test.go (about) 1 package backend 2 3 import ( 4 "context" 5 6 "github.com/stretchr/testify/mock" 7 "google.golang.org/grpc/codes" 8 "google.golang.org/grpc/status" 9 10 "github.com/onflow/flow/protobuf/go/flow/access" 11 "github.com/onflow/flow/protobuf/go/flow/execution" 12 13 "github.com/koko1123/flow-go-1/model/flow" 14 "github.com/koko1123/flow-go-1/module/metrics" 15 protocol "github.com/koko1123/flow-go-1/state/protocol/mock" 16 realstorage "github.com/koko1123/flow-go-1/storage" 17 "github.com/koko1123/flow-go-1/utils/unittest" 18 ) 19 20 // TestTransactionRetry tests that the retry mechanism will send retries at specific times 21 func (suite *Suite) TestTransactionRetry() { 22 23 // ctx := context.Background() 24 collection := unittest.CollectionFixture(1) 25 transactionBody := collection.Transactions[0] 26 block := unittest.BlockFixture() 27 // Height needs to be at least DefaultTransactionExpiry before we start doing retries 28 block.Header.Height = flow.DefaultTransactionExpiry + 1 29 transactionBody.SetReferenceBlockID(block.ID()) 30 headBlock := unittest.BlockFixture() 31 headBlock.Header.Height = block.Header.Height - 1 // head is behind the current block 32 suite.state.On("Final").Return(suite.snapshot, nil).Maybe() 33 34 suite.snapshot.On("Head").Return(headBlock.Header, nil) 35 snapshotAtBlock := new(protocol.Snapshot) 36 snapshotAtBlock.On("Head").Return(block.Header, nil) 37 suite.state.On("AtBlockID", block.ID()).Return(snapshotAtBlock, nil) 38 39 // collection storage returns a not found error 40 suite.collections.On("LightByTransactionID", transactionBody.ID()).Return(nil, realstorage.ErrNotFound) 41 42 // txID := transactionBody.ID() 43 // blockID := block.ID() 44 // Setup Handler + Retry 45 backend := New(suite.state, 46 suite.colClient, 47 nil, 48 suite.blocks, 49 suite.headers, 50 suite.collections, 51 suite.transactions, 52 suite.receipts, 53 suite.results, 54 suite.chainID, 55 metrics.NewNoopCollector(), 56 nil, 57 false, 58 DefaultMaxHeightRange, 59 nil, 60 nil, 61 suite.log, 62 DefaultSnapshotHistoryLimit, 63 ) 64 retry := newRetry().SetBackend(backend).Activate() 65 backend.retry = retry 66 67 retry.RegisterTransaction(block.Header.Height, transactionBody) 68 69 suite.colClient.On("SendTransaction", mock.Anything, mock.Anything).Return(&access.SendTransactionResponse{}, nil) 70 71 // Don't retry on every height 72 retry.Retry(block.Header.Height + 1) 73 74 suite.colClient.AssertNotCalled(suite.T(), "SendTransaction", mock.Anything, mock.Anything) 75 76 // Retry every `retryFrequency` 77 retry.Retry(block.Header.Height + retryFrequency) 78 79 suite.colClient.AssertNumberOfCalls(suite.T(), "SendTransaction", 1) 80 81 // do not retry if expired 82 retry.Retry(block.Header.Height + retryFrequency + flow.DefaultTransactionExpiry) 83 84 // Should've still only been called once 85 suite.colClient.AssertNumberOfCalls(suite.T(), "SendTransaction", 1) 86 87 suite.assertAllExpectations() 88 } 89 90 // TestSuccessfulTransactionsDontRetry tests that the retry mechanism will send retries at specific times 91 func (suite *Suite) TestSuccessfulTransactionsDontRetry() { 92 93 ctx := context.Background() 94 collection := unittest.CollectionFixture(1) 95 transactionBody := collection.Transactions[0] 96 block := unittest.BlockFixture() 97 // Height needs to be at least DefaultTransactionExpiry before we start doing retries 98 block.Header.Height = flow.DefaultTransactionExpiry + 1 99 transactionBody.SetReferenceBlockID(block.ID()) 100 101 light := collection.Light() 102 suite.state.On("Final").Return(suite.snapshot, nil).Maybe() 103 // transaction storage returns the corresponding transaction 104 suite.transactions.On("ByID", transactionBody.ID()).Return(transactionBody, nil) 105 // collection storage returns the corresponding collection 106 suite.collections.On("LightByTransactionID", transactionBody.ID()).Return(&light, nil) 107 // block storage returns the corresponding block 108 suite.blocks.On("ByCollectionID", collection.ID()).Return(&block, nil) 109 110 txID := transactionBody.ID() 111 blockID := block.ID() 112 exeEventReq := execution.GetTransactionResultRequest{ 113 BlockId: blockID[:], 114 TransactionId: txID[:], 115 } 116 exeEventResp := execution.GetTransactionResultResponse{ 117 Events: nil, 118 } 119 120 _, enIDs := suite.setupReceipts(&block) 121 suite.snapshot.On("Identities", mock.Anything).Return(enIDs, nil) 122 connFactory := suite.setupConnectionFactory() 123 124 // Setup Handler + Retry 125 backend := New(suite.state, 126 suite.colClient, 127 nil, 128 suite.blocks, 129 suite.headers, 130 suite.collections, 131 suite.transactions, 132 suite.receipts, 133 suite.results, 134 suite.chainID, 135 metrics.NewNoopCollector(), 136 connFactory, 137 false, 138 DefaultMaxHeightRange, 139 nil, 140 nil, 141 suite.log, 142 DefaultSnapshotHistoryLimit, 143 ) 144 retry := newRetry().SetBackend(backend).Activate() 145 backend.retry = retry 146 147 retry.RegisterTransaction(block.Header.Height, transactionBody) 148 149 suite.colClient.On("SendTransaction", mock.Anything, mock.Anything).Return(&access.SendTransactionResponse{}, nil) 150 151 // return not found to return finalized status 152 suite.execClient.On("GetTransactionResult", ctx, &exeEventReq).Return(&exeEventResp, status.Errorf(codes.NotFound, "not found")).Once() 153 // first call - when block under test is greater height than the sealed head, but execution node does not know about Tx 154 result, err := backend.GetTransactionResult(ctx, txID) 155 suite.checkResponse(result, err) 156 157 // status should be finalized since the sealed blocks is smaller in height 158 suite.Assert().Equal(flow.TransactionStatusFinalized, result.Status) 159 160 // Don't retry now now that block is finalized 161 retry.Retry(block.Header.Height + 1) 162 163 suite.colClient.AssertNotCalled(suite.T(), "SendTransaction", mock.Anything, mock.Anything) 164 165 // Don't retry now now that block is finalized 166 retry.Retry(block.Header.Height + retryFrequency) 167 168 suite.colClient.AssertNotCalled(suite.T(), "SendTransaction", mock.Anything, mock.Anything) 169 170 // Don't retry now now that block is finalized 171 retry.Retry(block.Header.Height + retryFrequency + flow.DefaultTransactionExpiry) 172 173 // Should've still should not be called 174 suite.colClient.AssertNotCalled(suite.T(), "SendTransaction", mock.Anything, mock.Anything) 175 176 suite.assertAllExpectations() 177 }