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

feat: add cursor-based pagination options #72

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jinoosss
Copy link
Member

Descriptions

Add a query option to utilize the indexer.

Changes in this PR.

  • Added after, size, ascending options to transactions, blocks query input parameters.

    • after: Retrieves the next value at the after cursor.
    • size: The size of the list being retrieved.
    • ascending: Sort in ascending or descending order. (default: true)
  • Added pageInfo and edges data to response.

    • pageInfo: PageInfo is an information about the current page of edges.
    • edges: Edges contains provided edges of the sequential list. The edge data can retrieve cursor information along with data such as block, transaction, etc.

Example

image

Copy link
Member

@zivkovicmilos zivkovicmilos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks solid to me 💯

Waiting on a review by @ajnavarro before moving forward 🙏

fromKey := keyBlock(fromBlockNum)

if toBlockNum == 0 {
toBlockNum = math.MaxInt64
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the toBlockNum is math.MaxUint64?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!
I'll add exception handling for large numbers.

@@ -222,17 +225,18 @@ type PebbleBlockIter struct {
i *pebble.Iterator
s *pebble.Snapshot

init bool
init bool
ascending bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be true by default, to keep with current expected functionality

Copy link
Collaborator

@ajnavarro ajnavarro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR needs several changes:

We should create a PebbleReverseTxIterator that will be used in a new storage method called ReverseTxIterator to avoid all the custom logic used with that reverse boolean all around the code.

Cursor type has highly coupled code that is related to several use cases.

My proposal is to make Cursor an interface with several implementations:

type Cursor interface {
	json.Marshaller
	ID() (string, error)
}

type BlockHeightCursor uint64

// TODO implement marshaller

func (bhc BlockHeightCursor) ID() (string, error) {
	// TODO: implement

	return "", nil
}

type BlockHeightTxIndexCursor struct {
	Height uint64
	TxIndex uint32
}

// TODO implement marshaller

func (bhtc BlockHeightTxIndexCursor) ID() (string, error) {
	// TODO: implement

	return "", nil
}

More changes might be needed after these ones, I didn't do a deep review of the pagination logic.


// Create a cursor by base64-encoding the ID.
func NewCursor(id string) Cursor {
return Cursor(base64.StdEncoding.EncodeToString([]byte(id)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to encode the id string to base64?

switch input := input.(type) {
case string:
*c = Cursor(input)
case int32:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a cursor is an int? this code is a bit flaky, making it difficult to keep it compatible with new types, like int.

Comment on lines +34 to +37
// MarshalJSON encodes a cursor to JSON for transport.
func (c Cursor) MarshalJSON() ([]byte, error) {
return strconv.AppendQuote(nil, string(c)), nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a custom marshaller? Cursor is already a string and it will be serialized as intended.

Comment on lines +55 to +72
// Get the block height from the ID of the Cursor.
func (c *Cursor) BlockHeight() (uint64, error) {
id, err := c.ID()
if err != nil {
return 0, err
}

if id == "" {
return 0, nil
}

blockHeight, err := strconv.ParseUint(id, 0, 64)
if err != nil {
return 0, err
}

return blockHeight, nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is highly coupled code. We are trying to do two totally different behaviors on the same Cursor type...

Comment on lines +39 to 52
// Adjusts the iterator range based on the value of after cursor.
if afterBlockHeight > 0 {
if ascending {
if fromBlockHeight <= afterBlockHeight {
fromBlockHeight = afterBlockHeight + 1
fromIndex = uint32(afterIndex)
}
} else {
if toBlockHeight == 0 || toBlockHeight >= afterBlockHeight {
toBlockHeight = afterBlockHeight - 1
toIndex = uint32(afterIndex)
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't belong here, that logic should be on the iterator.

@@ -172,17 +174,16 @@ func (s *Pebble) BlockIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types
return nil, multierr.Append(snap.Close(), err)
}

return &PebbleBlockIter{i: it, s: snap}, nil
return &PebbleBlockIter{i: it, s: snap, ascending: ascending}, nil
}

func (s *Pebble) TxIterator(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of sending a flag for reversing the iterator, it might be less convoluted to create a method for the functionality you want to have, in that case, ReverseTxIterator

Doing that you don't need to tweak ranges outside the iterator logic itself.

@zivkovicmilos
Copy link
Member

Hey @jinoosss, is this PR still relevant? 🙏

@jinoosss
Copy link
Member Author

jinoosss commented Oct 2, 2024

@zivkovicmilos @ajnavarro ,
I apologise for the late response, I missed it.

I think this PR is still useful.
The functionality is to get the filtered items partially, not fully. It is not the same as filtering by block height.

I'll take a look at your change request by the end of the week 🙏

Additionally, what do you think about adding this functionality to the getBlocks and getTransactions functions that use the added generic filter?

@ajnavarro
Copy link
Collaborator

ajnavarro commented Oct 7, 2024

Hey @jinoosss ! Thanks for your reply.

Additionally, what do you think about adding this functionality to the getBlocks and getTransactions functions that use the added generic filter?

Yes, I'm figuring out how to add pagination for the new filters. I'll open a PR ASAP.

@zivkovicmilos
Copy link
Member

@ajnavarro @jinoosss what's the status of this PR?

@ajnavarro
Copy link
Collaborator

@zivkovicmilos, I'm waiting for the requested changes to be applied from my side!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress
Status: In Review
Development

Successfully merging this pull request may close these issues.

3 participants