Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Access] Add util command to backfill tx error messages db #6525

Draft
wants to merge 67 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
527efed
Added implementation of storing in the badger db transaction result e…
UlyanaAndrukhiv Sep 10, 2024
5222dca
Added fetching and storing transaction result error messages, refacto…
UlyanaAndrukhiv Sep 12, 2024
5394c36
Updated API LookupErrorMessage calls, updated tx result error message…
UlyanaAndrukhiv Sep 17, 2024
61c2fb4
Merged with master
UlyanaAndrukhiv Sep 17, 2024
c5d0d57
Linted
UlyanaAndrukhiv Sep 17, 2024
ce87d79
Added verification check for process transaction result error messages
UlyanaAndrukhiv Sep 17, 2024
910768b
Added tests for storing transaction result error messages
UlyanaAndrukhiv Sep 17, 2024
b068995
Updated storing tx result error messages by index, updated tests
UlyanaAndrukhiv Sep 18, 2024
66bf7cb
Added constant message storing to db if no execution nodes return a v…
UlyanaAndrukhiv Sep 18, 2024
5dbfbe2
Updated storing failed tx error messages for LookupErrorMessageByTran…
UlyanaAndrukhiv Sep 18, 2024
894bae6
Added missed check for LookupErrorMessageByTransactionID
UlyanaAndrukhiv Sep 18, 2024
0f5c2bd
Updated lookup error messages api calls
UlyanaAndrukhiv Sep 19, 2024
8fe719c
Updated lookup error messages api checks, added part of backend tx tests
UlyanaAndrukhiv Sep 19, 2024
0b50f46
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Sep 19, 2024
e8c2305
Added more tests for LookupErrorMessages, added godoc for tests
UlyanaAndrukhiv Sep 19, 2024
2c6e42e
Removed outdated comment
UlyanaAndrukhiv Sep 19, 2024
e4dedc3
Updated access tests
UlyanaAndrukhiv Sep 19, 2024
7201c34
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Sep 20, 2024
0f66bbb
Simplified creation of mock connection factory in backend tests
UlyanaAndrukhiv Sep 20, 2024
bc77f44
Added functional test for properly fetching processed and storing tra…
UlyanaAndrukhiv Sep 23, 2024
d25af6a
Refactored backend api according to suggestion
UlyanaAndrukhiv Sep 23, 2024
a1b7f8a
Added integration test for storing tx error messages
UlyanaAndrukhiv Sep 24, 2024
75368bb
Updated last commit
UlyanaAndrukhiv Sep 24, 2024
0898bfa
Updated TestTransactionResultErrorMessagesAreFetched
UlyanaAndrukhiv Sep 24, 2024
1eac423
Linted
UlyanaAndrukhiv Sep 24, 2024
91773f3
Created ExecutionNodeIdentitiesProvider, refactored ExecutionNodesFor…
UlyanaAndrukhiv Sep 25, 2024
a3b1fed
Moved ExecutionNodeIdentitiesProvider to node builder, updated tests
UlyanaAndrukhiv Sep 25, 2024
69ea579
Renamed field to execNodeIdentitiesProvider
UlyanaAndrukhiv Sep 25, 2024
6bcff19
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Sep 25, 2024
4d99fea
Removed unnecessary changes
UlyanaAndrukhiv Sep 25, 2024
4df1b76
Merged
UlyanaAndrukhiv Sep 25, 2024
9306618
Added command to access node, implemented Validator to command
UlyanaAndrukhiv Sep 26, 2024
010492e
Removed unused storages in test
UlyanaAndrukhiv Sep 30, 2024
be6e165
Merge branch 'UlyanaAndrukhiv/6497-refactor-executionNodesForBlockID'…
UlyanaAndrukhiv Sep 30, 2024
975ee02
Changed creation place of TransactionResultErrorMessages storage, upd…
UlyanaAndrukhiv Oct 1, 2024
ebb10bb
Added missed check for engine to avoid extra work
UlyanaAndrukhiv Oct 1, 2024
4003662
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Oct 1, 2024
5544cb4
Merge branch 'UlyanaAndrukhiv/6302-store-transaction-result-error-mes…
UlyanaAndrukhiv Oct 1, 2024
cce8ebd
Added testing db Exists
UlyanaAndrukhiv Oct 1, 2024
059e75c
Merge branch 'master' of github.com:The-K-R-O-K/flow-go into UlyanaAn…
UlyanaAndrukhiv Oct 2, 2024
93a321e
Merged with master
UlyanaAndrukhiv Oct 2, 2024
7393e09
Merge branch 'UlyanaAndrukhiv/6302-store-transaction-result-error-mes…
UlyanaAndrukhiv Oct 2, 2024
28ec5cd
Updated validation for command
UlyanaAndrukhiv Oct 3, 2024
a9d2abf
Added tests for command
UlyanaAndrukhiv Oct 3, 2024
86a4e48
Updated readme for admin commands
UlyanaAndrukhiv Oct 3, 2024
811a623
Updated test
UlyanaAndrukhiv Oct 3, 2024
039366c
Merged with UlyanaAndrukhiv/6302-store-transaction-result-error-messages
UlyanaAndrukhiv Oct 3, 2024
bd5fe3a
Merged with master
UlyanaAndrukhiv Oct 3, 2024
a9acd73
Merge branch 'UlyanaAndrukhiv/6497-refactor-executionNodesForBlockID'…
UlyanaAndrukhiv Oct 3, 2024
3666c1e
Added godoc for tests
UlyanaAndrukhiv Oct 3, 2024
35e4fc1
Moved mock closer to unittest mock module, updated tests
UlyanaAndrukhiv Oct 3, 2024
b062f8a
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Oct 4, 2024
ac842df
Updated according to suggested comments
UlyanaAndrukhiv Oct 4, 2024
dfbeb9c
Updated api according to comments
UlyanaAndrukhiv Oct 4, 2024
bc180d9
Updated godoc for storage
UlyanaAndrukhiv Oct 7, 2024
16cb115
Updated store tx error message as suggested
UlyanaAndrukhiv Oct 7, 2024
3a9474b
Added another trigger point to get error messages, updated error hand…
UlyanaAndrukhiv Oct 7, 2024
7f0abdb
Udated tests
UlyanaAndrukhiv Oct 7, 2024
ccbc4bb
Merge branch 'master' into UlyanaAndrukhiv/6497-refactor-executionNod…
UlyanaAndrukhiv Oct 7, 2024
7768a35
Added db Exists testing to transaction result messages testing
UlyanaAndrukhiv Oct 7, 2024
203e10f
Merge branch 'master' into UlyanaAndrukhiv/6302-store-transaction-res…
UlyanaAndrukhiv Oct 7, 2024
38467d5
Merge branch 'master' into UlyanaAndrukhiv/6497-refactor-executionNod…
UlyanaAndrukhiv Oct 8, 2024
9d5211c
Merged with UlyanaAndrukhiv/6302-store-transaction-result-error-mess…
UlyanaAndrukhiv Oct 8, 2024
bb54eab
Merge branch 'master' into UlyanaAndrukhiv/6413-backfill-tx-error-mes…
UlyanaAndrukhiv Oct 8, 2024
e882b61
Merge branch 'UlyanaAndrukhiv/6497-refactor-executionNodesForBlockID'…
UlyanaAndrukhiv Oct 8, 2024
abc1a2c
Added godoc
UlyanaAndrukhiv Oct 8, 2024
95d3849
Updated godoc
UlyanaAndrukhiv Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,8 @@ curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"
curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "protocol-snapshot"}'
curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "protocol-snapshot", "data": { "blocks-to-skip": 10 }}'
```

### To backfill transaction error messages
```
curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "backfill-tx-error-messages", "data": { "start-height": 340, "end-height": 343, "execution-node-ids":["ec7b934df29248d574ae1cc33ae77f22f0fcf96a79e009224c46374d1837824e", "8cbdc8d24a28899a33140cb68d4146cd6f2f6c18c57f54c299f26351d126919e"] }}'
```
239 changes: 239 additions & 0 deletions admin/commands/storage/backfill_tx_error_messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package storage

import (
"context"
"fmt"

execproto "github.com/onflow/flow/protobuf/go/flow/execution"

"github.com/onflow/flow-go/admin"
"github.com/onflow/flow-go/admin/commands"
"github.com/onflow/flow-go/engine/access/index"
"github.com/onflow/flow-go/engine/access/rpc/backend"
commonrpc "github.com/onflow/flow-go/engine/common/rpc"
"github.com/onflow/flow-go/engine/common/rpc/convert"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/model/flow/filter"
"github.com/onflow/flow-go/state/protocol"
"github.com/onflow/flow-go/storage"
)

var _ commands.AdminCommand = (*BackfillTxErrorMessagesCommand)(nil)

// backfillTxErrorMessagesRequest represents the input parameters for
// backfilling transaction error messages.
type backfillTxErrorMessagesRequest struct {
startHeight uint64 // Start height from which to begin backfilling.
endHeight uint64 // End height up to which backfilling is performed.
executionNodeIds flow.IdentityList // List of execution node IDs to be used for backfilling.
}

// BackfillTxErrorMessagesCommand executes a command to backfill
// transaction error messages by fetching them from execution nodes.
type BackfillTxErrorMessagesCommand struct {
state protocol.State
txResultsIndex *index.TransactionResultsIndex
txErrorMessages storage.TransactionResultErrorMessages
backend *backend.Backend
}

// NewBackfillTxErrorMessagesCommand creates a new instance of BackfillTxErrorMessagesCommand
func NewBackfillTxErrorMessagesCommand(
state protocol.State,
txResultsIndex *index.TransactionResultsIndex,
txErrorMessages storage.TransactionResultErrorMessages,
backend *backend.Backend,
) commands.AdminCommand {
return &BackfillTxErrorMessagesCommand{
state: state,
txResultsIndex: txResultsIndex,
txErrorMessages: txErrorMessages,
backend: backend,
}
}

// Validator validates the input for the backfill command. The input is validated
// for field types, boundaries, and coherence of start and end heights.
//
// Expected errors during normal operation:
// - admin.InvalidAdminReqError - if start-height is greater than end-height or
// if the input format is invalid, if an invalid execution node ID is provided.
func (b *BackfillTxErrorMessagesCommand) Validator(request *admin.CommandRequest) error {
input, ok := request.Data.(map[string]interface{})
if !ok {
return admin.NewInvalidAdminReqFormatError("expected map[string]any")
}

data := &backfillTxErrorMessagesRequest{}

rootHeight := b.state.Params().SealedRoot().Height
data.startHeight = rootHeight // Default value

if startHeightIn, ok := input["start-height"]; ok {
if startHeight, err := parseN(startHeightIn); err != nil {
return admin.NewInvalidAdminReqErrorf("invalid 'start-height' field: %w", err)
} else if startHeight > rootHeight {
data.startHeight = startHeight
}
}

sealed, err := b.state.Sealed().Head()
if err != nil {
return fmt.Errorf("failed to lookup sealed header: %w", err)
}
data.endHeight = sealed.Height // Default value

if endHeightIn, ok := input["end-height"]; ok {
if endHeight, err := parseN(endHeightIn); err != nil {
return admin.NewInvalidAdminReqErrorf("invalid 'end-height' field: %w", err)
} else if endHeight < sealed.Height {
data.endHeight = endHeight
}
}

if data.endHeight < data.startHeight {
return admin.NewInvalidAdminReqErrorf("start-height %d should not be smaller than end-height %d", data.startHeight, data.endHeight)
}

identities, err := b.state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution))
if err != nil {
return fmt.Errorf("failed to retreive execution IDs: %w", err)
}

if executionNodeIdsIn, ok := input["execution-node-ids"]; ok {
executionNodeIds, err := b.parseExecutionNodeIds(executionNodeIdsIn, identities)
if err != nil {
return err
}
data.executionNodeIds = executionNodeIds
} else {
// in case no execution node ids provided, the command will use any valid execution node
data.executionNodeIds = identities
}

request.ValidatorData = data

return nil
}

// Handler performs the backfilling operation by fetching missing transaction
// error messages for blocks within the specified height range. Uses execution nodes
// from data.executionNodeIds if available, otherwise defaults to valid execution nodes.
//
// No errors are expected during normal operation.
func (b *BackfillTxErrorMessagesCommand) Handler(ctx context.Context, request *admin.CommandRequest) (interface{}, error) {
if b.txErrorMessages == nil {
return nil, fmt.Errorf("failed to backfill, could not get transaction error messages storage")
}

data := request.ValidatorData.(*backfillTxErrorMessagesRequest)

for height := data.startHeight; height <= data.endHeight; height++ {
header, err := b.state.AtHeight(height).Head()
if err != nil {
return nil, fmt.Errorf("failed to get block header: %w", err)
}

blockID := header.ID()

exists, err := b.txErrorMessages.Exists(blockID)
if err != nil {
return nil, fmt.Errorf("could not check existance of transaction result error messages: %w", err)
}

if exists {
continue
}

results, err := b.txResultsIndex.ByBlockID(blockID, height)
if err != nil {
return nil, fmt.Errorf("failed to get result by block ID: %w", err)
}

fetchTxErrorMessages := false
for _, txResult := range results {
if txResult.Failed {
fetchTxErrorMessages = true
}
}

if !fetchTxErrorMessages {
continue
}

req := &execproto.GetTransactionErrorMessagesByBlockIDRequest{
BlockId: convert.IdentifierToMessage(blockID),
}

resp, execNode, err := b.backend.GetTransactionErrorMessagesFromAnyEN(ctx, data.executionNodeIds.ToSkeleton(), req)
if err != nil {
return nil, fmt.Errorf("failed to retrieve transaction error messages for block id %#v: %w", blockID, err)
}

err = b.storeTransactionResultErrorMessages(blockID, resp, execNode)
if err != nil {
return nil, fmt.Errorf("could not store error messages: %w", err)
}
}

return nil, nil
}

// parseExecutionNodeIds converts a list of node IDs from input to flow.IdentityList.
// Returns an error if the IDs are invalid or empty.
//
// Expected errors during normal operation:
// - admin.InvalidAdminReqParameterError - if execution-node-ids is empty or has an invalid format.
func (b *BackfillTxErrorMessagesCommand) parseExecutionNodeIds(executionNodeIdsIn interface{}, allIdentities flow.IdentityList) (flow.IdentityList, error) {
var ids flow.IdentityList

switch executionNodeIds := executionNodeIdsIn.(type) {
case []string:
if len(executionNodeIds) == 0 {
return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "must be a non empty list of string", executionNodeIdsIn)
}
requestedENIdentifiers, err := commonrpc.IdentifierList(executionNodeIds)
if err != nil {
return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", err.Error(), executionNodeIdsIn)
}

for _, en := range requestedENIdentifiers {
id, exists := allIdentities.ByNodeID(en)
if !exists {
return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "could not found execution nodes by provided ids", executionNodeIdsIn)
}
ids = append(ids, id)
}
default:
return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "must be a list of string", executionNodeIdsIn)
}

return ids, nil
}

// storeTransactionResultErrorMessages saves retrieved error messages for a given block ID.
//
// No errors are expected during normal operation.
func (b *BackfillTxErrorMessagesCommand) storeTransactionResultErrorMessages(
blockID flow.Identifier,
errorMessagesResponses []*execproto.GetTransactionErrorMessagesResponse_Result,
execNode *flow.IdentitySkeleton,
) error {
errorMessages := make([]flow.TransactionResultErrorMessage, 0, len(errorMessagesResponses))
for _, value := range errorMessagesResponses {
errorMessage := flow.TransactionResultErrorMessage{
ErrorMessage: value.ErrorMessage,
TransactionID: convert.MessageToIdentifier(value.TransactionId),
Index: value.Index,
ExecutorID: execNode.NodeID,
}
errorMessages = append(errorMessages, errorMessage)
}

err := b.txErrorMessages.Store(blockID, errorMessages)
if err != nil {
return fmt.Errorf("failed to store transaction error messages: %w", err)
}

return nil
}
Loading
Loading