Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions sei-tendermint/internal/rpc/core/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,40 +65,51 @@ func (env *Environment) TxSearch(ctx context.Context, req *coretypes.RequestTxSe
return nil, err
}

// Validate order_by up front so we can push the ordering (and the result
// cap) down into the indexer; a broad query is then bounded at the scan
// path rather than after materializing and sorting the full match set.
var orderDesc bool
switch req.OrderBy {
case DescendingOrder, "":
orderDesc = true

case AscendingOrder:
orderDesc = false

default:
return nil, fmt.Errorf("expected order_by to be either `asc` or `desc` or empty: %w", coretypes.ErrInvalidRequest)
}

for _, sink := range env.EventSinks {
if sink.Type() == indexer.KV {
// TODO(PLT-748): the kv tx indexer currently ignores these opts and
// the cap below still applies after sort (PLT-700). Once the tx
// scan path is bounded like the block indexer, this pushes the cap
// and ordering down to the scan.
results, err := sink.SearchTxEvents(ctx, q, indexer.SearchOptions{
Limit: env.Config.MaxTxSearchResults,
OrderDesc: req.OrderBy != AscendingOrder,
OrderDesc: orderDesc,
})
if err != nil {
return nil, err
}

// sort results (must be done before cap and pagination)
switch req.OrderBy {
case DescendingOrder, "":
if orderDesc {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[nit] Minor: the KV indexer now already returns results ordered and capped to MaxTxSearchResults, so this RPC-layer re-sort is redundant work for the KV sink. It's intentional and documented as a safety net for sinks that ignore the limit/ordering, and the data set is bounded by MaxTxSearchResults, so the cost is negligible — noting only for awareness. No change required.

sort.Slice(results, func(i, j int) bool {
if results[i].Height == results[j].Height {
return results[i].Index > results[j].Index
}
return results[i].Height > results[j].Height
})
case AscendingOrder:
} else {
sort.Slice(results, func(i, j int) bool {
if results[i].Height == results[j].Height {
return results[i].Index < results[j].Index
}
return results[i].Height < results[j].Height
})
default:
return nil, fmt.Errorf("expected order_by to be either `asc` or `desc` or empty: %w", coretypes.ErrInvalidRequest)
}

// Safety net: the kv indexer already bounds to MaxTxSearchResults,
// but keep the cap so the response stays bounded for any sink that
// ignores the limit.
if max := env.Config.MaxTxSearchResults; max > 0 && len(results) > max {
results = results[:max]
}
Expand Down
6 changes: 3 additions & 3 deletions sei-tendermint/internal/state/indexer/block/kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func (idx *BlockerIndexer) searchBounded(ctx context.Context, plan boundedPlan,
}
defer func() { _ = it.Close() }()

results := make([]int64, 0, boundedCap(opts.Limit))
results := make([]int64, 0, indexer.BoundedCap(opts.Limit))
seen := make(map[int64]struct{})

for ; it.Valid(); it.Next() {
Expand Down Expand Up @@ -310,7 +310,7 @@ func (idx *BlockerIndexer) searchBounded(ctx context.Context, plan boundedPlan,
// event index.
func (idx *BlockerIndexer) candidateMatches(h int64, plan boundedPlan) (bool, error) {
for i := range plan.heightRanges {
if !heightInRange(h, plan.heightRanges[i]) {
if !indexer.HeightInRange(h, plan.heightRanges[i]) {
return false, nil
}
}
Expand Down Expand Up @@ -338,7 +338,7 @@ func (idx *BlockerIndexer) prefixIterator(prefix []byte, desc bool) (dbm.Iterato
if !desc {
return dbm.IteratePrefix(idx.store, prefix)
}
return idx.store.ReverseIterator(prefix, prefixUpperBound(prefix))
return idx.store.ReverseIterator(prefix, indexer.PrefixUpperBound(prefix))
}

// hasEvent reports whether the block at the given height has an indexed event
Expand Down
49 changes: 0 additions & 49 deletions sei-tendermint/internal/state/indexer/block/kv/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,9 @@ import (
"github.com/google/orderedcode"

"github.com/sei-protocol/sei-chain/sei-tendermint/internal/pubsub/query/syntax"
"github.com/sei-protocol/sei-chain/sei-tendermint/internal/state/indexer"
"github.com/sei-protocol/sei-chain/sei-tendermint/types"
)

// maxBoundedPrealloc caps how much the bounded fast path preallocates for its
// result slice, so a very large (or disabled) limit does not eagerly allocate.
const maxBoundedPrealloc = 4096

// boundedCap returns a sensible initial capacity for a result slice given a
// limit. A non-positive limit means "unbounded", in which case we let the
// slice grow on demand.
func boundedCap(limit int) int {
if limit <= 0 {
return 0
}
if limit < maxBoundedPrealloc {
return limit
}
return maxBoundedPrealloc
}

// heightInRange reports whether height h falls within the (already
// inclusivity-adjusted) bounds of a numeric block.height query range.
func heightInRange(h int64, qr indexer.QueryRange) bool {
if lower := qr.LowerBoundValue(); lower != nil {
if lb, ok := lower.(int64); ok && h < lb {
return false
}
}
if upper := qr.UpperBoundValue(); upper != nil {
if ub, ok := upper.(int64); ok && h > ub {
return false
}
}
return true
}

// prefixUpperBound returns the exclusive end key for iterating over prefix,
// i.e. the smallest key strictly greater than every key having the prefix.
// It returns nil when prefix is empty or all bytes are 0xFF (no upper bound).
func prefixUpperBound(prefix []byte) []byte {
end := make([]byte, len(prefix))
copy(end, prefix)
for i := len(end) - 1; i >= 0; i-- {
if end[i] != 0xFF {
end[i]++
return end[:i+1]
}
}
return nil
}

func intInSlice(a int, list []int) bool {
for _, b := range list {
if b == a {
Expand Down
Loading
Loading