From 4ce75ecc78b4b5ab21d7cf2f043679518a4fbceb Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 16 Apr 2024 17:11:39 -0300 Subject: [PATCH] limit index scans to the maximum of total returnable events. --- badger/query.go | 25 ++++++++++++++++++------- bolt/query.go | 19 +++++++++++++------ lmdb/query.go | 19 +++++++++++++------ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/badger/query.go b/badger/query.go index 9618233..eaf2e74 100644 --- a/badger/query.go +++ b/badger/query.go @@ -34,12 +34,21 @@ func (b BadgerBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (ch return nil, err } + // max number of events we'll return + limit := b.MaxLimit / 4 + if filter.Limit > 0 && filter.Limit < b.MaxLimit { + limit = filter.Limit + } + go func() { defer close(ch) // actually iterate for _, q := range queries { q := q + + pulled := 0 // this query will be hardcapped at this global limit + go b.View(func(txn *badger.Txn) error { // iterate only through keys and in reverse order opts := badger.IteratorOptions{ @@ -87,7 +96,15 @@ func (b BadgerBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (ch // check if this matches the other filters that were not part of the index if extraFilter == nil || extraFilter.Matches(evt) { - q.results <- evt + select { + case q.results <- evt: + pulled++ + if pulled > limit { + break + } + case <-ctx.Done(): + break + } } return nil @@ -98,12 +115,6 @@ func (b BadgerBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (ch }) } - // max number of events we'll return - limit := b.MaxLimit - if filter.Limit > 0 && filter.Limit < limit { - limit = filter.Limit - } - // receive results and ensure we only return the most recent ones always emittedEvents := 0 diff --git a/bolt/query.go b/bolt/query.go index e30985c..dc657ca 100644 --- a/bolt/query.go +++ b/bolt/query.go @@ -34,12 +34,21 @@ func (b *BoltBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha return nil, err } + // max number of events we'll return + limit := b.MaxLimit / 4 + if filter.Limit > 0 && filter.Limit < b.MaxLimit { + limit = filter.Limit + } + ch := make(chan *nostr.Event) go func() { defer close(ch) for _, q := range queries { q := q + + pulled := 0 // this query will be hardcapped at this global limit + go b.db.View(func(txn *bolt.Tx) error { defer close(q.results) @@ -76,6 +85,10 @@ func (b *BoltBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha if extraFilter == nil || extraFilter.Matches(evt) { select { case q.results <- evt: + pulled++ + if pulled > limit { + break + } case <-ctx.Done(): break } @@ -85,12 +98,6 @@ func (b *BoltBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha }) } - // max number of events we'll return - limit := b.MaxLimit - if filter.Limit > 0 && filter.Limit < limit { - limit = filter.Limit - } - // receive results and ensure we only return the most recent ones always emittedEvents := 0 diff --git a/lmdb/query.go b/lmdb/query.go index c02ebea..aacd3f3 100644 --- a/lmdb/query.go +++ b/lmdb/query.go @@ -35,12 +35,21 @@ func (b *LMDBBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha return nil, err } + // max number of events we'll return + limit := b.MaxLimit / 4 + if filter.Limit > 0 && filter.Limit < b.MaxLimit { + limit = filter.Limit + } + ch := make(chan *nostr.Event) go func() { defer close(ch) for _, q := range queries { q := q + + pulled := 0 // this will be hard-capped at the global limit of the query + go b.lmdbEnv.View(func(txn *lmdb.Txn) error { txn.RawRead = true defer close(q.results) @@ -105,6 +114,10 @@ func (b *LMDBBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha if extraFilter == nil || extraFilter.Matches(evt) { select { case q.results <- evt: + pulled++ + if pulled >= limit { + break + } case <-ctx.Done(): break } @@ -120,12 +133,6 @@ func (b *LMDBBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (cha log.Printf("lmdb: error on cursor iteration: %v\n", err) } - // max number of events we'll return - limit := b.MaxLimit - if filter.Limit > 0 && filter.Limit < limit { - limit = filter.Limit - } - // receive results and ensure we only return the most recent ones always emittedEvents := 0