diff --git a/src/context/QueryContext.cpp b/src/context/QueryContext.cpp index 84575586252a31bb9ec6ba9caea4261bee54bcc4..65940a58a1bd8918496d6cdaf72ef124aed14b76 100644 --- a/src/context/QueryContext.cpp +++ b/src/context/QueryContext.cpp @@ -6,8 +6,6 @@ #include "context/QueryContext.h" -#include "common/interface/gen-cpp2/graph_types.h" - namespace nebula { namespace graph { @@ -39,24 +37,5 @@ void QueryContext::init() { vctx_ = std::make_unique<ValidateContext>(std::make_unique<AnonVarGenerator>(symTable_.get())); } -void QueryContext::addProfilingData(int64_t planNodeId, ProfilingStats&& profilingStats) { - // return directly if not enable profile - if (!planDescription_) return; - - auto found = planDescription_->nodeIndexMap.find(planNodeId); - DCHECK(found != planDescription_->nodeIndexMap.end()); - auto idx = found->second; - auto& planNodeDesc = planDescription_->planNodeDescs[idx]; - if (planNodeDesc.profiles == nullptr) { - planNodeDesc.profiles.reset(new std::vector<ProfilingStats>()); - } - planNodeDesc.profiles->emplace_back(std::move(profilingStats)); -} - -void QueryContext::fillPlanDescription() { - DCHECK(ep_ != nullptr); - ep_->fillPlanDescription(planDescription_.get()); -} - } // namespace graph } // namespace nebula diff --git a/src/context/QueryContext.h b/src/context/QueryContext.h index 5b9c6637ce02c043d1f0fefd90eef49fb67448c6..21f61dfd487f6c051442b4f17c148b7771de097b 100644 --- a/src/context/QueryContext.h +++ b/src/context/QueryContext.h @@ -8,29 +8,24 @@ #define CONTEXT_QUERYCONTEXT_H_ #include "common/base/Base.h" +#include "common/base/ObjectPool.h" #include "common/charset/Charset.h" #include "common/clients/meta/MetaClient.h" #include "common/clients/storage/GraphStorageClient.h" #include "common/cpp/helpers.h" #include "common/datatypes/Value.h" -#include "common/meta/SchemaManager.h" #include "common/meta/IndexManager.h" +#include "common/meta/SchemaManager.h" #include "context/ExecutionContext.h" +#include "context/Symbols.h" #include "context/ValidateContext.h" #include "parser/SequentialSentences.h" #include "service/RequestContext.h" #include "util/IdGenerator.h" -#include "common/base/ObjectPool.h" -#include "context/Symbols.h" namespace nebula { namespace graph { -namespace cpp2 { -class ProfilingStats; -class PlanDescription; -} // namespace cpp2 - /*************************************************************************** * * The context for each query request @@ -98,10 +93,6 @@ public: return ep_.get(); } - void setPlan(std::unique_ptr<ExecutionPlan> plan) { - ep_ = std::move(plan); - } - meta::SchemaManager* schemaMng() const { return sm_; } @@ -130,18 +121,6 @@ public: return idGen_->id(); } - void addProfilingData(int64_t planNodeId, ProfilingStats&& profilingStats); - - PlanDescription* planDescription() const { - return planDescription_.get(); - } - - void setPlanDescription(std::unique_ptr<PlanDescription> planDescription) { - planDescription_ = std::move(planDescription); - } - - void fillPlanDescription(); - SymbolTable* symTable() const { return symTable_.get(); } @@ -162,9 +141,6 @@ private: // The Object Pool holds all internal generated objects. // e.g. expressions, plan nodes, executors std::unique_ptr<ObjectPool> objPool_; - - // plan description for explain and profile query - std::unique_ptr<PlanDescription> planDescription_; std::unique_ptr<IdGenerator> idGen_; std::unique_ptr<SymbolTable> symTable_; }; diff --git a/src/executor/Executor.cpp b/src/executor/Executor.cpp index d02d4f2df47748cd6a75b5d1c02bd55247bc086f..15fd63065d0d279a33cbf7da3d6e7e041f0cdca1 100644 --- a/src/executor/Executor.cpp +++ b/src/executor/Executor.cpp @@ -532,7 +532,7 @@ Status Executor::close() { stats.otherStats = std::make_unique<std::unordered_map<std::string, std::string>>(std::move(otherStats_)); } - qctx()->addProfilingData(node_->id(), std::move(stats)); + qctx()->plan()->addProfileStats(node_->id(), std::move(stats)); return Status::OK(); } diff --git a/src/planner/ExecutionPlan.cpp b/src/planner/ExecutionPlan.cpp index 934a87e10695dccdbf14b99c2391198a32e77aa7..4fda35189b9806774942f30796c5e767c141f303 100644 --- a/src/planner/ExecutionPlan.cpp +++ b/src/planner/ExecutionPlan.cpp @@ -7,6 +7,7 @@ #include "planner/ExecutionPlan.h" #include "common/graph/Response.h" +#include "common/interface/gen-cpp2/graph_types.h" #include "planner/Logic.h" #include "planner/PlanNode.h" #include "planner/Query.h" @@ -29,6 +30,7 @@ static size_t makePlanNodeDesc(const PlanNode* node, PlanDescription* planDesc) planDesc->nodeIndexMap.emplace(node->id(), planNodeDescPos); planDesc->planNodeDescs.emplace_back(std::move(*node->explain())); auto& planNodeDesc = planDesc->planNodeDescs.back(); + planNodeDesc.profiles = std::make_unique<std::vector<ProfilingStats>>(); switch (node->kind()) { case PlanNode::Kind::kStart: { @@ -84,11 +86,23 @@ static size_t makePlanNodeDesc(const PlanNode* node, PlanDescription* planDesc) return planNodeDescPos; } -void ExecutionPlan::fillPlanDescription(PlanDescription* planDesc) const { - DCHECK(planDesc != nullptr); - planDesc->optimize_time_in_us = optimizeTimeInUs_; +void ExecutionPlan::describe(PlanDescription* planDesc) { + planDescription_ = DCHECK_NOTNULL(planDesc); + planDescription_->optimize_time_in_us = optimizeTimeInUs_; + planDescription_->format = explainFormat_; makePlanNodeDesc(root_, planDesc); } +void ExecutionPlan::addProfileStats(int64_t planNodeId, ProfilingStats&& profilingStats) { + // return directly if not enable profile + if (!planDescription_) return; + + auto found = planDescription_->nodeIndexMap.find(planNodeId); + DCHECK(found != planDescription_->nodeIndexMap.end()); + auto idx = found->second; + auto& planNodeDesc = planDescription_->planNodeDescs[idx]; + planNodeDesc.profiles->emplace_back(std::move(profilingStats)); +} + } // namespace graph } // namespace nebula diff --git a/src/planner/ExecutionPlan.h b/src/planner/ExecutionPlan.h index 6a9ddd15b4fc1b2b20b91c69435ab32d37933c08..48fde90cbd59577bcee8cbd255b9fb3d4e963fcb 100644 --- a/src/planner/ExecutionPlan.h +++ b/src/planner/ExecutionPlan.h @@ -8,10 +8,13 @@ #define PLANNER_EXECUTIONPLAN_H_ #include <cstdint> +#include <memory> namespace nebula { +struct ProfilingStats; struct PlanDescription; +struct PlanNodeDescription; namespace graph { @@ -22,14 +25,14 @@ public: explicit ExecutionPlan(PlanNode* root = nullptr); ~ExecutionPlan(); - void setRoot(PlanNode* root) { - root_ = root; - } - int64_t id() const { return id_; } + void setRoot(PlanNode* root) { + root_ = root; + } + PlanNode* root() const { return root_; } @@ -38,12 +41,21 @@ public: return &optimizeTimeInUs_; } - void fillPlanDescription(PlanDescription* planDesc) const; + void addProfileStats(int64_t planNodeId, ProfilingStats&& profilingStats); + + void describe(PlanDescription* planDesc); + + void setExplainFormat(const std::string& format) { + explainFormat_ = format; + } private: int32_t optimizeTimeInUs_{0}; int64_t id_{-1}; PlanNode* root_{nullptr}; + // plan description for explain and profile query + PlanDescription* planDescription_{nullptr}; + std::string explainFormat_; }; } // namespace graph diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 8dfdfa87b2166a774c4b695b73789d98aeb279c4..c7c9e27ce9efd1e2e0950c58d6389a11c852e138 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -171,7 +171,7 @@ public: void setOutputVar(const std::string &var); - std::string outputVar(size_t index = 0) const { + const std::string& outputVar(size_t index = 0) const { DCHECK_LT(index, outputVars_.size()); return outputVars_[index]->name; } diff --git a/src/service/QueryInstance.cpp b/src/service/QueryInstance.cpp index 403d2da5ccdf31e369123333a8a738c1e75a6447..d95eb9f6aad94a5109ffea6cdfdce64a2bae9a24 100644 --- a/src/service/QueryInstance.cpp +++ b/src/service/QueryInstance.cpp @@ -14,16 +14,15 @@ #include "planner/ExecutionPlan.h" #include "planner/PlanNode.h" #include "scheduler/Scheduler.h" +#include "stats/StatsDef.h" +#include "util/AstUtils.h" #include "util/ScopedTimer.h" #include "validator/Validator.h" -#include "util/AstUtils.h" -#include "stats/StatsDef.h" using nebula::opt::Optimizer; using nebula::opt::OptRule; using nebula::opt::RuleSet; - namespace nebula { namespace graph { @@ -33,7 +32,6 @@ QueryInstance::QueryInstance(std::unique_ptr<QueryContext> qctx, Optimizer *opti scheduler_ = std::make_unique<Scheduler>(qctx_.get()); } - void QueryInstance::execute() { Status status = validateAndOptimize(); if (!status.ok()) { @@ -58,7 +56,6 @@ void QueryInstance::execute() { .onError([this](const std::exception &e) { onError(Status::Error("%s", e.what())); }); } - Status QueryInstance::validateAndOptimize() { auto *rctx = qctx()->rctx(); VLOG(1) << "Parsing query: " << rctx->query(); @@ -67,63 +64,32 @@ Status QueryInstance::validateAndOptimize() { sentence_ = std::move(result).value(); NG_RETURN_IF_ERROR(Validator::validate(sentence_.get(), qctx())); - - auto plan = std::make_unique<ExecutionPlan>(); - { - SCOPED_TIMER(plan->optimizeTimeInUs()); - auto rootStatus = optimizer_->findBestPlan(qctx_.get()); - NG_RETURN_IF_ERROR(rootStatus); - plan->setRoot(const_cast<PlanNode *>(std::move(rootStatus).value())); - } - qctx_->setPlan(std::move(plan)); + NG_RETURN_IF_ERROR(findBestPlan()); return Status::OK(); } - bool QueryInstance::explainOrContinue() { if (sentence_->kind() != Sentence::Kind::kExplain) { return true; } - qctx_->fillPlanDescription(); + auto &resp = qctx_->rctx()->resp(); + resp.planDesc = std::make_unique<PlanDescription>(); + DCHECK_NOTNULL(qctx_->plan())->describe(resp.planDesc.get()); return static_cast<const ExplainSentence *>(sentence_.get())->isProfile(); } - void QueryInstance::onFinish() { auto rctx = qctx()->rctx(); VLOG(1) << "Finish query: " << rctx->query(); - auto ectx = qctx()->ectx(); auto &spaceName = rctx->session()->space().name; rctx->resp().spaceName = std::make_unique<std::string>(spaceName); - auto name = qctx()->plan()->root()->outputVar(); - if (ectx->exist(name)) { - auto &&value = ectx->moveValue(name); - if (value.type() == Value::Type::DATASET) { - auto result = value.moveDataSet(); - if (!result.colNames.empty()) { - rctx->resp().data = std::make_unique<DataSet>(std::move(result)); - } else { - LOG(ERROR) << "Empty column name list"; - rctx->resp().errorCode = ErrorCode::E_EXECUTION_ERROR; - rctx->resp().errorMsg = std::make_unique<std::string>( - "Internal error: empty column name list"); - } - } - } - if (qctx()->planDescription() != nullptr) { - rctx->resp().planDesc = std::make_unique<PlanDescription>( - std::move(*qctx()->planDescription())); - } + fillRespData(&rctx->resp()); auto latency = rctx->duration().elapsedInUSec(); rctx->resp().latencyInUs = latency; - stats::StatsManager::addValue(kQueryLatencyUs, latency); - if (latency > static_cast<uint64_t>(FLAGS_slow_query_threshold_us)) { - stats::StatsManager::addValue(kNumSlowQueries); - stats::StatsManager::addValue(kSlowQueryLatencyUs, latency); - } + addSlowQueryStats(latency); rctx->finish(); // The `QueryInstance' is the root node holding all resources during the execution. @@ -133,7 +99,6 @@ void QueryInstance::onFinish() { delete this; } - void QueryInstance::onError(Status status) { LOG(ERROR) << status; auto *rctx = qctx()->rctx(); @@ -179,14 +144,48 @@ void QueryInstance::onError(Status status) { rctx->resp().errorMsg = std::make_unique<std::string>(status.toString()); auto latency = rctx->duration().elapsedInUSec(); rctx->resp().latencyInUs = latency; - stats::StatsManager::addValue(kQueryLatencyUs, latency); stats::StatsManager::addValue(kNumQueryErrors); + addSlowQueryStats(latency); + rctx->finish(); + delete this; +} + +void QueryInstance::addSlowQueryStats(uint64_t latency) const { + stats::StatsManager::addValue(kQueryLatencyUs, latency); if (latency > static_cast<uint64_t>(FLAGS_slow_query_threshold_us)) { stats::StatsManager::addValue(kNumSlowQueries); stats::StatsManager::addValue(kSlowQueryLatencyUs, latency); } - rctx->finish(); - delete this; +} + +void QueryInstance::fillRespData(ExecutionResponse *resp) { + auto ectx = DCHECK_NOTNULL(qctx_->ectx()); + auto plan = DCHECK_NOTNULL(qctx_->plan()); + const auto &name = plan->root()->outputVar(); + if (!ectx->exist(name)) return; + + auto &&value = ectx->moveValue(name); + if (!value.isDataSet()) return; + + // fill dataset + auto result = value.moveDataSet(); + if (!result.colNames.empty()) { + resp->data = std::make_unique<DataSet>(std::move(result)); + } else { + resp->errorCode = ErrorCode::E_EXECUTION_ERROR; + resp->errorMsg = std::make_unique<std::string>("Internal error: empty column name list"); + LOG(ERROR) << "Empty column name list"; + } +} + +Status QueryInstance::findBestPlan() { + auto plan = qctx_->plan(); + SCOPED_TIMER(plan->optimizeTimeInUs()); + auto rootStatus = optimizer_->findBestPlan(qctx_.get()); + NG_RETURN_IF_ERROR(rootStatus); + auto root = std::move(rootStatus).value(); + plan->setRoot(const_cast<PlanNode *>(root)); + return Status::OK(); } } // namespace graph diff --git a/src/service/QueryInstance.h b/src/service/QueryInstance.h index 7cade8477a272c7add8c4818e9fe93e7286aa2cb..a1fad9ac1141c5c6705c02688857da001f31ceec 100644 --- a/src/service/QueryInstance.h +++ b/src/service/QueryInstance.h @@ -52,6 +52,9 @@ private: Status validateAndOptimize(); // return true if continue to execute bool explainOrContinue(); + void addSlowQueryStats(uint64_t latency) const; + void fillRespData(ExecutionResponse* resp); + Status findBestPlan(); std::unique_ptr<Sentence> sentence_; std::unique_ptr<QueryContext> qctx_; diff --git a/src/validator/ExplainValidator.cpp b/src/validator/ExplainValidator.cpp index 7697cfc30f65acce0793ce387b3dfb2dbdb76a19..535c8dbd06156818c38899bd6a848dc7cd30d54b 100644 --- a/src/validator/ExplainValidator.cpp +++ b/src/validator/ExplainValidator.cpp @@ -58,9 +58,7 @@ Status ExplainValidator::validateImpl() { auto status = toExplainFormatType(explain->formatType()); NG_RETURN_IF_ERROR(status); - auto planDesc = std::make_unique<PlanDescription>(); - planDesc->format = std::move(status).value(); - qctx_->setPlanDescription(std::move(planDesc)); + qctx_->plan()->setExplainFormat(std::move(status).value()); NG_RETURN_IF_ERROR(validator_->validate());