Compare commits
4 Commits
add-manual
...
add-gnuplo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f81d4d092 | ||
|
|
27d99b620d | ||
|
|
0220e9b4f7 | ||
|
|
0ff389ffc4 |
27
.github/workflows/diff.yaml
vendored
27
.github/workflows/diff.yaml
vendored
@@ -271,30 +271,3 @@ jobs:
|
||||
source ve3/bin/activate
|
||||
cd e2e
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../libs/mgclient/lib python runner.py --workloads-root-directory ./distributed_queries
|
||||
|
||||
- name: Run query performance tests
|
||||
run: |
|
||||
cd tests/manual
|
||||
./query_performance_runner.py
|
||||
|
||||
- name: Get branch name (merge)
|
||||
if: github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV
|
||||
|
||||
- name: Get branch name (pull request)
|
||||
if: github.event_name == 'pull_request'
|
||||
shell: bash
|
||||
run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF} | tr / -)" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload macro benchmark results
|
||||
run: |
|
||||
cd tools/bench-graph-client
|
||||
virtualenv -p python3 ve3
|
||||
source ve3/bin/activate
|
||||
pip install -r requirements.txt
|
||||
./main.py --benchmark-name "query_performance" \
|
||||
--benchmark-results-path "../../build/tests/manual/query_performance_benchmark/summary.json" \
|
||||
--github-run-id "${{ github.run_id }}" \
|
||||
--github-run-number "${{ github.run_number }}" \
|
||||
--head-branch-name "${{ env.BRANCH_NAME }}"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
rev: 22.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -51,7 +51,7 @@ constexpr char kId[] = "ID";
|
||||
|
||||
namespace MG_INJECTED_NAMESPACE_NAME {
|
||||
namespace detail {
|
||||
using antlropencypher::v2::MemgraphCypher;
|
||||
using antlropencypher::MemgraphCypher;
|
||||
|
||||
template <typename TVisitor>
|
||||
std::optional<std::pair<Expression *, size_t>> VisitMemoryLimit(MemgraphCypher::MemoryLimitContext *memory_limit_ctx,
|
||||
@@ -211,13 +211,13 @@ inline std::string_view ToString(const PulsarConfigKey key) {
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
using antlropencypher::v2::MemgraphCypher;
|
||||
using antlropencypher::MemgraphCypher;
|
||||
|
||||
struct ParsingContext {
|
||||
bool is_query_cached = false;
|
||||
};
|
||||
|
||||
class CypherMainVisitor : public antlropencypher::v2::MemgraphCypherBaseVisitor {
|
||||
class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
public:
|
||||
explicit CypherMainVisitor(ParsingContext context, AstStorage *storage) : context_(context), storage_(storage) {}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "io/message_histogram_collector.hpp"
|
||||
#include "io/time.hpp"
|
||||
#include "io/transport.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
namespace memgraph::io::local_transport {
|
||||
|
||||
@@ -109,7 +108,6 @@ class LocalTransportHandle {
|
||||
auto type_info = TypeInfoFor(message);
|
||||
|
||||
std::any message_any(std::forward<M>(message));
|
||||
MG_RAII_TIMER(timer, message_any.type().name());
|
||||
OpaqueMessage opaque_message{.to_address = to_address,
|
||||
.from_address = from_address,
|
||||
.request_id = request_id,
|
||||
|
||||
@@ -23,7 +23,7 @@ add_custom_command(
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
||||
COMMAND
|
||||
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.10.1-complete.jar
|
||||
-Dlanguage=Cpp -visitor -package antlropencypher::v2
|
||||
-Dlanguage=Cpp -visitor -package antlropencypher
|
||||
-o ${opencypher_generated}
|
||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -14,10 +14,10 @@
|
||||
#include <string>
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "parser/opencypher/generated/MemgraphCypher.h"
|
||||
#include "parser/opencypher/generated/MemgraphCypherLexer.h"
|
||||
#include "utils/concepts.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace memgraph::frontend::opencypher {
|
||||
|
||||
@@ -32,9 +32,11 @@ class SyntaxException : public utils::BasicException {
|
||||
* This thing must me a class since parser.cypher() returns pointer and there is
|
||||
* no way for us to get ownership over the object.
|
||||
*/
|
||||
enum class ParserOpTag : uint8_t { CYPHER, EXPRESSION };
|
||||
enum class ParserOpTag : uint8_t {
|
||||
CYPHER, EXPRESSION
|
||||
};
|
||||
|
||||
template <ParserOpTag Tag = ParserOpTag::CYPHER>
|
||||
template<ParserOpTag Tag = ParserOpTag::CYPHER>
|
||||
class Parser {
|
||||
public:
|
||||
/**
|
||||
@@ -44,9 +46,10 @@ class Parser {
|
||||
Parser(const std::string query) : query_(std::move(query)) {
|
||||
parser_.removeErrorListeners();
|
||||
parser_.addErrorListener(&error_listener_);
|
||||
if constexpr (Tag == ParserOpTag::CYPHER) {
|
||||
if constexpr(Tag == ParserOpTag::CYPHER) {
|
||||
tree_ = parser_.cypher();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
tree_ = parser_.expression();
|
||||
}
|
||||
if (parser_.getNumberOfSyntaxErrors()) {
|
||||
@@ -72,11 +75,11 @@ class Parser {
|
||||
FirstMessageErrorListener error_listener_;
|
||||
std::string query_;
|
||||
antlr4::ANTLRInputStream input_{query_};
|
||||
antlropencypher::v2::MemgraphCypherLexer lexer_{&input_};
|
||||
antlropencypher::MemgraphCypherLexer lexer_{&input_};
|
||||
antlr4::CommonTokenStream tokens_{&lexer_};
|
||||
|
||||
// generate ast
|
||||
antlropencypher::v2::MemgraphCypher parser_{&tokens_};
|
||||
antlropencypher::MemgraphCypher parser_{&tokens_};
|
||||
antlr4::tree::ParseTree *tree_ = nullptr;
|
||||
};
|
||||
} // namespace memgraph::frontend::opencypher
|
||||
|
||||
@@ -48,20 +48,18 @@ add_dependencies(mg-query generate_lcp_query)
|
||||
target_include_directories(mg-query PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-query dl cppitertools Boost::headers)
|
||||
target_link_libraries(mg-query mg-integrations-pulsar mg-integrations-kafka mg-storage-v2 mg-license mg-utils mg-kvstore mg-memory)
|
||||
|
||||
if(NOT "${MG_PYTHON_PATH}" STREQUAL "")
|
||||
set(Python3_ROOT_DIR "${MG_PYTHON_PATH}")
|
||||
endif()
|
||||
|
||||
if("${MG_PYTHON_VERSION}" STREQUAL "")
|
||||
find_package(Python3 3.5 REQUIRED COMPONENTS Development)
|
||||
else()
|
||||
find_package(Python3 "${MG_PYTHON_VERSION}" EXACT REQUIRED COMPONENTS Development)
|
||||
endif()
|
||||
|
||||
target_link_libraries(mg-query Python3::Python)
|
||||
|
||||
# Generate Antlr openCypher parser
|
||||
|
||||
set(opencypher_frontend ${CMAKE_CURRENT_SOURCE_DIR}/frontend/opencypher)
|
||||
set(opencypher_generated ${opencypher_frontend}/generated)
|
||||
set(opencypher_lexer_grammar ${opencypher_frontend}/grammar/MemgraphCypherLexer.g4)
|
||||
@@ -84,15 +82,15 @@ add_custom_command(
|
||||
OUTPUT ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
|
||||
COMMAND
|
||||
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.10.1-complete.jar
|
||||
-Dlanguage=Cpp -visitor -package antlropencypher
|
||||
-o ${opencypher_generated}
|
||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.10.1-complete.jar
|
||||
-Dlanguage=Cpp -visitor -package antlropencypher
|
||||
-o ${opencypher_generated}
|
||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
DEPENDS
|
||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||
${opencypher_frontend}/grammar/CypherLexer.g4
|
||||
${opencypher_frontend}/grammar/Cypher.g4)
|
||||
${opencypher_lexer_grammar} ${opencypher_parser_grammar}
|
||||
${opencypher_frontend}/grammar/CypherLexer.g4
|
||||
${opencypher_frontend}/grammar/Cypher.g4)
|
||||
|
||||
add_custom_target(generate_opencypher_parser
|
||||
DEPENDS ${antlr_opencypher_generated_src} ${antlr_opencypher_generated_include})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -63,7 +63,7 @@ class CachedPlan {
|
||||
|
||||
private:
|
||||
std::unique_ptr<LogicalPlan> plan_;
|
||||
utils::Timer<> cache_timer_;
|
||||
utils::Timer cache_timer_;
|
||||
};
|
||||
|
||||
struct CachedQuery {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -1861,7 +1861,6 @@ Produce::ProduceCursor::ProduceCursor(const Produce &self, utils::MemoryResource
|
||||
|
||||
bool Produce::ProduceCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("Produce");
|
||||
MG_RAII_TIMER(timer, "PRODUCE_PULL v2");
|
||||
|
||||
if (input_cursor_->Pull(frame, context)) {
|
||||
// Produce should always yield the latest results.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "query/v2/request_router.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_HIDDEN_bool(query_v2_cost_planner, true, "Use the cost-estimating query planner.");
|
||||
DEFINE_HIDDEN_bool(query_cost_planner, true, "Use the cost-estimating query planner.");
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_int32(query_v2_plan_cache_ttl, 60, "Time to live for cached query plans, in seconds.",
|
||||
DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60, "Time to live for cached query plans, in seconds.",
|
||||
FLAG_IN_RANGE(0, std::numeric_limits<int32_t>::max()));
|
||||
|
||||
namespace memgraph::query::v2 {
|
||||
@@ -123,7 +123,7 @@ std::unique_ptr<LogicalPlan> MakeLogicalPlan(AstStorage ast_storage, CypherQuery
|
||||
auto vertex_counts = plan::MakeVertexCountCache(request_router);
|
||||
auto symbol_table = expr::MakeSymbolTable(query, predefined_identifiers);
|
||||
auto planning_context = plan::MakePlanningContext(&ast_storage, &symbol_table, query, &vertex_counts);
|
||||
auto [root, cost] = plan::MakeLogicalPlan(&planning_context, parameters, FLAGS_query_v2_cost_planner);
|
||||
auto [root, cost] = plan::MakeLogicalPlan(&planning_context, parameters, FLAGS_query_cost_planner);
|
||||
return std::make_unique<SingleNodeLogicalPlan>(std::move(root), cost, std::move(ast_storage),
|
||||
std::move(symbol_table));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -22,9 +22,9 @@
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(query_v2_cost_planner);
|
||||
DECLARE_bool(query_cost_planner);
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int32(query_v2_plan_cache_ttl);
|
||||
DECLARE_int32(query_plan_cache_ttl);
|
||||
|
||||
namespace memgraph::query::v2 {
|
||||
|
||||
@@ -58,12 +58,12 @@ class CachedPlan {
|
||||
|
||||
bool IsExpired() const {
|
||||
// NOLINTNEXTLINE (modernize-use-nullptr)
|
||||
return cache_timer_.Elapsed() > std::chrono::seconds(FLAGS_query_v2_plan_cache_ttl);
|
||||
return cache_timer_.Elapsed() > std::chrono::seconds(FLAGS_query_plan_cache_ttl);
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<LogicalPlan> plan_;
|
||||
utils::Timer<> cache_timer_;
|
||||
utils::Timer cache_timer_;
|
||||
};
|
||||
|
||||
struct CachedQuery {
|
||||
|
||||
@@ -1108,7 +1108,6 @@ Produce::ProduceCursor::ProduceCursor(const Produce &self, utils::MemoryResource
|
||||
|
||||
bool Produce::ProduceCursor::Pull(Frame &frame, ExecutionContext &context) {
|
||||
SCOPED_PROFILE_OP("Produce");
|
||||
MG_RAII_TIMER(timer, "PRODUCE_PULL v3");
|
||||
|
||||
if (input_cursor_->Pull(frame, context)) {
|
||||
// Produce should always yield the latest results.
|
||||
@@ -3088,18 +3087,25 @@ class DistributedExpandCursor : public Cursor {
|
||||
MG_ASSERT(direction != EdgeAtom::Direction::BOTH);
|
||||
const auto &edge = frame[self_.common_.edge_symbol].ValueEdge();
|
||||
static constexpr auto get_dst_vertex = [](const EdgeAccessor &edge,
|
||||
const EdgeAtom::Direction direction) -> accessors::VertexAccessor {
|
||||
const EdgeAtom::Direction direction) -> msgs::VertexId {
|
||||
switch (direction) {
|
||||
case EdgeAtom::Direction::IN:
|
||||
return edge.From();
|
||||
return edge.From().Id();
|
||||
case EdgeAtom::Direction::OUT:
|
||||
return edge.To();
|
||||
return edge.To().Id();
|
||||
case EdgeAtom::Direction::BOTH:
|
||||
throw std::runtime_error("EdgeDirection Both not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
frame[self_.common_.node_symbol] = get_dst_vertex(edge, direction);
|
||||
msgs::GetPropertiesRequest request;
|
||||
// to not fetch any properties of the edges
|
||||
request.vertex_ids.push_back(get_dst_vertex(edge, direction));
|
||||
auto result_rows = context.request_router->GetProperties(std::move(request));
|
||||
MG_ASSERT(result_rows.size() == 1);
|
||||
auto &result_row = result_rows.front();
|
||||
frame[self_.common_.node_symbol] =
|
||||
accessors::VertexAccessor(msgs::Vertex{result_row.vertex}, result_row.props, context.request_router);
|
||||
}
|
||||
|
||||
bool InitEdges(Frame &frame, ExecutionContext &context) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -13,8 +13,7 @@
|
||||
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_HIDDEN_int64(query_v2_vertex_count_to_expand_existing, 10,
|
||||
DEFINE_VALIDATED_HIDDEN_int64(query_vertex_count_to_expand_existing, 10,
|
||||
"Maximum count of indexed vertices which provoke "
|
||||
"indexed lookup and then expand to existing, instead of "
|
||||
"a regular expand. Default is 10, to turn off use -1.",
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
#include "query/v2/plan/preprocess.hpp"
|
||||
#include "storage/v3/id_types.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_int64(query_v2_vertex_count_to_expand_existing);
|
||||
DECLARE_int64(query_vertex_count_to_expand_existing);
|
||||
|
||||
namespace memgraph::query::v2::plan {
|
||||
|
||||
@@ -101,7 +100,7 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
return true;
|
||||
}
|
||||
ScanAll dst_scan(expand.input(), expand.common_.node_symbol, expand.view_);
|
||||
auto indexed_scan = GenScanByIndex(dst_scan, FLAGS_query_v2_vertex_count_to_expand_existing);
|
||||
auto indexed_scan = GenScanByIndex(dst_scan, FLAGS_query_vertex_count_to_expand_existing);
|
||||
if (indexed_scan) {
|
||||
expand.set_input(std::move(indexed_scan));
|
||||
expand.common_.existing_node = true;
|
||||
@@ -130,7 +129,7 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
// unconditionally creating an indexed scan.
|
||||
indexed_scan = GenScanByIndex(dst_scan);
|
||||
} else {
|
||||
indexed_scan = GenScanByIndex(dst_scan, FLAGS_query_v2_vertex_count_to_expand_existing);
|
||||
indexed_scan = GenScanByIndex(dst_scan, FLAGS_query_vertex_count_to_expand_existing);
|
||||
}
|
||||
if (indexed_scan) {
|
||||
expand.set_input(std::move(indexed_scan));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -17,8 +17,7 @@
|
||||
#include "utils/flag_validation.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_VALIDATED_HIDDEN_uint64(query_v2_max_plans, 1000U, "Maximum number of generated plans for a query.",
|
||||
DEFINE_VALIDATED_HIDDEN_uint64(query_max_plans, 1000U, "Maximum number of generated plans for a query.",
|
||||
FLAG_IN_RANGE(1, std::numeric_limits<std::uint64_t>::max()));
|
||||
|
||||
namespace memgraph::query::v2::plan::impl {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
#include "query/v2/plan/rule_based_planner.hpp"
|
||||
|
||||
// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(query_v2_max_plans);
|
||||
DECLARE_uint64(query_max_plans);
|
||||
|
||||
namespace memgraph::query::v2::plan {
|
||||
|
||||
@@ -311,7 +310,7 @@ class VariableStartPlanner {
|
||||
for (const auto &query_part : query_parts) {
|
||||
alternative_query_parts.emplace_back(impl::VaryQueryPartMatching(query_part, symbol_table));
|
||||
}
|
||||
return iter::slice(MakeCartesianProduct(std::move(alternative_query_parts)), 0UL, FLAGS_query_v2_max_plans);
|
||||
return iter::slice(MakeCartesianProduct(std::move(alternative_query_parts)), 0UL, FLAGS_query_max_plans);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
#include "storage/v3/id_types.hpp"
|
||||
#include "storage/v3/value_conversions.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
namespace memgraph::query::v2 {
|
||||
|
||||
@@ -152,7 +151,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
}
|
||||
|
||||
void StartTransaction() override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
coordinator::HlcRequest req{.last_shard_map_version = shards_map_.GetHlc()};
|
||||
CoordinatorWriteRequests write_req = req;
|
||||
spdlog::trace("sending hlc request to start transaction");
|
||||
@@ -174,7 +172,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
}
|
||||
|
||||
void Commit() override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
coordinator::HlcRequest req{.last_shard_map_version = shards_map_.GetHlc()};
|
||||
CoordinatorWriteRequests write_req = req;
|
||||
spdlog::trace("sending hlc request before committing transaction");
|
||||
@@ -235,7 +232,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
}
|
||||
|
||||
bool IsPrimaryProperty(storage::v3::LabelId primary_label, storage::v3::PropertyId property) const override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
const auto schema_it = shards_map_.schemas.find(primary_label);
|
||||
MG_ASSERT(schema_it != shards_map_.schemas.end(), "Invalid primary label id: {}", primary_label.AsUint());
|
||||
|
||||
@@ -252,7 +248,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
|
||||
// TODO(kostasrim) Simplify return result
|
||||
std::vector<VertexAccessor> ScanVertices(std::optional<std::string> label) override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
// create requests
|
||||
auto requests_to_be_sent = RequestsForScanVertices(label);
|
||||
|
||||
@@ -288,7 +283,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
|
||||
std::vector<msgs::CreateVerticesResponse> CreateVertices(std::vector<msgs::NewVertex> new_vertices) override {
|
||||
MG_ASSERT(!new_vertices.empty());
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
|
||||
// create requests
|
||||
std::vector<ShardRequestState<msgs::CreateVerticesRequest>> requests_to_be_sent =
|
||||
@@ -316,7 +310,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
|
||||
std::vector<msgs::CreateExpandResponse> CreateExpand(std::vector<msgs::NewExpand> new_edges) override {
|
||||
MG_ASSERT(!new_edges.empty());
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
|
||||
// create requests
|
||||
std::vector<ShardRequestState<msgs::CreateExpandRequest>> requests_to_be_sent =
|
||||
@@ -339,7 +332,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
}
|
||||
|
||||
std::vector<msgs::ExpandOneResultRow> ExpandOne(msgs::ExpandOneRequest request) override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
// TODO(kostasrim)Update to limit the batch size here
|
||||
// Expansions of the destination must be handled by the caller. For example
|
||||
// match (u:L1 { prop : 1 })-[:Friend]-(v:L1)
|
||||
@@ -381,7 +373,6 @@ class RequestRouter : public RequestRouterInterface {
|
||||
}
|
||||
|
||||
std::vector<msgs::GetPropertiesResultRow> GetProperties(msgs::GetPropertiesRequest requests) override {
|
||||
MG_RAII_TIMER(timer, __PRETTY_FUNCTION__);
|
||||
requests.transaction_id = transaction_id_;
|
||||
// create requests
|
||||
std::vector<ShardRequestState<msgs::GetPropertiesRequest>> requests_to_be_sent =
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include "storage/v3/vertex_id.hpp"
|
||||
#include "storage/v3/view.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
using msgs::Label;
|
||||
@@ -517,7 +516,6 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CommitRequest &&req) {
|
||||
};
|
||||
|
||||
msgs::ReadResponses ShardRsm::HandleRead(msgs::GetPropertiesRequest &&req) {
|
||||
MG_RAII_TIMER(timer, "SHARD_RSM_HANDLE_GET_PROPS");
|
||||
if (!req.vertex_ids.empty() && !req.vertices_and_edges.empty()) {
|
||||
auto shard_error = SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
|
||||
auto error = CreateErrorResponse(shard_error, req.transaction_id, "");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -58,7 +58,7 @@ class Telemetry final {
|
||||
const std::string machine_id_;
|
||||
uint64_t num_{0};
|
||||
utils::Scheduler scheduler_;
|
||||
utils::Timer<> timer_;
|
||||
utils::Timer timer_;
|
||||
|
||||
const uint64_t send_every_n_;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -12,46 +12,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
// TODO(gitbuda): Figure out the relation with tsc.hpp
|
||||
// TODO(gitbuda): Consider adding system_clock as an option
|
||||
|
||||
// This class is threadsafe.
|
||||
template <typename TTime = std::chrono::duration<double>>
|
||||
class Timer {
|
||||
public:
|
||||
// TODO(gitbuda): Timer(TFun&& destroy_callback...
|
||||
Timer(std::function<void(decltype(std::declval<TTime>().count()) elapsed)> destroy_callback = nullptr)
|
||||
: start_time_(std::chrono::steady_clock::now()), destroy_callback_(destroy_callback) {}
|
||||
Timer() : start_time_(std::chrono::steady_clock::now()) {}
|
||||
|
||||
template <typename TDuration = std::chrono::duration<double>>
|
||||
TDuration Elapsed() const {
|
||||
return std::chrono::duration_cast<TDuration>(std::chrono::steady_clock::now() - start_time_);
|
||||
}
|
||||
|
||||
~Timer() {
|
||||
if (destroy_callback_) {
|
||||
destroy_callback_(Elapsed<TTime>().count());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::steady_clock::time_point start_time_;
|
||||
// TODO(gitbuda): std::function is an overhead, replace with TFun
|
||||
std::function<void(decltype(std::declval<TTime>().count()))> destroy_callback_ = nullptr;
|
||||
};
|
||||
|
||||
// The intention with the macro is to have an easy way to probe how long a
|
||||
// certain code block takes. It should be a short living piece of code. For
|
||||
// something that has to stay longer in the code, please use the Timer
|
||||
// directly.
|
||||
// TODO(gitbuda): Maybe a desired property would be to at any given time, turn
|
||||
// some of them ON/OFF (not all).
|
||||
//
|
||||
#define MG_RAII_TIMER(name, message) \
|
||||
memgraph::utils::Timer<> name([&](auto elapsed) { spdlog::critical("{} {}s", message, elapsed); })
|
||||
|
||||
} // namespace memgraph::utils
|
||||
|
||||
@@ -13,12 +13,7 @@ import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from common import (
|
||||
connection,
|
||||
execute_and_fetch_all,
|
||||
has_n_result_row,
|
||||
wait_for_shard_manager_to_initialize,
|
||||
)
|
||||
from common import connection, execute_and_fetch_all, has_n_result_row, wait_for_shard_manager_to_initialize
|
||||
|
||||
|
||||
def test_sequenced_expand_one(connection):
|
||||
@@ -33,12 +28,8 @@ def test_sequenced_expand_one(connection):
|
||||
results = execute_and_fetch_all(cursor, "MATCH (n)-[:TO]->(m)-[:TO]->(l) RETURN n,m,l")
|
||||
assert len(results) == 1
|
||||
n, m, l = results[0]
|
||||
assert (
|
||||
len(n.properties) == 0
|
||||
), "we don't return any properties of the node received from expansion and the bolt layer doesn't serialize the primary key of vertices"
|
||||
assert (
|
||||
len(m.properties) == 0
|
||||
), "we don't return any properties of the node received from expansion and the bolt layer doesn't serialize the primary key of vertices"
|
||||
assert n.properties["property"] == 1
|
||||
assert m.properties["property"] == 2
|
||||
assert l.properties["property"] == 3
|
||||
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import sys
|
||||
import time
|
||||
import typing
|
||||
|
||||
import mgclient
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
import time
|
||||
from common import *
|
||||
|
||||
|
||||
@@ -37,7 +35,7 @@ def test_vertex_creation_and_scanall(connection):
|
||||
assert len(results) == 9
|
||||
for (n, r, m) in results:
|
||||
n_props = n.properties
|
||||
assert len(n_props) == 0, "n is not expected to have properties, update the test!"
|
||||
assert len(n_props) == 1, "n is not expected to have properties, update the test!"
|
||||
assert len(n.labels) == 0, "n is not expected to have labels, update the test!"
|
||||
|
||||
assert r.type == "TO"
|
||||
|
||||
@@ -9,7 +9,6 @@ function(add_manual_test test_cpp)
|
||||
get_filename_component(exec_name ${test_cpp} NAME_WE)
|
||||
set(target_name ${test_prefix}${exec_name})
|
||||
add_executable(${target_name} ${test_cpp} ${ARGN})
|
||||
|
||||
# OUTPUT_NAME sets the real name of a target when it is built and can be
|
||||
# used to help create two targets of the same name even though CMake
|
||||
# requires unique logical target names
|
||||
@@ -22,7 +21,7 @@ target_link_libraries(${test_prefix}antlr_parser antlr_opencypher_parser_lib)
|
||||
|
||||
add_manual_test(antlr_sigsegv.cpp)
|
||||
target_link_libraries(${test_prefix}antlr_sigsegv gtest gtest_main
|
||||
antlr_opencypher_parser_lib mg-utils)
|
||||
antlr_opencypher_parser_lib mg-utils)
|
||||
|
||||
add_manual_test(antlr_tree_pretty_print.cpp)
|
||||
target_link_libraries(${test_prefix}antlr_tree_pretty_print antlr_opencypher_parser_lib)
|
||||
@@ -38,15 +37,13 @@ target_link_libraries(${test_prefix}query_hash mg-query)
|
||||
|
||||
add_manual_test(query_planner.cpp interactive/planning.cpp)
|
||||
target_link_libraries(${test_prefix}query_planner mg-query)
|
||||
|
||||
if(READLINE_FOUND)
|
||||
if (READLINE_FOUND)
|
||||
target_link_libraries(${test_prefix}query_planner readline)
|
||||
endif()
|
||||
|
||||
add_manual_test(query_execution_dummy.cpp)
|
||||
target_link_libraries(${test_prefix}query_execution_dummy mg-query)
|
||||
|
||||
if(READLINE_FOUND)
|
||||
if (READLINE_FOUND)
|
||||
target_link_libraries(${test_prefix}query_execution_dummy readline)
|
||||
endif()
|
||||
|
||||
@@ -64,6 +61,3 @@ target_link_libraries(${test_prefix}ssl_client mg-communication)
|
||||
|
||||
add_manual_test(ssl_server.cpp)
|
||||
target_link_libraries(${test_prefix}ssl_server mg-communication)
|
||||
|
||||
add_manual_test(query_performance.cpp)
|
||||
target_link_libraries(${test_prefix}query_performance mg-communication mg-utils mg-io mg-io-simulator mg-coordinator mg-query-v2 mg-storage-v3 mg-query mg-storage-v2)
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
// This binary is meant to easily compare the performance of:
|
||||
// - Memgraph v2
|
||||
// - Memgraph v3
|
||||
// - Memgraph v3 with MultiFrame
|
||||
// This binary measures three things which provides a high level and easily understandable metric about the performance
|
||||
// difference between the different versions:
|
||||
// 1. Read time: how much time does it take to read the files:
|
||||
// 2. Init time: how much time does it take to run the init queries, including the index creation. For details please
|
||||
// check RunV2.
|
||||
// 3. Benchmark time: how much time does it take to run the benchmark queries.
|
||||
// To quickly compare performance of the different versions just change the query or queries in the benchmark queries
|
||||
// file you can see the different by running this executable. This way we don't have keep multiple binaries of Memgraph
|
||||
// v2 and Memgraph v3 with/without MultiFrame, start Memgraph and connect to it with mgconsole and other hassles. As
|
||||
// everything is run in this binary, it makes easier to generate perf reports/flamegraphs from the query execution of
|
||||
// different Memgraph versions compared to using the full blown version of Memgraph.
|
||||
//
|
||||
// A few important notes:
|
||||
// - All the input files are mandated to have an empty line at the end of the file as the reading logic expect that.
|
||||
// - tests/mgbench/dataset_creator_unwind.py is recommended to generate the dataset because it generates queries with
|
||||
// UNWIND that makes the import faster in Memgraph v3, thus we can compare the performance on non trivial datasets
|
||||
// also. To make it possible to use the generated dataset, you have to move the generated index queries into a
|
||||
// separate file that can be supplied as index queries file for this binary when using Memgraph v2. The reason for
|
||||
// this is Memgraph v3 cannot handle indices yet, thus it crashes.
|
||||
// - Check the command line flags and their description defined in this file.
|
||||
// - Also check out the --default-multi-frame-size command line flag if you want to play with that.
|
||||
// - The log level is manually set to warning in the main function to avoid the overwhelming log messages from Memgraph
|
||||
// v3. Apart from ease of use, the huge amount of looging can degrade the actual performance.
|
||||
//
|
||||
// Example usage with Memgraph v2:
|
||||
// ./query_performance
|
||||
// --index-queries-file indices.cypher
|
||||
// --init-queries-file dataset.cypher
|
||||
// --benchmark-queries-files expand.cypher,match.cypyher
|
||||
// --use-v3=false
|
||||
//
|
||||
// Example usage with Memgraph v3 without MultiFrame:
|
||||
// ./query_performance
|
||||
// --split-file split_file
|
||||
// --init-queries-file dataset.cypher
|
||||
// --benchmark-queries-files expand.cypher,match.cypyher
|
||||
// --use-v3=true
|
||||
// --use-multi-frame=false
|
||||
//
|
||||
// Example usage with Memgraph v3 with MultiFrame:
|
||||
// ./query_performance
|
||||
// --split-file split_file
|
||||
// --init-queries-file dataset.cypher
|
||||
// --benchmark-queries-files expand.cypher,match.cypyher
|
||||
// --use-v3=true
|
||||
// --use-multi-frame=true
|
||||
//
|
||||
// The examples are using only the necessary flags, however specifying all of them is not a problem, so if you specify
|
||||
// --index-queries-file for Memgraph v3, then it will be safely ignored just as --split-file for Memgraph v2.
|
||||
//
|
||||
// To generate flamegraph you can use the following command:
|
||||
// flamegraph --cmd "record -F 997 --call-graph fp -g" --root -o flamegraph.svg -- ./query_performance <flags>
|
||||
// Using the default option (dwarf) for --call-graph when calling perf might result in too long runtine of flamegraph
|
||||
// because of address resolution. See https://github.com/flamegraph-rs/flamegraph/issues/74.
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <thread>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <spdlog/cfg/env.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <json/json.hpp>
|
||||
|
||||
// v3 includes
|
||||
#include "io/address.hpp"
|
||||
#include "io/local_transport/local_system.hpp"
|
||||
#include "io/message_histogram_collector.hpp"
|
||||
#include "machine_manager/machine_manager.hpp"
|
||||
#include "query/discard_value_stream.hpp"
|
||||
#include "query/v2/discard_value_stream.hpp"
|
||||
#include "query/v2/interpreter.hpp"
|
||||
#include "query/v2/request_router.hpp"
|
||||
|
||||
// v2 includes
|
||||
#include "query/interpreter.hpp"
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
// common includes
|
||||
#include "utils/string.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(index_queries_file, "",
|
||||
"Path to the file which contains the queries to create indices. Used only for v2. Must contain an empty "
|
||||
"line at the end of the file after the queries.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(split_file, "",
|
||||
"Path to the split file which contains the predefined labels, properties, edge types and shard-ranges. "
|
||||
"Used only for v3. Must contain an empty line at the end of the file.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(init_queries_file, "",
|
||||
"Path to the file that is used to insert the initial dataset, one query per line. Must contain an empty "
|
||||
"line at the end of the file after the queries.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_string(benchmark_queries_files, "",
|
||||
"Comma separated paths to the files that contain the queries that we want to compare, one query per "
|
||||
"line. Must contain an empty line at the end of each file after the queries.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(use_v3, true, "If set to true, then Memgraph v3 will be used, otherwise Memgraph v2 will be used.");
|
||||
|
||||
DEFINE_string(export_json_results, "", "If not empty, then the results will be exported as a json file.");
|
||||
|
||||
DEFINE_string(data_directory, "mg_data", "Path to directory to use as storage directory for Memgraph v2.");
|
||||
|
||||
namespace memgraph::tests::manual {
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
struct DependantTypes {};
|
||||
|
||||
template <>
|
||||
struct DependantTypes<query::InterpreterContext> {
|
||||
using Interpreter = query::Interpreter;
|
||||
using DiscardValueResultStream = query::DiscardValueResultStream;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DependantTypes<query::v2::InterpreterContext> {
|
||||
using Interpreter = query::v2::Interpreter;
|
||||
using DiscardValueResultStream = query::v2::DiscardValueResultStream;
|
||||
};
|
||||
|
||||
template <typename TRep, typename TPeriod>
|
||||
void PutResult(nlohmann::json &json, const std::string_view name, std::chrono::duration<TRep, TPeriod> duration) {
|
||||
json[name] = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
}
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
using Interpreter = typename DependantTypes<TInterpreterContext>::Interpreter;
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
using DiscardValueResultStream = typename DependantTypes<TInterpreterContext>::DiscardValueResultStream;
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
void RunQueries(TInterpreterContext &interpreter_context, const std::vector<std::string> &queries) {
|
||||
Interpreter<TInterpreterContext> interpreter{&interpreter_context};
|
||||
DiscardValueResultStream<TInterpreterContext> stream;
|
||||
|
||||
for (const auto &query : queries) {
|
||||
auto result = interpreter.Prepare(query, {}, nullptr);
|
||||
interpreter.Pull(&stream, std::nullopt, result.qid);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
void RunInitQueries(TInterpreterContext &interpreter_context, const std::vector<std::string> &init_queries) {
|
||||
RunQueries(interpreter_context, init_queries);
|
||||
}
|
||||
|
||||
template <typename TInterpreterContext>
|
||||
void RunBenchmarkQueries(TInterpreterContext &interpreter_context, const std::vector<std::string> &benchmark_queries) {
|
||||
RunQueries(interpreter_context, benchmark_queries);
|
||||
}
|
||||
|
||||
std::vector<std::string> ReadQueries(const std::string &file_name) {
|
||||
std::vector<std::string> queries{};
|
||||
std::string buffer;
|
||||
|
||||
std::ifstream file{file_name, std::ios::in};
|
||||
MG_ASSERT(file.good(), "Cannot open queries file to read: {}", file_name);
|
||||
while (file.good()) {
|
||||
std::getline(file, buffer);
|
||||
if (buffer.empty()) {
|
||||
continue;
|
||||
}
|
||||
// Trim the trailing `;`
|
||||
queries.push_back(buffer.substr(0, buffer.size() - 1));
|
||||
}
|
||||
return queries;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> ReadBenchmarkQueries(const std::string benchmark_queries_files) {
|
||||
auto benchmark_files = utils::Split(benchmark_queries_files, ",");
|
||||
std::map<std::string, std::vector<std::string>> result;
|
||||
for (const auto &benchmark_file : benchmark_files) {
|
||||
const auto path = std::filesystem::path(benchmark_file);
|
||||
result.emplace(path.stem().string(), ReadQueries(benchmark_file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RunV2() {
|
||||
spdlog::critical("Running V2");
|
||||
const auto run_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
const auto index_queries = ReadQueries(FLAGS_index_queries_file);
|
||||
const auto init_queries = ReadQueries(FLAGS_init_queries_file);
|
||||
const auto benchmarks = ReadBenchmarkQueries(FLAGS_benchmark_queries_files);
|
||||
|
||||
storage::Storage storage{
|
||||
storage::Config{.durability{.storage_directory = FLAGS_data_directory,
|
||||
.snapshot_wal_mode = storage::Config::Durability::SnapshotWalMode::DISABLED}}};
|
||||
|
||||
memgraph::query::InterpreterContext interpreter_context{
|
||||
&storage,
|
||||
{.query = {.allow_load_csv = false},
|
||||
.execution_timeout_sec = 0,
|
||||
.replication_replica_check_frequency = std::chrono::seconds(0),
|
||||
.default_kafka_bootstrap_servers = "",
|
||||
.default_pulsar_service_url = "",
|
||||
.stream_transaction_conflict_retries = 0,
|
||||
.stream_transaction_retry_interval = std::chrono::milliseconds(0)},
|
||||
FLAGS_data_directory};
|
||||
|
||||
const auto init_start = std::chrono::high_resolution_clock::now();
|
||||
RunInitQueries(interpreter_context, index_queries);
|
||||
RunInitQueries(interpreter_context, init_queries);
|
||||
const auto benchmark_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
spdlog::critical("Read: {}ms", std::chrono::duration_cast<std::chrono::milliseconds>(init_start - run_start).count());
|
||||
spdlog::critical("Init: {}ms",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(benchmark_start - init_start).count());
|
||||
|
||||
std::map<std::string, std::chrono::nanoseconds> benchmark_results;
|
||||
for (const auto &[name, queries] : benchmarks) {
|
||||
const auto current_start = std::chrono::high_resolution_clock::now();
|
||||
RunBenchmarkQueries(interpreter_context, queries);
|
||||
const auto current_stop = std::chrono::high_resolution_clock::now();
|
||||
const auto elapsed = current_stop - current_start;
|
||||
spdlog::critical("Benchmark {}: {}ms", name,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count());
|
||||
benchmark_results.emplace(name, elapsed);
|
||||
}
|
||||
|
||||
const auto benchmark_end = std::chrono::high_resolution_clock::now();
|
||||
spdlog::critical("Benchmark: {}ms",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(benchmark_end - benchmark_start).count());
|
||||
|
||||
if (!FLAGS_export_json_results.empty()) {
|
||||
nlohmann::json results;
|
||||
PutResult(results, "init", benchmark_start - init_start);
|
||||
nlohmann::json benchmark_results_json;
|
||||
for (const auto &[name, duration] : benchmark_results) {
|
||||
PutResult(benchmark_results_json, name, duration);
|
||||
}
|
||||
results["benchmarks"] = std::move(benchmark_results_json);
|
||||
std::ofstream results_file{FLAGS_export_json_results};
|
||||
results_file << results.dump();
|
||||
}
|
||||
}
|
||||
|
||||
void RunV3() {
|
||||
spdlog::critical("Running V3");
|
||||
MG_RAII_TIMER(timer, "WHOLE v3");
|
||||
const auto run_start = std::chrono::high_resolution_clock::now();
|
||||
std::ifstream sm_file{FLAGS_split_file, std::ios::in};
|
||||
MG_ASSERT(sm_file.good(), "Cannot open split file to read: {}", FLAGS_split_file);
|
||||
auto sm = memgraph::coordinator::ShardMap::Parse(sm_file);
|
||||
|
||||
const auto init_queries = ReadQueries(FLAGS_init_queries_file);
|
||||
const auto benchmarks = ReadBenchmarkQueries(FLAGS_benchmark_queries_files);
|
||||
|
||||
io::local_transport::LocalSystem ls;
|
||||
|
||||
auto unique_local_addr_query = io::Address::UniqueLocalAddress();
|
||||
auto io = ls.Register(unique_local_addr_query);
|
||||
|
||||
memgraph::machine_manager::MachineConfig config{
|
||||
.coordinator_addresses = std::vector<memgraph::io::Address>{unique_local_addr_query},
|
||||
.is_storage = true,
|
||||
.is_coordinator = true,
|
||||
.listen_ip = unique_local_addr_query.last_known_ip,
|
||||
.listen_port = unique_local_addr_query.last_known_port,
|
||||
.shard_worker_threads = 2,
|
||||
};
|
||||
|
||||
memgraph::coordinator::Coordinator coordinator{sm};
|
||||
|
||||
memgraph::machine_manager::MachineManager<memgraph::io::local_transport::LocalTransport> mm{io, config, coordinator};
|
||||
std::jthread mm_thread([&mm] { mm.Run(); });
|
||||
|
||||
auto rr_factory = std::make_unique<memgraph::query::v2::LocalRequestRouterFactory>(io);
|
||||
|
||||
query::v2::InterpreterContext interpreter_context{(memgraph::storage::v3::Shard *)(nullptr),
|
||||
{.execution_timeout_sec = 0},
|
||||
"data",
|
||||
std::move(rr_factory),
|
||||
mm.CoordinatorAddress()};
|
||||
|
||||
// without this it fails sometimes because the CreateVertices request might reach the shard worker faster than the
|
||||
// ShardToInitialize
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
||||
|
||||
const auto init_start = std::chrono::high_resolution_clock::now();
|
||||
RunInitQueries(interpreter_context, init_queries);
|
||||
const auto benchmark_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
spdlog::critical("Read: {}ms", std::chrono::duration_cast<std::chrono::milliseconds>(init_start - run_start).count());
|
||||
spdlog::critical("Init: {}ms",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(benchmark_start - init_start).count());
|
||||
|
||||
std::map<std::string, std::chrono::nanoseconds> benchmark_results;
|
||||
for (const auto &[name, queries] : benchmarks) {
|
||||
const auto current_start = std::chrono::high_resolution_clock::now();
|
||||
RunBenchmarkQueries(interpreter_context, queries);
|
||||
const auto current_stop = std::chrono::high_resolution_clock::now();
|
||||
const auto elapsed = current_stop - current_start;
|
||||
spdlog::critical("Benchmark {}: {}ms", name,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count());
|
||||
benchmark_results.emplace(name, elapsed);
|
||||
}
|
||||
|
||||
const auto benchmark_end = std::chrono::high_resolution_clock::now();
|
||||
spdlog::critical("Benchmark: {}ms",
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(benchmark_end - benchmark_start).count());
|
||||
|
||||
ls.ShutDown();
|
||||
|
||||
if (!FLAGS_export_json_results.empty()) {
|
||||
nlohmann::json results;
|
||||
PutResult(results, "init", benchmark_start - init_start);
|
||||
nlohmann::json benchmark_results_json;
|
||||
for (const auto &[name, duration] : benchmark_results) {
|
||||
PutResult(benchmark_results_json, name, duration);
|
||||
}
|
||||
results["benchmarks"] = std::move(benchmark_results_json);
|
||||
std::ofstream results_file{FLAGS_export_json_results};
|
||||
results_file << results.dump();
|
||||
}
|
||||
}
|
||||
} // namespace memgraph::tests::manual
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
spdlog::set_level(spdlog::level::warn);
|
||||
spdlog::cfg::load_env_levels();
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (FLAGS_use_v3) {
|
||||
memgraph::tests::manual::RunV3();
|
||||
} else {
|
||||
memgraph::tests::manual::RunV2();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2023 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import argparse
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
import requests
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", ".."))
|
||||
BUILD_DIR = os.path.join(PROJECT_DIR, "build")
|
||||
BINARY_DIR = os.path.join(BUILD_DIR, "tests/manual")
|
||||
DEFAULT_BENCHMARK_DIR = os.path.join(BINARY_DIR, "query_performance_benchmark")
|
||||
DATA_URL = (
|
||||
"https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/dataset/query_performance/query_performance_benchmark.tar.gz"
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
"--binary",
|
||||
type=str,
|
||||
default=os.path.join(BINARY_DIR, "query_performance"),
|
||||
help="Path to the binary to use for the benchmark.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--data-dir",
|
||||
type=str,
|
||||
default=tempfile.TemporaryDirectory().name,
|
||||
help="Path to directory that can be used as a data directory for ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--summary-path",
|
||||
type=str,
|
||||
default=os.path.join(DEFAULT_BENCHMARK_DIR, "summary.json"),
|
||||
help="Path to which file write the summary.",
|
||||
)
|
||||
|
||||
parser.add_argument("--init-queries-file", type=str, default=os.path.join(DEFAULT_BENCHMARK_DIR, "dataset.cypher"))
|
||||
parser.add_argument("--index-queries-file", type=str, default=os.path.join(DEFAULT_BENCHMARK_DIR, "indices.cypher"))
|
||||
parser.add_argument("--split-file", type=str, default=os.path.join(DEFAULT_BENCHMARK_DIR, "split_file"))
|
||||
|
||||
parser.add_argument(
|
||||
"--benchmark-queries-files",
|
||||
type=str,
|
||||
default=",".join(
|
||||
[os.path.join(DEFAULT_BENCHMARK_DIR, file_name) for file_name in ["expand.cypher", "match_files.cypher"]]
|
||||
),
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
v2_results_path = os.path.join(DEFAULT_BENCHMARK_DIR, "v2_results.json")
|
||||
v3_results_path = os.path.join(DEFAULT_BENCHMARK_DIR, "v3_results.json")
|
||||
|
||||
|
||||
if os.path.exists(DEFAULT_BENCHMARK_DIR):
|
||||
print(f"Using cachced data from {DEFAULT_BENCHMARK_DIR}")
|
||||
else:
|
||||
print(f"Downloading benchmark data to {DEFAULT_BENCHMARK_DIR}")
|
||||
r = requests.get(DATA_URL)
|
||||
assert r.ok, "Cannot download data"
|
||||
file_like_object = io.BytesIO(r.content)
|
||||
tar = tarfile.open(fileobj=file_like_object)
|
||||
tar.extractall(os.path.dirname(DEFAULT_BENCHMARK_DIR))
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
args.binary,
|
||||
f"--split-file={args.split_file}",
|
||||
f"--index-queries-file={args.index_queries_file}",
|
||||
f"--init-queries-file={args.init_queries_file}",
|
||||
f"--benchmark-queries-files={args.benchmark_queries_files}",
|
||||
"--use-v3=false",
|
||||
"--use-multi-frame=true",
|
||||
f"--export-json-results={v2_results_path}",
|
||||
f"--data-directory={args.data_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
args.binary,
|
||||
f"--split-file={args.split_file}",
|
||||
f"--index-queries-file={args.index_queries_file}",
|
||||
f"--init-queries-file={args.init_queries_file}",
|
||||
f"--benchmark-queries-files={args.benchmark_queries_files}",
|
||||
"--use-v3=true",
|
||||
"--use-multi-frame=true",
|
||||
f"--export-json-results={v3_results_path}",
|
||||
f"--data-directory={args.data_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
v2_results_file = open(v2_results_path)
|
||||
v2_results = json.load(v2_results_file)
|
||||
v3_results_file = open(v3_results_path)
|
||||
v3_results = json.load(v3_results_file)
|
||||
|
||||
with open(args.summary_path, "w") as summary:
|
||||
json.dump({"v2": v2_results, "v3": v3_results}, summary)
|
||||
@@ -51,22 +51,10 @@ import helpers
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--number_of_identities",
|
||||
type=int,
|
||||
default=10,
|
||||
help="Determines how many :Identity nodes will the dataset contain.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--number_of_files", type=int, default=10, help="Determines how many :File nodes will the dataset contain."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--percentage_of_permissions",
|
||||
type=float,
|
||||
default=1.0,
|
||||
help="Determines approximately what percentage of the all possible identity-permission-file connections will be created.",
|
||||
)
|
||||
parser.add_argument("--filename", default="dataset.cypher", help="The name of the output file.")
|
||||
parser.add_argument("--number_of_identities", type=int, default=10)
|
||||
parser.add_argument("--number_of_files", type=int, default=10)
|
||||
parser.add_argument("--percentage_of_permissions", type=float, default=1.0)
|
||||
parser.add_argument("--filename", default="dataset.cypher")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
@@ -51,22 +51,10 @@ import helpers
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--number_of_identities",
|
||||
type=int,
|
||||
default=10,
|
||||
help="Determines how many :Identity nodes will the dataset contain.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--number_of_files", type=int, default=10, help="Determines how many :File nodes will the dataset contain."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--percentage_of_permissions",
|
||||
type=float,
|
||||
default=1.0,
|
||||
help="Determines approximately what percentage of the all possible identity-permission-file connections will be created.",
|
||||
)
|
||||
parser.add_argument("--filename", default="dataset.cypher", help="The name of the output file.")
|
||||
parser.add_argument("--number_of_identities", type=int, default=10)
|
||||
parser.add_argument("--number_of_files", type=int, default=10)
|
||||
parser.add_argument("--percentage_of_permissions", type=float, default=1.0)
|
||||
parser.add_argument("--filename", default="dataset.cypher")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -92,7 +92,7 @@ class GraphSession {
|
||||
|
||||
std::mt19937 generator_;
|
||||
|
||||
memgraph::utils::Timer<> timer_;
|
||||
memgraph::utils::Timer timer_;
|
||||
|
||||
private:
|
||||
double GetRandom() { return std::generate_canonical<double, 10>(generator_); }
|
||||
|
||||
2
tools/plot/.gitignore
vendored
Normal file
2
tools/plot/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.dat
|
||||
*.out
|
||||
7
tools/plot/pool_size_batch_size_query_latency.gnuplot
Normal file
7
tools/plot/pool_size_batch_size_query_latency.gnuplot
Normal file
@@ -0,0 +1,7 @@
|
||||
set dgrid3d 30,30
|
||||
set hidden3d
|
||||
set label "pool size" at 8, 11000, 0
|
||||
set label "multiframe size" at 20,6000,0
|
||||
set label "execution time (ms)" at 14,0,5000
|
||||
splot "frames1.dat" u 1:2:3 with lines
|
||||
pause mouse close
|
||||
6
tools/plot/thread_shard_scanall_latency.gnuplot
Normal file
6
tools/plot/thread_shard_scanall_latency.gnuplot
Normal file
@@ -0,0 +1,6 @@
|
||||
set dgrid3d 30,30
|
||||
set hidden3d
|
||||
set label "shards" at 20,-1,10000
|
||||
set label "threads" at -10,5,10000
|
||||
splot "data.dat" u 1:2:3 with lines
|
||||
pause mouse close
|
||||
Reference in New Issue
Block a user