Compare commits

...

21 Commits

Author SHA1 Message Date
Eric Cox
8e112f7a91 fix tripping boost invariant 2022-06-28 15:34:10 +00:00
Eric Cox
44a40ed8dd fix nullptr deref 2022-06-27 15:45:28 +00:00
Eric Cox
3d4c0a15fd fflag default true 2022-06-27 14:29:04 +00:00
Eric Cox
519ad6e521 update unittests 2022-06-24 15:03:12 +00:00
Eric Cox
cdb509d90a fmt 2022-06-24 14:41:48 +00:00
Eric Cox
1ce8f717a0 cleanup 2022-06-24 14:39:38 +00:00
Eric Cox
b01d44832e fix merge with IndexScanStage 2022-06-24 14:13:10 +00:00
Eric Cox
e41eb06388 Merge branch 'master' into eric/id-hack-ix-scan-refactor 2022-06-24 13:52:42 +00:00
Eric Cox
e27fb37145 wip refactor stage builder 2022-06-24 13:49:26 +00:00
Eric Cox
d8ae3f7be6 SERVER-66437 Refactor IndexScanStage to take seek keys as EExpressions 2022-06-23 15:35:35 +00:00
Eric Cox
51fd0d447b rm slotId 2022-06-22 01:39:54 +00:00
Eric Cox
330b4c092b fast path + inlined ks 2022-06-16 14:58:02 +00:00
Eric Cox
30cf050f75 hacking with fast path and ixScan eexpr 2022-06-15 22:02:28 +00:00
Eric Cox
08f108f5a2 hacks in stage builder (doesn't work with plan cache) 2022-06-14 19:57:11 +00:00
Eric Cox
630fb62eb3 no limit=1 2022-06-14 14:09:33 +00:00
Eric Cox
792c13cafc Merge branch 'master' into eric/sbe-id-hack-perf 2022-06-07 20:25:08 +00:00
Eric Cox
0d1ebb9878 debug 2022-06-07 20:24:12 +00:00
Eric Cox
8e31a264fd try limit 1 IDHack 2022-06-07 19:57:48 +00:00
Eric Cox
ed7caa14b4 default featureFlagSbeFull to true 2022-06-06 20:27:12 +00:00
Eric Cox
b24df431f6 rm debug print 2022-06-06 20:17:34 +00:00
Eric Cox
c6ddce5629 SERVER-66437 Improve performance of IDhack plans in SBE 2022-06-06 20:16:06 +00:00
7 changed files with 103 additions and 103 deletions

View File

@@ -1019,45 +1019,22 @@ std::unique_ptr<sbe::PlanStage> SBENodeLowering::walk(const IndexScanNode& n, co
const PlanNodeId planNodeId = _nodeToGroupPropsMap.at(&n)._planNodeId;
auto projectForKeyStringBounds = sbe::makeS<sbe::LimitSkipStage>(
sbe::makeS<sbe::CoScanStage>(planNodeId), 1, boost::none, planNodeId);
if (hasLowerBound) {
seekKeySlotLower = _slotIdGenerator.generate();
correlatedSlotsForJoin.push_back(seekKeySlotLower.value());
projectForKeyStringBounds = sbe::makeProjectStage(std::move(projectForKeyStringBounds),
planNodeId,
seekKeySlotLower.value(),
std::move(lowerBoundExpr));
}
if (hasUpperBound) {
seekKeySlotUpper = _slotIdGenerator.generate();
correlatedSlotsForJoin.push_back(seekKeySlotUpper.value());
projectForKeyStringBounds = sbe::makeProjectStage(std::move(projectForKeyStringBounds),
planNodeId,
seekKeySlotUpper.value(),
std::move(upperBoundExpr));
}
// Unused.
boost::optional<sbe::value::SlotId> resultSlot;
auto result = sbe::makeS<sbe::IndexScanStage>(nss.uuid().get(),
indexDefName,
!indexSpec.isReverseOrder(),
resultSlot,
ridSlot,
boost::none,
indexKeysToInclude,
vars,
seekKeySlotLower,
seekKeySlotUpper,
nullptr /*yieldPolicy*/,
planNodeId);
return sbe::makeS<sbe::LoopJoinStage>(std::move(projectForKeyStringBounds),
std::move(result),
sbe::makeSV(),
std::move(correlatedSlotsForJoin),
nullptr,
planNodeId);
return sbe::makeS<sbe::IndexScanStage>(nss.uuid().get(),
indexDefName,
!indexSpec.isReverseOrder(),
resultSlot,
ridSlot,
boost::none,
indexKeysToInclude,
vars,
std::move(lowerBoundExpr),
std::move(upperBoundExpr),
nullptr /*yieldPolicy*/,
planNodeId);
}
std::unique_ptr<sbe::PlanStage> SBENodeLowering::walk(const SeekNode& n,

View File

@@ -170,8 +170,8 @@ TEST_F(PlanSizeTest, IndexScan) {
generateSlotId(),
IndexKeysInclusionSet(1),
mockSV(),
generateSlotId(),
generateSlotId(),
makeE<EVariable>(generateSlotId()),
makeE<EVariable>(generateSlotId()),
nullptr,
kEmptyPlanNodeId);
assertPlanSize(*stage);

View File

@@ -47,12 +47,12 @@ IndexScanStage::IndexScanStage(UUID collUuid,
boost::optional<value::SlotId> snapshotIdSlot,
IndexKeysInclusionSet indexKeysToInclude,
value::SlotVector vars,
boost::optional<value::SlotId> seekKeySlotLow,
boost::optional<value::SlotId> seekKeySlotHigh,
std::unique_ptr<EExpression> seekKeyLow,
std::unique_ptr<EExpression> seekKeyHigh,
PlanYieldPolicy* yieldPolicy,
PlanNodeId nodeId,
bool participateInTrialRunTracking)
: PlanStage(seekKeySlotLow ? "ixseek"_sd : "ixscan"_sd,
: PlanStage(seekKeyLow ? "ixseek"_sd : "ixscan"_sd,
yieldPolicy,
nodeId,
participateInTrialRunTracking),
@@ -64,11 +64,11 @@ IndexScanStage::IndexScanStage(UUID collUuid,
_snapshotIdSlot(snapshotIdSlot),
_indexKeysToInclude(indexKeysToInclude),
_vars(std::move(vars)),
_seekKeySlotLow(seekKeySlotLow),
_seekKeySlotHigh(seekKeySlotHigh) {
_seekKeyLow(std::move(seekKeyLow)),
_seekKeyHigh(std::move(seekKeyHigh)) {
// The valid state is when both boundaries, or none is set, or only low key is set.
invariant((_seekKeySlotLow && _seekKeySlotHigh) || (!_seekKeySlotLow && !_seekKeySlotHigh) ||
(_seekKeySlotLow && !_seekKeySlotHigh));
invariant((_seekKeyLow && _seekKeyHigh) || (!_seekKeyLow && !_seekKeyHigh) ||
(_seekKeyLow && !_seekKeyHigh));
invariant(_indexKeysToInclude.count() == _vars.size());
}
@@ -82,8 +82,8 @@ std::unique_ptr<PlanStage> IndexScanStage::clone() const {
_snapshotIdSlot,
_indexKeysToInclude,
_vars,
_seekKeySlotLow,
_seekKeySlotHigh,
_seekKeyLow ? _seekKeyLow->clone() : nullptr,
_seekKeyHigh ? _seekKeyHigh->clone() : nullptr,
_yieldPolicy,
_commonStats.nodeId,
_participateInTrialRunTracking);
@@ -108,11 +108,13 @@ void IndexScanStage::prepare(CompileCtx& ctx) {
uassert(4822821, str::stream() << "duplicate slot: " << _vars[idx], inserted);
}
if (_seekKeySlotLow) {
_seekKeyLowAccessor = ctx.getAccessor(*_seekKeySlotLow);
if (_seekKeyLow) {
ctx.root = this;
_seekKeyLowCodes = _seekKeyLow->compile(ctx);
}
if (_seekKeySlotHigh) {
_seekKeyHiAccessor = ctx.getAccessor(*_seekKeySlotHigh);
if (_seekKeyHigh) {
ctx.root = this;
_seekKeyHighCodes = _seekKeyHigh->compile(ctx);
_seekKeyHighHolder = std::make_unique<value::OwnedValueAccessor>();
}
_seekKeyLowHolder = std::make_unique<value::OwnedValueAccessor>();
@@ -283,28 +285,28 @@ void IndexScanStage::open(bool reOpen) {
_cursor = entry->accessMethod()->asSortedData()->newCursor(_opCtx, _forward);
}
if (_seekKeyLowAccessor && _seekKeyHiAccessor) {
auto [tagLow, valLow] = _seekKeyLowAccessor->getViewOfValue();
if (_seekKeyLow && _seekKeyHigh) {
auto [ownedLow, tagLow, valLow] = _bytecode.run(_seekKeyLowCodes.get());
const auto msgTagLow = tagLow;
uassert(4822851,
str::stream() << "seek key is wrong type: " << msgTagLow,
tagLow == value::TypeTags::ksValue);
_seekKeyLowHolder->reset(false, tagLow, valLow);
_seekKeyLowHolder->reset(ownedLow, tagLow, valLow);
auto [tagHi, valHi] = _seekKeyHiAccessor->getViewOfValue();
auto [ownedHi, tagHi, valHi] = _bytecode.run(_seekKeyHighCodes.get());
const auto msgTagHi = tagHi;
uassert(4822852,
str::stream() << "seek key is wrong type: " << msgTagHi,
tagHi == value::TypeTags::ksValue);
_seekKeyHighHolder->reset(false, tagHi, valHi);
} else if (_seekKeyLowAccessor) {
auto [tagLow, valLow] = _seekKeyLowAccessor->getViewOfValue();
_seekKeyHighHolder->reset(ownedHi, tagHi, valHi);
} else if (_seekKeyLow) {
auto [ownedLow, tagLow, valLow] = _bytecode.run(_seekKeyLowCodes.get());
const auto msgTagLow = tagLow;
uassert(4822853,
str::stream() << "seek key is wrong type: " << msgTagLow,
tagLow == value::TypeTags::ksValue);
_seekKeyLowHolder->reset(false, tagLow, valLow);
_seekKeyLowHolder->reset(ownedLow, tagLow, valLow);
} else {
auto sdi = entry->accessMethod()->asSortedData()->getSortedDataInterface();
KeyString::Builder kb(sdi->getKeyStringVersion(),
@@ -414,6 +416,7 @@ std::unique_ptr<PlanStageStats> IndexScanStage::getStats(bool includeDebugInfo)
ret->specific = std::make_unique<IndexScanStats>(_specificStats);
if (includeDebugInfo) {
DebugPrinter printer;
BSONObjBuilder bob;
bob.append("indexName", _indexName);
bob.appendNumber("keysExamined", static_cast<long long>(_specificStats.keysExamined));
@@ -428,11 +431,11 @@ std::unique_ptr<PlanStageStats> IndexScanStage::getStats(bool includeDebugInfo)
if (_snapshotIdSlot) {
bob.appendNumber("snapshotIdSlot", static_cast<long long>(*_snapshotIdSlot));
}
if (_seekKeySlotLow) {
bob.appendNumber("seekKeySlotLow", static_cast<long long>(*_seekKeySlotLow));
if (_seekKeyLow) {
bob.append("seekKeyLow", printer.print(_seekKeyLow->debugPrint()));
}
if (_seekKeySlotHigh) {
bob.appendNumber("seekKeySlotHigh", static_cast<long long>(*_seekKeySlotHigh));
if (_seekKeyHigh) {
bob.append("seekKeyHigh", printer.print(_seekKeyHigh->debugPrint()));
}
bob.append("outputSlots", _vars.begin(), _vars.end());
bob.append("indexKeysToInclude", _indexKeysToInclude.to_string());
@@ -449,10 +452,10 @@ const SpecificStats* IndexScanStage::getSpecificStats() const {
std::vector<DebugPrinter::Block> IndexScanStage::debugPrint() const {
auto ret = PlanStage::debugPrint();
if (_seekKeySlotLow) {
DebugPrinter::addIdentifier(ret, _seekKeySlotLow.get());
if (_seekKeySlotHigh) {
DebugPrinter::addIdentifier(ret, _seekKeySlotHigh.get());
if (_seekKeyLow) {
DebugPrinter::addBlocks(ret, _seekKeyLow->debugPrint());
if (_seekKeyHigh) {
DebugPrinter::addBlocks(ret, _seekKeyHigh->debugPrint());
} else {
DebugPrinter::addIdentifier(ret, DebugPrinter::kNoneKeyword);
}

View File

@@ -31,22 +31,24 @@
#include "mongo/bson/ordering.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/sbe/expressions/expression.h"
#include "mongo/db/exec/sbe/stages/collection_helpers.h"
#include "mongo/db/exec/sbe/stages/stages.h"
#include "mongo/db/exec/sbe/vm/vm.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/sorted_data_interface.h"
namespace mongo::sbe {
/**
* A stage that iterates the entries of a collection index, starting from a bound specified by the
* value in 'seekKeySlotLow' and ending (via IS_EOF) with the 'seekKeySlotHigh' bound. (An
* unspecified 'seekKeySlotHigh' scans to the end of the index. Leaving both bounds unspecified
* value in 'seekKeyLow' and ending (via IS_EOF) with the 'seekKeyHigh' bound. (An
* unspecified 'seekKeyHigh' scans to the end of the index. Leaving both bounds unspecified
* scans the index from beginning to end.)
*
* The input 'seekKeySlotLow' and 'seekKeySlotHigh' slots get read as part of the open (or re-open)
* call. A common use case for an IndexScanStage is to place it as the inner child of LoopJoinStage.
* The outer side of the LoopJoinStage determines the bounds, and the inner IndexScanStage iterates
* through all the entries within those bounds.
* The input 'seekKeyLow' and 'seekKeyHigh' EExpressions get evaluated as part of the open
* (or re-open) call. A common use case for an IndexScanStage is to place it as the inner child of
* LoopJoinStage. The outer side of the LoopJoinStage determines the bounds, and the inner
* IndexScanStage iterates through all the entries within those bounds.
*
* The "output" slots are
* - 'recordSlot': the "KeyString" representing the index entry,
@@ -80,8 +82,8 @@ public:
boost::optional<value::SlotId> snapshotIdSlot,
IndexKeysInclusionSet indexKeysToInclude,
value::SlotVector vars,
boost::optional<value::SlotId> seekKeySlotLow,
boost::optional<value::SlotId> seekKeySlotHigh,
std::unique_ptr<EExpression> seekKeyLow,
std::unique_ptr<EExpression> seekKeyHigh,
PlanYieldPolicy* yieldPolicy,
PlanNodeId planNodeId,
bool participateInTrialRunTracking = true);
@@ -128,8 +130,15 @@ private:
const boost::optional<value::SlotId> _snapshotIdSlot;
const IndexKeysInclusionSet _indexKeysToInclude;
const value::SlotVector _vars;
const boost::optional<value::SlotId> _seekKeySlotLow;
const boost::optional<value::SlotId> _seekKeySlotHigh;
std::unique_ptr<EExpression> _seekKeyLow;
std::unique_ptr<EExpression> _seekKeyHigh;
// Carries the compiled bytecode for the above '_seekKeyLow' and '_seekKeyHigh'.
std::unique_ptr<vm::CodeFragment> _seekKeyLowCodes;
std::unique_ptr<vm::CodeFragment> _seekKeyHighCodes;
vm::ByteCode _bytecode;
// These members are default constructed to boost::none and are initialized when 'prepare()'
// is called. Once they are set, they are never modified again.

View File

@@ -143,9 +143,10 @@ feature_flags:
featureFlagSbeFull:
description: "Feature flag to enable using SBE for a larger number of queries"
cpp_varname: gFeatureFlagSbeFull
default: false
default: true
version: 6.0
featureFlagTimeSeriesChangeStreams:
description: "Feature flag for $changeStream support for time series"
cpp_varname: gFeatureFlagTimeSeriesChangeStreams
default: false
default: false

View File

@@ -318,8 +318,8 @@ generateOptimizedMultiIntervalIndexScan(StageBuilderState& state,
indexSnapshotSlot,
indexKeysToInclude,
std::move(indexKeySlots),
lowKeySlot,
highKeySlot,
makeVariable(lowKeySlot),
makeVariable(highKeySlot),
yieldPolicy,
planNodeId);
@@ -453,8 +453,8 @@ makeRecursiveBranchForGenericIndexScan(const CollectionPtr& collection,
snapshotIdSlot,
indexKeysToInclude,
std::move(savedIndexKeySlots),
lowKeySlot,
boost::none,
makeVariable(lowKeySlot),
nullptr /* seekKeyHigh */,
yieldPolicy,
planNodeId);
@@ -870,26 +870,35 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
// Construct a constant table scan to deliver a single row with two fields 'lowKeySlot' and
// 'highKeySlot', representing seek boundaries, into the index scan if 'lowKey' and 'highKey'
// are present. Otherwise the low and high keys will be obtained via variable references to
// runtime environment slots.
sbe::value::SlotMap<std::unique_ptr<sbe::EExpression>> projects;
auto makeKeySlot = [&](std::unique_ptr<KeyString::Value> key) {
// are present. If 'lowKey' and 'highKey' are present they will be presented to the index scan
// stage as a constant expression. Otherwise the low and high keys will be obtained via variable
// references to runtime environment slots.
auto makeKeyExpr = [&](std::unique_ptr<KeyString::Value> key)
-> std::pair<std::unique_ptr<sbe::EExpression>, boost::optional<sbe::value::SlotId>> {
if (key) {
auto keySlot = slotIdGenerator->generate();
projects.emplace(
keySlot,
return std::pair{
makeConstant(sbe::value::TypeTags::ksValue,
sbe::value::bitcastFrom<KeyString::Value*>(key.release())));
return keySlot;
sbe::value::bitcastFrom<KeyString::Value*>(key.release())),
boost::none};
} else {
auto keySlot = state.data->env->registerSlot(
sbe::value::TypeTags::Nothing, 0, true /* owned */, slotIdGenerator);
return keySlot;
return std::pair{makeVariable(keySlot), keySlot};
}
};
auto lowKeySlot = makeKeySlot(std::move(lowKey));
auto highKeySlot = makeKeySlot(std::move(highKey));
std::unique_ptr<sbe::EExpression> lowKeyExpr;
boost::optional<sbe::value::SlotId> lowKeySlot;
std::tie(lowKeyExpr, lowKeySlot) = makeKeyExpr(std::move(lowKey));
std::unique_ptr<sbe::EExpression> highKeyExpr;
boost::optional<sbe::value::SlotId> highKeySlot;
std::tie(highKeyExpr, highKeySlot) = makeKeyExpr(std::move(highKey));
tassert(0000000,
"Either both lowKeySlot and highKeySlot will exist or none of them will exist",
(lowKeySlot && highKeySlot) || (!lowKeySlot && !highKeySlot));
sbe::value::SlotMap<std::unique_ptr<sbe::EExpression>> projects;
if (indexIdSlot) {
// Construct a copy of 'indexName' to project for use in the index consistency check.
projects.emplace(*indexIdSlot, makeConstant(indexName));
@@ -924,8 +933,8 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
return sbe::makeS<sbe::FilterStage</* IsConst */ true, /* IsEof */ false>>(
std::move(childStage),
makeBinaryOp(sbe::EPrimBinary::logicAnd,
makeFunction("exists", makeVariable(lowKeySlot)),
makeFunction("exists", makeVariable(highKeySlot))),
makeFunction("exists", lowKeyExpr->clone()),
makeFunction("exists", highKeyExpr->clone())),
planNodeId);
}();
@@ -948,8 +957,8 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
indexSnapshotSlot,
indexKeysToInclude,
std::move(indexKeySlots),
lowKeySlot,
highKeySlot,
std::move(lowKeyExpr),
std::move(highKeyExpr),
yieldPolicy,
planNodeId);
@@ -975,11 +984,12 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
sbe::makeS<sbe::LoopJoinStage>(std::move(lowHighKeyBranch),
std::move(stage),
std::move(outerSv),
sbe::makeSV(lowKeySlot, highKeySlot),
sbe::makeSV(),
nullptr,
planNodeId),
boost::make_optional(shouldRegisterLowHighKeyInRuntimeEnv,
std::pair(lowKeySlot, highKeySlot))};
shouldRegisterLowHighKeyInRuntimeEnv
? boost::make_optional(std::pair(*lowKeySlot, *highKeySlot))
: boost::none};
}
std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> generateIndexScan(

View File

@@ -804,8 +804,8 @@ std::pair<SlotId, std::unique_ptr<sbe::PlanStage>> buildIndexJoinLookupStage(
snapshotIdSlot,
IndexKeysInclusionSet{} /* indexKeysToInclude */,
makeSV() /* vars */,
lowKeySlot,
highKeySlot,
makeVariable(lowKeySlot),
makeVariable(highKeySlot),
yieldPolicy,
nodeId);