index-only scans for pubkey+kind

This commit is contained in:
Doug Hoyte
2023-01-29 04:22:30 -05:00
parent 9b83093be7
commit ee612416e0
3 changed files with 28 additions and 15 deletions

1
TODO
View File

@ -15,7 +15,6 @@ features
less verbose default logging less verbose default logging
nice new config "units" feature, ie 1d instead of 86400 nice new config "units" feature, ie 1d instead of 86400
make it easier for a thread to setup a quadrable env make it easier for a thread to setup a quadrable env
opt: PubkeyKind scans could be done index-only
rate limits rate limits
slow-reader detection and back-pressure slow-reader detection and back-pressure

View File

@ -49,10 +49,16 @@ struct DBScan {
std::string resumeKey; std::string resumeKey;
uint64_t resumeVal; uint64_t resumeVal;
enum class KeyMatchResult {
Yes,
No,
NoButContinue,
};
std::function<bool()> isComplete; std::function<bool()> isComplete;
std::function<void()> nextFilterItem; std::function<void()> nextFilterItem;
std::function<void()> resetResume; std::function<void()> resetResume;
std::function<bool(std::string_view, bool&)> keyMatch; std::function<KeyMatchResult(std::string_view, bool&)> keyMatch;
DBScan(const NostrFilter &f_) : f(f_) { DBScan(const NostrFilter &f_) : f(f_) {
remainingLimit = f.limit; remainingLimit = f.limit;
@ -74,7 +80,7 @@ struct DBScan {
resumeVal = MAX_U64; resumeVal = MAX_U64;
}; };
keyMatch = [&, state](std::string_view k, bool&){ keyMatch = [&, state](std::string_view k, bool&){
return k.starts_with(state->prefix); return k.starts_with(state->prefix) ? KeyMatchResult::Yes : KeyMatchResult::No;
}; };
} else if (f.authors && f.kinds) { } else if (f.authors && f.kinds) {
scanState = PubkeyKindScan{}; scanState = PubkeyKindScan{};
@ -98,16 +104,22 @@ struct DBScan {
resumeVal = MAX_U64; resumeVal = MAX_U64;
}; };
keyMatch = [&, state](std::string_view k, bool &skipBack){ keyMatch = [&, state](std::string_view k, bool &skipBack){
if (!k.starts_with(state->prefix)) return false; if (!k.starts_with(state->prefix)) return KeyMatchResult::No;
if (state->prefix.size() == 32 + 8) return true; if (state->prefix.size() == 32 + 8) return KeyMatchResult::Yes;
ParsedKey_StringUint64Uint64 parsedKey(k); ParsedKey_StringUint64Uint64 parsedKey(k);
if (parsedKey.n1 <= f.kinds->at(state->indexKind)) return true; if (parsedKey.n1 == f.kinds->at(state->indexKind)) {
return KeyMatchResult::Yes;
} else if (parsedKey.n1 < f.kinds->at(state->indexKind)) {
// With a prefix pubkey, continue scanning (pubkey,kind) backwards because with this index
// we don't know the next pubkey to jump back to
return KeyMatchResult::NoButContinue;
}
resumeKey = makeKey_StringUint64Uint64(parsedKey.s, f.kinds->at(state->indexKind), MAX_U64); resumeKey = makeKey_StringUint64Uint64(parsedKey.s, f.kinds->at(state->indexKind), MAX_U64);
resumeVal = MAX_U64; resumeVal = MAX_U64;
skipBack = true; skipBack = true;
return false; return KeyMatchResult::No;
}; };
} else if (f.authors) { } else if (f.authors) {
scanState = PubkeyScan{}; scanState = PubkeyScan{};
@ -126,7 +138,7 @@ struct DBScan {
resumeVal = MAX_U64; resumeVal = MAX_U64;
}; };
keyMatch = [&, state](std::string_view k, bool&){ keyMatch = [&, state](std::string_view k, bool&){
return k.starts_with(state->prefix); return k.starts_with(state->prefix) ? KeyMatchResult::Yes : KeyMatchResult::No;
}; };
} else if (f.tags.size()) { } else if (f.tags.size()) {
scanState = TagScan{f.tags.begin()}; scanState = TagScan{f.tags.begin()};
@ -150,7 +162,7 @@ struct DBScan {
resumeVal = MAX_U64; resumeVal = MAX_U64;
}; };
keyMatch = [&, state](std::string_view k, bool&){ keyMatch = [&, state](std::string_view k, bool&){
return k.substr(0, state->search.size()) == state->search; return k.substr(0, state->search.size()) == state->search ? KeyMatchResult::Yes : KeyMatchResult::No;
}; };
} else if (f.kinds) { } else if (f.kinds) {
scanState = KindScan{}; scanState = KindScan{};
@ -170,7 +182,7 @@ struct DBScan {
}; };
keyMatch = [&, state](std::string_view k, bool&){ keyMatch = [&, state](std::string_view k, bool&){
ParsedKey_Uint64Uint64 parsedKey(k); ParsedKey_Uint64Uint64 parsedKey(k);
return parsedKey.n1 == state->kind; return parsedKey.n1 == state->kind ? KeyMatchResult::Yes : KeyMatchResult::No;
}; };
} else { } else {
scanState = CreatedAtScan{}; scanState = CreatedAtScan{};
@ -188,7 +200,7 @@ struct DBScan {
resumeVal = MAX_U64; resumeVal = MAX_U64;
}; };
keyMatch = [&, state](std::string_view k, bool&){ keyMatch = [&, state](std::string_view k, bool&){
return true; return KeyMatchResult::Yes;
}; };
} }
} }
@ -208,7 +220,8 @@ struct DBScan {
return false; return false;
} }
if (!keyMatch(k, skipBack)) return false; auto matched = keyMatch(k, skipBack);
if (matched == KeyMatchResult::No) return false;
uint64_t created; uint64_t created;
@ -234,7 +247,9 @@ struct DBScan {
bool sent = false; bool sent = false;
uint64_t levId = lmdb::from_sv<uint64_t>(v); uint64_t levId = lmdb::from_sv<uint64_t>(v);
if (f.indexOnlyScans) { if (matched == KeyMatchResult::NoButContinue) {
// Don't attempt to match filter
} else if (f.indexOnlyScans) {
if (f.doesMatchTimes(created)) { if (f.doesMatchTimes(created)) {
handleEvent(levId); handleEvent(levId);
sent = true; sent = true;

View File

@ -168,8 +168,7 @@ struct NostrFilter {
if (limit > maxFilterLimit) limit = maxFilterLimit; if (limit > maxFilterLimit) limit = maxFilterLimit;
indexOnlyScans = numMajorFields <= 1; indexOnlyScans = (numMajorFields <= 1) || (numMajorFields == 2 && authors && kinds);
// FIXME: pubkeyKind scan could be serviced index-only too
} }
bool doesMatchTimes(uint64_t created) const { bool doesMatchTimes(uint64_t created) const {