Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5f45c6e
first draft, added cancel of traversal in one sided enumerator, and t…
hkernbach Apr 28, 2025
9f751e8
added bidirectionalCircle as new graph in graph test suite
hkernbach May 6, 2025
51be506
added hugeCompleteGraph
hkernbach May 6, 2025
8f6659a
added query cancel observer to twosided enumerator as well, so kpaths…
hkernbach May 13, 2025
e3f7ab8
only print debug creation info if set manually in constructor
hkernbach May 14, 2025
6fec0fc
added additional check for cancelation of query in the twosidedenumer…
hkernbach May 14, 2025
0268917
eslint
hkernbach May 14, 2025
5242085
eslint
hkernbach May 16, 2025
89f440e
clang format
hkernbach May 16, 2025
298877b
clang format
hkernbach May 16, 2025
b6c8690
batchwise verted and edge insertion during fillGraph (integration tests)
hkernbach May 19, 2025
105dbae
try to help garbage collection v8
hkernbach May 19, 2025
9ebfb80
slightly increased timeout to 10s in clsuter, 5s in single server
hkernbach May 19, 2025
324aa21
added early exit to weighted two sided enumerator, yens still missing
hkernbach May 19, 2025
dad761a
debug flag on
hkernbach May 19, 2025
b158f12
no need to call clear manually. will be called in destructor automat…
hkernbach May 20, 2025
053e706
format
hkernbach May 20, 2025
fbc3be7
improved wait & kill mechanism - should now be way more stable
hkernbach Jul 10, 2025
7db1e7b
adaptive kill query
hkernbach Jul 10, 2025
d3b10ba
increase debug logging. Some are optional, some are mandatory. If our…
hkernbach Jul 10, 2025
670eb98
eslint
hkernbach Jul 10, 2025
80271fb
more test output
hkernbach Jul 11, 2025
9ab0e8c
Change test suite params
jbajic Jul 11, 2025
a42d44f
refactored generic graph tests based on Jures suggestion
hkernbach Jul 14, 2025
7717203
incr. completegraph to consist out of 1k nodes to make computation mo…
hkernbach Jul 14, 2025
00dc624
Some cleanups
markuspf Sep 4, 2025
af1e1ae
Re-constify
markuspf Sep 4, 2025
b3bb974
Guard against a query finishing before it is observed as running
markuspf Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions arangod/Aql/ExecutionNode/EnumeratePathsNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ std::unique_ptr<ExecutionBlock> EnumeratePathsNode::createBlock(
TRI_ASSERT(pathType() != arangodb::graph::PathType::Type::ShortestPath);

arangodb::graph::TwoSidedEnumeratorOptions enumeratorOptions{
opts->getMinDepth(), opts->getMaxDepth(), pathType()};
opts->getMinDepth(), opts->getMaxDepth(), pathType(), opts->query()};
PathValidatorOptions validatorOptions(opts->tmpVar(),
opts->getExpressionCtx());

Expand Down Expand Up @@ -476,13 +476,14 @@ std::unique_ptr<ExecutionBlock> EnumeratePathsNode::createBlock(
SingleServerBaseProviderOptions forwardProviderOptions(
opts->tmpVar(), std::move(usedIndexes), opts->getExpressionCtx(), {},
opts->collectionToShard(), opts->getVertexProjections(),
opts->getEdgeProjections(), opts->produceVertices(), opts->useCache());
opts->getEdgeProjections(), opts->produceVertices(), opts->useCache(),
opts->query());

SingleServerBaseProviderOptions backwardProviderOptions(
opts->tmpVar(), std::move(reversedUsedIndexes),
opts->getExpressionCtx(), {}, opts->collectionToShard(),
opts->getVertexProjections(), opts->getEdgeProjections(),
opts->produceVertices(), opts->useCache());
opts->produceVertices(), opts->useCache(), opts->query());

