Skip to content

Commit

Permalink
use better struct name and handle json rpc error
Browse files Browse the repository at this point in the history
  • Loading branch information
Ubuntu committed Mar 19, 2024
1 parent 3501813 commit 23634ec
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 35 deletions.
36 changes: 15 additions & 21 deletions common/geth/failover.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,36 @@ import (
"github.com/Layr-Labs/eigensdk-go/logging"
)

type FailoverController struct {
NumberRpcFault uint64
currentRPCIndex int
NumRPCClient int
Logger logging.Logger
mu *sync.Mutex
type RPCStatistics struct {
numberRpcFault uint64
Logger logging.Logger
mu *sync.Mutex
}

func NewFailoverController(numRPCClient int, logger logging.Logger) *FailoverController {
return &FailoverController{
NumRPCClient: numRPCClient,
currentRPCIndex: 0,
Logger: logger,
mu: &sync.Mutex{},
func NewRPCStatistics(logger logging.Logger) *RPCStatistics {
return &RPCStatistics{
Logger: logger,
mu: &sync.Mutex{},
}
}

// To use the Failover controller, one must insert this function
// after every call that uses RPC.
// This function attribute the error and update statistics
func (f *FailoverController) ProcessError(err error) {
// ProcessError attributes the error and updates total number of fault for RPC
func (f *RPCStatistics) ProcessError(err error) {
f.mu.Lock()
defer f.mu.Unlock()
if err == nil {
return
}

rpcFault := f.handleError(err)
serverFault := f.handleError(err)

if rpcFault {
f.NumberRpcFault += 1
if serverFault {
f.numberRpcFault += 1
}
}

func (f *FailoverController) GetTotalNumberRpcFault() uint64 {
func (f *RPCStatistics) GetTotalNumberRpcFault() uint64 {
f.mu.Lock()
defer f.mu.Unlock()
return f.NumberRpcFault
return f.numberRpcFault
}
47 changes: 41 additions & 6 deletions common/geth/handle_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,66 @@ import (
)

// handleHttpError returns a boolean indicating if error atrributes to remote RPC
func (f *FailoverController) handleHttpError(httpRespError rpc.HTTPError) bool {
func (f *RPCStatistics) handleHttpError(httpRespError rpc.HTTPError) bool {
sc := httpRespError.StatusCode
f.Logger.Info("[RPC Error]", "Status Code", sc)
f.Logger.Info("[HTTP Response Error]", "Status Code", sc)
if sc >= 200 && sc < 300 {
// 2xx error
return false
} else if sc >= 400 && sc < 500 {
}

if sc >= 400 && sc < 500 {
// 4xx error, Client Error. Alchemy documents 400,401,403,429
// https://docs.alchemy.com/reference/error-reference
return false
} else if sc >= 500 {
}

if sc >= 500 {
// 5xx codes, Server Error, Alchemy documents 500, 503
return true
}

// by default, attribute to rpc
return true
}

// handleError returns a boolean indicating if error atrributes to remote RPC
func (f *FailoverController) handleError(err error) bool {
// handleJsonRPCError returns a boolean indicating if error atrributes to remote Server
func (f *RPCStatistics) handleJsonRPCError(rpcError rpc.Error) bool {
ec := rpcError.ErrorCode()

// Based on JSON-RPC 2.0, https://www.jsonrpc.org/specification#error_object
// Parse Error, Invalid Request,Method not found,Invalid params,Internal error
if ec == -32700 || ec == -32600 || ec == -32601 || ec == -32602 || ec == -32603 {
return false
}

// server error
if ec >= -32099 && ec <= -32000 {
return true
}

// execution revert, see https://docs.alchemy.com/reference/error-reference
if ec == 3 {
return false
}

return true
}

// handleError returns a boolean indicating if error atrributes to remote Server
func (f *RPCStatistics) handleError(err error) bool {

var httpRespError rpc.HTTPError
if errors.As(err, &httpRespError) {
// if error is http error
return f.handleHttpError(httpRespError)
} else {
// it might be websocket error or ipc error. Parse json error code
var nonHttpRespError rpc.Error
if errors.As(err, &nonHttpRespError) {
return f.handleJsonRPCError(nonHttpRespError)
}

// If no http response is returned, it is a connection issue,
// since we can't accurately attribute the network issue to neither sender nor receiver
// side. Optimistically, switch rpc client
Expand Down
12 changes: 6 additions & 6 deletions common/geth/multihoming_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type MultiHomingClient struct {
rpcUrls []string
NumRetries int
Logger logging.Logger
*FailoverController
*RPCStatistics
}

var _ dacommon.EthClient = (*MultiHomingClient)(nil)
Expand All @@ -31,13 +31,13 @@ var _ dacommon.EthClient = (*MultiHomingClient)(nil)
func NewMultiHomingClient(config EthClientConfig, logger logging.Logger) (*MultiHomingClient, error) {
rpcUrls := config.RPCURLs

controller := NewFailoverController(len(rpcUrls), logger)
rpcStatistics := NewRPCStatistics(logger)

client := &MultiHomingClient{
rpcUrls: rpcUrls,
NumRetries: config.NumRetries,
FailoverController: controller,
Logger: logger,
rpcUrls: rpcUrls,
NumRetries: config.NumRetries,
RPCStatistics: rpcStatistics,
Logger: logger,
}

for i := 0; i < len(rpcUrls); i++ {
Expand Down
4 changes: 2 additions & 2 deletions common/geth/multihoming_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ func makeTestMultihomingClient(numRetries int, designatedError error) (*geth.Mul
}

mockClient := geth.MultiHomingClient{}
controller := geth.NewFailoverController(len(rpcURLs), logger)
statistics := geth.NewRPCStatistics(logger)

//mockClient.rpcUrls = rpcURLs
mockClient.Logger = logger
mockClient.NumRetries = ethClientCfg.NumRetries
mockClient.FailoverController = controller
mockClient.RPCStatistics = statistics

for i := 0; i < len(rpcURLs); i++ {
mockEthClient := &damock.MockEthClient{}
Expand Down

0 comments on commit 23634ec

Please sign in to comment.