-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
[BCF-3250]: Fix FilterLog error handling in LogPoller #11654
Merged
reductionista
merged 7 commits into
develop
from
fix/logpoller-too-many-results-message
Aug 27, 2024
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9ebfe5e
Fix error handling of results from FilterLogs()
reductionista bf6450b
pnpm changeset
reductionista 06856f1
Fix whitespace for lint
reductionista d9f3bcc
Add ErrorData() implementation to satisfy rpc.DataError
reductionista 1629fa4
Add more sophisticated classificaiton of FilterLogs error codes
reductionista bf13331
Add TooManyResults to docs example & full-config.toml
reductionista 7d12d19
Add tests for IsTooManyResults()
reductionista File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"chainlink": minor | ||
--- | ||
|
||
#bugfix More robust error handling in LogPoller, including no more misleading CRITICAL errors emitted under non-critical conditions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import ( | |
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/rpc" | ||
pkgerrors "github.com/pkg/errors" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
|
@@ -62,6 +63,7 @@ const ( | |
Fatal | ||
ServiceUnavailable | ||
TerminallyStuck | ||
TooManyResults | ||
) | ||
|
||
type ClientErrors map[int]*regexp.Regexp | ||
|
@@ -298,6 +300,7 @@ func ClientErrorRegexes(errsRegex config.ClientErrors) *ClientErrors { | |
TransactionAlreadyMined: regexp.MustCompile(errsRegex.TransactionAlreadyMined()), | ||
Fatal: regexp.MustCompile(errsRegex.Fatal()), | ||
ServiceUnavailable: regexp.MustCompile(errsRegex.ServiceUnavailable()), | ||
TooManyResults: regexp.MustCompile(errsRegex.TooManyResults()), | ||
} | ||
} | ||
|
||
|
@@ -457,6 +460,11 @@ func isFatalSendError(err error) bool { | |
return false | ||
} | ||
|
||
var ( | ||
_ rpc.Error = JsonError{} | ||
_ rpc.DataError = JsonError{} | ||
) | ||
|
||
// [email protected]/rpc/json.go | ||
type JsonError struct { | ||
Code int `json:"code"` | ||
|
@@ -471,7 +479,17 @@ func (err JsonError) Error() string { | |
return err.Message | ||
} | ||
|
||
func (err *JsonError) String() string { | ||
// To satisfy rpc.Error interface | ||
func (err JsonError) ErrorCode() int { | ||
return err.Code | ||
} | ||
|
||
// To satisfy rpc.DataError | ||
func (err JsonError) ErrorData() interface{} { | ||
return err.Data | ||
} | ||
|
||
func (err JsonError) String() string { | ||
return fmt.Sprintf("json-rpc error { Code = %d, Message = '%s', Data = '%v' }", err.Code, err.Message, err.Data) | ||
} | ||
|
||
|
@@ -610,3 +628,88 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger. | |
lggr.Criticalw("Unknown error encountered when sending transaction", "err", err, "etx", tx) | ||
return commonclient.Unknown | ||
} | ||
|
||
var infura = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`(: |^)query returned more than [0-9]+ results. Try with this block range \[0x[0-9A-F]+, 0x[0-9A-F]+\].$`), | ||
} | ||
|
||
var alchemy = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`(: |^)Log response size exceeded. You can make eth_getLogs requests with up to a [0-9A-Z]+ block range and no limit on the response size, or you can request any block range with a cap of [0-9A-Z]+ logs in the response. Based on your parameters and the response size limit, this block range should work: \[0x[0-9a-f]+, 0x[0-9a-f]+\]$`), | ||
} | ||
|
||
var quicknode = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`(: |^)eth_getLogs is limited to a [0-9,]+ range$`), | ||
} | ||
|
||
var simplyvc = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`too wide blocks range, the limit is [0-9,]+$`), | ||
} | ||
|
||
var drpc = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`(: |^)requested too many blocks from [0-9]+ to [0-9]+, maximum is set to [0-9,]+$`), | ||
} | ||
|
||
// Linkpool, Blockdaemon, and Chainstack all return "request timed out" if the log results are too large for them to process | ||
var defaultClient = ClientErrors{ | ||
TooManyResults: regexp.MustCompile(`request timed out`), | ||
} | ||
|
||
// JSON-RPC error codes which can indicate a refusal of the server to process an eth_getLogs request because the result set is too large | ||
const ( | ||
jsonRpcServerError = -32000 // Server error. SimplyVC uses this error code when too many results are returned | ||
|
||
// Server timeout. When the rpc server has its own limit on how long it can take to compile the results | ||
// Examples: Linkpool, Chainstack, Block Daemon | ||
jsonRpcTimedOut = -32002 | ||
|
||
// See: https://github.com/ethereum/go-ethereum/blob/master/rpc/errors.go#L63 | ||
// Can occur if the rpc server is configured with a maximum byte limit on the response size of batch requests | ||
jsonRpcResponseTooLarge = -32003 | ||
|
||
// Not implemented in geth by default, but is defined in EIP 1474 and implemented by infura and some other 3rd party rpc servers | ||
// See: https://community.infura.io/t/getlogs-error-query-returned-more-than-1000-results/358/5 | ||
jsonRpcLimitExceeded = -32005 // See also: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md | ||
|
||
jsonRpcInvalidParams = -32602 // Invalid method params. Returned by alchemy if the block range is too large or there are too many results to return | ||
|
||
jsonRpcQuicknodeTooManyResults = -32614 // Undocumented error code used by Quicknode for too many results error | ||
) | ||
|
||
func IsTooManyResults(err error, clientErrors config.ClientErrors) bool { | ||
var rpcErr rpc.Error | ||
|
||
if !pkgerrors.As(err, &rpcErr) { | ||
return false | ||
} | ||
configErrors := ClientErrorRegexes(clientErrors) | ||
if configErrors.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
|
||
switch rpcErr.ErrorCode() { | ||
case jsonRpcResponseTooLarge: | ||
return true | ||
case jsonRpcLimitExceeded: | ||
if infura.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
case jsonRpcInvalidParams: | ||
if alchemy.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
case jsonRpcQuicknodeTooManyResults: | ||
if quicknode.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
case jsonRpcTimedOut: | ||
if defaultClient.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
case jsonRpcServerError: | ||
if simplyvc.ErrIs(rpcErr, TooManyResults) || | ||
drpc.ErrIs(rpcErr, TooManyResults) { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the logic below makes sense in the case of
LimitExceeded
. Why group them together?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic was designed specifically for the
LimitExceeded
error, which is returned whenever the number of results that would have come back from aneth_getLogs
request exceeds a limit set by the rpc server. (In my testing, this was found to be 10,000 for most 3rd party rpc servers, such was alchemy and infura.)That one has been in the PR since Dec, I just added this other one (
ResultsTooLarge
) because the name of it sounds like it might get returned by some rpc servers under similar circumstances--we have no actual evidence that this is the case, I'm just guessing on that because the name sounds like whatLimitExceeded
does in practice. We can leave this one out, but we can't leave outLimitExceeded
since that's what this error handling was added for.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the current comment for
LimitExceeded
accurate? Rate limiting is significantly different from result size limiting.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It had been a while since I tested this, so I just went through and tested the responses using curl on a dozen or more different rpc servers.
Sadly, they are much more varied than I'd remembered (although in Dec I think I'd only tried it on 3 providers)--we may have to rely primarily on string parsing 😩, or at least use that in conjunction with error codes.
Here is the response infura gives, which is the LimitExceeded error:
Looking at their docs, they also return the same error code if you've exceeded your quota for the day, or are sending requests too fast. So the comment is accurate, but you're right that they are lumping very different types of errors together under the same error code--I guess we would need to check either "data" or "message" to differentiate it from other types of rate limiting.
Some other examples of responses (I was either mistaken about Alchemy, or they changed it since then).
Alchemy:
(from their docs, -32602 means "invalid method parameters")
LinkPool, Blockdaemon, Chainstack:
Quicknode BSC, Fantom:
(their docs list a number of custom error codes, up to and including -32613 but don't mention -32614 !)
SimplyVC Fanton:
Drpc Avalanche:
Nirvana Labs:
(after pause for 60+ seconds... )
(I double checked that the Nirvana Labs endpoint is valid, and responds to an eth_getLogs request with a smaller block range properly--very bad behavior on their part!)