using Provider = SingleServerProvider<SingleServerProviderStep>;
if (opts->query().queryOptions().getTraversalProfileLevel() ==
Expand Down Expand Up @@ -678,11 +679,11 @@ std::unique_ptr<ExecutionBlock> EnumeratePathsNode::createBlock(
} else { // Cluster case (on coordinator)
auto cache = std::make_shared<RefactoredClusterTraverserCache>(
opts->query().resourceMonitor());
ClusterBaseProviderOptions forwardProviderOptions(cache, engines(), false,
opts->produceVertices());
ClusterBaseProviderOptions forwardProviderOptions(
cache, engines(), false, opts->produceVertices(), opts->query());
forwardProviderOptions.setClearEdgeCacheOnClear(false);
ClusterBaseProviderOptions backwardProviderOptions(cache, engines(), true,
opts->produceVertices());
ClusterBaseProviderOptions backwardProviderOptions(
cache, engines(), true, opts->produceVertices(), opts->query());
backwardProviderOptions.setClearEdgeCacheOnClear(false);
// A comment is in order here: For all cases covered here
// (k-shortest-paths, all shortest paths, k-paths) we do not need to
Expand Down
15 changes: 8 additions & 7 deletions arangod/Aql/ExecutionNode/ShortestPathNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ std::unique_ptr<ExecutionBlock> ShortestPathNode::createBlock(

arangodb::graph::TwoSidedEnumeratorOptions enumeratorOptions{
0, std::numeric_limits<size_t>::max(),
arangodb::graph::PathType::Type::ShortestPath};
arangodb::graph::PathType::Type::ShortestPath, opts->query()};
PathValidatorOptions validatorOptions(opts->tmpVar(),
opts->getExpressionCtx());

Expand All @@ -437,13 +437,14 @@ std::unique_ptr<ExecutionBlock> ShortestPathNode::createBlock(
SingleServerBaseProviderOptions forwardProviderOptions(
opts->tmpVar(), std::move(usedIndexes), opts->getExpressionCtx(), {},
opts->collectionToShard(), opts->getVertexProjections(),
opts->getEdgeProjections(), opts->produceVertices(), opts->useCache());
opts->getEdgeProjections(), opts->produceVertices(), opts->useCache(),
opts->query());

SingleServerBaseProviderOptions backwardProviderOptions(
opts->tmpVar(), std::move(reversedUsedIndexes),
opts->getExpressionCtx(), {}, opts->collectionToShard(),
opts->getVertexProjections(), opts->getEdgeProjections(),
opts->produceVertices(), opts->useCache());
opts->produceVertices(), opts->useCache(), opts->query());

auto usesWeight =
checkWeight(forwardProviderOptions, backwardProviderOptions);
Expand Down Expand Up @@ -510,10 +511,10 @@ std::unique_ptr<ExecutionBlock> ShortestPathNode::createBlock(
using ClusterProvider = ClusterProvider<ClusterProviderStep>;
auto cache = std::make_shared<RefactoredClusterTraverserCache>(
opts->query().resourceMonitor());
ClusterBaseProviderOptions forwardProviderOptions(cache, engines(), false,
opts->produceVertices());
ClusterBaseProviderOptions backwardProviderOptions(cache, engines(), true,
opts->produceVertices());
ClusterBaseProviderOptions forwardProviderOptions(
cache, engines(), false, opts->produceVertices(), opts->query());
ClusterBaseProviderOptions backwardProviderOptions(
cache, engines(), true, opts->produceVertices(), opts->query());

auto usesWeight =
checkWeight(forwardProviderOptions, backwardProviderOptions);
Expand Down
10 changes: 6 additions & 4 deletions arangod/Aql/ExecutionNode/TraversalNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -817,8 +817,8 @@ std::unique_ptr<ExecutionBlock> TraversalNode::createBlock(
bool isSmart) const {
TraverserOptions* opts = this->options();

arangodb::graph::OneSidedEnumeratorOptions options{opts->minDepth,
opts->maxDepth};
arangodb::graph::OneSidedEnumeratorOptions options{
opts->minDepth, opts->maxDepth, opts->query()};
/*
* PathValidator Disjoint Helper (TODO [GraphRefactor]: Copy from createBlock)
* Clean this up as soon we clean up the whole TraversalNode as well.
Expand Down Expand Up @@ -915,7 +915,8 @@ ClusterBaseProviderOptions TraversalNode::getClusterBaseProviderOptions(
opts->produceVertices(),
&opts->getExpressionCtx(),
filterConditionVariables,
std::move(availableDepthsSpecificConditions)};
std::move(availableDepthsSpecificConditions),
opts->query()};
}

SingleServerBaseProviderOptions
Expand All @@ -936,7 +937,8 @@ TraversalNode::getSingleServerBaseProviderOptions(
opts->getVertexProjections(),
opts->getEdgeProjections(),
opts->produceVertices(),
opts->useCache()};
opts->useCache(),
opts->query()};
}

/// @brief creates corresponding ExecutionBlock
Expand Down
7 changes: 5 additions & 2 deletions arangod/Graph/Enumerators/OneSidedEnumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ void OneSidedEnumerator<Configuration>::clearProvider() {
}

template<class Configuration>
auto OneSidedEnumerator<Configuration>::computeNeighbourhoodOfNextVertex()
-> void {
void OneSidedEnumerator<Configuration>::computeNeighbourhoodOfNextVertex() {
if (_options.isKilled()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_KILLED);
}

// Pull next element from Queue
// Do 1 step search
TRI_ASSERT(!_queue.isEmpty());
Expand Down
11 changes: 11 additions & 0 deletions arangod/Graph/Enumerators/TwoSidedEnumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ template<class QueueType, class PathStoreType, class ProviderType,
auto TwoSidedEnumerator<QueueType, PathStoreType, ProviderType, PathValidator>::
Ball::computeNeighbourhoodOfNextVertex(Ball& other, ResultList& results)
-> void {
if (_graphOptions.isKilled()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_KILLED);
}

// Pull next element from Queue
// Do 1 step search
TRI_ASSERT(!_queue.isEmpty());
Expand Down Expand Up @@ -451,6 +455,13 @@ void TwoSidedEnumerator<QueueType, PathStoreType, ProviderType,
PathValidator>::searchMoreResults() {
while (_results.empty() && !searchDone()) {
_resultsFetched = false;

// Check for kill signal before proceeding
// We will also do additional checks in computeNeighbourhoodOfNextVertex
if (_options.isKilled()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_KILLED);
}

if (_searchLeft) {
if (ADB_UNLIKELY(_left.doneWithDepth())) {
startNextDepth();
Expand Down
7 changes: 7 additions & 0 deletions arangod/Graph/Enumerators/WeightedTwoSidedEnumerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ auto WeightedTwoSidedEnumerator<QueueType, PathStoreType, ProviderType,
PathValidator>::Ball::
computeNeighbourhoodOfNextVertex(Ball& other, CandidatesStore& candidates)
-> void {
if (_graphOptions.isKilled()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_KILLED);
}

ensureQueueHasProcessableElement();
auto tmp = _queue.pop();

Expand Down Expand Up @@ -862,6 +866,9 @@ template<class QueueType, class PathStoreType, class ProviderType,
class PathValidator>
auto WeightedTwoSidedEnumerator<QueueType, PathStoreType, ProviderType,
PathValidator>::searchDone() const -> bool {
if (_options.isKilled()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_KILLED);
}
if ((_left.noPathLeft() && _right.noPathLeft()) || isAlgorithmFinished()) {
return true;
}
Expand Down
23 changes: 14 additions & 9 deletions arangod/Graph/Enumerators/WeightedTwoSidedEnumerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,15 @@ class WeightedTwoSidedEnumerator {
return _haveSeenOtherSide;
}

auto setForbiddenVertices(std::shared_ptr<VertexSet> forbidden)
-> void requires HasForbidden<PathValidatorType> {
auto setForbiddenVertices(std::shared_ptr<VertexSet> forbidden) -> void
requires HasForbidden<PathValidatorType>
{
_validator.setForbiddenVertices(std::move(forbidden));
};

auto setForbiddenEdges(std::shared_ptr<EdgeSet> forbidden)
-> void requires HasForbidden<PathValidatorType> {
auto setForbiddenEdges(std::shared_ptr<EdgeSet> forbidden) -> void
requires HasForbidden<PathValidatorType>
{
_validator.setForbiddenEdges(std::move(forbidden));
};

Expand Down Expand Up @@ -398,19 +400,22 @@ class WeightedTwoSidedEnumerator {
*/
auto stealStats() -> aql::TraversalStats;

auto setForbiddenVertices(std::shared_ptr<VertexSet> forbidden)
-> void requires HasForbidden<PathValidatorType> {
auto setForbiddenVertices(std::shared_ptr<VertexSet> forbidden) -> void
requires HasForbidden<PathValidatorType>
{
_left.setForbiddenVertices(forbidden);
_right.setForbiddenVertices(std::move(forbidden));
};

auto setForbiddenEdges(std::shared_ptr<EdgeSet> forbidden)
-> void requires HasForbidden<PathValidatorType> {
auto setForbiddenEdges(std::shared_ptr<EdgeSet> forbidden) -> void
requires HasForbidden<PathValidatorType>
{
_left.setForbiddenEdges(forbidden);
_right.setForbiddenEdges(std::move(forbidden));
};

private : [[nodiscard]] auto searchDone() const -> bool;
private:
[[nodiscard]] auto searchDone() const -> bool;
// Ensure that we have fetched all vertices in the _results list. Otherwise,
// we will not be able to generate the resulting path
auto fetchResults() -> void;
Expand Down
5 changes: 3 additions & 2 deletions arangod/Graph/Options/OneSidedEnumeratorOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ using namespace arangodb;
using namespace arangodb::graph;

OneSidedEnumeratorOptions::OneSidedEnumeratorOptions(size_t minDepth,
size_t maxDepth)
: _minDepth(minDepth), _maxDepth(maxDepth) {}
size_t maxDepth,
aql::QueryContext& query)
: _minDepth(minDepth), _maxDepth(maxDepth), _observer(query) {}

OneSidedEnumeratorOptions::~OneSidedEnumeratorOptions() = default;

Expand Down
6 changes: 5 additions & 1 deletion arangod/Graph/Options/OneSidedEnumeratorOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,24 @@

#pragma once

#include "Graph/Options/QueryContextObserver.h"
#include <cstddef>

namespace arangodb::graph {

struct OneSidedEnumeratorOptions {
public:
OneSidedEnumeratorOptions(size_t minDepth, size_t maxDepth);
OneSidedEnumeratorOptions(size_t minDepth, size_t maxDepth,
aql::QueryContext& query);
~OneSidedEnumeratorOptions();

[[nodiscard]] size_t getMinDepth() const noexcept;
[[nodiscard]] size_t getMaxDepth() const noexcept;
[[nodiscard]] bool isKilled() const noexcept { return _observer.isKilled(); }

private:
size_t const _minDepth;
size_t const _maxDepth;
QueryContextObserver _observer;
};
} // namespace arangodb::graph
52 changes: 52 additions & 0 deletions arangod/Graph/Options/QueryContextObserver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2024 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Business Source License 1.1 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// https://github.com/arangodb/arangodb/blob/devel/LICENSE
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Heiko Kernbach
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "Aql/QueryContext.h"

// This class serves as a wrapper around QueryContext to explicitly track where
// query killing is being used in the graph traversal code. It provides a single
// point of access to check if a query has been killed, making it easier to
// maintain and modify the query killing behavior if needed.
//
// While this adds a small layer of indirection, it helps with code clarity and
// maintainability. If profiling shows this wrapper causes significant overhead,
// we can remove it and use QueryContext directly.
//
// We can change this or discuss if this approach is not liked.

namespace arangodb::graph {

class QueryContextObserver {
public:
explicit QueryContextObserver(aql::QueryContext& query) : _query(query) {}

[[nodiscard]] bool isKilled() const { return _query.killed(); }

private:
aql::QueryContext& _query;
};

} // namespace arangodb::graph
8 changes: 6 additions & 2 deletions arangod/Graph/Options/TwoSidedEnumeratorOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ using namespace arangodb::graph;

TwoSidedEnumeratorOptions::TwoSidedEnumeratorOptions(size_t minDepth,
size_t maxDepth,
PathType::Type pathType)
: _minDepth(minDepth), _maxDepth(maxDepth), _pathType(pathType) {
PathType::Type pathType,
aql::QueryContext& query)
: _minDepth(minDepth),
_maxDepth(maxDepth),
_pathType(pathType),
_observer(query) {
if (getPathType() == PathType::Type::AllShortestPaths) {
setStopAtFirstDepth(true);
} else if (getPathType() == PathType::Type::ShortestPath) {
Expand Down
10 changes: 9 additions & 1 deletion arangod/Graph/Options/TwoSidedEnumeratorOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,23 @@
#pragma once

#include "Graph/PathType.h"
#include "Graph/Options/QueryContextObserver.h"

#include <numeric>
#include <cstddef>

namespace arangodb {

namespace aql {
class QueryContext;
}

namespace graph {

struct TwoSidedEnumeratorOptions {
public:
TwoSidedEnumeratorOptions(size_t minDepth, size_t maxDepth,
PathType::Type pathType);
PathType::Type pathType, aql::QueryContext& query);

~TwoSidedEnumeratorOptions();

Expand All @@ -46,6 +52,7 @@ struct TwoSidedEnumeratorOptions {
[[nodiscard]] PathType::Type getPathType() const;
[[nodiscard]] bool getStopAtFirstDepth() const;
[[nodiscard]] bool onlyProduceOnePath() const;
[[nodiscard]] bool isKilled() const noexcept { return _observer.isKilled(); }

void setStopAtFirstDepth(bool stopAtFirstDepth);
void setOnlyProduceOnePath(bool onlyProduceOnePath);
Expand All @@ -57,6 +64,7 @@ struct TwoSidedEnumeratorOptions {
bool _stopAtFirstDepth{false};
bool _onlyProduceOnePath{false};
PathType::Type _pathType;
QueryContextObserver _observer;
};
} // namespace graph
} // namespace arangodb
Loading