Skip to content

Commit

Permalink
feat: Add ordering on new filtered methods. (#106)
Browse files Browse the repository at this point in the history
ajnavarro authored Nov 28, 2024
1 parent aceadf4 commit dbea8f5
Showing 11 changed files with 468 additions and 56 deletions.
13 changes: 13 additions & 0 deletions internal/mock/storage.go
Original file line number Diff line number Diff line change
@@ -66,6 +66,19 @@ func (m *Storage) TxIterator(
panic("not implemented") // TODO: Implement
}

func (m *Storage) BlockReverseIterator(_, _ uint64) (storage.Iterator[*types.Block], error) {
panic("not implemented") // TODO: Implement
}

func (m *Storage) TxReverseIterator(
_,
_ uint64,
_,
_ uint32,
) (storage.Iterator[*types.TxResult], error) {
panic("not implemented") // TODO: Implement
}

// WriteBatch provides a batch intended to do a write action that
// can be cancelled or committed all at the same time
func (m *Storage) WriteBatch() storage.Batch {
60 changes: 44 additions & 16 deletions serve/graph/all.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions serve/graph/gen/generate.go
Original file line number Diff line number Diff line change
@@ -18,14 +18,14 @@ type Query {
Incomplete results due to errors return both the partial Blocks and
the associated errors.
"""
getBlocks(where: FilterBlock!): [Block!]
getBlocks(where: FilterBlock!, order: BlockOrder): [Block!]
"""
EXPERIMENTAL: Retrieves a list of Transactions that match the given
where criteria. If the result is incomplete due to errors, both partial
results and errors are returned.
"""
getTransactions(where: FilterTransaction!): [Transaction!]
getTransactions(where: FilterTransaction!, order: TransactionOrder): [Transaction!]
}
type Subscription {
169 changes: 159 additions & 10 deletions serve/graph/generated.go
50 changes: 50 additions & 0 deletions serve/graph/model/models_gen.go
8 changes: 8 additions & 0 deletions serve/graph/schema/schema.graphql
Original file line number Diff line number Diff line change
@@ -7,3 +7,11 @@ schema {
Field representing a point on time. It is following the RFC3339Nano format ("2006-01-02T15:04:05.999999999Z07:00")
"""
scalar Time

"""
Order defines the output order for hte method, It can be in DESC (descending) or ASC (ascending) order.
"""
enum Order {
ASC
DESC
}
4 changes: 4 additions & 0 deletions serve/graph/schema/types/block.graphql
Original file line number Diff line number Diff line change
@@ -121,3 +121,7 @@ type BlockTransaction {
"""
content_raw: String!
}

input BlockOrder {
height: Order!
}
4 changes: 4 additions & 0 deletions serve/graph/schema/types/transaction.graphql
Original file line number Diff line number Diff line change
@@ -384,3 +384,7 @@ type UnknownEvent {
"""
value: String! @filterable
}

input TransactionOrder {
heightAndIndex: Order!
}
4 changes: 4 additions & 0 deletions storage/encode.go
Original file line number Diff line number Diff line change
@@ -42,6 +42,8 @@ func encodeUint32Ascending(b []byte, v uint32) []byte {
// decodeUint32Ascending decodes a uint32 from the input buffer, treating
// the input as a big-endian 4 byte uint32 representation. The remainder
// of the input buffer and the decoded uint32 are returned.
//
//nolint:unparam // We want to keep all returning params
func decodeUint32Ascending(b []byte) ([]byte, uint32, error) {
if len(b) < 4 {
return nil, 0, fmt.Errorf("insufficient bytes to decode uint32 int value")
@@ -156,6 +158,8 @@ func unsafeConvertStringToBytes(s string) []byte {
// temporary buffer in order to avoid memory allocations. The remainder of the
// input buffer and the decoded string are returned. Note that the returned
// string may share storage with the input buffer.
//
//nolint:unparam // We want to keep all returning params
func decodeUnsafeStringAscending(b, r []byte) ([]byte, string, error) {
b, r, err := decodeBytesAscending(b, r)

200 changes: 172 additions & 28 deletions storage/pebble.go
Original file line number Diff line number Diff line change
@@ -153,7 +153,38 @@ func (s *Pebble) GetTxByHash(txHash string) (*types.TxResult, error) {
return decodeTx(tx)
}

func (s *Pebble) loadBlockIterator(fromBlockNum, toBlockNum uint64) (*pebble.Iterator, *pebble.Snapshot, error) {
fromKey := keyBlock(fromBlockNum)

if toBlockNum == 0 {
toBlockNum = math.MaxInt64
}

toKey := keyBlock(toBlockNum)

snap := s.db.NewSnapshot()

it, err := snap.NewIter(&pebble.IterOptions{
LowerBound: fromKey,
UpperBound: toKey,
})
if err != nil {
return nil, nil, multierr.Append(snap.Close(), err)
}

return it, snap, nil
}

func (s *Pebble) BlockIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types.Block], error) {
it, snap, err := s.loadBlockIterator(fromBlockNum, toBlockNum)
if err != nil {
return nil, err
}

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

func (s *Pebble) BlockReverseIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types.Block], error) {
fromKey := keyBlock(fromBlockNum)

if toBlockNum == 0 {
@@ -172,15 +203,15 @@ func (s *Pebble) BlockIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types
return nil, multierr.Append(snap.Close(), err)
}

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

func (s *Pebble) TxIterator(
func (s *Pebble) loadTxIterator(
fromBlockNum,
toBlockNum uint64,
fromTxIndex,
toTxIndex uint32,
) (Iterator[*types.TxResult], error) {
) (*pebble.Iterator, *pebble.Snapshot, error) {
fromKey := keyTx(fromBlockNum, fromTxIndex)

if toBlockNum == 0 {
@@ -200,10 +231,52 @@ func (s *Pebble) TxIterator(
UpperBound: toKey,
})
if err != nil {
return nil, multierr.Append(snap.Close(), err)
return nil, nil, multierr.Append(snap.Close(), err)
}

return &PebbleTxIter{i: it, s: snap, fromIndex: fromTxIndex, toIndex: toTxIndex}, nil
return it, snap, nil
}

func (s *Pebble) TxIterator(
fromBlockNum,
toBlockNum uint64,
fromTxIndex,
toTxIndex uint32,
) (Iterator[*types.TxResult], error) {
it, snap, err := s.loadTxIterator(fromBlockNum, toBlockNum, fromTxIndex, toTxIndex)
if err != nil {
return nil, err
}

return &PebbleTxIter{
pebbleBaseTxIter: pebbleBaseTxIter{
i: it,
s: snap,
fromIndex: fromTxIndex,
toIndex: toTxIndex,
},
}, nil
}

func (s *Pebble) TxReverseIterator(
fromBlockNum,
toBlockNum uint64,
fromTxIndex,
toTxIndex uint32,
) (Iterator[*types.TxResult], error) {
it, snap, err := s.loadTxIterator(fromBlockNum, toBlockNum, fromTxIndex, toTxIndex)
if err != nil {
return nil, err
}

return &PebbleReverseTxIter{
pebbleBaseTxIter: pebbleBaseTxIter{
i: it,
s: snap,
fromIndex: fromTxIndex,
toIndex: toTxIndex,
},
}, nil
}

func (s *Pebble) WriteBatch() Batch {
@@ -216,15 +289,31 @@ func (s *Pebble) Close() error {
return s.db.Close()
}

var _ Iterator[*types.Block] = &PebbleBlockIter{}

type PebbleBlockIter struct {
type pebbleBaseBlockIter struct {
i *pebble.Iterator
s *pebble.Snapshot

init bool
}

func (pi *pebbleBaseBlockIter) Error() error {
return pi.i.Error()
}

func (pi *pebbleBaseBlockIter) Value() (*types.Block, error) {
return decodeBlock(pi.i.Value())
}

func (pi *pebbleBaseBlockIter) Close() error {
return multierr.Append(pi.i.Close(), pi.s.Close())
}

var _ Iterator[*types.Block] = &PebbleBlockIter{}

type PebbleBlockIter struct {
pebbleBaseBlockIter
}

func (pi *PebbleBlockIter) Next() bool {
if !pi.init {
pi.init = true
@@ -235,21 +324,23 @@ func (pi *PebbleBlockIter) Next() bool {
return pi.i.Valid() && pi.i.Next()
}

func (pi *PebbleBlockIter) Error() error {
return pi.i.Error()
}
var _ Iterator[*types.Block] = &PebbleReverseBlockIter{}

func (pi *PebbleBlockIter) Value() (*types.Block, error) {
return decodeBlock(pi.i.Value())
type PebbleReverseBlockIter struct {
pebbleBaseBlockIter
}

func (pi *PebbleBlockIter) Close() error {
return multierr.Append(pi.i.Close(), pi.s.Close())
}
func (pi *PebbleReverseBlockIter) Next() bool {
if !pi.init {
pi.init = true

var _ Iterator[*types.TxResult] = &PebbleTxIter{}
return pi.i.Last()
}

type PebbleTxIter struct {
return pi.i.Valid() && pi.i.Prev()
}

type pebbleBaseTxIter struct {
nextError error
i *pebble.Iterator
s *pebble.Snapshot
@@ -258,6 +349,28 @@ type PebbleTxIter struct {
init bool
}

func (pi *pebbleBaseTxIter) Error() error {
if pi.nextError != nil {
return pi.nextError
}

return pi.i.Error()
}

func (pi *pebbleBaseTxIter) Value() (*types.TxResult, error) {
return decodeTx(pi.i.Value())
}

func (pi *pebbleBaseTxIter) Close() error {
return multierr.Append(pi.i.Close(), pi.s.Close())
}

var _ Iterator[*types.TxResult] = &PebbleTxIter{}

type PebbleTxIter struct {
pebbleBaseTxIter
}

func (pi *PebbleTxIter) Next() bool {
for {
if !pi.init {
@@ -299,20 +412,51 @@ func (pi *PebbleTxIter) Next() bool {
}
}

func (pi *PebbleTxIter) Error() error {
if pi.nextError != nil {
return pi.nextError
}
var _ Iterator[*types.TxResult] = &PebbleReverseTxIter{}

return pi.i.Error()
type PebbleReverseTxIter struct {
pebbleBaseTxIter
}

func (pi *PebbleTxIter) Value() (*types.TxResult, error) {
return decodeTx(pi.i.Value())
}
func (pi *PebbleReverseTxIter) Next() bool {
for {
if !pi.init {
if !pi.i.Last() {
return false
}

func (pi *PebbleTxIter) Close() error {
return multierr.Append(pi.i.Close(), pi.s.Close())
pi.init = true
} else if !pi.i.Prev() {
return false
}

var buf []byte

key, _, err := decodeUnsafeStringAscending(pi.i.Key(), buf)
if err != nil {
pi.nextError = err

return false
}

key, _, err = decodeUint64Ascending(key)
if err != nil {
pi.nextError = err

return false
}

_, txIdx, err := decodeUint32Ascending(key)
if err != nil {
pi.nextError = err

return false
}

if txIdx >= pi.fromIndex && txIdx < pi.toIndex {
return true
}
}
}

var _ Batch = &PebbleBatch{}
8 changes: 8 additions & 0 deletions storage/types.go
Original file line number Diff line number Diff line change
@@ -31,9 +31,17 @@ type Reader interface {
// BlockIterator iterates over Blocks, limiting the results to be between the provided block numbers
BlockIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types.Block], error)

// BlockReverseIterator iterates over Blocks in reverse order,
// limiting the results to be between the provided block numbers
BlockReverseIterator(fromBlockNum, toBlockNum uint64) (Iterator[*types.Block], error)

// TxIterator iterates over transactions, limiting the results to be between the provided block numbers
// and transaction indexes
TxIterator(fromBlockNum, toBlockNum uint64, fromTxIndex, toTxIndex uint32) (Iterator[*types.TxResult], error)

// TxReverseIterator iterates over transactions in reverse order,
// limiting the results to be between the provided block numbers and transaction indexes
TxReverseIterator(fromBlockNum, toBlockNum uint64, fromTxIndex, toTxIndex uint32) (Iterator[*types.TxResult], error)
}

type Iterator[T any] interface {

0 comments on commit dbea8f5

Please sign in to comment.