github.com/onflow/flow-go@v0.33.17/engine/access/rpc/backend/node_communicator.go (about) 1 package backend 2 3 import ( 4 "github.com/hashicorp/go-multierror" 5 "github.com/sony/gobreaker" 6 "google.golang.org/grpc/codes" 7 "google.golang.org/grpc/status" 8 9 "github.com/onflow/flow-go/model/flow" 10 ) 11 12 // maxFailedRequestCount represents the maximum number of failed requests before returning errors. 13 const maxFailedRequestCount = 3 14 15 type Communicator interface { 16 CallAvailableNode( 17 //List of node identifiers to execute callback on 18 nodes flow.IdentityList, 19 //Callback function that represents an action to be performed on a node. 20 //It takes a node as input and returns an error indicating the result of the action. 21 call func(node *flow.Identity) error, 22 // Callback function that determines whether an error should terminate further execution. 23 // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. 24 shouldTerminateOnError func(node *flow.Identity, err error) bool, 25 ) error 26 } 27 28 var _ Communicator = (*NodeCommunicator)(nil) 29 30 // NodeCommunicator is responsible for calling available nodes in the backend. 31 type NodeCommunicator struct { 32 nodeSelectorFactory NodeSelectorFactory 33 } 34 35 // NewNodeCommunicator creates a new instance of NodeCommunicator. 36 func NewNodeCommunicator(circuitBreakerEnabled bool) *NodeCommunicator { 37 return &NodeCommunicator{ 38 nodeSelectorFactory: NodeSelectorFactory{circuitBreakerEnabled: circuitBreakerEnabled}, 39 } 40 } 41 42 // CallAvailableNode calls the provided function on the available nodes. 43 // It iterates through the nodes and executes the function. 44 // If an error occurs, it applies the custom error terminator (if provided) and keeps track of the errors. 45 // If the error occurs in circuit breaker, it continues to the next node. 46 // If the maximum failed request count is reached, it returns the accumulated errors. 47 func (b *NodeCommunicator) CallAvailableNode( 48 //List of node identifiers to execute callback on 49 nodes flow.IdentityList, 50 //Callback function that determines whether an error should terminate further execution. 51 // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. 52 call func(id *flow.Identity) error, 53 // Callback function that determines whether an error should terminate further execution. 54 // It takes an error as input and returns a boolean value indicating whether the error should be considered terminal. 55 shouldTerminateOnError func(node *flow.Identity, err error) bool, 56 ) error { 57 var errs *multierror.Error 58 nodeSelector, err := b.nodeSelectorFactory.SelectNodes(nodes) 59 if err != nil { 60 return err 61 } 62 63 for node := nodeSelector.Next(); node != nil; node = nodeSelector.Next() { 64 err := call(node) 65 if err == nil { 66 return nil 67 } 68 69 if shouldTerminateOnError != nil && shouldTerminateOnError(node, err) { 70 return err 71 } 72 73 if err == gobreaker.ErrOpenState { 74 if !nodeSelector.HasNext() && errs == nil { 75 errs = multierror.Append(errs, status.Error(codes.Unavailable, "there are no available nodes")) 76 } 77 continue 78 } 79 80 errs = multierror.Append(errs, err) 81 if len(errs.Errors) >= maxFailedRequestCount { 82 return errs.ErrorOrNil() 83 } 84 } 85 86 return errs.ErrorOrNil() 87 }