1
0
Fork 0

Fixed responsibility for Traversal Options. Now the TraversalNode is always responsible for all options and has to free them. All others just use these. In Cluster case each DBServer get's it's own TraverserEngine which is initialized with a copy of the Options.

This commit is contained in:
Michael Hackstein 2016-07-29 17:07:40 +02:00
parent 207d978afc
commit 5ee93a8d6c
8 changed files with 122 additions and 82 deletions

View File

@ -206,6 +206,10 @@ struct Instanciator final : public WalkerWorker<ExecutionNode> {
virtual void after(ExecutionNode* en) override final {
ExecutionBlock* block = nullptr;
{
if (en->getType() == ExecutionNode::TRAVERSAL) {
// We have to prepare the options before we build the block
static_cast<TraversalNode*>(en)->prepareOptions();
}
std::unique_ptr<ExecutionBlock> eb(CreateBlock(engine, en, cache));
if (eb == nullptr) {

View File

@ -70,10 +70,10 @@ static uint64_t checkTraversalDepthValue(AstNode const* node) {
return static_cast<uint64_t>(v);
}
static traverser::TraverserOptions CreateTraversalOptions(
static std::unique_ptr<traverser::TraverserOptions> CreateTraversalOptions(
Transaction* trx, AstNode const* direction, AstNode const* optionsNode) {
traverser::TraverserOptions options(trx);
auto options = std::make_unique<traverser::TraverserOptions>(trx);
TRI_ASSERT(direction != nullptr);
TRI_ASSERT(direction->type == NODE_TYPE_DIRECTION);
@ -83,14 +83,14 @@ static traverser::TraverserOptions CreateTraversalOptions(
if (steps->isNumericValue()) {
// Check if a double value is integer
options.minDepth = checkTraversalDepthValue(steps);
options.maxDepth = options.minDepth;
options->minDepth = checkTraversalDepthValue(steps);
options->maxDepth = options->minDepth;
} else if (steps->type == NODE_TYPE_RANGE) {
// Range depth
options.minDepth = checkTraversalDepthValue(steps->getMember(0));
options.maxDepth = checkTraversalDepthValue(steps->getMember(1));
options->minDepth = checkTraversalDepthValue(steps->getMember(0));
options->maxDepth = checkTraversalDepthValue(steps->getMember(1));
if (options.maxDepth < options.minDepth) {
if (options->maxDepth < options->minDepth) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_PARSE,
"invalid traversal depth");
}
@ -112,29 +112,27 @@ static traverser::TraverserOptions CreateTraversalOptions(
TRI_ASSERT(value->isConstant());
if (name == "bfs") {
options.useBreadthFirst = value->isTrue();
options->useBreadthFirst = value->isTrue();
} else if (name == "uniqueVertices" && value->isStringValue()) {
if (value->stringEquals("path", true)) {
options.uniqueVertices =
options->uniqueVertices =
arangodb::traverser::TraverserOptions::UniquenessLevel::PATH;
} else if (value->stringEquals("global", true)) {
options.uniqueVertices =
options->uniqueVertices =
arangodb::traverser::TraverserOptions::UniquenessLevel::GLOBAL;
}
} else if (name == "uniqueEdges" && value->isStringValue()) {
if (value->stringEquals("none", true)) {
options.uniqueEdges =
options->uniqueEdges =
arangodb::traverser::TraverserOptions::UniquenessLevel::NONE;
} else if (value->stringEquals("global", true)) {
options.uniqueEdges =
options->uniqueEdges =
arangodb::traverser::TraverserOptions::UniquenessLevel::GLOBAL;
}
}
}
}
}
return options;
}
@ -755,8 +753,8 @@ ExecutionNode* ExecutionPlan::fromNodeTraversal(ExecutionNode* previous,
previous = calc;
}
traverser::TraverserOptions options = CreateTraversalOptions(
getAst()->query()->trx(), direction, node->getMember(3));
auto options = CreateTraversalOptions(getAst()->query()->trx(), direction,
node->getMember(3));
// First create the node
auto travNode = new TraversalNode(this, nextId(), _ast->query()->vocbase(),

View File

@ -42,7 +42,7 @@ using namespace arangodb::aql;
TraversalBlock::TraversalBlock(ExecutionEngine* engine, TraversalNode const* ep)
: ExecutionBlock(engine, ep),
_posInPaths(0),
_opts(new traverser::TraverserOptions(_trx)),
_opts(nullptr),
_traverser(nullptr),
_useRegister(false),
_usedConstant(false),
@ -60,16 +60,16 @@ TraversalBlock::TraversalBlock(ExecutionEngine* engine, TraversalNode const* ep)
_inRegs.emplace_back(it->second.registerId);
}
ep->fillTraversalOptions(_opts.get(), _trx);
_opts = ep->options();
if (arangodb::ServerState::instance()->isCoordinator()) {
_traverser.reset(new arangodb::traverser::ClusterTraverser(
ep->edgeColls(), _opts.get(),
ep->edgeColls(), _opts,
std::string(_trx->vocbase()->_name, strlen(_trx->vocbase()->_name)),
_trx));
} else {
_traverser.reset(
new arangodb::traverser::SingleServerTraverser(_opts.get(), _trx));
new arangodb::traverser::SingleServerTraverser(_opts, _trx));
}
if (!ep->usesInVariable()) {
_vertexId = ep->getStartVertex();

View File

@ -73,7 +73,7 @@ class TraversalBlock : public ExecutionBlock {
size_t _posInPaths;
/// @brief Options for the travereser
std::unique_ptr<arangodb::traverser::TraverserOptions> _opts;
arangodb::traverser::TraverserOptions* _opts;
/// @brief Traverser object
std::unique_ptr<arangodb::traverser::Traverser> _traverser;

View File

@ -114,7 +114,7 @@ static TRI_edge_direction_e parseDirection (AstNode const* node) {
TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
TRI_vocbase_t* vocbase, AstNode const* direction,
AstNode const* start, AstNode const* graph,
traverser::TraverserOptions const& options)
std::unique_ptr<traverser::TraverserOptions>& options)
: ExecutionNode(plan, id),
_vocbase(vocbase),
_vertexOutVariable(nullptr),
@ -123,7 +123,6 @@ TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
_inVariable(nullptr),
_graphObj(nullptr),
_condition(nullptr),
_options(options),
_specializedNeighborsSearch(false),
_tmpObjVariable(_plan->getAst()->variables()->createTemporaryVariable()),
_tmpObjVarNode(_plan->getAst()->createNodeReference(_tmpObjVariable)),
@ -131,11 +130,13 @@ TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
_fromCondition(nullptr),
_toCondition(nullptr),
_globalEdgeCondition(nullptr),
_globalVertexCondition(nullptr) {
_globalVertexCondition(nullptr),
_optionsBuild(false) {
TRI_ASSERT(_vocbase != nullptr);
TRI_ASSERT(direction != nullptr);
TRI_ASSERT(start != nullptr);
TRI_ASSERT(graph != nullptr);
_options.reset(options.release());
auto ast = _plan->getAst();
// Let us build the conditions on _from and _to. Just in case we need them.
@ -280,7 +281,7 @@ TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
Variable const* inVariable,
std::string const& vertexId,
std::vector<TRI_edge_direction_e> directions,
traverser::TraverserOptions const& options)
std::unique_ptr<traverser::TraverserOptions>& options)
: ExecutionNode(plan, id),
_vocbase(vocbase),
_vertexOutVariable(nullptr),
@ -291,10 +292,12 @@ TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
_directions(directions),
_graphObj(nullptr),
_condition(nullptr),
_options(options),
_specializedNeighborsSearch(false),
_fromCondition(nullptr),
_toCondition(nullptr) {
_toCondition(nullptr),
_optionsBuild(false) {
_options.reset(options.release());
_graphJson = arangodb::basics::Json(arangodb::basics::Json::Array, edgeColls.size());
for (auto& it : edgeColls) {
@ -313,7 +316,6 @@ TraversalNode::TraversalNode(ExecutionPlan* plan,
_inVariable(nullptr),
_graphObj(nullptr),
_condition(nullptr),
_options(_plan->getAst()->query()->trx(), base),
_specializedNeighborsSearch(false),
_tmpObjVariable(nullptr),
_tmpObjVarNode(nullptr),
@ -321,7 +323,10 @@ TraversalNode::TraversalNode(ExecutionPlan* plan,
_fromCondition(nullptr),
_toCondition(nullptr),
_globalEdgeCondition(nullptr),
_globalVertexCondition(nullptr) {
_globalVertexCondition(nullptr),
_optionsBuild(false) {
_options = std::make_unique<arangodb::traverser::TraverserOptions>(
_plan->getAst()->query()->trx(), base);
auto dirList = base.get("directions");
TRI_ASSERT(dirList.json() != nullptr);
for (size_t i = 0; i < dirList.size(); ++i) {
@ -512,9 +517,9 @@ int TraversalNode::checkIsOutVariable(size_t variableId) const {
/// @brief check whether an access is inside the specified range
bool TraversalNode::isInRange(uint64_t depth, bool isEdge) const {
if (isEdge) {
return (depth < _options.maxDepth);
return (depth < _options->maxDepth);
}
return (depth <= _options.maxDepth);
return (depth <= _options->maxDepth);
}
/// @brief check if all directions are equal
@ -604,7 +609,7 @@ void TraversalNode::toVelocyPackHelper(arangodb::velocypack::Builder& nodes,
}
nodes.add(VPackValue("traversalFlags"));
_options.toVelocyPack(nodes);
_options->toVelocyPack(nodes);
// Traversal Filter Conditions
@ -666,9 +671,11 @@ void TraversalNode::toVelocyPackHelper(arangodb::velocypack::Builder& nodes,
/// @brief clone ExecutionNode recursively
ExecutionNode* TraversalNode::clone(ExecutionPlan* plan, bool withDependencies,
bool withProperties) const {
auto c =
new TraversalNode(plan, _id, _vocbase, _edgeColls, _inVariable, _vertexId,
_directions, _options);
TRI_ASSERT(!_optionsBuild);
auto tmp =
std::make_unique<arangodb::traverser::TraverserOptions>(*_options.get());
auto c = new TraversalNode(plan, _id, _vocbase, _edgeColls, _inVariable,
_vertexId, _directions, tmp);
if (usesVertexOutVariable()) {
auto vertexOutVariable = _vertexOutVariable;
@ -783,27 +790,25 @@ double TraversalNode::estimateCost(size_t& nrItems) const {
}
nrItems = static_cast<size_t>(
incoming *
std::pow(expectedEdgesPerDepth, static_cast<double>(_options.maxDepth)));
std::pow(expectedEdgesPerDepth, static_cast<double>(_options->maxDepth)));
if (nrItems == 0 && incoming > 0) {
nrItems = 1; // min value
}
return depCost + nrItems;
}
void TraversalNode::fillTraversalOptions(
arangodb::traverser::TraverserOptions* opts,
arangodb::Transaction* trx) const {
opts->minDepth = _options.minDepth;
opts->maxDepth = _options.maxDepth;
opts->_tmpVar = _tmpObjVariable;
void TraversalNode::prepareOptions() {
TRI_ASSERT(!_optionsBuild);
_options->_tmpVar = _tmpObjVariable;
size_t numEdgeColls = _edgeColls.size();
AstNode* condition = nullptr;
bool res = false;
EdgeConditionBuilder globalEdgeConditionBuilder(this);
Ast* ast = _plan->getAst();
auto trx = ast->query()->trx();
opts->_baseLookupInfos.reserve(numEdgeColls);
_options->_baseLookupInfos.reserve(numEdgeColls);
// Compute Edge Indexes. First default indexes:
for (size_t i = 0; i < numEdgeColls; ++i) {
auto dir = _directions[i];
@ -833,7 +838,7 @@ void TraversalNode::fillTraversalOptions(
infoIn.idxHandles[0]);
TRI_ASSERT(res); // Right now we have an enforced edge index which will
// always fit.
opts->_baseLookupInfos.emplace_back(std::move(infoIn));
_options->_baseLookupInfos.emplace_back(std::move(infoIn));
break;
}
@ -844,11 +849,12 @@ void TraversalNode::fillTraversalOptions(
info.idxHandles[0]);
TRI_ASSERT(res); // Right now we have an enforced edge index which will
// always fit.
opts->_baseLookupInfos.emplace_back(std::move(info));
_options->_baseLookupInfos.emplace_back(std::move(info));
}
for (std::pair<size_t, EdgeConditionBuilder> it : _edgeConditions) {
auto ins = opts->_depthLookupInfo.emplace(it.first, std::vector<traverser::TraverserOptions::LookupInfo>());
auto ins = _options->_depthLookupInfo.emplace(
it.first, std::vector<traverser::TraverserOptions::LookupInfo>());
TRI_ASSERT(ins.second);
auto& infos = ins.first->second;
infos.reserve(numEdgeColls);
@ -898,13 +904,10 @@ void TraversalNode::fillTraversalOptions(
}
for (auto& it : _vertexConditions) {
opts->_vertexExpressions.emplace(it.first, new Expression(ast, it.second));
TRI_ASSERT(!opts->_vertexExpressions[it.first]->isV8());
_options->_vertexExpressions.emplace(it.first, new Expression(ast, it.second));
TRI_ASSERT(!_options->_vertexExpressions[it.first]->isV8());
}
opts->useBreadthFirst = _options.useBreadthFirst;
opts->uniqueVertices = _options.uniqueVertices;
opts->uniqueEdges = _options.uniqueEdges;
_optionsBuild = true;
}
/// @brief remember the condition to execute for early traversal abortion.
@ -964,6 +967,10 @@ void TraversalNode::registerGlobalCondition(bool isConditionOnEdge,
}
}
arangodb::traverser::TraverserOptions* TraversalNode::options() const {
return _options.get();
}
AstNode* TraversalNode::getTemporaryRefNode() const {
return _tmpObjVarNode;
}

View File

@ -73,7 +73,7 @@ class TraversalNode : public ExecutionNode {
TraversalNode(ExecutionPlan* plan, size_t id, TRI_vocbase_t* vocbase,
AstNode const* direction, AstNode const* start,
AstNode const* graph,
traverser::TraverserOptions const& options);
std::unique_ptr<traverser::TraverserOptions>& options);
TraversalNode(ExecutionPlan* plan, arangodb::basics::Json const& base);
@ -87,7 +87,7 @@ class TraversalNode : public ExecutionNode {
std::vector<std::string> const& edgeColls,
Variable const* inVariable, std::string const& vertexId,
std::vector<TRI_edge_direction_e> directions,
traverser::TraverserOptions const& options);
std::unique_ptr<traverser::TraverserOptions>& options);
public:
/// @brief return the type of the node
@ -192,11 +192,6 @@ class TraversalNode : public ExecutionNode {
std::string const getStartVertex() const { return _vertexId; }
/// @brief Fill the traversal options with all values known to this node or
/// with default values.
void fillTraversalOptions(arangodb::traverser::TraverserOptions* opts,
arangodb::Transaction*) const;
std::vector<std::string> const edgeColls() const { return _edgeColls; }
/// @brief remember the condition to execute for early traversal abortion.
@ -225,17 +220,26 @@ class TraversalNode : public ExecutionNode {
void specializeToNeighborsSearch();
traverser::TraverserOptions const* options() const { return &_options; }
traverser::TraverserOptions* options() const;
AstNode* getTemporaryRefNode() const;
void getConditionVariables(std::vector<Variable const*>&) const;
/// @brief Compute the traversal options containing the expressions
/// MUST! be called after optimization and before creation
/// of blocks.
void prepareOptions();
private:
#ifdef TRI_ENABLE_MAINTAINER_MODE
void checkConditionsDefined() const;
#endif
private:
/// @brief the database
TRI_vocbase_t* _vocbase;
@ -273,7 +277,7 @@ class TraversalNode : public ExecutionNode {
std::unordered_set<Variable const*> _conditionVariables;
/// @brief Options for traversals
traverser::TraverserOptions _options;
std::unique_ptr<traverser::TraverserOptions> _options;
/// @brief Defines if you use a specialized neighbors search instead of general purpose
/// traversal
@ -309,6 +313,11 @@ class TraversalNode : public ExecutionNode {
/// @brief List of all depth specific conditions for vertices
std::unordered_map<size_t, AstNode*> _vertexConditions;
/// @brief Flag if options are already prepared. After
/// this flag was set the node cannot be cloned
/// any more.
bool _optionsBuild;
};
} // namespace arangodb::aql

View File

@ -116,8 +116,20 @@ void arangodb::traverser::TraverserOptions::LookupInfo::buildEngineInfo(
}
arangodb::traverser::TraverserOptions::TraverserOptions(
arangodb::Transaction* trx, Json const& json) {
arangodb::Transaction* trx, Json const& json)
: _trx(trx),
_tmpVar(nullptr),
_ctx(new aql::FixedVarExpressionContext()),
minDepth(1),
maxDepth(1),
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {
Json obj = json.get("traversalFlags");
minDepth = JsonHelper::getNumericValue<uint64_t>(obj.json(), "minDepth", 1);
maxDepth = JsonHelper::getNumericValue<uint64_t>(obj.json(), "maxDepth", 1);
TRI_ASSERT(minDepth <= maxDepth);
useBreadthFirst = JsonHelper::getBooleanValue(obj.json(), "bfs", false);
std::string tmp = JsonHelper::getStringValue(obj.json(), "uniqueVertices", "");
if (tmp == "path") {
@ -146,7 +158,14 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
arangodb::traverser::TraverserOptions::TraverserOptions(
arangodb::aql::Query* query, VPackSlice info, VPackSlice collections)
: _trx(query->trx()), _ctx(new aql::FixedVarExpressionContext()) {
: _trx(query->trx()),
_tmpVar(nullptr),
_ctx(new aql::FixedVarExpressionContext()),
minDepth(1),
maxDepth(1),
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {
// NOTE collections is an array of arrays of strings
VPackSlice read = info.get("minDepth");
if (!read.isInteger()) {
@ -271,6 +290,22 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
_tmpVar = query->ast()->variables()->createVariable(read);
}
arangodb::traverser::TraverserOptions::TraverserOptions(
TraverserOptions const& other)
: _trx(other._trx),
_tmpVar(nullptr),
_ctx(new aql::FixedVarExpressionContext()),
minDepth(other.minDepth),
maxDepth(other.maxDepth),
useBreadthFirst(other.useBreadthFirst),
uniqueVertices(other.uniqueVertices),
uniqueEdges(other.uniqueEdges) {
TRI_ASSERT(other._baseLookupInfos.empty());
TRI_ASSERT(other._depthLookupInfo.empty());
TRI_ASSERT(other._vertexExpressions.empty());
TRI_ASSERT(other._tmpVar == nullptr);
}
arangodb::traverser::TraverserOptions::~TraverserOptions() {
for (auto& pair : _vertexExpressions) {
if (pair.second != nullptr) {
@ -282,24 +317,11 @@ arangodb::traverser::TraverserOptions::~TraverserOptions() {
}
}
arangodb::traverser::TraverserOptions::TraverserOptions(TraverserOptions const& other) {
TRI_ASSERT(_baseLookupInfos.empty());
TRI_ASSERT(_depthLookupInfo.empty());
TRI_ASSERT(_vertexExpressions.empty());
TRI_ASSERT(_tmpVar == nullptr);
_trx = other._trx;
_ctx = new aql::FixedVarExpressionContext();
minDepth = other.minDepth;
maxDepth = other.maxDepth;
useBreadthFirst = other.useBreadthFirst;
uniqueVertices = other.uniqueVertices;
uniqueEdges = other.uniqueEdges;
}
void arangodb::traverser::TraverserOptions::toVelocyPack(VPackBuilder& builder) const {
VPackObjectBuilder guard(&builder);
builder.add("minDepth", VPackValue(minDepth));
builder.add("maxDepth", VPackValue(maxDepth));
builder.add("bfs", VPackValue(useBreadthFirst));
switch (uniqueVertices) {

View File

@ -108,25 +108,25 @@ struct TraverserOptions {
explicit TraverserOptions(arangodb::Transaction* trx)
: _trx(trx),
_tmpVar(nullptr),
_ctx(new aql::FixedVarExpressionContext()),
minDepth(1),
maxDepth(1),
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {
}
uniqueEdges(UniquenessLevel::PATH) {}
TraverserOptions(arangodb::Transaction*, arangodb::basics::Json const&);
TraverserOptions(arangodb::aql::Query*, arangodb::velocypack::Slice,
arangodb::velocypack::Slice);
~TraverserOptions();
/// @brief This copy constructor is only working during planning phase.
/// After planning this node should not be copied anywhere.
TraverserOptions(TraverserOptions const&);
~TraverserOptions();
/// @brief Build a velocypack for cloning in the plan.
void toVelocyPack(arangodb::velocypack::Builder&) const;