-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
feat: check eth events indexed in range #12728
base: master
Are you sure you want to change the base?
Changes from all commits
d0d52b5
9f824d1
1bcf220
17ab3d3
37c3f85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -17,12 +17,14 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||
"github.com/filecoin-project/go-address" | ||||||||||||||||||||||||||||||||||||||||||||||
amt4 "github.com/filecoin-project/go-amt-ipld/v4" | ||||||||||||||||||||||||||||||||||||||||||||||
"github.com/filecoin-project/go-state-types/abi" | ||||||||||||||||||||||||||||||||||||||||||||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
"github.com/filecoin-project/lotus/chain/types" | ||||||||||||||||||||||||||||||||||||||||||||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" | ||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
var ErrMaxResultsReached = fmt.Errorf("filter matches too many events, try a more restricted filter") | ||||||||||||||||||||||||||||||||||||||||||||||
var ( | ||||||||||||||||||||||||||||||||||||||||||||||
ErrMaxResultsReached = fmt.Errorf("filter matches too many events, try a more restricted filter") | ||||||||||||||||||||||||||||||||||||||||||||||
ErrRangeInFuture = fmt.Errorf("range end is in the future") | ||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const maxLookBackForWait = 120 // one hour of tipsets | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -236,48 +238,98 @@ func loadExecutedMessages(ctx context.Context, cs ChainStore, recomputeTipSetSta | |||||||||||||||||||||||||||||||||||||||||||||
return ems, nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// checkTipsetIndexedStatus verifies if a specific tipset is indexed based on the EventFilter. | ||||||||||||||||||||||||||||||||||||||||||||||
// It returns nil if the tipset is indexed, ErrNotFound if it's not indexed or not specified, | ||||||||||||||||||||||||||||||||||||||||||||||
func (si *SqliteIndexer) checkTipsetIndexedStatus(ctx context.Context, f *EventFilter) error { | ||||||||||||||||||||||||||||||||||||||||||||||
var tipsetKeyCid []byte | ||||||||||||||||||||||||||||||||||||||||||||||
var err error | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Determine the tipset to check based on the filter | ||||||||||||||||||||||||||||||||||||||||||||||
switch { | ||||||||||||||||||||||||||||||||||||||||||||||
case f.TipsetCid != cid.Undef: | ||||||||||||||||||||||||||||||||||||||||||||||
tipsetKeyCid = f.TipsetCid.Bytes() | ||||||||||||||||||||||||||||||||||||||||||||||
case f.MinHeight >= 0 && f.MinHeight == f.MaxHeight: | ||||||||||||||||||||||||||||||||||||||||||||||
tipsetKeyCid, err = si.getTipsetKeyCidByHeight(ctx, f.MinHeight) | ||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
if err == ErrNotFound { | ||||||||||||||||||||||||||||||||||||||||||||||
// this means that this is a null round and there exist no events for this epoch | ||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
// checkRangeIndexedStatus verifies if a range of heights is indexed. | ||||||||||||||||||||||||||||||||||||||||||||||
// It checks for the existence of non-null rounds at the range boundaries. | ||||||||||||||||||||||||||||||||||||||||||||||
func (si *SqliteIndexer) checkRangeIndexedStatus(ctx context.Context, f *EventFilter) error { | ||||||||||||||||||||||||||||||||||||||||||||||
minHeight := f.MinHeight | ||||||||||||||||||||||||||||||||||||||||||||||
maxHeight := f.MaxHeight | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("failed to get tipset key cid by height: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
// Find the first non-null round in the range | ||||||||||||||||||||||||||||||||||||||||||||||
startCid, err := si.findFirstNonNullRound(ctx, &minHeight, maxHeight) | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of the pointer to update it here is clever, but a little too non-obvious from a maintenance perspective, there's no obvious notice here it gets altered. I think you'd be better off returning a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
which is another point - not being a CID, let's be careful with naming of these things, |
||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("failed to find first non-null round: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// If all rounds are null, consider the range valid | ||||||||||||||||||||||||||||||||||||||||||||||
if startCid == nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Find the last non-null round in the range | ||||||||||||||||||||||||||||||||||||||||||||||
endCid, err := si.findLastNonNullRound(ctx, &maxHeight, minHeight) | ||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
if errors.Is(err, ErrRangeInFuture) { | ||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("range end is in the future: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
default: | ||||||||||||||||||||||||||||||||||||||||||||||
// This function distinguishes between two scenarios: | ||||||||||||||||||||||||||||||||||||||||||||||
// 1. Missing events: The requested tipset is not present in the Index (an error condition). | ||||||||||||||||||||||||||||||||||||||||||||||
// 2. Valid case: The tipset exists but contains no events (a normal situation). | ||||||||||||||||||||||||||||||||||||||||||||||
// Currently, this distinction is only made for the common use case where a user requests events for a single tipset. | ||||||||||||||||||||||||||||||||||||||||||||||
// TODO: Implement this functionality for a range of tipsets. This is expensive and not a common use case so it's deferred for now. | ||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("failed to find last non-null round: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// If all rounds are null, consider the range valid | ||||||||||||||||||||||||||||||||||||||||||||||
if endCid == nil { | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+267
to
+268
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oo, interesting case, you should never get here, right, since we do the walk for |
||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// If we couldn't determine a specific tipset, return ErrNotFound | ||||||||||||||||||||||||||||||||||||||||||||||
if tipsetKeyCid == nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return ErrNotFound | ||||||||||||||||||||||||||||||||||||||||||||||
// Check indexing for start and end tipsets | ||||||||||||||||||||||||||||||||||||||||||||||
if err := si.checkTipsetByKeyCid(ctx, startCid, minHeight); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return err | ||||||||||||||||||||||||||||||||||||||||||||||
rvagg marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Check if the determined tipset is indexed | ||||||||||||||||||||||||||||||||||||||||||||||
if exists, err := si.isTipsetIndexed(ctx, tipsetKeyCid); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("failed to check if tipset is indexed: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||
} else if exists { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil // Tipset is indexed | ||||||||||||||||||||||||||||||||||||||||||||||
if err := si.checkTipsetByKeyCid(ctx, endCid, maxHeight); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return err | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// checkTipsetByKeyCid checks if a tipset identified by its key CID is indexed. | ||||||||||||||||||||||||||||||||||||||||||||||
func (si *SqliteIndexer) checkTipsetByKeyCid(ctx context.Context, tipsetKeyCid []byte, height abi.ChainEpoch) error { | ||||||||||||||||||||||||||||||||||||||||||||||
exists, err := si.isTipsetIndexed(ctx, tipsetKeyCid) | ||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||
return xerrors.Errorf("failed to check if tipset at height %d is indexed: %w", height, err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
if exists { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil // null round | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return ErrNotFound // tipset is not indexed | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+285
to
+296
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure the
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// findFirstNonNullRound finds the first non-null round starting from minHeight up to maxHeight | ||||||||||||||||||||||||||||||||||||||||||||||
func (si *SqliteIndexer) findFirstNonNullRound(ctx context.Context, minHeight *abi.ChainEpoch, maxHeight abi.ChainEpoch) ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||||||
for height := *minHeight; height <= maxHeight; height++ { | ||||||||||||||||||||||||||||||||||||||||||||||
cid, err := si.getTipsetKeyCidByHeight(ctx, height) | ||||||||||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||||||||||
*minHeight = height // Update the minHeight to the found height | ||||||||||||||||||||||||||||||||||||||||||||||
return cid, nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
if !errors.Is(err, ErrNotFound) { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, xerrors.Errorf("failed to get tipset key cid for height %d: %w", height, err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+301
to
+308
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's switch this for readability, since programmers are used to seeing errors handled first (but not always, just the majority of the time), we introduce a cognitive hiccup when we switch things around; but if we keep it close to the expected flow then it's easier to skim
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return nil, nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// findLastNonNullRound finds the last non-null round starting from maxHeight down to minHeight | ||||||||||||||||||||||||||||||||||||||||||||||
func (si *SqliteIndexer) findLastNonNullRound(ctx context.Context, maxHeight *abi.ChainEpoch, minHeight abi.ChainEpoch) ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||||||
head := si.cs.GetHeaviestTipSet() | ||||||||||||||||||||||||||||||||||||||||||||||
if head == nil || *maxHeight > head.Height() { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, ErrRangeInFuture | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
for height := *maxHeight; height >= minHeight; height-- { | ||||||||||||||||||||||||||||||||||||||||||||||
cid, err := si.getTipsetKeyCidByHeight(ctx, height) | ||||||||||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||||||||||
*maxHeight = height // Update the maxHeight to the found height | ||||||||||||||||||||||||||||||||||||||||||||||
return cid, nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
if !errors.Is(err, ErrNotFound) { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, xerrors.Errorf("failed to get tipset key cid for height %d: %w", height, err) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return ErrNotFound // Tipset is not indexed | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, nil | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// getTipsetKeyCidByHeight retrieves the tipset key CID for a given height. | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -460,7 +512,7 @@ func (si *SqliteIndexer) GetEventsForFilter(ctx context.Context, f *EventFilter) | |||||||||||||||||||||||||||||||||||||||||||||
// if the height is old enough, we'll assume the index is caught up to it and not bother | ||||||||||||||||||||||||||||||||||||||||||||||
// waiting for it to be indexed | ||||||||||||||||||||||||||||||||||||||||||||||
if height <= maxLookBackHeight { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, si.checkTipsetIndexedStatus(ctx, f) | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, si.checkRangeIndexedStatus(ctx, f) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -474,7 +526,7 @@ func (si *SqliteIndexer) GetEventsForFilter(ctx context.Context, f *EventFilter) | |||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
if len(ces) == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, si.checkTipsetIndexedStatus(ctx, f) | ||||||||||||||||||||||||||||||||||||||||||||||
return nil, si.checkRangeIndexedStatus(ctx, f) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
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.
you've lost all of this logic in the process of refactoring, so we have a couple of problems now:
TipsetCid
- which we can do directly without needing to look at the range (case 1 above)f.MinHeight
andf.MaxHeight
are useful, but that may not be the case. They're allowed to have-1
values, which signifies that neither have been specified (we do that inlotus/node/impl/full/eth.go
Line 1844 in 12d76bd
>= 0
check existed in the original case.So I think you'll need to restore some form of the original
switch
to accommodate those two cases above and then for the third case wheref.MinHeight >= 0 && f.MaxHeight >= 0
, and maybe&& f.MinHeight < f.MaxHeight
just for good measure, just to be defensive about possible inputs.