Compare commits

...

10 Commits

Author SHA1 Message Date
Deda
5bf5dee730 Add concurrency rules for release tests 2024-02-15 15:07:39 +01:00
Deda
8d5fef3ba8 Make release builds run on push to release branch 2024-02-14 15:59:47 +01:00
Aidar Samerkhanov
6074edb6eb Fix accumulated path evaluation in builtin algorithms. (#1642)
Fix accumulated path evaluation in DFS, BFS, WeghtedShortestPath and AllShortestPath algorithm.
2024-02-14 15:54:37 +01:00
Deda
3d606d1f06 Turn e2e tests on 2024-02-14 15:48:45 +01:00
Deda
4905ae298f Update deprecated github actions 2024-02-14 15:46:06 +01:00
Marko Barišić
7817f9ddc0 Update release CI job timeouts (#1683)
* Set timeout for jobs in release CI to 60 minutes

* Set timout for stress test large to 12 hours
2024-02-14 15:03:04 +01:00
Marko Barišić
fe0b292e57 Make release CI predictable (#1658)
* Move stress test large to a separate workflow
* Combine Debian10 and Ubuntu20.04 stress tests into one workflow
* Remove BigMemory tag from release_debian10
* Move e2e tests to a separate job
* Move debug interation tests to a separate job
* Move release durability and stress tests to a separate job
* Move release benchamarks to separate job
* Add 90 min timeout restriction to all jobs
* Move env variables to workflow level
* Move BUILD_TYPE env var to workflow level
---------

Co-authored-by: Aidar Samerkhanov <aidar.samerkhanov@memgraph.io>
2024-02-14 14:52:38 +01:00
Marko Barišić
1919afa850 Add step to refresh jepsen cluster before test (#1667)
refresh jepsen during diff workflow
2024-02-14 14:52:19 +01:00
Deda
474fd43f37 Override version to 2.14.0 2024-01-22 14:28:15 +01:00
Gareth Lloyd
639d43c3ef Bugfix don't use _Py_IsFinalizing
This is an unstable function and would bump our dependency to python 3.7
2024-01-22 14:27:15 +01:00
14 changed files with 633 additions and 204 deletions

View File

@@ -365,6 +365,11 @@ jobs:
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make -j$THREADS memgraph
- name: Refresh Jepsen Cluster
run: |
cd tests/jepsen
./run.sh cluster-refresh
- name: Run Jepsen tests
run: |
cd tests/jepsen

View File

@@ -1,4 +1,7 @@
name: Release Debian 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
on:
workflow_dispatch:
@@ -10,23 +13,30 @@ on:
options:
- Release
- RelWithDebInfo
push:
branches:
- "release/**"
tags:
- "v*.*.*-rc*"
- "v*.*-rc*"
schedule:
- cron: "0 22 * * *"
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
BUILD_TYPE: ${{ github.event.inputs.build_type || 'Release' }}
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, Linux, X64, Debian10]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -40,10 +50,6 @@ jobs:
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
@@ -65,10 +71,11 @@ jobs:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -110,22 +117,19 @@ jobs:
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Code coverage"
name: "Code coverage(Coverage build)"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, Linux, X64, Debian10]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -157,10 +161,6 @@ jobs:
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
tests/integration/run.sh
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
@@ -171,23 +171,49 @@ jobs:
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Code coverage"
name: "Code coverage(Debug build)"
path: tools/github/cppcheck_and_clang_format.txt
release_build:
name: "Release build"
runs-on: [self-hosted, Linux, X64, Debian10, BigMemory]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
debug_integration_test:
name: "Debug integration tests"
runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run integration tests
run: |
tests/integration/run.sh
release_build:
name: "Release build"
runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -201,10 +227,6 @@ jobs:
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
@@ -226,11 +248,60 @@ jobs:
cpack -G DEB --config ../CPackConfig.cmake
- name: Save enterprise DEB package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Enterprise DEB package"
path: build/output/memgraph*.deb
- name: Run GQL Behave tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
cd gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v4
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
release_benchmark_tests:
name: "Release Benchmark Tests"
runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build release binaries
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Run micro benchmark tests
run: |
# Activate toolchain.
@@ -257,29 +328,30 @@ jobs:
--num-database-workers 9 --num-clients-workers 30 \
--no-strict
- name: Run GQL Behave tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
cd gql_behave
./continuous_integration
release_e2e_test:
name: "Release End-to-end Test"
runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60
- name: Save quality assurance status
uses: actions/upload-artifact@v3
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Run unit tests
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Run unit tests.
# Build release binaries
cd build
ctest -R memgraph__unit --output-on-failure
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Ensure Kafka and Pulsar are up
run: |
@@ -304,6 +376,32 @@ jobs:
cd ../pulsar
docker-compose down
release_durability_stress_tests:
name: "Release durability and stress tests"
runs-on: [self-hosted, Linux, X64, Debian10]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Run stress test (plain)
run: |
cd tests/stress
@@ -314,11 +412,6 @@ jobs:
cd tests/stress
./continuous_integration --use-ssl
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
- name: Run durability test (plain)
run: |
cd tests/stress
@@ -334,15 +427,11 @@ jobs:
release_jepsen_test:
name: "Release Jepsen Test"
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -355,22 +444,23 @@ jobs:
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build only memgraph release binary.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS memgraph
- name: Refresh Jepsen Cluster
run: |
cd tests/jepsen
./run.sh cluster-refresh
- name: Run Jepsen tests
run: |
cd tests/jepsen
./run.sh test-all-individually --binary ../../build/memgraph --ignore-run-stdout-logs --ignore-run-stderr-logs
- name: Save Jepsen report
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: "Jepsen Report"

View File

@@ -1,4 +1,7 @@
name: Release Ubuntu 20.04
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
on:
workflow_dispatch:
@@ -10,23 +13,30 @@ on:
options:
- Release
- RelWithDebInfo
push:
branches:
- "release/**"
tags:
- "v*.*.*-rc*"
- "v*.*-rc*"
schedule:
- cron: "0 22 * * *"
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
BUILD_TYPE: ${{ github.event.inputs.build_type || 'Release' }}
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -40,10 +50,6 @@ jobs:
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DMG_ENTERPRISE=OFF ..
@@ -61,14 +67,11 @@ jobs:
coverage_build:
name: "Coverage build"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -110,22 +113,19 @@ jobs:
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Code coverage"
name: "Code coverage(Coverage build)"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -157,10 +157,6 @@ jobs:
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
tests/integration/run.sh
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
@@ -171,23 +167,49 @@ jobs:
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Code coverage"
name: "Code coverage(Debug build)"
path: tools/github/cppcheck_and_clang_format.txt
debug_integration_test:
name: "Debug integration tests"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run integration tests
run: |
tests/integration/run.sh
release_build:
name: "Release build"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
timeout-minutes: 960
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
@@ -201,10 +223,6 @@ jobs:
# Initialize dependencies.
./init
# Set default build_type to Release
INPUT_BUILD_TYPE=${{ github.event.inputs.build_type }}
BUILD_TYPE=${INPUT_BUILD_TYPE:-"Release"}
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
@@ -226,11 +244,60 @@ jobs:
cpack -G DEB --config ../CPackConfig.cmake
- name: Save enterprise DEB package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "Enterprise DEB package"
path: build/output/memgraph*.deb
- name: Run GQL Behave tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
cd gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v4
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
release_benchmark_tests:
name: "Release Benchmark Tests"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build release binaries
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Run micro benchmark tests
run: |
# Activate toolchain.
@@ -257,29 +324,30 @@ jobs:
--num-database-workers 9 --num-clients-workers 30 \
--no-strict
- name: Run GQL Behave tests
run: |
cd tests
./setup.sh /opt/toolchain-v4/activate
cd gql_behave
./continuous_integration
release_e2e_test:
name: "Release End-to-end Test"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
timeout-minutes: 60
- name: Save quality assurance status
uses: actions/upload-artifact@v3
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Run unit tests
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Run unit tests.
# Build release binaries
cd build
ctest -R memgraph__unit --output-on-failure
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Ensure Kafka and Pulsar are up
run: |
@@ -304,6 +372,32 @@ jobs:
cd ../pulsar
docker-compose down
release_durability_stress_tests:
name: "Release durability and stress tests"
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
timeout-minutes: 60
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Run stress test (plain)
run: |
cd tests/stress
@@ -314,11 +408,6 @@ jobs:
cd tests/stress
./continuous_integration --use-ssl
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
- name: Run durability test (plain)
run: |
cd tests/stress

View File

@@ -0,0 +1,68 @@
name: Stress test large
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
build_type:
type: choice
description: "Memgraph Build type. Default value is Release."
default: 'Release'
options:
- Release
- RelWithDebInfo
push:
tags:
- "v*.*.*-rc*"
- "v*.*-rc*"
schedule:
- cron: "0 22 * * *"
env:
THREADS: 24
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
BUILD_TYPE: ${{ github.event.inputs.build_type || 'Release' }}
jobs:
stress_test_large:
name: "Stress test large"
timeout-minutes: 720
strategy:
matrix:
os: [Debian10, Ubuntu20.04]
extra: [BigMemory, Gen8]
exclude:
- os: Debian10
extra: Gen8
- os: Ubuntu20.04
extra: BigMemory
runs-on: [self-hosted, Linux, X64, "${{ matrix.os }}", "${{ matrix.extra }}"]
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v4/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
make -j$THREADS
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset

View File

@@ -64,7 +64,7 @@ option(MG_ENTERPRISE "Build Memgraph Enterprise Edition" ON)
# Set the current version here to override the automatic version detection. The
# version must be specified as `X.Y.Z`. Primarily used when building new patch
# versions.
set(MEMGRAPH_OVERRIDE_VERSION "")
set(MEMGRAPH_OVERRIDE_VERSION "2.14.0")
# Custom suffix that this version should have. The suffix can be any arbitrary
# string. Primarily used when building a version for a specific customer.

View File

@@ -1,4 +1,4 @@
// Copyright 2022 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
@@ -114,12 +114,17 @@ class Path {
/** Expands the path with the given vertex. */
void Expand(const VertexAccessor &vertex) {
DMG_ASSERT(vertices_.size() == edges_.size(), "Illegal path construction order");
DMG_ASSERT(edges_.empty() || (!edges_.empty() && (edges_.back().To().Gid() == vertex.Gid() ||
edges_.back().From().Gid() == vertex.Gid())),
"Illegal path construction order");
vertices_.emplace_back(vertex);
}
/** Expands the path with the given edge. */
void Expand(const EdgeAccessor &edge) {
DMG_ASSERT(vertices_.size() - 1 == edges_.size(), "Illegal path construction order");
DMG_ASSERT(vertices_.back().Gid() == edge.From().Gid() || vertices_.back().Gid() == edge.To().Gid(),
"Illegal path construction order");
edges_.emplace_back(edge);
}
@@ -130,6 +135,14 @@ class Path {
Expand(others...);
}
void Shrink() {
DMG_ASSERT(!vertices_.empty(), "Vertices should not be empty in the path before shrink.");
vertices_.pop_back();
if (!edges_.empty()) {
edges_.pop_back();
}
}
/** Returns the number of expansions (edges) in this path. */
auto size() const { return edges_.size(); }

View File

@@ -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
@@ -1057,7 +1057,8 @@ class ExpandVariableCursor : public Cursor {
if (!self_.common_.existing_node) {
frame[self_.common_.node_symbol] = start_vertex;
return true;
} else if (CheckExistingNode(start_vertex, self_.common_.node_symbol, frame)) {
}
if (CheckExistingNode(start_vertex, self_.common_.node_symbol, frame)) {
return true;
}
}
@@ -1243,6 +1244,10 @@ class ExpandVariableCursor : public Cursor {
MG_ASSERT(frame[self_.filter_lambda_.accumulated_path_symbol.value()].IsPath(),
"Accumulated path must be path");
Path &accumulated_path = frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath();
// Shrink the accumulated path including current level if necessary
while (accumulated_path.size() >= edges_on_frame.size()) {
accumulated_path.Shrink();
}
accumulated_path.Expand(current_edge.first);
accumulated_path.Expand(current_vertex);
}
@@ -1260,10 +1265,9 @@ class ExpandVariableCursor : public Cursor {
if (self_.common_.existing_node && !CheckExistingNode(current_vertex, self_.common_.node_symbol, frame)) continue;
// We only yield true if we satisfy the lower bound.
if (static_cast<int64_t>(edges_on_frame.size()) >= lower_bound_)
if (static_cast<int64_t>(edges_on_frame.size()) >= lower_bound_) {
return true;
else
continue;
}
}
}
};
@@ -1527,8 +1531,8 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
: self_(self),
input_cursor_(self_.input()->MakeCursor(mem)),
processed_(mem),
to_visit_current_(mem),
to_visit_next_(mem) {
to_visit_next_(mem),
to_visit_current_(mem) {
MG_ASSERT(!self_.common_.existing_node,
"Single source shortest path algorithm "
"should not be used when `existing_node` "
@@ -1558,12 +1562,14 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
#endif
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
frame[self_.filter_lambda_.inner_node_symbol] = vertex;
std::optional<Path> curr_acc_path = std::nullopt;
if (self_.filter_lambda_.accumulated_path_symbol) {
MG_ASSERT(frame[self_.filter_lambda_.accumulated_path_symbol.value()].IsPath(),
"Accumulated path must have Path type");
Path &accumulated_path = frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath();
accumulated_path.Expand(edge);
accumulated_path.Expand(vertex);
curr_acc_path = accumulated_path;
}
if (self_.filter_lambda_.expression) {
@@ -1578,21 +1584,33 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
throw QueryRuntimeException("Expansion condition must evaluate to boolean or null.");
}
}
to_visit_next_.emplace_back(edge, vertex);
to_visit_next_.emplace_back(edge, vertex, std::move(curr_acc_path));
processed_.emplace(vertex, edge);
};
auto restore_frame_state_after_expansion = [this, &frame]() {
if (self_.filter_lambda_.accumulated_path_symbol) {
frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath().Shrink();
}
};
// populates the to_visit_next_ structure with expansions
// from the given vertex. skips expansions that don't satisfy
// the "where" condition.
auto expand_from_vertex = [this, &expand_pair](const auto &vertex) {
auto expand_from_vertex = [this, &expand_pair, &restore_frame_state_after_expansion](const auto &vertex) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) expand_pair(edge, edge.To());
for (const auto &edge : out_edges) {
expand_pair(edge, edge.To());
restore_frame_state_after_expansion();
}
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) expand_pair(edge, edge.From());
for (const auto &edge : in_edges) {
expand_pair(edge, edge.From());
restore_frame_state_after_expansion();
}
}
};
@@ -1638,14 +1656,14 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
}
// take the next expansion from the queue
auto expansion = to_visit_current_.back();
auto [curr_edge, curr_vertex, curr_acc_path] = to_visit_current_.back();
to_visit_current_.pop_back();
// create the frame value for the edges
auto *pull_memory = context.evaluation_context.memory;
utils::pmr::vector<TypedValue> edge_list(pull_memory);
edge_list.emplace_back(expansion.first);
auto last_vertex = expansion.second;
edge_list.emplace_back(curr_edge);
auto last_vertex = curr_vertex;
while (true) {
const EdgeAccessor &last_edge = edge_list.back().ValueEdge();
last_vertex = last_edge.From() == last_vertex ? last_edge.To() : last_edge.From();
@@ -1657,11 +1675,17 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
}
// expand only if what we've just expanded is less then max depth
if (static_cast<int64_t>(edge_list.size()) < upper_bound_) expand_from_vertex(expansion.second);
if (static_cast<int64_t>(edge_list.size()) < upper_bound_) {
if (self_.filter_lambda_.accumulated_path_symbol) {
MG_ASSERT(curr_acc_path.has_value(), "Expected non-null accumulated path");
frame[self_.filter_lambda_.accumulated_path_symbol.value()] = std::move(curr_acc_path.value());
}
expand_from_vertex(curr_vertex);
}
if (static_cast<int64_t>(edge_list.size()) < lower_bound_) continue;
frame[self_.common_.node_symbol] = expansion.second;
frame[self_.common_.node_symbol] = curr_vertex;
// place edges on the frame in the correct order
std::reverse(edge_list.begin(), edge_list.end());
@@ -1693,9 +1717,9 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
// edge because the root does not get expanded from anything.
// contains visited vertices as well as those scheduled to be visited.
utils::pmr::unordered_map<VertexAccessor, std::optional<EdgeAccessor>> processed_;
// edge/vertex pairs we have yet to visit, for current and next depth
utils::pmr::vector<std::pair<EdgeAccessor, VertexAccessor>> to_visit_current_;
utils::pmr::vector<std::pair<EdgeAccessor, VertexAccessor>> to_visit_next_;
// edge, vertex we have yet to visit, for current and next depth and their accumulated paths
utils::pmr::vector<std::tuple<EdgeAccessor, VertexAccessor, std::optional<Path>>> to_visit_next_;
utils::pmr::vector<std::tuple<EdgeAccessor, VertexAccessor, std::optional<Path>>> to_visit_current_;
};
namespace {
@@ -1768,6 +1792,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::OLD);
auto create_state = [this](const VertexAccessor &vertex, int64_t depth) {
return std::make_pair(vertex, upper_bound_set_ ? depth : 0);
};
@@ -1791,6 +1816,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
frame[self_.weight_lambda_->inner_node_symbol] = vertex;
TypedValue next_weight = CalculateNextWeight(self_.weight_lambda_, total_weight, evaluator);
std::optional<Path> curr_acc_path = std::nullopt;
if (self_.filter_lambda_.expression) {
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
frame[self_.filter_lambda_.inner_node_symbol] = vertex;
@@ -1800,6 +1826,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
Path &accumulated_path = frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath();
accumulated_path.Expand(edge);
accumulated_path.Expand(vertex);
curr_acc_path = accumulated_path;
if (self_.filter_lambda_.accumulated_weight_symbol) {
frame[self_.filter_lambda_.accumulated_weight_symbol.value()] = next_weight;
@@ -1815,24 +1842,32 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
if (found_it != total_cost_.end() && (found_it->second.IsNull() || (found_it->second <= next_weight).ValueBool()))
return;
pq_.emplace(next_weight, depth + 1, vertex, edge);
pq_.emplace(next_weight, depth + 1, vertex, edge, curr_acc_path);
};
auto restore_frame_state_after_expansion = [this, &frame]() {
if (self_.filter_lambda_.accumulated_path_symbol) {
frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath().Shrink();
}
};
// Populates the priority queue structure with expansions
// from the given vertex. skips expansions that don't satisfy
// the "where" condition.
auto expand_from_vertex = [this, &expand_pair](const VertexAccessor &vertex, const TypedValue &weight,
int64_t depth) {
auto expand_from_vertex = [this, &expand_pair, &restore_frame_state_after_expansion](
const VertexAccessor &vertex, const TypedValue &weight, int64_t depth) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
expand_pair(edge, edge.To(), weight, depth);
restore_frame_state_after_expansion();
}
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) {
expand_pair(edge, edge.From(), weight, depth);
restore_frame_state_after_expansion();
}
}
};
@@ -1850,9 +1885,12 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
// Skip expansion for such nodes.
if (node.IsNull()) continue;
}
std::optional<Path> curr_acc_path;
if (self_.filter_lambda_.accumulated_path_symbol) {
// Add initial vertex of path to the accumulated path
frame[self_.filter_lambda_.accumulated_path_symbol.value()] = Path(vertex);
curr_acc_path = Path(vertex);
frame[self_.filter_lambda_.accumulated_path_symbol.value()] = curr_acc_path.value();
}
if (self_.upper_bound_) {
upper_bound_ = EvaluateInt(&evaluator, self_.upper_bound_, "Max depth in weighted shortest path expansion");
@@ -1876,7 +1914,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
total_cost_.clear();
yielded_vertices_.clear();
pq_.emplace(current_weight, 0, vertex, std::nullopt);
pq_.emplace(current_weight, 0, vertex, std::nullopt, curr_acc_path);
// We are adding the starting vertex to the set of yielded vertices
// because we don't want to yield paths that end with the starting
// vertex.
@@ -1885,7 +1923,7 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
while (!pq_.empty()) {
AbortCheck(context);
auto [current_weight, current_depth, current_vertex, current_edge] = pq_.top();
auto [current_weight, current_depth, current_vertex, current_edge, curr_acc_path] = pq_.top();
pq_.pop();
auto current_state = create_state(current_vertex, current_depth);
@@ -1898,7 +1936,12 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
total_cost_.emplace(current_state, current_weight);
// Expand only if what we've just expanded is less than max depth.
if (current_depth < upper_bound_) expand_from_vertex(current_vertex, current_weight, current_depth);
if (current_depth < upper_bound_) {
if (self_.filter_lambda_.accumulated_path_symbol) {
frame[self_.filter_lambda_.accumulated_path_symbol.value()] = std::move(curr_acc_path.value());
}
expand_from_vertex(current_vertex, current_weight, current_depth);
}
// If we yielded a path for a vertex already, make the expansion but
// don't return the path again.
@@ -1921,12 +1964,12 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
// Place destination node on the frame, handle existence flag.
if (self_.common_.existing_node) {
const auto &node = frame[self_.common_.node_symbol];
if ((node != TypedValue(current_vertex, pull_memory)).ValueBool())
if ((node != TypedValue(current_vertex, pull_memory)).ValueBool()) {
continue;
else
// Prevent expanding other paths, because we found the
// shortest to existing node.
ClearQueue();
}
// Prevent expanding other paths, because we found the
// shortest to existing node.
ClearQueue();
} else {
frame[self_.common_.node_symbol] = current_vertex;
}
@@ -1979,8 +2022,9 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
// Priority queue comparator. Keep lowest weight on top of the queue.
class PriorityQueueComparator {
public:
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>> &lhs,
const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>> &rhs) {
bool operator()(
const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>, std::optional<Path>> &lhs,
const std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>, std::optional<Path>> &rhs) {
const auto &lhs_weight = std::get<0>(lhs);
const auto &rhs_weight = std::get<0>(rhs);
// Null defines minimum value for all types
@@ -1997,8 +2041,9 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
}
};
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>>,
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>>>,
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>, std::optional<Path>>,
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, std::optional<EdgeAccessor>,
std::optional<Path>>>,
PriorityQueueComparator>
pq_;
@@ -2024,6 +2069,9 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::OLD);
auto *memory = context.evaluation_context.memory;
auto create_state = [this](const VertexAccessor &vertex, int64_t depth) {
return std::make_pair(vertex, upper_bound_set_ ? depth : 0);
};
@@ -2041,6 +2089,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
TypedValue next_weight = CalculateNextWeight(self_.weight_lambda_, total_weight, evaluator);
// If filter expression exists, evaluate filter
std::optional<Path> curr_acc_path = std::nullopt;
if (self_.filter_lambda_.expression) {
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
frame[self_.filter_lambda_.inner_node_symbol] = next_vertex;
@@ -2050,6 +2099,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
Path &accumulated_path = frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath();
accumulated_path.Expand(edge);
accumulated_path.Expand(next_vertex);
curr_acc_path = accumulated_path;
if (self_.filter_lambda_.accumulated_weight_symbol) {
frame[self_.filter_lambda_.accumulated_weight_symbol.value()] = next_weight;
@@ -2076,14 +2126,20 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
}
DirectedEdge directed_edge = {edge, direction, next_weight};
pq_.emplace(next_weight, depth + 1, next_vertex, directed_edge);
pq_.emplace(next_weight, depth + 1, next_vertex, directed_edge, curr_acc_path);
};
auto restore_frame_state_after_expansion = [this, &frame]() {
if (self_.filter_lambda_.accumulated_path_symbol) {
frame[self_.filter_lambda_.accumulated_path_symbol.value()].ValuePath().Shrink();
}
};
// Populates the priority queue structure with expansions
// from the given vertex. skips expansions that don't satisfy
// the "where" condition.
auto expand_from_vertex = [this, &expand_vertex, &context](const VertexAccessor &vertex, const TypedValue &weight,
int64_t depth) {
auto expand_from_vertex = [this, &expand_vertex, &context, &restore_frame_state_after_expansion](
const VertexAccessor &vertex, const TypedValue &weight, int64_t depth) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
@@ -2096,6 +2152,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
}
#endif
expand_vertex(edge, EdgeAtom::Direction::OUT, weight, depth);
restore_frame_state_after_expansion();
}
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
@@ -2110,12 +2167,12 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
}
#endif
expand_vertex(edge, EdgeAtom::Direction::IN, weight, depth);
restore_frame_state_after_expansion();
}
}
};
std::optional<VertexAccessor> start_vertex;
auto *memory = context.evaluation_context.memory;
auto create_path = [this, &frame, &memory]() {
auto &current_level = traversal_stack_.back();
@@ -2167,11 +2224,11 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
return true;
};
auto create_DFS_traversal_tree = [this, &context, &memory, &create_state, &expand_from_vertex]() {
auto create_DFS_traversal_tree = [this, &context, &memory, &frame, &create_state, &expand_from_vertex]() {
while (!pq_.empty()) {
AbortCheck(context);
const auto [current_weight, current_depth, current_vertex, directed_edge] = pq_.top();
auto [current_weight, current_depth, current_vertex, directed_edge, acc_path] = pq_.top();
pq_.pop();
const auto &[current_edge, direction, weight] = directed_edge;
@@ -2183,6 +2240,10 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
} else {
total_cost_.emplace(current_state, current_weight);
if (current_depth < upper_bound_) {
if (self_.filter_lambda_.accumulated_path_symbol) {
DMG_ASSERT(acc_path.has_value(), "Path must be already filled in AllShortestPath DFS traversals");
frame[self_.filter_lambda_.accumulated_path_symbol.value()] = std::move(acc_path.value());
}
expand_from_vertex(current_vertex, current_weight, current_depth);
}
}
@@ -2315,8 +2376,8 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
// Priority queue comparator. Keep lowest weight on top of the queue.
class PriorityQueueComparator {
public:
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &lhs,
const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge> &rhs) {
bool operator()(const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge, std::optional<Path>> &lhs,
const std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge, std::optional<Path>> &rhs) {
const auto &lhs_weight = std::get<0>(lhs);
const auto &rhs_weight = std::get<0>(rhs);
// Null defines minimum value for all types
@@ -2335,9 +2396,10 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
// Priority queue - core element of the algorithm.
// Stores: {weight, depth, next vertex, edge and direction}
std::priority_queue<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>,
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge>>,
PriorityQueueComparator>
std::priority_queue<
std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge, std::optional<Path>>,
utils::pmr::vector<std::tuple<TypedValue, int64_t, VertexAccessor, DirectedEdge, std::optional<Path>>>,
PriorityQueueComparator>
pq_;
void ClearQueue() {

View File

@@ -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
@@ -868,7 +868,10 @@ py::Object MgpListToPyTuple(mgp_list *list, PyObject *py_graph) {
}
void PyCollectGarbage() {
if (!Py_IsInitialized() || _Py_IsFinalizing()) {
// NOTE: No need to call _Py_IsFinalizing(), we ensure
// Python GC thread is stopped before Py_Finalize() is called
// in memgraph.cpp
if (!Py_IsInitialized()) {
// Calling EnsureGIL will crash the program if this is true.
return;
}

View File

@@ -762,6 +762,16 @@ Feature: Match
| path |
| <(:label1 {id: 1})-[:type2 {id: 10}]->(:label3 {id: 3})> |
Scenario: Test DFS variable expand using IN edges with filter by edge type1
Given graph "graph_edges"
When executing query:
"""
MATCH path=(:label3)<-[* (e, n, p | NOT(type(e)='type1' AND type(last(relationships(p))) = 'type1'))]-(:label1) RETURN path;
"""
Then the result should be:
| path |
| <(:label3 {id: 3})<-[:type2 {id: 10}]-(:label1 {id: 1})> |
Scenario: Test DFS variable expand with filter by edge type2
Given graph "graph_edges"
When executing query:
@@ -772,6 +782,16 @@ Feature: Match
| path |
| <(:label1 {id: 1})-[:type1 {id: 1}]->(:label2 {id: 2})-[:type1 {id: 2}]->(:label3 {id: 3})> |
Scenario: Test DFS variable expand using IN edges with filter by edge type2
Given graph "graph_edges"
When executing query:
"""
MATCH path=(:label3)<-[* (e, n, p | NOT(type(e)='type2' AND type(last(relationships(p))) = 'type2'))]-(:label1) RETURN path;
"""
Then the result should be:
| path |
| <(:label3 {id: 3})<-[:type1 {id: 2}]-(:label2 {id: 2})<-[:type1 {id: 1}]-(:label1 {id: 1})> |
Scenario: Using path indentifier from CREATE in MERGE
Given an empty graph
And having executed:

View File

@@ -205,11 +205,7 @@ Feature: All Shortest Path
| 20.3 |
Scenario: Test match AllShortest with accumulated path filtered by order of ids
Given an empty graph
And having executed:
"""
CREATE (:label1 {id: 1})-[:type1 {id:1}]->(:label2 {id: 2})-[:type1 {id: 2}]->(:label3 {id: 3})-[:type1 {id: 3}]->(:label4 {id: 4});
"""
Given graph "graph_edges"
When executing query:
"""
MATCH pth=(:label1)-[*ALLSHORTEST (r, n | r.id) total_weight (e,n,p | e.id > 0 and (nodes(p)[-1]).id > (nodes(p)[-2]).id)]->(:label4) RETURN pth, total_weight;
@@ -218,6 +214,16 @@ Feature: All Shortest Path
| pth | total_weight |
| <(:label1{id:1})-[:type1{id:1}]->(:label2{id:2})-[:type1{id:2}]->(:label3{id:3})-[:type1{id:3}]->(:label4{id:4})> | 6 |
Scenario: Test match AllShortest using IN edges with accumulated path filtered by order of ids
Given graph "graph_edges"
When executing query:
"""
MATCH pth=(:label4)<-[*ALLSHORTEST (r, n | r.id) total_weight (e,n,p | e.id > 0 and (nodes(p)[-1]).id < (nodes(p)[-2]).id)]-(:label1) RETURN pth, total_weight;
"""
Then the result should be:
| pth | total_weight |
| <(:label4{id:4})<-[:type1{id:3}]-(:label3{id:3})<-[:type1{id:2}]-(:label2{id:2})<-[:type1{id:1}]-(:label1{id:1})> | 6 |
Scenario: Test match AllShortest with accumulated path filtered by edge type1
Given graph "graph_edges"
When executing query:

View File

@@ -136,6 +136,42 @@ Feature: Bfs
| pth |
| <(:label1{id:1})-[:type1{id:1}]->(:label2{id:2})-[:type1{id:2}]->(:label3{id:3})> |
Scenario: Test BFS variable expand using IN edges with filter by last edge type of accumulated path
Given graph "graph_edges"
When executing query:
"""
MATCH pth=(:label3)<-[*BFS (e,n,p | type(relationships(p)[-1]) = 'type1')]-(:label1) return pth;
"""
Then the result should be:
| pth |
| <(:label3 {id: 3})<-[:type1 {id: 2}]-(:label2 {id: 2})<-[:type1 {id: 1}]-(:label1 {id: 1})> |
Scenario: Test BFS variable expand using IN edges with filter by number of vertices in the accumulated path
Given graph "graph_edges"
When executing query:
"""
MATCH p=(n)<-[*BFS (r, n, p | size(nodes(p)) > 0)]-(m {id:1}) return p;
"""
Then the result should be:
| p |
| <(:label2 {id: 2})<-[:type1 {id: 1}]-(:label1 {id: 1})> |
| <(:label3 {id: 3})<-[:type2 {id: 10}]-(:label1 {id: 1})> |
| <(:label4 {id: 4})<-[:type1 {id: 3}]-(:label3 {id: 3})<-[:type2 {id: 10}]-(:label1 {id: 1})> |
| <(:label5 {id: 5})<-[:type3 {id: 20}]-(:label1 {id: 1})> |
Scenario: Test BFS variable expand using IN edges with filter by id of vertices but accumulated path is not used
Given graph "graph_edges"
When executing query:
"""
MATCH p=(n)<-[*BFS (r, n, p | r.id > 0)]-(m {id:1}) return p;
"""
Then the result should be:
| p |
| <(:label2 {id: 2})<-[:type1 {id: 1}]-(:label1 {id: 1})> |
| <(:label3 {id: 3})<-[:type2 {id: 10}]-(:label1 {id: 1})> |
| <(:label4 {id: 4})<-[:type1 {id: 3}]-(:label3 {id: 3})<-[:type2 {id: 10}]-(:label1 {id: 1})> |
| <(:label5 {id: 5})<-[:type3 {id: 20}]-(:label1 {id: 1})> |
Scenario: Test BFS variable expand with restict filter by last edge type of accumulated path
Given an empty graph
And having executed:

View File

@@ -170,6 +170,20 @@ Feature: Weighted Shortest Path
| pth | total_weight |
| <(:label1{id:1})-[:type1{id:1}]->(:label2{id:2})-[:type1{id:2}]->(:label3{id:3})-[:type1{id:3}]->(:label4{id:4})> | 6 |
Scenario: Test match wShortest using IN edges with accumulated path filtered by order of ids
Given an empty graph
And having executed:
"""
CREATE (:label1 {id: 1})-[:type1 {id:1}]->(:label2 {id: 2})-[:type1 {id: 2}]->(:label3 {id: 3})-[:type1 {id: 3}]->(:label4 {id: 4});
"""
When executing query:
"""
MATCH pth=(:label4)<-[*WSHORTEST (r, n | r.id) total_weight (e,n,p | e.id > 0 and (nodes(p)[-1]).id < (nodes(p)[-2]).id)]-(:label1) RETURN pth, total_weight;
"""
Then the result should be:
| pth | total_weight |
| <(:label4{id:4})<-[:type1{id:3}]-(:label3{id:3})<-[:type1{id:2}]-(:label2{id:2})<-[:type1{id:1}]-(:label1{id:1})> | 6 |
Scenario: Test match wShortest with accumulated path filtered by edge type1
Given graph "graph_edges"
When executing query:

View File

@@ -1,2 +1,3 @@
CREATE (:label1 {id: 1})-[:type1 {id:1}]->(:label2 {id: 2})-[:type1 {id: 2}]->(:label3 {id: 3})-[:type1 {id: 3}]->(:label4 {id: 4});
MATCH (n :label1), (m :label3) CREATE (n)-[:type2 {id: 10}]->(m);
MATCH (n :label1) CREATE (n)-[:type3 {id: 20}]->(:label5 { id: 5 });

View File

@@ -24,7 +24,7 @@ PRINT_CONTEXT() {
HELP_EXIT() {
echo ""
echo "HELP: $0 help|cluster-up|cluster-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."
@@ -184,6 +184,37 @@ PROCESS_RESULTS() {
INFO "Result processing (printing and packing) DONE."
}
CLUSTER_UP() {
PRINT_CONTEXT
"$script_dir/jepsen/docker/bin/up" --daemon
sleep 10
# Ensure all SSH connections between Jepsen containers work
for node in $(docker ps --filter name=jepsen* --filter status=running --format "{{.Names}}"); do
if [ "$node" == "jepsen-control" ]; then
continue
fi
node_hostname="${node##jepsen-}"
docker exec jepsen-control bash -c "ssh -oStrictHostKeyChecking=no -t $node_hostname exit"
done
}
CLUSTER_DEALLOC() {
ps=$(docker ps --filter name=jepsen* --filter status=running -q)
if [[ ! -z ${ps} ]]; then
echo "Killing ${ps}"
docker rm -f ${ps}
imgs=$(docker images "jepsen*" -q)
if [[ ! -z ${imgs} ]]; then
echo "Removing ${imgs}"
docker images "jepsen*" -q | xargs docker image rmi -f
else
echo "No Jepsen images detected!"
fi
else
echo "No Jepsen containers detected!"
fi
}
# Initialize testing context by copying source/binary files. Inside CI,
# Memgraph is tested on a single machine cluster based on Docker containers.
# Once these tests will be part of the official Jepsen repo, the majority of
@@ -196,8 +227,16 @@ case $1 in
# the current cluster is broken because it relies on the folder. That can
# happen easiliy because the jepsen folder is git ignored.
cluster-up)
PRINT_CONTEXT
"$script_dir/jepsen/docker/bin/up" --daemon
CLUSTER_UP
;;
cluster-refresh)
CLUSTER_DEALLOC
CLUSTER_UP
;;
cluster-dealloc)
CLUSTER_DEALLOC
;;
cluster-cleanup)
@@ -212,23 +251,6 @@ case $1 in
done
;;
cluster-dealloc)
ps=$(docker ps --filter name=jepsen* --filter status=running -q)
if [[ ! -z ${ps} ]]; then
echo "Killing ${ps}"
docker rm -f ${ps}
imgs=$(docker images "jepsen*" -q)
if [[ ! -z ${imgs} ]]; then
echo "Removing ${imgs}"
docker images "jepsen*" -q | xargs docker image rmi -f
else
echo "No Jepsen images detected!"
fi
else
echo "No Jepsen containers detected!"
fi
;;
mgbuild)
PRINT_CONTEXT
echo ""