Compare commits
32 Commits
saml-authe
...
query-exec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ff58aa783 | ||
|
|
e143d68c24 | ||
|
|
04f93ab46c | ||
|
|
9ec55ac099 | ||
|
|
c85e510faa | ||
|
|
75d8a216c3 | ||
|
|
358c34d444 | ||
|
|
f517f8f368 | ||
|
|
7f335052be | ||
|
|
53458b4962 | ||
|
|
2416d0fd15 | ||
|
|
065c88612d | ||
|
|
ed3a8a9328 | ||
|
|
34f2a1e10b | ||
|
|
ce9bba8e83 | ||
|
|
671f51f21d | ||
|
|
60de9e5a25 | ||
|
|
90fa6d9226 | ||
|
|
4d930fb73b | ||
|
|
d30a7c70de | ||
|
|
c0483576db | ||
|
|
6536d3b21d | ||
|
|
ec3ba6a408 | ||
|
|
1145ea87ad | ||
|
|
8fdc199d2b | ||
|
|
112c4528d3 | ||
|
|
11299981df | ||
|
|
2c0fc680de | ||
|
|
2b30ba3fef | ||
|
|
d0babcddc5 | ||
|
|
8ba1f160d4 | ||
|
|
b6a55e534b |
8
.github/workflows/diff.yaml
vendored
8
.github/workflows/diff.yaml
vendored
@@ -4,10 +4,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
@@ -475,8 +471,8 @@ jobs:
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
OS: debian-12
|
||||
TOOLCHAIN: v5
|
||||
OS: debian-10
|
||||
TOOLCHAIN: v4
|
||||
ARCH: amd
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
|
||||
@@ -5,20 +5,17 @@ IFS=' '
|
||||
# NOTE: docker_image_name could be local image build based on release/package images.
|
||||
# NOTE: each line has to be under quotes, docker_container_type, script_name and docker_image_name separate with a space.
|
||||
# "docker_container_type script_name docker_image_name"
|
||||
# docker_container_type OPTIONS:
|
||||
# * mgrun -> running plain/empty operating system for the purposes of testing native memgraph package
|
||||
# * mgbuild -> running the builder container to build memgraph inside it -> it's possible create builder images using release/package/run.sh
|
||||
OPERATING_SYSTEMS=(
|
||||
# "mgrun amzn-2 amazonlinux:2"
|
||||
# "mgrun centos-7 centos:7"
|
||||
# "mgrun centos-9 dokken/centos-stream-9"
|
||||
# "mgrun debian-10 debian:10"
|
||||
# "mgrun debian-11 debian:11"
|
||||
# "mgrun fedora-36 fedora:36"
|
||||
# "mgrun ubuntu-18.04 ubuntu:18.04"
|
||||
# "mgrun ubuntu-20.04 ubuntu:20.04"
|
||||
# "mgrun ubuntu-22.04 ubuntu:22.04"
|
||||
# "mgbuild debian-12 memgraph/memgraph-builder:v5_debian-12"
|
||||
"mgrun amzn-2 amazonlinux:2"
|
||||
"mgrun centos-7 centos:7"
|
||||
"mgrun centos-9 dokken/centos-stream-9"
|
||||
"mgrun debian-10 debian:10"
|
||||
"mgrun debian-11 debian:11"
|
||||
"mgrun fedora-36 fedora:36"
|
||||
"mgrun ubuntu-18.04 ubuntu:18.04"
|
||||
"mgrun ubuntu-20.04 ubuntu:20.04"
|
||||
"mgrun ubuntu-22.04 ubuntu:22.04"
|
||||
# "mgbuild centos-7 package-mgbuild_centos-7"
|
||||
)
|
||||
|
||||
if [ ! "$(docker info)" ]; then
|
||||
@@ -36,24 +33,14 @@ print_help () {
|
||||
# NOTE: This is an idempotent operation!
|
||||
# TODO(gitbuda): Consider making docker_run always delete + start a new container or add a new function.
|
||||
docker_run () {
|
||||
cnt_type="$1"
|
||||
if [[ "$cnt_type" != "mgbuild" && "$cnt_type" != "mgrun" ]]; then
|
||||
echo "ERROR: Wrong docker_container_type -> valid options are mgbuild, mgrun"
|
||||
exit 1
|
||||
fi
|
||||
cnt_name="$2"
|
||||
cnt_image="$3"
|
||||
cnt_name="$1"
|
||||
cnt_image="$2"
|
||||
if [ ! "$(docker ps -q -f name=$cnt_name)" ]; then
|
||||
if [ "$(docker ps -aq -f status=exited -f name=$cnt_name)" ]; then
|
||||
echo "Cleanup of the old exited container..."
|
||||
docker rm $cnt_name
|
||||
fi
|
||||
if [[ "$cnt_type" == "mgbuild" ]]; then
|
||||
docker run -d --volume "$SCRIPT_DIR/../../:/memgraph" --network host --name "$cnt_name" "$cnt_image"
|
||||
fi
|
||||
if [[ "$cnt_type" == "mgrun" ]]; then
|
||||
docker run -d --volume "$SCRIPT_DIR/../../:/memgraph" --network host --name "$cnt_name" "$cnt_image" sleep infinity
|
||||
fi
|
||||
docker run -d --volume "$SCRIPT_DIR/../../:/memgraph" --network host --name "$cnt_name" "$cnt_image" sleep infinity
|
||||
fi
|
||||
echo "The $cnt_image container is active under $cnt_name name!"
|
||||
}
|
||||
@@ -68,9 +55,9 @@ docker_stop_and_rm () {
|
||||
cnt_name="$1"
|
||||
if [ "$(docker ps -q -f name=$cnt_name)" ]; then
|
||||
docker stop "$1"
|
||||
fi
|
||||
if [ "$(docker ps -aq -f status=exited -f name=$cnt_name)" ]; then
|
||||
docker rm "$1"
|
||||
if [ "$(docker ps -aq -f status=exited -f name=$cnt_name)" ]; then
|
||||
docker rm "$1"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -84,7 +71,7 @@ start_all () {
|
||||
docker_name="${docker_container_type}_$script_name"
|
||||
echo ""
|
||||
echo "~~~~ OPERATING ON $docker_image as $docker_name..."
|
||||
docker_run "$docker_container_type" "$docker_name" "$docker_image"
|
||||
docker_run "$docker_name" "$docker_image"
|
||||
docker_exec "$docker_name" "/memgraph/environment/os/$script_name.sh install NEW_DEPS"
|
||||
echo "---- DONE EVERYHING FOR $docker_image as $docker_name..."
|
||||
echo ""
|
||||
|
||||
@@ -851,4 +851,23 @@ inline void func_result_set_value(mgp_func_result *res, mgp_value *value, mgp_me
|
||||
MgInvokeVoid(mgp_func_result_set_value, res, value, memory);
|
||||
}
|
||||
|
||||
inline mgp_execution_result *execute_query(mgp_graph *graph, const char *query, mgp_map *params, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_execution_result *>(mgp_execute_query, graph, memory, query, params);
|
||||
}
|
||||
|
||||
inline mgp_execution_headers *fetch_execution_headers(mgp_execution_result *exec_result) {
|
||||
return MgInvoke<mgp_execution_headers *>(mgp_fetch_execution_headers, exec_result);
|
||||
}
|
||||
|
||||
inline size_t execution_headers_size(mgp_execution_headers *headers) {
|
||||
return MgInvoke<size_t>(mgp_execution_headers_size, headers);
|
||||
}
|
||||
|
||||
inline const char *execution_headers_at(mgp_execution_headers *headers, size_t index) {
|
||||
return MgInvoke<const char *>(mgp_execution_headers_at, headers, index);
|
||||
}
|
||||
|
||||
inline mgp_map *pull_one(mgp_execution_result *result, mgp_graph *graph, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_map *>(mgp_pull_one, result, graph, memory);
|
||||
}
|
||||
} // namespace mgp
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -1800,6 +1800,24 @@ enum mgp_error mgp_func_result_set_error_msg(struct mgp_func_result *result, con
|
||||
/// mgp_func_result.
|
||||
enum mgp_error mgp_func_result_set_value(struct mgp_func_result *result, struct mgp_value *value,
|
||||
struct mgp_memory *memory);
|
||||
|
||||
struct mgp_execution_headers;
|
||||
|
||||
enum mgp_error mgp_execution_headers_at(struct mgp_execution_headers *headers, size_t index, const char **result);
|
||||
|
||||
enum mgp_error mgp_execution_headers_size(struct mgp_execution_headers *headers, size_t *result);
|
||||
|
||||
struct mgp_execution_result;
|
||||
|
||||
enum mgp_error mgp_execute_query(struct mgp_graph *graph, struct mgp_memory *memory, const char *query,
|
||||
struct mgp_map *params, struct mgp_execution_result **result);
|
||||
|
||||
enum mgp_error mgp_fetch_execution_headers(struct mgp_execution_result *exec_result,
|
||||
struct mgp_execution_headers **headers);
|
||||
|
||||
enum mgp_error mgp_pull_one(struct mgp_execution_result *exec_result, struct mgp_graph *graph,
|
||||
struct mgp_memory *memory, struct mgp_map **result);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
167
include/mgp.hpp
167
include/mgp.hpp
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
@@ -93,6 +94,10 @@ class Relationship;
|
||||
struct MapItem;
|
||||
class Duration;
|
||||
class Value;
|
||||
class QueryExecution;
|
||||
class ExecutionResult;
|
||||
class ExecutionHeaders;
|
||||
class ExecutionRow;
|
||||
|
||||
struct StealType {};
|
||||
inline constexpr StealType steal{};
|
||||
@@ -595,6 +600,7 @@ class Map {
|
||||
friend class Record;
|
||||
friend class Result;
|
||||
friend class Parameter;
|
||||
friend class QueryExecution;
|
||||
|
||||
public:
|
||||
/// @brief Creates a Map from the copy of the given @ref mgp_map.
|
||||
@@ -1550,6 +1556,95 @@ class Return {
|
||||
mgp_type *GetMGPType() const;
|
||||
};
|
||||
|
||||
class ExecutionHeaders {
|
||||
public:
|
||||
ExecutionHeaders(mgp_execution_headers *headers);
|
||||
size_t Size() const;
|
||||
std::string At(size_t index) const;
|
||||
|
||||
std::string_view operator[](size_t index) const;
|
||||
|
||||
class Iterator {
|
||||
private:
|
||||
friend class ExecutionHeaders;
|
||||
|
||||
public:
|
||||
using value_type = ExecutionHeaders;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const ExecutionHeaders *;
|
||||
using reference = const ExecutionHeaders &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
bool operator==(const Iterator &other) const;
|
||||
|
||||
bool operator!=(const Iterator &other) const;
|
||||
|
||||
Iterator &operator++();
|
||||
|
||||
std::string_view operator*() const;
|
||||
|
||||
private:
|
||||
Iterator(const ExecutionHeaders *iterable, size_t index);
|
||||
|
||||
const ExecutionHeaders *iterable_;
|
||||
size_t index_;
|
||||
};
|
||||
|
||||
Iterator begin();
|
||||
Iterator end();
|
||||
|
||||
Iterator cbegin();
|
||||
Iterator cend();
|
||||
|
||||
private:
|
||||
mgp_execution_headers *headers_;
|
||||
};
|
||||
|
||||
class QueryExecution {
|
||||
public:
|
||||
QueryExecution(mgp_graph *graph);
|
||||
ExecutionResult ExecuteQuery(std::string_view query, Map params = Map()) const;
|
||||
|
||||
private:
|
||||
mgp_graph *graph_;
|
||||
};
|
||||
|
||||
class ExecutionRow {
|
||||
private:
|
||||
Map row_;
|
||||
|
||||
public:
|
||||
ExecutionRow(mgp_map *row);
|
||||
|
||||
/// @brief Returns the size of the map.
|
||||
size_t Size() const;
|
||||
|
||||
/// @brief Returns whether the map is empty.
|
||||
bool Empty() const;
|
||||
|
||||
/// @brief Returns the value at the given `key`.
|
||||
Value operator[](std::string_view key) const;
|
||||
|
||||
/// @brief Returns the value at the given `key`.
|
||||
Value At(std::string_view key) const;
|
||||
|
||||
/// @brief Returns true if the given `key` exists.
|
||||
bool KeyExists(std::string_view key) const;
|
||||
|
||||
mgp::Map Values() const;
|
||||
};
|
||||
|
||||
class ExecutionResult {
|
||||
public:
|
||||
ExecutionResult(mgp_execution_result *result, mgp_graph *graph);
|
||||
ExecutionHeaders Headers() const;
|
||||
std::optional<ExecutionRow> PullOne() const;
|
||||
|
||||
private:
|
||||
mgp_execution_result *result_;
|
||||
mgp_graph *graph_;
|
||||
};
|
||||
|
||||
enum class ProcedureType : uint8_t {
|
||||
Read,
|
||||
Write,
|
||||
@@ -4286,6 +4381,76 @@ inline mgp_type *Return::GetMGPType() const {
|
||||
return util::ToMGPType(type_);
|
||||
}
|
||||
|
||||
inline ExecutionHeaders::ExecutionHeaders(mgp_execution_headers *headers) : headers_(headers) {}
|
||||
|
||||
inline size_t ExecutionHeaders::Size() const { return mgp::execution_headers_size(headers_); }
|
||||
|
||||
inline std::string ExecutionHeaders::At(size_t index) const {
|
||||
return std::string(mgp::execution_headers_at(headers_, index));
|
||||
}
|
||||
|
||||
inline QueryExecution::QueryExecution(mgp_graph *graph) : graph_(graph) {}
|
||||
|
||||
inline ExecutionResult QueryExecution::ExecuteQuery(std::string_view query, mgp::Map params) const {
|
||||
return ExecutionResult(mgp::MemHandlerCallback(execute_query, graph_, query.data(), params.ptr_), graph_);
|
||||
}
|
||||
|
||||
inline ExecutionResult::ExecutionResult(mgp_execution_result *result, mgp_graph *graph)
|
||||
: result_(result), graph_(graph) {}
|
||||
|
||||
inline ExecutionHeaders ExecutionResult::Headers() const { return mgp::fetch_execution_headers(result_); };
|
||||
|
||||
inline std::optional<ExecutionRow> ExecutionResult::PullOne() const {
|
||||
auto *value = mgp::MemHandlerCallback(pull_one, result_, graph_);
|
||||
if (!value) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ExecutionRow(value);
|
||||
}
|
||||
|
||||
inline bool ExecutionHeaders::Iterator::operator==(const Iterator &other) const {
|
||||
return iterable_ == other.iterable_ && index_ == other.index_;
|
||||
}
|
||||
|
||||
inline bool ExecutionHeaders::Iterator::operator!=(const Iterator &other) const { return !(*this == other); }
|
||||
|
||||
inline ExecutionHeaders::Iterator &ExecutionHeaders::Iterator::operator++() {
|
||||
index_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline std::string_view ExecutionHeaders::Iterator::operator*() const { return (*iterable_)[index_]; }
|
||||
|
||||
inline ExecutionHeaders::Iterator::Iterator(const ExecutionHeaders *iterable, size_t index)
|
||||
: iterable_(iterable), index_(index) {}
|
||||
|
||||
inline std::string_view ExecutionHeaders::operator[](size_t index) const {
|
||||
return std::string_view(mgp::execution_headers_at(headers_, index));
|
||||
}
|
||||
|
||||
inline ExecutionHeaders::Iterator ExecutionHeaders::begin() { return Iterator(this, 0); }
|
||||
|
||||
inline ExecutionHeaders::Iterator ExecutionHeaders::end() { return Iterator(this, Size()); }
|
||||
|
||||
inline ExecutionHeaders::Iterator ExecutionHeaders::cbegin() { return Iterator(this, 0); }
|
||||
|
||||
inline ExecutionHeaders::Iterator ExecutionHeaders::cend() { return Iterator(this, Size()); }
|
||||
|
||||
inline ExecutionRow::ExecutionRow(mgp_map *row) : row_(row) {}
|
||||
|
||||
inline size_t ExecutionRow::Size() const { return row_.Size(); }
|
||||
|
||||
inline bool ExecutionRow::Empty() const { return row_.Empty(); }
|
||||
|
||||
inline Value ExecutionRow::operator[](std::string_view key) const { return row_[key]; }
|
||||
|
||||
inline Value ExecutionRow::At(std::string_view key) const { return row_.At(key); }
|
||||
|
||||
inline bool ExecutionRow::KeyExists(std::string_view key) const { return row_.KeyExists(key); }
|
||||
|
||||
inline mgp::Map ExecutionRow::Values() const { return mgp::Map(row_); }
|
||||
|
||||
// do not enter
|
||||
namespace detail {
|
||||
inline void AddParamsReturnsToProc(mgp_proc *proc, std::vector<Parameter> ¶meters,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/etc/memgraph/memgraph.conf
|
||||
/etc/memgraph/apoc_compatibility_mappings.json
|
||||
/etc/memgraph/auth_module/ldap.example.yaml
|
||||
/etc/memgraph/auth_module/saml/settings.json
|
||||
/etc/logrotate.d/memgraph
|
||||
|
||||
@@ -211,7 +211,6 @@ check_support() {
|
||||
build_memgraph () {
|
||||
local build_container="mgbuild_${toolchain_version}_${os}"
|
||||
local ACTIVATE_TOOLCHAIN="source /opt/toolchain-${toolchain_version}/activate"
|
||||
local ACTIVATE_CARGO="source $MGBUILD_HOME_DIR/.cargo/env"
|
||||
local container_build_dir="$MGBUILD_ROOT_DIR/build"
|
||||
local container_output_dir="$container_build_dir/output"
|
||||
local arm_flag=""
|
||||
@@ -317,7 +316,7 @@ build_memgraph () {
|
||||
|
||||
# Define cmake command
|
||||
local cmake_cmd="cmake $build_type_flag $arm_flag $community_flag $telemetry_id_override_flag $coverage_flag $asan_flag $ubsan_flag .."
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO && $cmake_cmd"
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $cmake_cmd"
|
||||
|
||||
# ' is used instead of " because we need to run make within the allowed
|
||||
# container resources.
|
||||
@@ -327,11 +326,11 @@ build_memgraph () {
|
||||
# support nproc
|
||||
# shellcheck disable=SC2016
|
||||
if [[ "$threads" == 0 ]]; then
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO "'&& make -j$(nproc)'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO "'&& make -j$(nproc) -B mgconsole'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN "'&& make -j$(nproc)'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN "'&& make -j$(nproc) -B mgconsole'
|
||||
else
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO "'&& make -j$threads'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO "'&& make -j$threads -B mgconsole'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN "'&& make -j$threads'
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN "'&& make -j$threads -B mgconsole'
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -397,7 +396,6 @@ copy_memgraph() {
|
||||
test_memgraph() {
|
||||
local ACTIVATE_TOOLCHAIN="source /opt/toolchain-${toolchain_version}/activate"
|
||||
local ACTIVATE_VENV="./setup.sh /opt/toolchain-${toolchain_version}/activate"
|
||||
local ACTIVATE_CARGO="source $MGBUILD_HOME_DIR/.cargo/env"
|
||||
local EXPORT_LICENSE="export MEMGRAPH_ENTERPRISE_LICENSE=$enterprise_license"
|
||||
local EXPORT_ORG_NAME="export MEMGRAPH_ORGANIZATION_NAME=$organization_name"
|
||||
local BUILD_DIR="$MGBUILD_ROOT_DIR/build"
|
||||
@@ -483,7 +481,7 @@ test_memgraph() {
|
||||
# docker network connect --alias $kafka_hostname $build_container_network $kafka_container > /dev/null 2>&1 || echo "Kafka container already inside correct network or something went wrong ..."
|
||||
# docker network connect --alias $pulsar_hostname $build_container_network $pulsar_container > /dev/null 2>&1 || echo "Kafka container already inside correct network or something went wrong ..."
|
||||
docker exec -u mg $build_container bash -c "pip install --user networkx && pip3 install --user networkx"
|
||||
docker exec -u mg $build_container bash -c "$EXPORT_LICENSE && $EXPORT_ORG_NAME && $ACTIVATE_CARGO && cd $MGBUILD_ROOT_DIR/tests && $ACTIVATE_VENV && source ve3/bin/activate_e2e && cd $MGBUILD_ROOT_DIR/tests/e2e "'&& ./run.sh'
|
||||
docker exec -u mg $build_container bash -c "$EXPORT_LICENSE && $EXPORT_ORG_NAME && cd $MGBUILD_ROOT_DIR/tests && $ACTIVATE_VENV && source ve3/bin/activate_e2e && cd $MGBUILD_ROOT_DIR/tests/e2e "'&& ./run.sh'
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown test '$1'"
|
||||
|
||||
@@ -134,7 +134,6 @@ echo "Don't forget to switch to the 'memgraph' user to use Memgraph" || exit 1
|
||||
%config(noreplace) "/etc/memgraph/memgraph.conf"
|
||||
%config(noreplace) "/etc/memgraph/apoc_compatibility_mappings.json"
|
||||
%config(noreplace) "/etc/memgraph/auth_module/ldap.example.yaml"
|
||||
%config(noreplace) "/etc/memgraph/auth_module/saml/settings.json"
|
||||
%config(noreplace) "/etc/logrotate.d/memgraph"
|
||||
|
||||
@CPACK_RPM_USER_INSTALL_FILES@
|
||||
|
||||
@@ -29,7 +29,3 @@ install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/reference_modules/ldap.py
|
||||
DESTINATION lib/memgraph/auth_module)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/reference_modules/ldap.example.yaml
|
||||
DESTINATION /etc/memgraph/auth_module)
|
||||
install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/reference_modules/saml/saml.py
|
||||
DESTINATION lib/memgraph/auth_module/saml)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/reference_modules/saml/settings.json
|
||||
DESTINATION /etc/memgraph/auth_module/saml)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"strict": false,
|
||||
"debug": true,
|
||||
"sp": {
|
||||
"entityId": "http://localhost:5000/metadata/",
|
||||
"assertionConsumerService": {
|
||||
"url": "http://localhost:8000/?acs",
|
||||
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
},
|
||||
"singleLogoutService": {
|
||||
"url": "http://localhost:5000/?sls",
|
||||
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
||||
},
|
||||
"NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
||||
"x509cert": "",
|
||||
"privateKey": ""
|
||||
},
|
||||
"idp": {
|
||||
"entityId": "https://saml.example.com/entityid",
|
||||
"singleSignOnService": {
|
||||
"url": "https://mocksaml.com/api/saml/sso",
|
||||
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
||||
},
|
||||
"singleLogoutService": {
|
||||
"url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/<onelogin_connector_id>",
|
||||
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
||||
},
|
||||
"x509cert": "MIIC4jCCAcoCCQC33wnybT5QZDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJVSzEPMA0GA1UECgwGQm94eUhRMRIwEAYDVQQDDAlNb2NrIFNBTUwwIBcNMjIwMjI4MjE0NjM4WhgPMzAyMTA3MDEyMTQ2MzhaMDIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQKDAZCb3h5SFExEjAQBgNVBAMMCU1vY2sgU0FNTDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGfYettMsct1T6tVUwTudNJH5Pnb9GGnkXi9Zw/e6x45DD0RuRONbFlJ2T4RjAE/uG+AjXxXQ8o2SZfb9+GgmCHuTJFNgHoZ1nFVXCmb/Hg8Hpd4vOAGXndixaReOiq3EH5XvpMjMkJ3+8+9VYMzMZOjkgQtAqO36eAFFfNKX7dTj3VpwLkvz6/KFCq8OAwY+AUi4eZm5J57D31GzjHwfjH9WTeX0MyndmnNB1qV75qQR3b2/W5sGHRv+9AarggJkF+ptUkXoLtVA51wcfYm6hILptpde5FQC8RWY1YrswBWAEZNfyrR4JeSweElNHg4NVOs4TwGjOPwWGqzTfgTlECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAYRlYflSXAWoZpFfwNiCQVE5d9zZ0DPzNdWhAybXcTyMf0z5mDf6FWBW5Gyoi9u3EMEDnzLcJNkwJAAc39Apa4I2/tml+Jy29dk8bTyX6m93ngmCgdLh5Za4khuU3AM3L63g7VexCuO7kwkjh/+LqdcIXsVGO6XDfu2QOs1Xpe9zIzLpwm/RNYeXUjbSj5ce/jekpAw7qyVVL4xOyh8AtUW1ek3wIw1MJvEgEPt0d16oshWJpoS1OT8Lr/22SvYEo3EmSGdTVGgk3x3s+A0qWAqTcyjr7Q4s/GKYRFfomGwz0TZ4Iw1ZN99Mm0eo2USlSRTVl7QHRTuiuSThHpLKQQ=="
|
||||
}
|
||||
}
|
||||
@@ -131,10 +131,6 @@ DEFINE_uint64(storage_recovery_thread_count,
|
||||
DEFINE_bool(storage_enable_schema_metadata, false,
|
||||
"Controls whether metadata should be collected about the resident labels and edge types.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_delta_on_identical_property_update, true,
|
||||
"Controls whether updating a property with the same value should create a delta object.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(telemetry_enabled, false,
|
||||
"Set to true to enable telemetry. We collect information about the "
|
||||
|
||||
@@ -84,8 +84,6 @@ DECLARE_bool(storage_parallel_schema_recovery);
|
||||
DECLARE_uint64(storage_recovery_thread_count);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_enable_schema_metadata);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_delta_on_identical_property_update);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(telemetry_enabled);
|
||||
|
||||
@@ -332,8 +332,7 @@ int main(int argc, char **argv) {
|
||||
.durability_directory = FLAGS_data_directory + "/rocksdb_durability",
|
||||
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"},
|
||||
.salient.items = {.properties_on_edges = FLAGS_storage_properties_on_edges,
|
||||
.enable_schema_metadata = FLAGS_storage_enable_schema_metadata,
|
||||
.delta_on_identical_property_update = FLAGS_storage_delta_on_identical_property_update},
|
||||
.enable_schema_metadata = FLAGS_storage_enable_schema_metadata},
|
||||
.salient.storage_mode = memgraph::flags::ParseStorageMode()};
|
||||
spdlog::info("config recover on startup {}, flags {} {}", db_config.durability.recover_on_startup,
|
||||
FLAGS_storage_recover_on_startup, FLAGS_data_recovery_on_startup);
|
||||
@@ -437,12 +436,12 @@ int main(int argc, char **argv) {
|
||||
|
||||
auto db_acc = dbms_handler.Get();
|
||||
|
||||
memgraph::query::InterpreterContext interpreter_context_(interp_config, &dbms_handler, &repl_state, system,
|
||||
auto *interpreter_context_ =
|
||||
memgraph::query::InterpreterContext::getInstance(interp_config, &dbms_handler, &repl_state, system,
|
||||
#ifdef MG_ENTERPRISE
|
||||
&coordinator_state,
|
||||
&coordinator_state,
|
||||
#endif
|
||||
auth_handler.get(), auth_checker.get(),
|
||||
&replication_handler);
|
||||
auth_handler.get(), auth_checker.get(), &replication_handler);
|
||||
MG_ASSERT(db_acc, "Failed to access the main database");
|
||||
|
||||
memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(memgraph::flags::ParseQueryModulesDirectory(),
|
||||
@@ -455,9 +454,9 @@ int main(int argc, char **argv) {
|
||||
spdlog::info("Running init file...");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file, &audit_log);
|
||||
InitFromCypherlFile(*interpreter_context_, db_acc, FLAGS_init_file, &audit_log);
|
||||
} else {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file);
|
||||
InitFromCypherlFile(*interpreter_context_, db_acc, FLAGS_init_file);
|
||||
}
|
||||
#else
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_file);
|
||||
@@ -465,20 +464,20 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
dbms_handler.RestoreTriggers(&interpreter_context_);
|
||||
dbms_handler.RestoreStreams(&interpreter_context_);
|
||||
dbms_handler.RestoreTriggers(interpreter_context_);
|
||||
dbms_handler.RestoreStreams(interpreter_context_);
|
||||
#else
|
||||
{
|
||||
// Triggers can execute query procedures, so we need to reload the modules first and then
|
||||
// the triggers
|
||||
auto storage_accessor = db_acc->Access();
|
||||
auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
|
||||
db_acc->trigger_store()->RestoreTriggers(&interpreter_context_.ast_cache, &dba, interpreter_context_.config.query,
|
||||
db_acc->trigger_store()->RestoreTriggers(interpreter_context_.ast_cache, &dba, interpreter_context_.config.query,
|
||||
interpreter_context_.auth_checker);
|
||||
}
|
||||
|
||||
// As the Stream transformations are using modules, they have to be restored after the query modules are loaded.
|
||||
db_acc->streams()->RestoreStreams(db_acc, &interpreter_context_);
|
||||
db_acc->streams()->RestoreStreams(db_acc, interpreter_context_);
|
||||
#endif
|
||||
|
||||
ServerContext context;
|
||||
@@ -494,9 +493,9 @@ int main(int argc, char **argv) {
|
||||
auto server_endpoint = memgraph::communication::v2::ServerEndpoint{
|
||||
boost::asio::ip::address::from_string(FLAGS_bolt_address), static_cast<uint16_t>(FLAGS_bolt_port)};
|
||||
#ifdef MG_ENTERPRISE
|
||||
Context session_context{&interpreter_context_, &auth_, &audit_log};
|
||||
Context session_context{interpreter_context_, &auth_, &audit_log};
|
||||
#else
|
||||
Context session_context{&interpreter_context_, &auth_};
|
||||
Context session_context{interpreter_context_, &auth_};
|
||||
#endif
|
||||
memgraph::glue::ServerT server(server_endpoint, &session_context, &context, FLAGS_bolt_session_inactivity_timeout,
|
||||
service_name, FLAGS_bolt_num_workers);
|
||||
@@ -541,14 +540,14 @@ int main(int argc, char **argv) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
&metrics_server,
|
||||
#endif
|
||||
&websocket_server, &server, &interpreter_context_] {
|
||||
&websocket_server, &server, interpreter_context_] {
|
||||
// Server needs to be shutdown first and then the database. This prevents
|
||||
// a race condition when a transaction is accepted during server shutdown.
|
||||
server.Shutdown();
|
||||
// After the server is notified to stop accepting and processing
|
||||
// connections we tell the execution engine to stop processing all pending
|
||||
// queries.
|
||||
interpreter_context_.Shutdown();
|
||||
interpreter_context_->Shutdown();
|
||||
websocket_server.Shutdown();
|
||||
#ifdef MG_ENTERPRISE
|
||||
metrics_server.Shutdown();
|
||||
@@ -576,12 +575,12 @@ int main(int argc, char **argv) {
|
||||
MG_ASSERT(db_acc, "Failed to gain access to the main database");
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_data_file, &audit_log);
|
||||
InitFromCypherlFile(*interpreter_context_, db_acc, FLAGS_init_data_file, &audit_log);
|
||||
} else {
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_data_file);
|
||||
InitFromCypherlFile(*interpreter_context_, db_acc, FLAGS_init_data_file);
|
||||
}
|
||||
#else
|
||||
InitFromCypherlFile(interpreter_context_, db_acc, FLAGS_init_data_file);
|
||||
InitFromCypherlFile(*interpreter_context_, db_acc, FLAGS_init_data_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,12 @@ inline std::vector<storage::LabelId> NamesToLabels(const std::vector<std::string
|
||||
return labels;
|
||||
}
|
||||
|
||||
struct UserExecutionContextInfo {
|
||||
enum class UserMode { NONE, USER, ROLE };
|
||||
UserMode mode;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct ExecutionContext {
|
||||
DbAccessor *db_accessor{nullptr};
|
||||
SymbolTable symbol_table;
|
||||
@@ -86,6 +92,7 @@ struct ExecutionContext {
|
||||
TriggerContextCollector *trigger_context_collector{nullptr};
|
||||
FrameChangeCollector *frame_change_collector{nullptr};
|
||||
std::shared_ptr<utils::AsyncTimer> timer;
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role;
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::unique_ptr<FineGrainedAuthChecker> auth_checker{nullptr};
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// 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
|
||||
|
||||
@@ -1754,10 +1754,12 @@ PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters &pa
|
||||
ctx_.evaluation_context.parameters = parameters;
|
||||
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
||||
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
||||
ctx_.user_or_role = user_or_role;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (license::global_license_checker.IsEnterpriseValidFast() && user_or_role && *user_or_role && dba) {
|
||||
// Create only if an explicit user is defined
|
||||
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(std::move(user_or_role), dba);
|
||||
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(user_or_role, dba);
|
||||
|
||||
// if the user has global privileges to read, edit and write anything, we don't need to perform authorization
|
||||
// otherwise, we do assign the auth checker to check for label access control
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "system/include/system/system.hpp"
|
||||
namespace memgraph::query {
|
||||
|
||||
InterpreterContext *InterpreterContext::instance = nullptr;
|
||||
|
||||
InterpreterContext::InterpreterContext(InterpreterConfig interpreter_config, dbms::DbmsHandler *dbms_handler,
|
||||
replication::ReplicationState *rs, memgraph::system::System &system,
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "storage/v2/transaction.hpp"
|
||||
#include "system/state.hpp"
|
||||
#include "system/system.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/gatekeeper.hpp"
|
||||
#include "utils/skip_list.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
@@ -54,13 +55,30 @@ struct QueryUserOrRole;
|
||||
*
|
||||
*/
|
||||
struct InterpreterContext {
|
||||
InterpreterContext(InterpreterConfig interpreter_config, dbms::DbmsHandler *dbms_handler,
|
||||
replication::ReplicationState *rs, memgraph::system::System &system,
|
||||
static InterpreterContext *instance;
|
||||
|
||||
static InterpreterContext *getInstance() {
|
||||
MG_ASSERT(instance != nullptr, "Interpreter context has not been initialized!");
|
||||
return instance;
|
||||
}
|
||||
|
||||
static InterpreterContext *getInstance(InterpreterConfig interpreter_config, dbms::DbmsHandler *dbms_handler,
|
||||
replication::ReplicationState *rs, memgraph::system::System &system,
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::coordination::CoordinatorState *coordinator_state,
|
||||
memgraph::coordination::CoordinatorState *coordinator_state,
|
||||
#endif
|
||||
AuthQueryHandler *ah = nullptr, AuthChecker *ac = nullptr,
|
||||
ReplicationQueryHandler *replication_handler = nullptr);
|
||||
AuthQueryHandler *ah = nullptr, AuthChecker *ac = nullptr,
|
||||
ReplicationQueryHandler *replication_handler = nullptr) {
|
||||
if (instance == nullptr) {
|
||||
instance = new InterpreterContext(interpreter_config, dbms_handler, rs, system,
|
||||
#ifdef MG_ENTERPRISE
|
||||
coordinator_state,
|
||||
#endif
|
||||
ah, ac, replication_handler);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
memgraph::dbms::DbmsHandler *dbms_handler;
|
||||
|
||||
@@ -98,6 +116,14 @@ struct InterpreterContext {
|
||||
std::vector<std::vector<TypedValue>> TerminateTransactions(
|
||||
std::vector<std::string> maybe_kill_transaction_ids, QueryUserOrRole *user_or_role,
|
||||
std::function<bool(QueryUserOrRole *, std::string const &)> privilege_checker);
|
||||
};
|
||||
|
||||
private:
|
||||
InterpreterContext(InterpreterConfig interpreter_config, dbms::DbmsHandler *dbms_handler,
|
||||
replication::ReplicationState *rs, memgraph::system::System &system,
|
||||
#ifdef MG_ENTERPRISE
|
||||
memgraph::coordination::CoordinatorState *coordinator_state,
|
||||
#endif
|
||||
AuthQueryHandler *ah = nullptr, AuthChecker *ac = nullptr,
|
||||
ReplicationQueryHandler *replication_handler = nullptr);
|
||||
};
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -329,7 +329,7 @@ CreateExpand::CreateExpand(NodeCreationInfo node_info, EdgeCreationInfo edge_inf
|
||||
ACCEPT_WITH_INPUT(CreateExpand)
|
||||
|
||||
UniqueCursorPtr CreateExpand::MakeCursor(utils::MemoryResource *mem) const {
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::CreateExpandOperator);
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::CreateNodeOperator);
|
||||
|
||||
return MakeUniqueCursorPtr<CreateExpandCursor>(mem, *this, mem);
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "glue/auth.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "mg_procedure.h"
|
||||
#include "module.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
#include "query/interpreter.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
#include "query/procedure/cypher_types.hpp"
|
||||
#include "query/procedure/fmt.hpp"
|
||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||
@@ -4021,3 +4024,131 @@ mgp_error mgp_untrack_current_thread_allocations(mgp_graph *graph) {
|
||||
std::visit([](auto *db_accessor) -> void { db_accessor->UntrackCurrentThreadAllocations(); }, graph->impl);
|
||||
});
|
||||
}
|
||||
|
||||
mgp_execution_headers::mgp_execution_headers(memgraph::utils::pmr::vector<memgraph::utils::pmr::string> &&storage)
|
||||
: headers(std::move(storage)){};
|
||||
|
||||
mgp_error mgp_execution_headers_size(mgp_execution_headers *headers, size_t *result) {
|
||||
static_assert(noexcept(headers->headers.size()));
|
||||
*result = headers->headers.size();
|
||||
return mgp_error::MGP_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
mgp_error mgp_execution_headers_at(mgp_execution_headers *headers, size_t index, const char **result) {
|
||||
return WrapExceptions(
|
||||
[headers, index] {
|
||||
if (index >= Call<size_t>(mgp_execution_headers_size, headers)) {
|
||||
throw std::out_of_range("Header cannot be retrieved, because index exceeds headers' size!");
|
||||
}
|
||||
return headers->headers[index].data();
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
mgp_execution_rows::mgp_execution_rows(
|
||||
memgraph::utils::pmr::vector<memgraph::utils::pmr::vector<memgraph::query::TypedValue>> &&tv_rows)
|
||||
: rows(std::move(tv_rows)) {}
|
||||
|
||||
struct MgProcedureResultStream final {
|
||||
explicit MgProcedureResultStream(mgp_memory *memory) : rows(memory->impl), memory(memory) {}
|
||||
using Row = std::vector<memgraph::query::TypedValue>;
|
||||
using Rows = std::vector<Row>;
|
||||
using PmrRow = memgraph::utils::pmr::vector<memgraph::query::TypedValue>;
|
||||
using PmrRows = memgraph::utils::pmr::vector<PmrRow>;
|
||||
|
||||
PmrRows rows;
|
||||
mgp_memory *memory;
|
||||
|
||||
void Result(const Row &row) {
|
||||
PmrRow pmr_row(memory->impl);
|
||||
for (auto &val : row) {
|
||||
pmr_row.emplace_back(std::move(val));
|
||||
}
|
||||
|
||||
rows.emplace_back(std::move(pmr_row));
|
||||
}
|
||||
};
|
||||
|
||||
std::map<std::string, memgraph::storage::PropertyValue> CreateQueryParams(mgp_map *params) {
|
||||
std::map<std::string, memgraph::storage::PropertyValue> query_params;
|
||||
for (auto &[k, v] : params->items) {
|
||||
query_params.emplace(k, ToPropertyValue(v));
|
||||
}
|
||||
|
||||
return query_params;
|
||||
}
|
||||
|
||||
struct mgp_execution_result::pImplMgpExecutionResult {
|
||||
std::unique_ptr<memgraph::query::Interpreter> interpreter;
|
||||
std::unique_ptr<mgp_execution_headers> headers;
|
||||
};
|
||||
|
||||
mgp_execution_result::mgp_execution_result() : pImpl(std::make_unique<pImplMgpExecutionResult>()) {
|
||||
auto *instance = memgraph::query::InterpreterContext::getInstance();
|
||||
pImpl->interpreter = std::make_unique<memgraph::query::Interpreter>(instance, instance->dbms_handler->Get());
|
||||
}
|
||||
|
||||
mgp_execution_result::~mgp_execution_result() {
|
||||
auto *instance = memgraph::query::InterpreterContext::getInstance();
|
||||
instance->interpreters.WithLock([this](auto &interpreters) { interpreters.erase(pImpl->interpreter.get()); });
|
||||
// interpreter will delete itself because it's a smart pointer
|
||||
}
|
||||
|
||||
mgp_error mgp_execute_query(mgp_graph *graph, mgp_memory *memory, const char *query, mgp_map *params,
|
||||
mgp_execution_result **result) {
|
||||
return WrapExceptions(
|
||||
[query, params, graph, memory]() {
|
||||
auto query_string = std::string(query);
|
||||
auto *instance = memgraph::query::InterpreterContext::getInstance();
|
||||
|
||||
mgp_execution_result *result = NewRawMgpObject<mgp_execution_result>(memory->impl);
|
||||
result->pImpl->interpreter->SetUser(graph->ctx->user_or_role);
|
||||
|
||||
instance->interpreters.WithLock(
|
||||
[result](auto &interpreters) { interpreters.insert(result->pImpl->interpreter.get()); });
|
||||
|
||||
const auto query_params = CreateQueryParams(params);
|
||||
|
||||
auto prepare_query_result = result->pImpl->interpreter->Prepare(query_string, query_params, {});
|
||||
|
||||
memgraph::utils::pmr::vector<memgraph::utils::pmr::string> headers(memory->impl);
|
||||
for (auto header : prepare_query_result.headers) {
|
||||
headers.emplace_back(header);
|
||||
}
|
||||
result->pImpl->headers = std::make_unique<mgp_execution_headers>(std::move(headers));
|
||||
|
||||
return result;
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
mgp_error mgp_fetch_execution_headers(mgp_execution_result *exec_result, mgp_execution_headers **result) {
|
||||
return WrapExceptions([exec_result]() { return exec_result->pImpl->headers.get(); }, result);
|
||||
}
|
||||
|
||||
mgp_error mgp_pull_one(mgp_execution_result *exec_result, mgp_graph *graph, mgp_memory *memory, mgp_map **result) {
|
||||
return WrapExceptions(
|
||||
[exec_result, graph, memory]() -> mgp_map * {
|
||||
MgProcedureResultStream stream(memory);
|
||||
|
||||
try {
|
||||
exec_result->pImpl->interpreter->Pull(&stream, 1, {});
|
||||
} catch (const std::exception &e) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream.rows.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t headers_size = exec_result->pImpl->headers->headers.size();
|
||||
memgraph::utils::pmr::map<memgraph::utils::pmr::string, mgp_value> items(memory->impl);
|
||||
for (size_t idx = 0; idx < headers_size; idx++) {
|
||||
items.emplace(exec_result->pImpl->headers->headers[idx],
|
||||
mgp_value{std::move(stream.rows[0][idx]), graph, memory->impl});
|
||||
}
|
||||
|
||||
return NewRawMgpObject<mgp_map>(memory->impl, std::move(items));
|
||||
},
|
||||
result);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "mg_procedure.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "query/context.hpp"
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/frontend/ast/ast.hpp"
|
||||
|
||||
#include "query/procedure/cypher_type_ptr.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
@@ -33,6 +35,7 @@
|
||||
#include "utils/pmr/vector.hpp"
|
||||
#include "utils/temporal.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
/// Wraps memory resource used in custom procedures.
|
||||
///
|
||||
/// This should have been `using mgp_memory = memgraph::utils::MemoryResource`, but that's
|
||||
@@ -993,3 +996,29 @@ struct mgp_messages {
|
||||
bool ContainsDeleted(const mgp_value *val);
|
||||
|
||||
memgraph::query::TypedValue ToTypedValue(const mgp_value &val, memgraph::utils::MemoryResource *memory);
|
||||
|
||||
struct mgp_execution_headers {
|
||||
using allocator_type = memgraph::utils::Allocator<mgp_execution_headers>;
|
||||
using storage_type = memgraph::utils::pmr::vector<memgraph::utils::pmr::string>;
|
||||
explicit mgp_execution_headers(storage_type &&storage);
|
||||
|
||||
~mgp_execution_headers() = default;
|
||||
|
||||
storage_type headers;
|
||||
};
|
||||
|
||||
struct mgp_execution_rows {
|
||||
explicit mgp_execution_rows(
|
||||
memgraph::utils::pmr::vector<memgraph::utils::pmr::vector<memgraph::query::TypedValue>> &&tv_rows);
|
||||
~mgp_execution_rows() = default;
|
||||
|
||||
memgraph::utils::pmr::vector<memgraph::utils::pmr::vector<memgraph::query::TypedValue>> rows;
|
||||
};
|
||||
|
||||
struct mgp_execution_result {
|
||||
explicit mgp_execution_result();
|
||||
~mgp_execution_result();
|
||||
|
||||
struct pImplMgpExecutionResult;
|
||||
std::unique_ptr<pImplMgpExecutionResult> pImpl;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ struct SalientConfig {
|
||||
struct Items {
|
||||
bool properties_on_edges{true};
|
||||
bool enable_schema_metadata{false};
|
||||
bool delta_on_identical_property_update{true};
|
||||
friend bool operator==(const Items &lrh, const Items &rhs) = default;
|
||||
} items;
|
||||
|
||||
|
||||
@@ -130,13 +130,9 @@ Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, co
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
using ReturnType = decltype(edge_.ptr->properties.GetProperty(property));
|
||||
std::optional<ReturnType> current_value;
|
||||
const bool skip_duplicate_write = !storage_->config_.salient.items.delta_on_identical_property_update;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[¤t_value, &property, &value, transaction = transaction_, edge = edge_, skip_duplicate_write]() {
|
||||
[¤t_value, &property, &value, transaction = transaction_, edge = edge_]() {
|
||||
current_value.emplace(edge.ptr->properties.GetProperty(property));
|
||||
if (skip_duplicate_write && current_value == value) {
|
||||
return;
|
||||
}
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
// one. This would save some memory as a delta would not be created as well as
|
||||
// avoid copying the value. The reason we are not doing that is because the
|
||||
@@ -188,14 +184,12 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> EdgeAc
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
const bool skip_duplicate_write = !storage_->config_.salient.items.delta_on_identical_property_update;
|
||||
using ReturnType = decltype(edge_.ptr->properties.UpdateProperties(properties));
|
||||
std::optional<ReturnType> id_old_new_change;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[transaction_ = transaction_, edge_ = edge_, &properties, &id_old_new_change, skip_duplicate_write]() {
|
||||
[transaction_ = transaction_, edge_ = edge_, &properties, &id_old_new_change]() {
|
||||
id_old_new_change.emplace(edge_.ptr->properties.UpdateProperties(properties));
|
||||
for (auto &[property, old_value, new_value] : *id_old_new_change) {
|
||||
if (skip_duplicate_write && old_value == new_value) continue;
|
||||
CreateAndLinkDelta(transaction_, edge_.ptr, Delta::SetPropertyTag(), property, std::move(old_value));
|
||||
}
|
||||
}};
|
||||
|
||||
@@ -261,31 +261,20 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
PropertyValue current_value;
|
||||
const bool skip_duplicate_write = !storage_->config_.salient.items.delta_on_identical_property_update;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[transaction = transaction_, vertex = vertex_, &value, &property, ¤t_value, skip_duplicate_write]() {
|
||||
current_value = vertex->properties.GetProperty(property);
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
// one. This would save some memory as a delta would not be created as well as
|
||||
// avoid copying the value. The reason we are not doing that is because the
|
||||
// current code always follows the logical pattern of "create a delta" and
|
||||
// "modify in-place". Additionally, the created delta will make other
|
||||
// transactions get a SERIALIZATION_ERROR.
|
||||
if (skip_duplicate_write && current_value == value) {
|
||||
return true;
|
||||
}
|
||||
auto current_value = vertex_->properties.GetProperty(property);
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
// one. This would save some memory as a delta would not be created as well as
|
||||
// avoid copying the value. The reason we are not doing that is because the
|
||||
// current code always follows the logical pattern of "create a delta" and
|
||||
// "modify in-place". Additionally, the created delta will make other
|
||||
// transactions get a SERIALIZATION_ERROR.
|
||||
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[transaction = transaction_, vertex = vertex_, &value, &property, ¤t_value]() {
|
||||
CreateAndLinkDelta(transaction, vertex, Delta::SetPropertyTag(), property, current_value);
|
||||
vertex->properties.SetProperty(property, value);
|
||||
|
||||
return false;
|
||||
}};
|
||||
const bool early_exit = std::invoke(atomic_memory_block);
|
||||
|
||||
if (early_exit) {
|
||||
return std::move(current_value);
|
||||
}
|
||||
std::invoke(atomic_memory_block);
|
||||
|
||||
if (transaction_->constraint_verification_info) {
|
||||
if (!value.IsNull()) {
|
||||
@@ -350,29 +339,27 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> Vertex
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
const bool skip_duplicate_update = storage_->config_.salient.items.delta_on_identical_property_update;
|
||||
using ReturnType = decltype(vertex_->properties.UpdateProperties(properties));
|
||||
std::optional<ReturnType> id_old_new_change;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{[storage = storage_, transaction = transaction_, vertex = vertex_,
|
||||
&properties, &id_old_new_change, skip_duplicate_update]() {
|
||||
id_old_new_change.emplace(vertex->properties.UpdateProperties(properties));
|
||||
if (!id_old_new_change.has_value()) {
|
||||
return;
|
||||
}
|
||||
for (auto &[id, old_value, new_value] : *id_old_new_change) {
|
||||
storage->indices_.UpdateOnSetProperty(id, new_value, vertex, *transaction);
|
||||
if (skip_duplicate_update && old_value == new_value) continue;
|
||||
CreateAndLinkDelta(transaction, vertex, Delta::SetPropertyTag(), id, std::move(old_value));
|
||||
transaction->manyDeltasCache.Invalidate(vertex, id);
|
||||
if (transaction->constraint_verification_info) {
|
||||
if (!new_value.IsNull()) {
|
||||
transaction->constraint_verification_info->AddedProperty(vertex);
|
||||
} else {
|
||||
transaction->constraint_verification_info->RemovedProperty(vertex);
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[storage = storage_, transaction = transaction_, vertex = vertex_, &properties, &id_old_new_change]() {
|
||||
id_old_new_change.emplace(vertex->properties.UpdateProperties(properties));
|
||||
if (!id_old_new_change.has_value()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
for (auto &[id, old_value, new_value] : *id_old_new_change) {
|
||||
storage->indices_.UpdateOnSetProperty(id, new_value, vertex, *transaction);
|
||||
CreateAndLinkDelta(transaction, vertex, Delta::SetPropertyTag(), id, std::move(old_value));
|
||||
transaction->manyDeltasCache.Invalidate(vertex, id);
|
||||
if (transaction->constraint_verification_info) {
|
||||
if (!new_value.IsNull()) {
|
||||
transaction->constraint_verification_info->AddedProperty(vertex);
|
||||
} else {
|
||||
transaction->constraint_verification_info->RemovedProperty(vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
std::invoke(atomic_memory_block);
|
||||
|
||||
return id_old_new_change.has_value() ? std::move(id_old_new_change.value()) : ReturnType{};
|
||||
|
||||
@@ -29,10 +29,12 @@ class [[nodiscard]] AtomicMemoryBlock {
|
||||
AtomicMemoryBlock &operator=(AtomicMemoryBlock &&) = delete;
|
||||
~AtomicMemoryBlock() = default;
|
||||
|
||||
auto operator()() -> std::invoke_result_t<Callable> {
|
||||
auto check_on_exit = OnScopeExit{[&] { total_memory_tracker.DoCheck(); }};
|
||||
utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_blocker;
|
||||
return function_();
|
||||
void operator()() {
|
||||
{
|
||||
utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_blocker;
|
||||
function_();
|
||||
}
|
||||
total_memory_tracker.DoCheck();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace memgraph::utils {
|
||||
* // long block of code, might throw an exception
|
||||
* }
|
||||
*/
|
||||
template <std::invocable Callable>
|
||||
template <typename Callable>
|
||||
class [[nodiscard]] OnScopeExit {
|
||||
public:
|
||||
template <typename U>
|
||||
@@ -46,7 +46,7 @@ class [[nodiscard]] OnScopeExit {
|
||||
OnScopeExit &operator=(OnScopeExit const &) = delete;
|
||||
OnScopeExit &operator=(OnScopeExit &&) = delete;
|
||||
~OnScopeExit() {
|
||||
if (doCall_) std::invoke(std::move(function_));
|
||||
if (doCall_) function_();
|
||||
}
|
||||
|
||||
void Disable() { doCall_ = false; }
|
||||
@@ -57,4 +57,5 @@ class [[nodiscard]] OnScopeExit {
|
||||
};
|
||||
template <typename Callable>
|
||||
OnScopeExit(Callable &&) -> OnScopeExit<Callable>;
|
||||
|
||||
} // namespace memgraph::utils
|
||||
|
||||
@@ -77,7 +77,6 @@ add_subdirectory(garbage_collection)
|
||||
add_subdirectory(query_planning)
|
||||
add_subdirectory(awesome_functions)
|
||||
add_subdirectory(high_availability)
|
||||
add_subdirectory(concurrency)
|
||||
|
||||
add_subdirectory(replication_experimental)
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
function(copy_concurrency_e2e_python_files FILE_NAME)
|
||||
copy_e2e_python_files(concurrency ${FILE_NAME})
|
||||
endfunction()
|
||||
|
||||
copy_concurrency_e2e_python_files(common.py)
|
||||
copy_concurrency_e2e_python_files(concurrency.py)
|
||||
@@ -1,60 +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.
|
||||
|
||||
import typing
|
||||
|
||||
import mgclient
|
||||
import pytest
|
||||
|
||||
|
||||
def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = {}) -> typing.List[tuple]:
|
||||
cursor.execute(query, params)
|
||||
return cursor.fetchall()
|
||||
|
||||
|
||||
def execute_and_fetch_all_with_commit(
|
||||
connection: mgclient.Connection, query: str, params: dict = {}
|
||||
) -> typing.List[tuple]:
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(query, params)
|
||||
results = cursor.fetchall()
|
||||
connection.commit()
|
||||
return results
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def first_connection(**kwargs) -> mgclient.Connection:
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
cursor = connection.cursor()
|
||||
execute_and_fetch_all(cursor, "USE DATABASE memgraph")
|
||||
try:
|
||||
execute_and_fetch_all(cursor, "DROP DATABASE clean")
|
||||
except:
|
||||
pass
|
||||
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
|
||||
connection.autocommit = False
|
||||
yield connection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def second_connection(**kwargs) -> mgclient.Connection:
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
cursor = connection.cursor()
|
||||
execute_and_fetch_all(cursor, "USE DATABASE memgraph")
|
||||
try:
|
||||
execute_and_fetch_all(cursor, "DROP DATABASE clean")
|
||||
except:
|
||||
pass
|
||||
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
|
||||
connection.autocommit = False
|
||||
yield connection
|
||||
@@ -1,57 +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.
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from common import execute_and_fetch_all, first_connection, second_connection
|
||||
|
||||
|
||||
def test_concurrency_if_no_delta_on_same_node_property_update(first_connection, second_connection):
|
||||
m1c = first_connection.cursor()
|
||||
m2c = second_connection.cursor()
|
||||
|
||||
execute_and_fetch_all(m1c, "CREATE (:Node {prop: 1})")
|
||||
first_connection.commit()
|
||||
|
||||
test_has_error = False
|
||||
try:
|
||||
m1c.execute("MATCH (n) SET n.prop = 1")
|
||||
m2c.execute("MATCH (n) SET n.prop = 1")
|
||||
first_connection.commit()
|
||||
second_connection.commit()
|
||||
except Exception as e:
|
||||
test_has_error = True
|
||||
|
||||
assert test_has_error is False
|
||||
|
||||
|
||||
def test_concurrency_if_no_delta_on_same_edge_property_update(first_connection, second_connection):
|
||||
m1c = first_connection.cursor()
|
||||
m2c = second_connection.cursor()
|
||||
|
||||
execute_and_fetch_all(m1c, "CREATE ()-[:TYPE {prop: 1}]->()")
|
||||
first_connection.commit()
|
||||
|
||||
test_has_error = False
|
||||
try:
|
||||
m1c.execute("MATCH (n)-[r]->(m) SET r.prop = 1")
|
||||
m2c.execute("MATCH (n)-[r]->(m) SET n.prop = 1")
|
||||
first_connection.commit()
|
||||
second_connection.commit()
|
||||
except Exception as e:
|
||||
test_has_error = True
|
||||
|
||||
assert test_has_error is False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-rA"]))
|
||||
@@ -1,14 +0,0 @@
|
||||
concurrency_cluster: &concurrency_cluster
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", "7687", "--log-level=TRACE", "--storage-delta-on-identical-property-update=false"]
|
||||
log_file: "concurrency.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
|
||||
|
||||
workloads:
|
||||
- name: "Concurrency"
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
args: ["concurrency/concurrency.py"]
|
||||
<<: *concurrency_cluster
|
||||
@@ -141,11 +141,6 @@ startup_config_dict = {
|
||||
"1",
|
||||
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: The MAIN instance allocates a new thread for each REPLICA.",
|
||||
),
|
||||
"storage_delta_on_identical_property_update": (
|
||||
"true",
|
||||
"true",
|
||||
"Controls whether updating a property with the same value should create a delta object.",
|
||||
),
|
||||
"storage_gc_cycle_sec": ("30", "30", "Storage garbage collector interval (in seconds)."),
|
||||
"storage_python_gc_cycle_sec": ("180", "180", "Storage python full garbage collection interval (in seconds)."),
|
||||
"storage_items_per_batch": (
|
||||
|
||||
13
tests/jepsen/jepsen_0.3.0.patch
Normal file
13
tests/jepsen/jepsen_0.3.0.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/docker/control/Dockerfile b/docker/control/Dockerfile
|
||||
index 6b2d3c0e..195a7a60 100644
|
||||
--- a/docker/control/Dockerfile
|
||||
+++ b/docker/control/Dockerfile
|
||||
@@ -7,7 +7,7 @@ ENV LEIN_ROOT true
|
||||
# Jepsen dependencies
|
||||
#
|
||||
RUN apt-get -y -q update && \
|
||||
- apt-get install -qy openjdk-17-jdk-headless \
|
||||
+ apt-get install -qy ca-certificates-java openjdk-17-jdk-headless \
|
||||
libjna-java \
|
||||
vim \
|
||||
emacs \
|
||||
@@ -5,10 +5,10 @@
|
||||
:url "https://github.com/memgraph/memgraph/blob/master/release/LICENSE_ENTERPRISE.md"}
|
||||
:main jepsen.memgraph.core
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
;; Details under https://clojars.org/jepsen/versions.
|
||||
[jepsen "0.3.5-SNAPSHOT"]
|
||||
;; 0.2.4-SNAPSHOT but 0.3.0, for more -> https://clojars.org/jepsen/versions
|
||||
[jepsen "0.2.4-SNAPSHOT"]
|
||||
[gorillalabs/neo4j-clj "4.1.0"]]
|
||||
:profiles {:test {:dependencies [#_[org.neo4j.test/neo4j-harness "4.1.0"]]}}
|
||||
;; The below line is required to run after Jepsen 0.3.0.
|
||||
:aot :all
|
||||
;; required to run 0.3.0
|
||||
; :aot :all
|
||||
:repl-options {:init-ns jepsen.memgraph.core})
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
set -Eeuo pipefail
|
||||
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
MEMGRAPH_BUILD_PATH="$script_dir/../../build"
|
||||
MEMGRAPH_BINARY_PATH="$MEMGRAPH_BUILD_PATH/memgraph"
|
||||
MEMGRAPH_BINARY_PATH="../../build/memgraph"
|
||||
# NOTE: Jepsen Git tags are not consistent, there are: 0.2.4, v0.3.0, 0.3.2, ...
|
||||
JEPSEN_VERSION="${JEPSEN_VERSION:-v0.3.5}"
|
||||
# NOTE: On Ubuntu 22.04 v0.3.2 uses non-existing docker compose --compatibility flag.
|
||||
# NOTE: On Ubuntu 22.04 v0.3.0 and v0.3.1 seems to be runnable.
|
||||
# TODO(gitbuda): Make sure Memgraph can be testes with Jepsen >= 0.3.0
|
||||
JEPSEN_VERSION="${JEPSEN_VERSION:-0.2.4}"
|
||||
JEPSEN_ACTIVE_NODES_NO=5
|
||||
CONTROL_LEIN_RUN_ARGS="test-all --node-configs resources/node-config.edn"
|
||||
CONTROL_LEIN_RUN_STDOUT_LOGS=1
|
||||
@@ -22,7 +24,7 @@ PRINT_CONTEXT() {
|
||||
|
||||
HELP_EXIT() {
|
||||
echo ""
|
||||
echo "HELP: $0 help|cluster-up|cluster-refresh|cluster-nodes-cleanup|cluster-dealloc|mgbuild|test|test-all-individually [args]"
|
||||
echo "HELP: $0 help|cluster-up|cluster-refresh|cluster-cleanup|cluster-dealloc|mgbuild|test|test-all-individually [args]"
|
||||
echo ""
|
||||
echo " test args --binary MEMGRAPH_BINARY_PATH"
|
||||
echo " --ignore-run-stdout-logs Ignore lein run stdout logs."
|
||||
@@ -41,18 +43,24 @@ INFO() {
|
||||
/bin/echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m" "$@"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
HELP_EXIT
|
||||
fi
|
||||
|
||||
if ! command -v docker > /dev/null 2>&1 || ! command -v docker-compose > /dev/null 2>&1; then
|
||||
ERROR "docker and docker-compose have to be installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$script_dir/jepsen" ]; then
|
||||
# TODO(deda): install apt get docker-compose-plugin on all build machines.
|
||||
git clone https://github.com/jepsen-io/jepsen.git -b "$JEPSEN_VERSION" "$script_dir/jepsen"
|
||||
if [ "$JEPSEN_VERSION" == "v0.3.0" ]; then
|
||||
if [ -f "$script_dir/jepsen_0.3.0.patch" ]; then
|
||||
cd "$script_dir/jepsen"
|
||||
git apply "$script_dir/jepsen_0.3.0.patch"
|
||||
cd "$script_dir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
HELP_EXIT
|
||||
fi
|
||||
|
||||
PROCESS_ARGS() {
|
||||
@@ -191,7 +199,7 @@ CLUSTER_UP() {
|
||||
}
|
||||
|
||||
CLUSTER_DEALLOC() {
|
||||
ps=$(docker ps -a --filter name=jepsen* -q)
|
||||
ps=$(docker ps --filter name=jepsen* --filter status=running -q)
|
||||
if [[ ! -z ${ps} ]]; then
|
||||
echo "Killing ${ps}"
|
||||
docker rm -f ${ps}
|
||||
@@ -205,7 +213,6 @@ CLUSTER_DEALLOC() {
|
||||
else
|
||||
echo "No Jepsen containers detected!"
|
||||
fi
|
||||
echo "Cluster dealloc DONE"
|
||||
}
|
||||
|
||||
# Initialize testing context by copying source/binary files. Inside CI,
|
||||
@@ -232,7 +239,7 @@ case $1 in
|
||||
CLUSTER_DEALLOC
|
||||
;;
|
||||
|
||||
cluster-nodes-cleanup)
|
||||
cluster-cleanup)
|
||||
jepsen_control_exec="docker exec jepsen-control bash -c"
|
||||
INFO "Deleting /jepsen/memgraph/store/* on jepsen-control"
|
||||
$jepsen_control_exec "rm -rf /jepsen/memgraph/store/*"
|
||||
@@ -245,13 +252,9 @@ case $1 in
|
||||
;;
|
||||
|
||||
mgbuild)
|
||||
PROCESS_ARGS "$@"
|
||||
PRINT_CONTEXT
|
||||
# docker cp -L mgbuild_debian-12:/memgraph/build/memgraph "${MEMGRAPH_BUILD_PATH}/"
|
||||
# NOTE: mgconsole is interesting inside jepsen container to inspect Memgraph state.
|
||||
# docker cp -L mgbuild_debian-12:/usr/local/bin/mgconsole "${MEMGRAPH_BUILD_PATH}/"
|
||||
echo ""
|
||||
echo "TODO(gitbuda): Build memgraph for Jepsen (on v0.3.5 for Debian 12) via memgraph/memgraph-builder"
|
||||
echo "TODO(gitbuda): Build memgraph for Debian 10 via memgraph/memgraph-builder"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
:--storage-recover-on-startup
|
||||
:--storage-wal-enabled
|
||||
:--storage-snapshot-interval-sec 300
|
||||
:--replication-restore-state-on-startup
|
||||
:--storage-properties-on-edges))
|
||||
|
||||
(defn stop-node!
|
||||
|
||||
Reference in New Issue
Block a user