Compare commits
122 Commits
fix_finali
...
fix-zstd-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
979ee3bca6 | ||
|
|
e8beee10b2 | ||
|
|
4787608c43 | ||
|
|
147c36b07c | ||
|
|
03d39bc189 | ||
|
|
734ccabf3c | ||
|
|
73509269ce | ||
|
|
ca27f3c21f | ||
|
|
3bc570ead8 | ||
|
|
3114c69627 | ||
|
|
34c7a8b098 | ||
|
|
fc0a71f3d5 | ||
|
|
3e20423050 | ||
|
|
35532254a9 | ||
|
|
971dd5c4cc | ||
|
|
2731c57a81 | ||
|
|
6aa9e2a78b | ||
|
|
a6eb14f742 | ||
|
|
743f7b343a | ||
|
|
e9aaf82985 | ||
|
|
a07d577bd5 | ||
|
|
388eb91320 | ||
|
|
b0003ac08e | ||
|
|
445eaaf82a | ||
|
|
4b46f1bb54 | ||
|
|
ea64f2e906 | ||
|
|
71d2a2f9ea | ||
|
|
c426f4a4a4 | ||
|
|
a919429ea5 | ||
|
|
9e0b857e25 | ||
|
|
5cea4d0c40 | ||
|
|
ecbe385e58 | ||
|
|
2ac3d85735 | ||
|
|
a90cd8c468 | ||
|
|
e762fd8870 | ||
|
|
d5d101e7dc | ||
|
|
cdf7b53aa7 | ||
|
|
a278ae1139 | ||
|
|
19642595e3 | ||
|
|
73cb1811e0 | ||
|
|
be561bb5d6 | ||
|
|
212ed5830f | ||
|
|
b04e40984d | ||
|
|
59b83d003f | ||
|
|
ff69a891d8 | ||
|
|
857dbfb84e | ||
|
|
3e7e0d896c | ||
|
|
b24afcde0a | ||
|
|
9486c3fc47 | ||
|
|
f61bf766bb | ||
|
|
55fd6dcc75 | ||
|
|
04410d242b | ||
|
|
c7be58e1dc | ||
|
|
2ef9d81a63 | ||
|
|
db3991db8f | ||
|
|
5f2de50d28 | ||
|
|
c2824e3dc0 | ||
|
|
1334a631b3 | ||
|
|
456074e3cf | ||
|
|
b7bc9c651e | ||
|
|
7b77981796 | ||
|
|
e4e3044fce | ||
|
|
0571da7116 | ||
|
|
64dcc2c60a | ||
|
|
e16c4a0208 | ||
|
|
c60b66fde7 | ||
|
|
2df3b09019 | ||
|
|
809a990cd5 | ||
|
|
88f8a0ae68 | ||
|
|
605a425bb3 | ||
|
|
296d6eb4ff | ||
|
|
60f8c3fc15 | ||
|
|
0335030c93 | ||
|
|
dbd3835ba2 | ||
|
|
2d2d21bab4 | ||
|
|
5c23e313e4 | ||
|
|
085b5b8284 | ||
|
|
8f0250c265 | ||
|
|
684ae0192b | ||
|
|
1d6fa4281d | ||
|
|
5fac8de106 | ||
|
|
99417f75b6 | ||
|
|
0c3220839c | ||
|
|
6fb4eb6099 | ||
|
|
29916a9db2 | ||
|
|
e78c69866f | ||
|
|
9a3a9d33e3 | ||
|
|
ba581f58ef | ||
|
|
00275f5736 | ||
|
|
bed5651716 | ||
|
|
753aebb895 | ||
|
|
6545a741b8 | ||
|
|
b067ded578 | ||
|
|
3595a9c3b5 | ||
|
|
5fb9324239 | ||
|
|
ce718c4646 | ||
|
|
5d5981ffa1 | ||
|
|
2ec08f7da5 | ||
|
|
584d86e774 | ||
|
|
5b22a7d3a4 | ||
|
|
fe7e230e21 | ||
|
|
ec35144265 | ||
|
|
102bf1df5e | ||
|
|
9d06d1b5db | ||
|
|
c4f8512677 | ||
|
|
6865616cae | ||
|
|
f0a2b67f33 | ||
|
|
6c2b448fe9 | ||
|
|
3737fac8f8 | ||
|
|
a5d849b5e9 | ||
|
|
0357d19238 | ||
|
|
63c9628cef | ||
|
|
e5f2ac36cc | ||
|
|
b4b5970ba6 | ||
|
|
71e03d1fa3 | ||
|
|
56b86089a6 | ||
|
|
d4059a89aa | ||
|
|
c3208b064b | ||
|
|
0baff463b1 | ||
|
|
3d40976756 | ||
|
|
510f2909ae | ||
|
|
df82b05725 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,6 +3,7 @@ name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: gitbuda
|
||||
---
|
||||
|
||||
**Memgraph version**
|
||||
|
||||
112
.github/workflows/diff.yaml
vendored
112
.github/workflows/diff.yaml
vendored
@@ -336,6 +336,118 @@ jobs:
|
||||
# multiple paths could be defined
|
||||
build/logs
|
||||
|
||||
experimental_build_ha:
|
||||
name: "High availability build"
|
||||
runs-on: [self-hosted, Linux, X64, Diff]
|
||||
env:
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
|
||||
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: |
|
||||
source /opt/toolchain-v4/activate
|
||||
./init
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DMG_EXPERIMENTAL_HIGH_AVAILABILITY=ON ..
|
||||
make -j$THREADS
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||
- name: Run e2e tests
|
||||
if: false
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh "Coordinator"
|
||||
./run.sh "Client initiated failover"
|
||||
./run.sh "Uninitialized cluster"
|
||||
- name: Save test data
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: "Test data(High availability build)"
|
||||
path: |
|
||||
# multiple paths could be defined
|
||||
build/logs
|
||||
|
||||
experimental_build_mt:
|
||||
name: "MultiTenancy replication build"
|
||||
runs-on: [self-hosted, Linux, X64, Diff]
|
||||
env:
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
|
||||
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 MT replication experimental binaries.
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -D MG_EXPERIMENTAL_REPLICATION_MULTITENANCY=ON ..
|
||||
make -j$THREADS
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
# Activate toolchain.
|
||||
source /opt/toolchain-v4/activate
|
||||
|
||||
# Run unit tests.
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||
|
||||
- name: Run e2e tests
|
||||
if: false
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
|
||||
# Just the replication based e2e tests
|
||||
./run.sh "Replicate multitenancy"
|
||||
./run.sh "Show"
|
||||
./run.sh "Show while creating invalid state"
|
||||
./run.sh "Delete edge replication"
|
||||
./run.sh "Read-write benchmark"
|
||||
./run.sh "Index replication"
|
||||
./run.sh "Constraints"
|
||||
|
||||
- name: Save test data
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: "Test data(MultiTenancy replication build)"
|
||||
path: |
|
||||
# multiple paths could be defined
|
||||
build/logs
|
||||
|
||||
release_jepsen_test:
|
||||
name: "Release Jepsen Test"
|
||||
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
|
||||
|
||||
162
.github/workflows/release_build_test.yaml
vendored
162
.github/workflows/release_build_test.yaml
vendored
@@ -1,162 +0,0 @@
|
||||
name: Release build test
|
||||
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:
|
||||
branches:
|
||||
- "release/**"
|
||||
tags:
|
||||
- "v*.*.*-rc*"
|
||||
- "v*.*-rc*"
|
||||
schedule:
|
||||
# UTC
|
||||
- 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:
|
||||
Debian10:
|
||||
uses: ./.github/workflows/release_debian10.yaml
|
||||
with:
|
||||
build_type: ${{ github.event.inputs.build_type || 'Release' }}
|
||||
secrets: inherit
|
||||
|
||||
Ubuntu20_04:
|
||||
uses: ./.github/workflows/release_ubuntu2004.yaml
|
||||
with:
|
||||
build_type: ${{ github.event.inputs.build_type || 'Release' }}
|
||||
secrets: inherit
|
||||
|
||||
PackageDebian10:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [Debian10]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-10 $BUILD_TYPE
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-10
|
||||
path: build/output/debian-10/memgraph*.deb
|
||||
|
||||
PackageUbuntu20_04:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [Ubuntu20_04]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-22.04 $BUILD_TYPE
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-22.04
|
||||
path: build/output/ubuntu-22.04/memgraph*.deb
|
||||
|
||||
PackageUbuntu20_04_ARM:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [Ubuntu20_04]
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64]
|
||||
# M1 Mac mini is sometimes slower
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package ubuntu-22.04-arm $BUILD_TYPE
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ubuntu-22.04-aarch64
|
||||
path: build/output/ubuntu-22.04-arm/memgraph*.deb
|
||||
|
||||
PackageDebian11:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [Debian10, Ubuntu20_04]
|
||||
runs-on: [self-hosted, DockerMgBuild, X64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-11 $BUILD_TYPE
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-11
|
||||
path: build/output/debian-11/memgraph*.deb
|
||||
|
||||
PackageDebian11_ARM:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [Debian10, Ubuntu20_04]
|
||||
runs-on: [self-hosted, DockerMgBuild, ARM64]
|
||||
# M1 Mac mini is sometimes slower
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: "Set up repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required because of release/get_version.py
|
||||
- name: "Build package"
|
||||
run: |
|
||||
./release/package/run.sh package debian-11-arm $BUILD_TYPE
|
||||
- name: "Upload package"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-11-aarch64
|
||||
path: build/output/debian-11-arm/memgraph*.deb
|
||||
|
||||
PushToS3:
|
||||
if: github.ref_type == 'tag'
|
||||
needs: [PackageDebian10, PackageDebian11, PackageDebian11_ARM, PackageUbuntu20_04, PackageUbuntu20_04_ARM]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
# name: # if name input parameter is not provided, all artifacts are downloaded
|
||||
# and put in directories named after each one.
|
||||
path: build/output/release
|
||||
- name: Upload to S3
|
||||
uses: jakejarvis/s3-sync-action@v0.5.1
|
||||
env:
|
||||
AWS_S3_BUCKET: "deps.memgraph.io"
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.S3_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: "eu-west-1"
|
||||
SOURCE_DIR: "build/output/release"
|
||||
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
|
||||
21
.github/workflows/release_debian10.yaml
vendored
21
.github/workflows/release_debian10.yaml
vendored
@@ -1,12 +1,6 @@
|
||||
name: Release Debian 10
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
type: string
|
||||
description: "Memgraph Build type. Default value is Release."
|
||||
default: 'Release'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
@@ -17,8 +11,10 @@ on:
|
||||
- Release
|
||||
- RelWithDebInfo
|
||||
|
||||
schedule:
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
env:
|
||||
OS: "Debian10"
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
@@ -115,7 +111,7 @@ jobs:
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
||||
name: "Code coverage(Coverage build)"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
|
||||
debug_build:
|
||||
@@ -169,7 +165,7 @@ jobs:
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
||||
name: "Code coverage(Debug build)"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
|
||||
debug_integration_test:
|
||||
@@ -246,7 +242,7 @@ jobs:
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Enterprise DEB package-${{ env.OS}}"
|
||||
name: "Enterprise DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run GQL Behave tests
|
||||
@@ -259,7 +255,7 @@ jobs:
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "GQL Behave Status-${{ env.OS }}"
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
@@ -325,6 +321,7 @@ jobs:
|
||||
--no-strict
|
||||
|
||||
release_e2e_test:
|
||||
if: false
|
||||
name: "Release End-to-end Test"
|
||||
runs-on: [self-hosted, Linux, X64, Debian10]
|
||||
timeout-minutes: 60
|
||||
@@ -459,5 +456,5 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: "Jepsen Report-${{ env.OS }}"
|
||||
name: "Jepsen Report"
|
||||
path: tests/jepsen/Jepsen.tar.gz
|
||||
|
||||
19
.github/workflows/release_ubuntu2004.yaml
vendored
19
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -1,12 +1,6 @@
|
||||
name: Release Ubuntu 20.04
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
type: string
|
||||
description: "Memgraph Build type. Default value is Release."
|
||||
default: 'Release'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
@@ -17,8 +11,10 @@ on:
|
||||
- Release
|
||||
- RelWithDebInfo
|
||||
|
||||
schedule:
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
env:
|
||||
OS: "Ubuntu 20.04"
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
@@ -111,7 +107,7 @@ jobs:
|
||||
- name: Save code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
||||
name: "Code coverage(Coverage build)"
|
||||
path: tools/github/generated/code_coverage.tar.gz
|
||||
|
||||
debug_build:
|
||||
@@ -165,7 +161,7 @@ jobs:
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
||||
name: "Code coverage(Debug build)"
|
||||
path: tools/github/cppcheck_and_clang_format.txt
|
||||
|
||||
debug_integration_test:
|
||||
@@ -242,7 +238,7 @@ jobs:
|
||||
- name: Save enterprise DEB package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "Enterprise DEB package-${{ env.OS }}"
|
||||
name: "Enterprise DEB package"
|
||||
path: build/output/memgraph*.deb
|
||||
|
||||
- name: Run GQL Behave tests
|
||||
@@ -255,7 +251,7 @@ jobs:
|
||||
- name: Save quality assurance status
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "GQL Behave Status-${{ env.OS }}"
|
||||
name: "GQL Behave Status"
|
||||
path: |
|
||||
tests/gql_behave/gql_behave_status.csv
|
||||
tests/gql_behave/gql_behave_status.html
|
||||
@@ -321,6 +317,7 @@ jobs:
|
||||
--no-strict
|
||||
|
||||
release_e2e_test:
|
||||
if: false
|
||||
name: "Release End-to-end Test"
|
||||
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
|
||||
timeout-minutes: 60
|
||||
|
||||
8
.github/workflows/stress_test_large.yaml
vendored
8
.github/workflows/stress_test_large.yaml
vendored
@@ -1,7 +1,4 @@
|
||||
name: Stress test large
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -13,10 +10,7 @@ on:
|
||||
options:
|
||||
- Release
|
||||
- RelWithDebInfo
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*-rc*"
|
||||
- "v*.*-rc*"
|
||||
|
||||
schedule:
|
||||
- cron: "0 22 * * *"
|
||||
|
||||
|
||||
@@ -211,13 +211,8 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
# ** Static linking is allowed only for executables! **
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||
|
||||
# Use lld linker to speedup build and use less memory.
|
||||
add_link_options(-fuse-ld=lld)
|
||||
# NOTE: Moving to latest Clang (probably starting from 15), lld stopped to work
|
||||
# without explicit link_directories call.
|
||||
string(REPLACE ":" " " LD_LIBS $ENV{LD_LIBRARY_PATH})
|
||||
separate_arguments(LD_LIBS)
|
||||
link_directories(${LD_LIBS})
|
||||
# Use lld linker to speedup build
|
||||
add_link_options(-fuse-ld=lld) # TODO: use mold linker
|
||||
|
||||
# release flags
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
@@ -276,6 +271,18 @@ endif()
|
||||
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
|
||||
add_subdirectory(libs EXCLUDE_FROM_ALL)
|
||||
|
||||
option(MG_EXPERIMENTAL_HIGH_AVAILABILITY "Feature flag for experimental high availability" OFF)
|
||||
|
||||
if (NOT MG_ENTERPRISE AND MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
set(MG_EXPERIMENTAL_HIGH_AVAILABILITY OFF)
|
||||
message(FATAL_ERROR "MG_EXPERIMENTAL_HIGH_AVAILABILITY can only be used with enterpise version of the code.")
|
||||
endif ()
|
||||
|
||||
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
add_compile_definitions(MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
endif ()
|
||||
|
||||
# Optional subproject configuration -------------------------------------------
|
||||
option(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF)
|
||||
option(TOOLS "Build tools binaries" ON)
|
||||
option(QUERY_MODULES "Build query modules containing custom procedures" ON)
|
||||
@@ -284,6 +291,16 @@ option(TSAN "Build with Thread Sanitizer. To get a reasonable performance option
|
||||
option(UBSAN "Build with Undefined Behaviour Sanitizer" OFF)
|
||||
|
||||
# Build feature flags
|
||||
option(MG_EXPERIMENTAL_REPLICATION_MULTITENANCY "Feature flag for experimental replicaition of multitenacy" OFF)
|
||||
|
||||
if (NOT MG_ENTERPRISE AND MG_EXPERIMENTAL_REPLICATION_MULTITENANCY)
|
||||
set(MG_EXPERIMENTAL_REPLICATION_MULTITENANCY OFF)
|
||||
message(FATAL_ERROR "MG_EXPERIMENTAL_REPLICATION_MULTITENANCY with community edition build isn't possible")
|
||||
endif ()
|
||||
|
||||
if (MG_EXPERIMENTAL_REPLICATION_MULTITENANCY)
|
||||
add_compile_definitions(MG_EXPERIMENTAL_REPLICATION_MULTITENANCY)
|
||||
endif ()
|
||||
|
||||
if (TEST_COVERAGE)
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -7,7 +9,7 @@ check_operating_system "amzn-2"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
git gcc gcc-c++ make # generic build tools
|
||||
gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
@@ -61,8 +63,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -61,8 +63,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -7,10 +9,8 @@ check_operating_system "centos-9"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
wget # used for archive download
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
# NOTE: Pure libcurl conflicts with libcurl-minimal
|
||||
libcurl-devel # cmake build requires it
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
@@ -64,8 +64,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
@@ -125,9 +123,7 @@ install() {
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
# --nobest is used because of libipt because we install custom versions
|
||||
# because libipt-devel is not available on CentOS 9 Stream
|
||||
yum update -y --nobest
|
||||
yum update -y
|
||||
yum install -y wget git python3 python3-pip
|
||||
|
||||
for pkg in $1; do
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "debian-10"
|
||||
check_architecture "x86_64"
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "debian-11"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -59,8 +61,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-12"
|
||||
check_architecture "arm64" "aarch64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg # used for archive signature verification
|
||||
tar gzip bzip2 xz-utils unzip # used for archive unpacking
|
||||
zlib1g-dev # zlib library used for all builds
|
||||
libexpat1-dev liblzma-dev python3-dev texinfo # for gdb
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
libedit-dev libpcre2-dev libpcre3-dev automake bison # for swig
|
||||
curl # snappy
|
||||
file # for libunwind
|
||||
libssl-dev # for libevent
|
||||
libgmp-dev
|
||||
gperf # for proxygen
|
||||
git # for fbthrift
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz-utils # used for archive unpacking
|
||||
zlib1g # zlib library used for all builds
|
||||
libexpat1 liblzma5 python3 # for gdb
|
||||
libcurl4 # for cmake
|
||||
file # for CPack
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi8 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
uuid-dev default-jre-headless # required by antlr
|
||||
libreadline-dev # for memgraph console
|
||||
libpython3-dev python3-dev # for query modules
|
||||
libssl-dev
|
||||
libseccomp-dev
|
||||
netcat # tests are using nc to wait for memgraph
|
||||
python3 virtualenv python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
|
||||
python3-yaml # for the configuration generator
|
||||
libcurl4-openssl-dev # mg-requests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
doxygen graphviz # source documentation generators
|
||||
mono-runtime mono-mcs zip unzip default-jdk-headless custom-maven3.9.3 # for driver tests
|
||||
dotnet-sdk-7.0 golang custom-golang1.18.9 nodejs npm
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
NEW_DEPS=(
|
||||
wget curl tar gzip
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
apt update
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests doesn't work the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
apt install -y wget
|
||||
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
install_custom_maven "3.9.3"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
install_custom_golang "1.18.9"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == dotnet-sdk-7.0 ]; then
|
||||
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https dotnet-sdk-7.0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -1,136 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "debian-12"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils gcc g++ build-essential make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg # used for archive signature verification
|
||||
tar gzip bzip2 xz-utils unzip # used for archive unpacking
|
||||
zlib1g-dev # zlib library used for all builds
|
||||
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
|
||||
libcurl4-openssl-dev # for cmake
|
||||
libreadline-dev # for cmake and llvm
|
||||
libffi-dev libxml2-dev # for llvm
|
||||
libedit-dev libpcre2-dev libpcre3-dev automake bison # for swig
|
||||
curl # snappy
|
||||
file # for libunwind
|
||||
libssl-dev # for libevent
|
||||
libgmp-dev
|
||||
gperf # for proxygen
|
||||
git # for fbthrift
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz-utils # used for archive unpacking
|
||||
zlib1g # zlib library used for all builds
|
||||
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
|
||||
libcurl4 # for cmake
|
||||
file # for CPack
|
||||
libreadline8 # for cmake and llvm
|
||||
libffi8 libxml2 # for llvm
|
||||
libssl-dev # for libevent
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make cmake pkg-config # build system
|
||||
curl wget # for downloading libs
|
||||
uuid-dev default-jre-headless # required by antlr
|
||||
libreadline-dev # for memgraph console
|
||||
libpython3-dev python3-dev # for query modules
|
||||
libssl-dev
|
||||
libseccomp-dev
|
||||
netcat-traditional # tests are using nc to wait for memgraph
|
||||
python3 virtualenv python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
|
||||
python3-yaml # for the configuration generator
|
||||
libcurl4-openssl-dev # mg-requests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
doxygen graphviz # source documentation generators
|
||||
mono-runtime mono-mcs zip unzip default-jdk-headless custom-maven3.9.3 # for driver tests
|
||||
dotnet-sdk-7.0 golang custom-golang1.18.9 nodejs npm
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
NEW_DEPS=(
|
||||
wget curl tar gzip
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
apt update
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests doesn't work the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
apt install -y wget
|
||||
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
install_custom_maven "3.9.3"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
install_custom_golang "1.18.9"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == dotnet-sdk-7.0 ]; then
|
||||
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
|
||||
wget -nv https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||
dpkg -i packages-microsoft-prod.deb
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https dotnet-sdk-7.0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
apt install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "fedora-36"
|
||||
check_architecture "x86_64"
|
||||
|
||||
@@ -27,7 +27,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
git
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -25,7 +27,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
git
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
@@ -57,16 +58,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
NEW_DEPS=(
|
||||
wget curl tar gzip
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
check_operating_system "fedora-39"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
wget # used for archive download
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
expat-devel xz-devel python3-devel texinfo libbabeltrace-devel # for gdb
|
||||
curl libcurl-devel # for cmake
|
||||
readline-devel # for cmake and llvm
|
||||
libffi-devel libxml2-devel # for llvm
|
||||
libedit-devel pcre-devel pcre2-devel automake bison # for swig
|
||||
file
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
diffutils
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
perl # for openssl
|
||||
git
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz # used for archive unpacking
|
||||
zlib # zlib library used for all builds
|
||||
expat xz-libs python3 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make pkgconf-pkg-config # build system
|
||||
wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
readline-devel # for memgraph console
|
||||
python3-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python3 python3-pip python3-virtualenv python3-virtualenvwrapper python3-pyyaml nmap-ncat # for tests
|
||||
libcurl-devel # mg-requests
|
||||
rpm-build rpmlint # for RPM package building
|
||||
doxygen graphviz # source documentation generators
|
||||
which nodejs golang zip unzip java-11-openjdk-devel # for driver tests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
NEW_DEPS=(
|
||||
wget curl tar gzip
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
if [ -v LD_LIBRARY_PATH ]; then
|
||||
# On Fedora 38 yum/dnf and python11 use newer glibc which is not compatible
|
||||
# with ours, so we need to momentarely disable env
|
||||
local OLD_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
|
||||
LD_LIBRARY_PATH=""
|
||||
fi
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
if [ -v OLD_LD_LIBRARY_PATH ]; then
|
||||
echo "Restoring LD_LIBRARY_PATH..."
|
||||
LD_LIBRARY_PATH=${OLD_LD_LIBRARY_PATH}
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root."
|
||||
exit 1
|
||||
fi
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests don't work without the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
dnf update -y
|
||||
for pkg in $1; do
|
||||
dnf install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -1,188 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# TODO(gitbuda): Rocky gets automatically updates -> figure out how to handle it.
|
||||
check_operating_system "rocky-9.3"
|
||||
check_architecture "x86_64"
|
||||
|
||||
TOOLCHAIN_BUILD_DEPS=(
|
||||
wget # used for archive download
|
||||
coreutils-common gcc gcc-c++ make # generic build tools
|
||||
# NOTE: Pure libcurl conflicts with libcurl-minimal
|
||||
libcurl-devel # cmake build requires it
|
||||
gnupg2 # used for archive signature verification
|
||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||
zlib-devel # zlib library used for all builds
|
||||
expat-devel xz-devel python3-devel perl-Unicode-EastAsianWidth texinfo libbabeltrace-devel # for gdb
|
||||
readline-devel # for cmake and llvm
|
||||
libffi-devel libxml2-devel # for llvm
|
||||
libedit-devel pcre-devel pcre2-devel automake bison # for swig
|
||||
file
|
||||
openssl-devel
|
||||
gmp-devel
|
||||
gperf
|
||||
diffutils
|
||||
libipt libipt-devel # intel
|
||||
patch
|
||||
)
|
||||
|
||||
TOOLCHAIN_RUN_DEPS=(
|
||||
make # generic build tools
|
||||
tar gzip bzip2 xz # used for archive unpacking
|
||||
zlib # zlib library used for all builds
|
||||
expat xz-libs python3 # for gdb
|
||||
readline # for cmake and llvm
|
||||
libffi libxml2 # for llvm
|
||||
openssl-devel
|
||||
perl # for openssl
|
||||
)
|
||||
|
||||
MEMGRAPH_BUILD_DEPS=(
|
||||
git # source code control
|
||||
make cmake pkgconf-pkg-config # build system
|
||||
wget # for downloading libs
|
||||
libuuid-devel java-11-openjdk # required by antlr
|
||||
readline-devel # for memgraph console
|
||||
python3-devel # for query modules
|
||||
openssl-devel
|
||||
libseccomp-devel
|
||||
python3 python3-pip python3-virtualenv nmap-ncat # for qa, macro_benchmark and stress tests
|
||||
#
|
||||
# IMPORTANT: python3-yaml does NOT exist on CentOS
|
||||
# Install it manually using `pip3 install PyYAML`
|
||||
#
|
||||
PyYAML # Package name here does not correspond to the yum package!
|
||||
libcurl-devel # mg-requests
|
||||
rpm-build rpmlint # for RPM package building
|
||||
doxygen graphviz # source documentation generators
|
||||
which nodejs golang custom-golang1.18.9 # for driver tests
|
||||
zip unzip java-11-openjdk-devel java-17-openjdk java-17-openjdk-devel custom-maven3.9.3 # for driver tests
|
||||
sbcl # for custom Lisp C++ preprocessing
|
||||
autoconf # for jemalloc code generation
|
||||
libtool # for protobuf code generation
|
||||
cyrus-sasl-devel
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp
|
||||
)
|
||||
|
||||
NEW_DEPS=(
|
||||
wget curl tar gzip
|
||||
)
|
||||
|
||||
list() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
local missing=""
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
if [ ! -f "/opt/apache-maven-3.9.3/bin/mvn" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
if [ ! -f "/opt/go1.18.9/go/bin/go" ]; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == "PyYAML" ]; then
|
||||
if ! python3 -c "import yaml" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == "python3-virtualenv" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
|
||||
missing="$pkg $missing"
|
||||
fi
|
||||
done
|
||||
if [ "$missing" != "" ]; then
|
||||
echo "MISSING PACKAGES: $missing"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
cd "$DIR"
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root."
|
||||
exit 1
|
||||
fi
|
||||
# If GitHub Actions runner is installed, append LANG to the environment.
|
||||
# Python related tests doesn't work the LANG export.
|
||||
if [ -d "/home/gh/actions-runner" ]; then
|
||||
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
|
||||
else
|
||||
echo "NOTE: export LANG=en_US.utf8"
|
||||
fi
|
||||
yum update -y
|
||||
yum install -y wget git python3 python3-pip
|
||||
|
||||
for pkg in $1; do
|
||||
if [ "$pkg" == custom-maven3.9.3 ]; then
|
||||
install_custom_maven "3.9.3"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == custom-golang1.18.9 ]; then
|
||||
install_custom_golang "1.18.9"
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == perl-Unicode-EastAsianWidth ]; then
|
||||
if ! dnf list installed perl-Unicode-EastAsianWidth >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/Packages/p/perl-Unicode-EastAsianWidth-12.0-7.el9.noarch.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == texinfo ]; then
|
||||
if ! dnf list installed texinfo >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/Packages/t/texinfo-6.7-15.el9.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == libbabeltrace-devel ]; then
|
||||
if ! dnf list installed libbabeltrace-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://dl.rockylinux.org/pub/rocky/9/devel/x86_64/os/Packages/l/libbabeltrace-devel-1.5.8-10.el9.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == libipt-devel ]; then
|
||||
if ! dnf list installed libipt-devel >/dev/null 2>/dev/null; then
|
||||
dnf install -y https://dl.rockylinux.org/pub/rocky/9/devel/x86_64/os/Packages/l/libipt-devel-2.0.4-5.el9.x86_64.rpm
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == PyYAML ]; then
|
||||
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
|
||||
pip3 install --user PyYAML
|
||||
else # Running using sudo.
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user PyYAML"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
if [ "$pkg" == python3-virtualenv ]; then
|
||||
if [ -z ${SUDO_USER+x} ]; then # Running as root (e.g. Docker).
|
||||
pip3 install virtualenv
|
||||
pip3 install virtualenvwrapper
|
||||
else # Running using sudo.
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenv"
|
||||
sudo -H -u "$SUDO_USER" bash -c "pip3 install virtualenvwrapper"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
yum install -y "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
deps=$2"[*]"
|
||||
"$1" "${!deps}"
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -18,10 +20,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
pkg
|
||||
)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
||||
|
||||
check_operating_system "ubuntu-18.04"
|
||||
check_architecture "x86_64"
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source "$DIR/../util.sh"
|
||||
|
||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
||||
libsasl2-dev
|
||||
)
|
||||
|
||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
||||
|
||||
MEMGRAPH_RUN_DEPS=(
|
||||
logrotate openssl python3 libseccomp2
|
||||
)
|
||||
|
||||
1
environment/toolchain/.gitignore
vendored
1
environment/toolchain/.gitignore
vendored
@@ -2,4 +2,3 @@ archives
|
||||
build
|
||||
output
|
||||
*.tar.gz
|
||||
tmp_build.sh
|
||||
|
||||
35
environment/toolchain/template_build.sh → environment/toolchain/v4/_tmp_build_zstd.sh
Normal file → Executable file
35
environment/toolchain/template_build.sh → environment/toolchain/v4/_tmp_build_zstd.sh
Normal file → Executable file
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# NOTE: Copy this under memgraph/environment/toolchain/vN/tmp_build.sh, edit and test.
|
||||
|
||||
# helpers
|
||||
pushd () { command pushd "$@" > /dev/null; }
|
||||
popd () { command popd "$@" > /dev/null; }
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
@@ -9,9 +8,7 @@ CPUS=$( grep -c processor < /proc/cpuinfo )
|
||||
cd "$DIR"
|
||||
source "$DIR/../../util.sh"
|
||||
DISTRO="$(operating_system)"
|
||||
TOOLCHAIN_VERSION=5
|
||||
NAME=toolchain-v$TOOLCHAIN_VERSION
|
||||
PREFIX=/opt/$NAME
|
||||
|
||||
function log_tool_name () {
|
||||
echo ""
|
||||
echo ""
|
||||
@@ -20,12 +17,21 @@ function log_tool_name () {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# HERE: Remove/clear dependencies from a given toolchain.
|
||||
TOOLCHAIN_VERSION=4
|
||||
NAME=toolchain-v$TOOLCHAIN_VERSION
|
||||
PREFIX=/opt/$NAME
|
||||
|
||||
rm -rf "$PREFIX/include/zstd.h"
|
||||
|
||||
# create archives directory
|
||||
mkdir -p archives && pushd archives
|
||||
# HERE: Download dependencies here.
|
||||
ZSTD_VERSION=1.5.5
|
||||
if [ ! -f zstd-$ZSTD_VERSION.tar.gz ]; then
|
||||
wget https://github.com/facebook/zstd/releases/download/v$ZSTD_VERSION/zstd-$ZSTD_VERSION.tar.gz -O zstd-$ZSTD_VERSION.tar.gz
|
||||
fi
|
||||
popd
|
||||
|
||||
# create build directory and activate toolchain
|
||||
mkdir -p build
|
||||
pushd build
|
||||
source $PREFIX/activate
|
||||
@@ -45,4 +51,17 @@ COMMON_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$PREFIX
|
||||
-DCMAKE_REQUIRED_INCLUDES=$PREFIX/include
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
|
||||
|
||||
# HERE: Add dependencies to test below.
|
||||
log_tool_name "zstd $ZSTD_VERSION"
|
||||
if [ ! -f $PREFIX/include/zstd.h ]; then
|
||||
if [ -d zstd-$ZSTD_VERSION ]; then
|
||||
rm -rf zstd-$ZSTD_VERSION
|
||||
fi
|
||||
tar -xzf ../archives/zstd-$ZSTD_VERSION.tar.gz
|
||||
pushd zstd-$ZSTD_VERSION
|
||||
# build is used by facebook builder
|
||||
mkdir _build
|
||||
pushd _build
|
||||
cmake ../build/cmake $COMMON_CMAKE_FLAGS -DZSTD_BUILD_SHARED=OFF
|
||||
make -j$CPUS install
|
||||
popd && popd
|
||||
fi
|
||||
@@ -307,7 +307,7 @@ if [ ! -f $PREFIX/bin/ld.gold ]; then
|
||||
fi
|
||||
|
||||
log_tool_name "GDB $GDB_VERSION"
|
||||
if [[ ! -f "$PREFIX/bin/gdb" && "$DISTRO" -ne "amzn-2" ]]; then
|
||||
if [ ! -f $PREFIX/bin/gdb ]; then
|
||||
if [ -d gdb-$GDB_VERSION ]; then
|
||||
rm -rf gdb-$GDB_VERSION
|
||||
fi
|
||||
@@ -671,6 +671,7 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||
# WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532
|
||||
|
||||
FLEX_VERSION=2.6.4
|
||||
FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b
|
||||
FMT_VERSION=10.1.1
|
||||
# NOTE: spdlog depends on exact fmt versions -> UPGRADE fmt and spdlog TOGETHER.
|
||||
@@ -689,8 +690,8 @@ LZ4_VERSION=1.9.4
|
||||
SNAPPY_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
|
||||
SNAPPY_VERSION=1.1.9
|
||||
XZ_VERSION=5.2.5 # for LZMA
|
||||
ZLIB_VERSION=1.3.1
|
||||
ZSTD_VERSION=1.5.5
|
||||
ZLIB_VERSION=1.3
|
||||
ZSTD_VERSION=1.5.0
|
||||
|
||||
pushd archives
|
||||
if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then
|
||||
@@ -699,7 +700,7 @@ if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then
|
||||
wget https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$BOOST_VERSION_UNDERSCORES.tar.gz -O boost_$BOOST_VERSION_UNDERSCORES.tar.gz
|
||||
fi
|
||||
if [ ! -f bzip2-$BZIP2_VERSION.tar.gz ]; then
|
||||
wget https://sourceware.org/pub/bzip2/bzip2-$BZIP2_VERSION.tar.gz -O bzip2-$BZIP2_VERSION.tar.gz
|
||||
wget https://sourceforge.net/projects/bzip2/files/bzip2-$BZIP2_VERSION.tar.gz -O bzip2-$BZIP2_VERSION.tar.gz
|
||||
fi
|
||||
if [ ! -f double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz ]; then
|
||||
wget https://github.com/google/double-conversion/archive/refs/tags/v$DOUBLE_CONVERSION_VERSION.tar.gz -O double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz
|
||||
@@ -707,7 +708,9 @@ fi
|
||||
if [ ! -f fizz-$FBLIBS_VERSION.tar.gz ]; then
|
||||
wget https://github.com/facebookincubator/fizz/releases/download/v$FBLIBS_VERSION/fizz-v$FBLIBS_VERSION.tar.gz -O fizz-$FBLIBS_VERSION.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -f flex-$FLEX_VERSION.tar.gz ]; then
|
||||
wget https://github.com/westes/flex/releases/download/v$FLEX_VERSION/flex-$FLEX_VERSION.tar.gz -O flex-$FLEX_VERSION.tar.gz
|
||||
fi
|
||||
if [ ! -f fmt-$FMT_VERSION.tar.gz ]; then
|
||||
wget https://github.com/fmtlib/fmt/archive/refs/tags/$FMT_VERSION.tar.gz -O fmt-$FMT_VERSION.tar.gz
|
||||
fi
|
||||
@@ -762,6 +765,14 @@ echo "$BZIP2_SHA256 bzip2-$BZIP2_VERSION.tar.gz" | sha256sum -c
|
||||
echo "$DOUBLE_CONVERSION_SHA256 double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz" | sha256sum -c
|
||||
# verify fizz
|
||||
echo "$FIZZ_SHA256 fizz-$FBLIBS_VERSION.tar.gz" | sha256sum -c
|
||||
# verify flex
|
||||
if [ ! -f flex-$FLEX_VERSION.tar.gz.sig ]; then
|
||||
wget https://github.com/westes/flex/releases/download/v$FLEX_VERSION/flex-$FLEX_VERSION.tar.gz.sig
|
||||
fi
|
||||
if false; then
|
||||
$GPG --keyserver $KEYSERVER --recv-keys 0xE4B29C8D64885307
|
||||
$GPG --verify flex-$FLEX_VERSION.tar.gz.sig flex-$FLEX_VERSION.tar.gz
|
||||
fi
|
||||
# verify fmt
|
||||
echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c
|
||||
# verify spdlog
|
||||
@@ -1014,6 +1025,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
||||
if [ -d gflags ]; then
|
||||
rm -rf gflags
|
||||
fi
|
||||
|
||||
git clone https://github.com/memgraph/gflags.git gflags
|
||||
pushd gflags
|
||||
git checkout $GFLAGS_COMMIT_HASH
|
||||
@@ -1022,7 +1034,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
||||
cmake .. $COMMON_CMAKE_FLAGS \
|
||||
-DREGISTER_INSTALL_PREFIX=OFF \
|
||||
-DBUILD_gflags_nothreads_LIB=OFF \
|
||||
-DGFLAGS_NO_FILENAMES=1
|
||||
-DGFLAGS_NO_FILENAMES=0
|
||||
make -j$CPUS install
|
||||
popd && popd
|
||||
fi
|
||||
@@ -1220,6 +1232,18 @@ if false; then
|
||||
fi
|
||||
fi
|
||||
|
||||
log_tool_name "flex $FLEX_VERSION"
|
||||
if [ ! -f $PREFIX/include/FlexLexer.h ]; then
|
||||
if [ -d flex-$FLEX_VERSION ]; then
|
||||
rm -rf flex-$FLEX_VERSION
|
||||
fi
|
||||
tar -xzf ../archives/flex-$FLEX_VERSION.tar.gz
|
||||
pushd flex-$FLEX_VERSION
|
||||
./configure $COMMON_CONFIGURE_FLAGS
|
||||
make -j$CPUS install
|
||||
popd
|
||||
fi
|
||||
|
||||
popd
|
||||
# NOTE: It's important/clean (e.g., easier upload to S3) to have a separated
|
||||
# folder to the output archive.
|
||||
|
||||
@@ -326,6 +326,20 @@ inline mgp_vertex *graph_get_vertex_by_id(mgp_graph *g, mgp_vertex_id id, mgp_me
|
||||
return MgInvoke<mgp_vertex *>(mgp_graph_get_vertex_by_id, g, id, memory);
|
||||
}
|
||||
|
||||
inline bool graph_has_text_index(mgp_graph *graph, const char *index_name) {
|
||||
return MgInvoke<int>(mgp_graph_has_text_index, graph, index_name);
|
||||
}
|
||||
|
||||
inline mgp_map *graph_search_text_index(mgp_graph *graph, const char *index_name, const char *search_query,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_map *>(mgp_graph_search_text_index, graph, index_name, search_query, memory);
|
||||
}
|
||||
|
||||
inline mgp_map *graph_regex_search_text_index(mgp_graph *graph, const char *index_name, const char *search_query,
|
||||
mgp_memory *memory) {
|
||||
return MgInvoke<mgp_map *>(mgp_graph_regex_search_text_index, graph, index_name, search_query, memory);
|
||||
}
|
||||
|
||||
inline mgp_vertices_iterator *graph_iter_vertices(mgp_graph *g, mgp_memory *memory) {
|
||||
return MgInvoke<mgp_vertices_iterator *>(mgp_graph_iter_vertices, g, memory);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -891,6 +891,23 @@ enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *m
|
||||
enum mgp_error mgp_graph_get_vertex_by_id(struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory,
|
||||
struct mgp_vertex **result);
|
||||
|
||||
/// Result is non-zero if the index with the given name exists.
|
||||
/// Current implementation always returns without errors.
|
||||
enum mgp_error mgp_graph_has_text_index(struct mgp_graph *graph, const char *index_name, int *result);
|
||||
|
||||
/// Search the named text index for the given query. The result is a list of the vertices whose text properties match
|
||||
/// the given query.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate search result vertices.
|
||||
enum mgp_error mgp_graph_search_text_index(struct mgp_graph *graph, const char *index_name, const char *search_query,
|
||||
struct mgp_memory *memory, struct mgp_map **result);
|
||||
|
||||
/// Search the named text index for the given regex. The result is a list of the vertices whose text properties match
|
||||
/// the given query.
|
||||
/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate search result vertices.
|
||||
enum mgp_error mgp_graph_regex_search_text_index(struct mgp_graph *graph, const char *index_name,
|
||||
const char *search_query, struct mgp_memory *memory,
|
||||
struct mgp_map **result);
|
||||
|
||||
/// Creates label index for given label.
|
||||
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
|
||||
/// if label index already exists, result will be 0, otherwise 1.
|
||||
|
||||
@@ -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
|
||||
@@ -4306,12 +4306,12 @@ inline void AddParamsReturnsToProc(mgp_proc *proc, std::vector<Parameter> ¶m
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline bool CreateLabelIndex(mgp_graph *memgaph_graph, const std::string_view label) {
|
||||
return create_label_index(memgaph_graph, label.data());
|
||||
inline bool CreateLabelIndex(mgp_graph *memgraph_graph, const std::string_view label) {
|
||||
return create_label_index(memgraph_graph, label.data());
|
||||
}
|
||||
|
||||
inline bool DropLabelIndex(mgp_graph *memgaph_graph, const std::string_view label) {
|
||||
return drop_label_index(memgaph_graph, label.data());
|
||||
inline bool DropLabelIndex(mgp_graph *memgraph_graph, const std::string_view label) {
|
||||
return drop_label_index(memgraph_graph, label.data());
|
||||
}
|
||||
|
||||
inline List ListAllLabelIndices(mgp_graph *memgraph_graph) {
|
||||
@@ -4322,14 +4322,14 @@ inline List ListAllLabelIndices(mgp_graph *memgraph_graph) {
|
||||
return List(label_indices);
|
||||
}
|
||||
|
||||
inline bool CreateLabelPropertyIndex(mgp_graph *memgaph_graph, const std::string_view label,
|
||||
inline bool CreateLabelPropertyIndex(mgp_graph *memgraph_graph, const std::string_view label,
|
||||
const std::string_view property) {
|
||||
return create_label_property_index(memgaph_graph, label.data(), property.data());
|
||||
return create_label_property_index(memgraph_graph, label.data(), property.data());
|
||||
}
|
||||
|
||||
inline bool DropLabelPropertyIndex(mgp_graph *memgaph_graph, const std::string_view label,
|
||||
inline bool DropLabelPropertyIndex(mgp_graph *memgraph_graph, const std::string_view label,
|
||||
const std::string_view property) {
|
||||
return drop_label_property_index(memgaph_graph, label.data(), property.data());
|
||||
return drop_label_property_index(memgraph_graph, label.data(), property.data());
|
||||
}
|
||||
|
||||
inline List ListAllLabelPropertyIndices(mgp_graph *memgraph_graph) {
|
||||
@@ -4340,6 +4340,29 @@ inline List ListAllLabelPropertyIndices(mgp_graph *memgraph_graph) {
|
||||
return List(label_property_indices);
|
||||
}
|
||||
|
||||
inline List RunTextSearchQuery(mgp_graph *memgraph_graph, std::string_view index_name, std::string_view search_query) {
|
||||
auto results_or_error =
|
||||
Map(mgp::MemHandlerCallback(graph_search_text_index, memgraph_graph, index_name.data(), search_query.data()));
|
||||
auto maybe_error = results_or_error["error_msg"].ValueString();
|
||||
if (!maybe_error.empty()) {
|
||||
throw std::runtime_error{maybe_error.data()};
|
||||
}
|
||||
|
||||
return results_or_error["search_results"].ValueList();
|
||||
}
|
||||
|
||||
inline List RunTextRegexSearchQuery(mgp_graph *memgraph_graph, std::string_view index_name,
|
||||
std::string_view search_query) {
|
||||
auto results_or_error = Map(
|
||||
mgp::MemHandlerCallback(graph_regex_search_text_index, memgraph_graph, index_name.data(), search_query.data()));
|
||||
auto maybe_error = results_or_error["error_msg"].ValueString();
|
||||
if (!maybe_error.empty()) {
|
||||
throw std::runtime_error{maybe_error.data()};
|
||||
}
|
||||
|
||||
return results_or_error["search_results"].ValueList();
|
||||
}
|
||||
|
||||
inline bool CreateExistenceConstraint(mgp_graph *memgraph_graph, const std::string_view label,
|
||||
const std::string_view property) {
|
||||
return create_existence_constraint(memgraph_graph, label.data(), property.data());
|
||||
|
||||
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@@ -7,4 +7,3 @@
|
||||
!pulsar.patch
|
||||
!antlr4.10.1.patch
|
||||
!rocksdb8.1.1.patch
|
||||
!nuraft2.1.0.patch
|
||||
|
||||
@@ -16,7 +16,7 @@ set(GFLAGS_NOTHREADS OFF)
|
||||
|
||||
# NOTE: config/generate.py depends on the gflags help XML format.
|
||||
find_package(gflags REQUIRED)
|
||||
find_package(fmt 8.0.1 REQUIRED)
|
||||
find_package(fmt 8.0.1)
|
||||
find_package(ZLIB 1.2.11 REQUIRED)
|
||||
|
||||
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -295,6 +295,40 @@ set_path_external_library(jemalloc STATIC
|
||||
|
||||
import_header_library(rangev3 ${CMAKE_CURRENT_SOURCE_DIR}/rangev3/include)
|
||||
|
||||
if(NOT DEFINED MGCXX_GIT_TAG)
|
||||
set(MGCXX_GIT_TAG "v0.0.3" CACHE STRING "mgcxx git tag")
|
||||
else()
|
||||
set(MGCXX_GIT_TAG "${MGCXX_GIT_TAG}" CACHE STRING "mgcxx git tag")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(mgcxx-proj
|
||||
PREFIX mgcxx-proj
|
||||
GIT_REPOSITORY https://github.com/memgraph/mgcxx
|
||||
GIT_TAG "origin/regex-queries"
|
||||
CMAKE_ARGS
|
||||
"-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>"
|
||||
"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}"
|
||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DENABLE_TESTS=OFF"
|
||||
INSTALL_DIR "${PROJECT_BINARY_DIR}/mgcxx"
|
||||
)
|
||||
ExternalProject_Get_Property(mgcxx-proj install_dir)
|
||||
set(MGCXX_ROOT ${install_dir})
|
||||
|
||||
add_library(tantivy_text_search STATIC IMPORTED GLOBAL)
|
||||
add_dependencies(tantivy_text_search mgcxx-proj)
|
||||
set_property(TARGET tantivy_text_search PROPERTY IMPORTED_LOCATION ${MGCXX_ROOT}/lib/libtantivy_text_search.a)
|
||||
|
||||
add_library(mgcxx_text_search STATIC IMPORTED GLOBAL)
|
||||
add_dependencies(mgcxx_text_search mgcxx-proj)
|
||||
set_property(TARGET mgcxx_text_search PROPERTY IMPORTED_LOCATION ${MGCXX_ROOT}/lib/libmgcxx_text_search.a)
|
||||
# We need to create the include directory first in order to be able to add it
|
||||
# as an include directory. The header files in the include directory will be
|
||||
# generated later during the build process.
|
||||
file(MAKE_DIRECTORY ${MGCXX_ROOT}/include)
|
||||
set_property(TARGET mgcxx_text_search PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MGCXX_ROOT}/include)
|
||||
|
||||
# Setup NuRaft
|
||||
import_external_library(nuraft STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nuraft/lib/libnuraft.a
|
||||
|
||||
@@ -5,7 +5,7 @@ index ee9b58c..31359a9 100644
|
||||
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
|
||||
# | Library Build and Install Properties |
|
||||
# +--------------------------------------------------------+
|
||||
|
||||
|
||||
-add_library(rdtsc SHARED
|
||||
+add_library(rdtsc
|
||||
src/cycles.c
|
||||
@@ -14,7 +14,7 @@ index ee9b58c..31359a9 100644
|
||||
@@ -72,15 +72,6 @@ target_include_directories(rdtsc
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
|
||||
-# Install directory changes depending on build mode
|
||||
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
||||
- # During debug, the library will be installed into a local directory
|
||||
@@ -27,15 +27,3 @@ index ee9b58c..31359a9 100644
|
||||
# Specifying what to export when installing (GNUInstallDirs required)
|
||||
install(TARGETS rdtsc
|
||||
EXPORT librstsc-config
|
||||
diff --git a/include/librdtsc/common_timer.h b/include/librdtsc/common_timer.h
|
||||
index a6922d8..080dc77 100644
|
||||
--- a/include/librdtsc/common_timer.h
|
||||
+++ b/include/librdtsc/common_timer.h
|
||||
@@ -2,6 +2,7 @@
|
||||
#define LIBRDTSC_COMMON_TIMER_H
|
||||
|
||||
#include <librdtsc/common.h>
|
||||
+#include <librdtsc/cycles.h>
|
||||
|
||||
extern uint64_t rdtsc_get_tsc_freq_arch();
|
||||
extern uint64_t rdtsc_get_tsc_freq();
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
diff --git a/include/libnuraft/asio_service_options.hxx b/include/libnuraft/asio_service_options.hxx
|
||||
index 8fe1ec9..9497355 100644
|
||||
--- a/include/libnuraft/asio_service_options.hxx
|
||||
+++ b/include/libnuraft/asio_service_options.hxx
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
+#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
diff --git a/include/libnuraft/callback.hxx b/include/libnuraft/callback.hxx
|
||||
index 7b71624..d48c1e2 100644
|
||||
--- a/include/libnuraft/callback.hxx
|
||||
+++ b/include/libnuraft/callback.hxx
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
#ifndef _CALLBACK_H_
|
||||
#define _CALLBACK_H_
|
||||
|
||||
+#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
21
libs/rocksdb.patch
Normal file
21
libs/rocksdb.patch
Normal file
@@ -0,0 +1,21 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 6761929..6a369af 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -220,6 +220,7 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -momit-leaf-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy -Wno-unused-but-set-variable")
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
@@ -997,7 +998,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
install(
|
||||
- TARGETS ${ROCKSDB_SHARED_LIB}
|
||||
+ TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL
|
||||
EXPORT RocksDBTargets
|
||||
COMPONENT runtime
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
@@ -127,6 +127,7 @@ declare -A primary_urls=(
|
||||
["jemalloc"]="http://$local_cache_host/git/jemalloc.git"
|
||||
["range-v3"]="http://$local_cache_host/git/range-v3.git"
|
||||
["nuraft"]="http://$local_cache_host/git/NuRaft.git"
|
||||
["zstd"]="http://$local_cache_host/git/zstd.git"
|
||||
)
|
||||
|
||||
# The goal of secondary urls is to have links to the "source of truth" of
|
||||
@@ -157,6 +158,7 @@ declare -A secondary_urls=(
|
||||
["jemalloc"]="https://github.com/jemalloc/jemalloc.git"
|
||||
["range-v3"]="https://github.com/ericniebler/range-v3.git"
|
||||
["nuraft"]="https://github.com/eBay/NuRaft.git"
|
||||
["zstd"]="https://github.com/facebook/zstd.git"
|
||||
)
|
||||
|
||||
# antlr
|
||||
@@ -168,11 +170,12 @@ pushd antlr4
|
||||
git apply ../antlr4.10.1.patch
|
||||
popd
|
||||
|
||||
cppitertools_ref="v2.1" # 2021-01-15
|
||||
# cppitertools v2.0 2019-12-23
|
||||
cppitertools_ref="cb3635456bdb531121b82b4d2e3afc7ae1f56d47"
|
||||
repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref"
|
||||
|
||||
# rapidcheck
|
||||
rapidcheck_tag="1c91f40e64d87869250cfb610376c629307bf77d" # (2023-08-15)
|
||||
rapidcheck_tag="7bc7d302191a4f3d0bf005692677126136e02f60" # (2020-05-04)
|
||||
repo_clone_try_double "${primary_urls[rapidcheck]}" "${secondary_urls[rapidcheck]}" "rapidcheck" "$rapidcheck_tag"
|
||||
|
||||
# google benchmark
|
||||
@@ -220,7 +223,7 @@ repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient
|
||||
mgconsole_tag="v1.4.0" # (2023-05-21)
|
||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
|
||||
|
||||
spdlog_tag="v1.12.0" # (2022-11-02)
|
||||
spdlog_tag="v1.9.2" # (2021-08-12)
|
||||
repo_clone_try_double "${primary_urls[spdlog]}" "${secondary_urls[spdlog]}" "spdlog" "$spdlog_tag" true
|
||||
|
||||
# librdkafka
|
||||
@@ -285,6 +288,9 @@ repo_clone_try_double "${primary_urls[range-v3]}" "${secondary_urls[range-v3]}"
|
||||
nuraft_tag="v2.1.0"
|
||||
repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true
|
||||
pushd nuraft
|
||||
git apply ../nuraft2.1.0.patch
|
||||
./prepare.sh
|
||||
popd
|
||||
|
||||
# zstd
|
||||
zstd_tag="v1.5.5"
|
||||
repo_clone_try_double "${primary_urls[zstd]}" "${secondary_urls[zstd]}" "zstd" "$zstd_tag" true
|
||||
|
||||
@@ -58,6 +58,22 @@ install(PROGRAMS $<TARGET_FILE:schema>
|
||||
# Also install the source of the example, so user can read it.
|
||||
install(FILES schema.cpp DESTINATION lib/memgraph/query_modules/src)
|
||||
|
||||
add_library(text SHARED text_search_module.cpp)
|
||||
target_include_directories(text PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
target_compile_options(text PRIVATE -Wall)
|
||||
target_link_libraries(text PRIVATE -static-libgcc -static-libstdc++)
|
||||
# Strip C++ example in release build.
|
||||
if (lower_build_type STREQUAL "release")
|
||||
add_custom_command(TARGET text POST_BUILD
|
||||
COMMAND strip -s $<TARGET_FILE:text>
|
||||
COMMENT "Stripping symbols and sections from the C++ text_search module")
|
||||
endif()
|
||||
install(PROGRAMS $<TARGET_FILE:text>
|
||||
DESTINATION lib/memgraph/query_modules
|
||||
RENAME text.so)
|
||||
# Also install the source of the example, so user can read it.
|
||||
install(FILES text_search_module.cpp DESTINATION lib/memgraph/query_modules/src)
|
||||
|
||||
# Install the Python example and modules
|
||||
install(FILES example.py DESTINATION lib/memgraph/query_modules RENAME py_example.py)
|
||||
install(FILES graph_analyzer.py DESTINATION lib/memgraph/query_modules)
|
||||
|
||||
105
query_modules/text_search_module.cpp
Normal file
105
query_modules/text_search_module.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <mgp.hpp>
|
||||
|
||||
namespace TextSearch {
|
||||
constexpr std::string_view kProcedureSearch = "search";
|
||||
constexpr std::string_view kProcedureRegexSearch = "regex_search";
|
||||
constexpr std::string_view kParameterIndexName = "index_name";
|
||||
constexpr std::string_view kParameterSearchString = "search_query";
|
||||
constexpr std::string_view kReturnNode = "node";
|
||||
|
||||
void Search(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
|
||||
void RegexSearch(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
|
||||
} // namespace TextSearch
|
||||
|
||||
void TextSearch::Search(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
mgp::MemoryDispatcherGuard guard{memory};
|
||||
const auto record_factory = mgp::RecordFactory(result);
|
||||
auto arguments = mgp::List(args);
|
||||
|
||||
try {
|
||||
const auto *index_name = arguments[0].ValueString().data();
|
||||
const auto *search_query = arguments[1].ValueString().data();
|
||||
|
||||
// 1. See if the given index_name is text-indexed
|
||||
if (!mgp::graph_has_text_index(memgraph_graph, index_name)) {
|
||||
record_factory.SetErrorMessage(fmt::format("Text index \"{}\" doesn’t exist.", index_name));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Run a text search of that index and return the search results
|
||||
for (const auto &node : mgp::RunTextSearchQuery(memgraph_graph, index_name, search_query)) {
|
||||
auto record = record_factory.NewRecord();
|
||||
record.Insert(TextSearch::kReturnNode.data(), node.ValueNode());
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
record_factory.SetErrorMessage(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void TextSearch::RegexSearch(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||
mgp::MemoryDispatcherGuard guard{memory};
|
||||
const auto record_factory = mgp::RecordFactory(result);
|
||||
auto arguments = mgp::List(args);
|
||||
|
||||
try {
|
||||
const auto *index_name = arguments[0].ValueString().data();
|
||||
const auto *search_query = arguments[1].ValueString().data();
|
||||
|
||||
// 1. See if the given index_name is text-indexed
|
||||
if (!mgp::graph_has_text_index(memgraph_graph, index_name)) {
|
||||
record_factory.SetErrorMessage(fmt::format("Text index \"{}\" doesn’t exist.", index_name));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Run a text search of that index and return the search results
|
||||
for (const auto &node : mgp::RunTextRegexSearchQuery(memgraph_graph, index_name, search_query)) {
|
||||
auto record = record_factory.NewRecord();
|
||||
record.Insert(TextSearch::kReturnNode.data(), node.ValueNode());
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
record_factory.SetErrorMessage(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
|
||||
try {
|
||||
mgp::MemoryDispatcherGuard guard{memory};
|
||||
|
||||
AddProcedure(TextSearch::Search, TextSearch::kProcedureSearch, mgp::ProcedureType::Read,
|
||||
{
|
||||
mgp::Parameter(TextSearch::kParameterIndexName, mgp::Type::String),
|
||||
mgp::Parameter(TextSearch::kParameterSearchString, mgp::Type::String),
|
||||
},
|
||||
{mgp::Return(TextSearch::kReturnNode, mgp::Type::Node)}, module, memory);
|
||||
|
||||
AddProcedure(TextSearch::RegexSearch, TextSearch::kProcedureRegexSearch, mgp::ProcedureType::Read,
|
||||
{
|
||||
mgp::Parameter(TextSearch::kParameterIndexName, mgp::Type::String),
|
||||
mgp::Parameter(TextSearch::kParameterSearchString, mgp::Type::String),
|
||||
},
|
||||
{mgp::Return(TextSearch::kReturnNode, mgp::Type::Node)}, module, memory);
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Error while initializing query module: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int mgp_shutdown_module() { return 0; }
|
||||
@@ -45,7 +45,7 @@ set(mg_single_node_v2_sources
|
||||
add_executable(memgraph ${mg_single_node_v2_sources})
|
||||
target_include_directories(memgraph PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(memgraph stdc++fs Threads::Threads
|
||||
mg-telemetry mg-communication mg-communication-metrics mg-memory mg-utils mg-license mg-settings mg-glue mg-flags mg::system mg::replication_handler)
|
||||
mg-telemetry mgcxx_text_search tantivy_text_search mg-communication mg-communication-metrics mg-memory mg-utils mg-license mg-settings mg-glue mg-flags mg::system mg::replication_handler)
|
||||
|
||||
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
|
||||
# should be dynamically exported, so that `dlopen` can correctly link th
|
||||
|
||||
@@ -35,42 +35,16 @@ DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth m
|
||||
}
|
||||
return true;
|
||||
});
|
||||
DEFINE_bool(auth_module_create_missing_user, true, "Set to false to disable creation of missing users.");
|
||||
DEFINE_bool(auth_module_create_missing_role, true, "Set to false to disable creation of missing roles.");
|
||||
DEFINE_bool(auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.");
|
||||
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
|
||||
"Timeout (in milliseconds) used when waiting for a "
|
||||
"response from the auth module.",
|
||||
FLAG_IN_RANGE(100, 1800000));
|
||||
|
||||
// DEPRECATED FLAGS
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_user, true,
|
||||
"Set to false to disable creation of missing users.", {
|
||||
spdlog::warn(
|
||||
"auth_module_create_missing_user flag is deprecated. It not possible to create "
|
||||
"users through the module anymore.");
|
||||
return true;
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_role, true,
|
||||
"Set to false to disable creation of missing roles.", {
|
||||
spdlog::warn(
|
||||
"auth_module_create_missing_role flag is deprecated. It not possible to create "
|
||||
"roles through the module anymore.");
|
||||
return true;
|
||||
});
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||
DEFINE_VALIDATED_HIDDEN_bool(
|
||||
auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.", {
|
||||
spdlog::warn(
|
||||
"auth_module_manage_roles flag is deprecated. It not possible to create roles through the module anymore.");
|
||||
return true;
|
||||
});
|
||||
|
||||
namespace memgraph::auth {
|
||||
|
||||
const Auth::Epoch Auth::kStartEpoch = 1;
|
||||
|
||||
namespace {
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
@@ -218,17 +192,6 @@ void MigrateVersions(kvstore::KVStore &store) {
|
||||
version_str = kVersionV1;
|
||||
}
|
||||
}
|
||||
|
||||
auto ParseJson(std::string_view str) {
|
||||
nlohmann::json data;
|
||||
try {
|
||||
data = nlohmann::json::parse(str);
|
||||
} catch (const nlohmann::json::parse_error &e) {
|
||||
throw AuthException("Couldn't load auth data!");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
Auth::Auth(std::string storage_directory, Config config)
|
||||
@@ -236,11 +199,8 @@ Auth::Auth(std::string storage_directory, Config config)
|
||||
MigrateVersions(storage_);
|
||||
}
|
||||
|
||||
std::optional<UserOrRole> Auth::Authenticate(const std::string &username, const std::string &password) {
|
||||
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) {
|
||||
if (module_.IsUsed()) {
|
||||
/*
|
||||
* MODULE AUTH STORAGE
|
||||
*/
|
||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||
if (license_check_result.HasError()) {
|
||||
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
||||
@@ -265,64 +225,108 @@ std::optional<UserOrRole> Auth::Authenticate(const std::string &username, const
|
||||
auto is_authenticated = ret_authenticated.get<bool>();
|
||||
const auto &rolename = ret_role.get<std::string>();
|
||||
|
||||
// Check if role is present
|
||||
auto role = GetRole(rolename);
|
||||
if (!role) {
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the role '{}' doesn't exist.",
|
||||
username, rolename, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Authenticate the user.
|
||||
if (!is_authenticated) return std::nullopt;
|
||||
|
||||
return RoleWUsername{username, std::move(*role)};
|
||||
}
|
||||
/**
|
||||
* TODO
|
||||
* The auth module should not update auth data.
|
||||
* There is now way to replicate it and we should not be storing sensitive data if we don't have to.
|
||||
*/
|
||||
|
||||
/*
|
||||
* LOCAL AUTH STORAGE
|
||||
*/
|
||||
auto user = GetUser(username);
|
||||
if (!user) {
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
||||
"https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!user->CheckPassword(password)) {
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (user->UpgradeHash(password)) {
|
||||
// Find or create the user and return it.
|
||||
auto user = GetUser(username);
|
||||
if (!user) {
|
||||
if (FLAGS_auth_module_create_missing_user) {
|
||||
user = AddUser(username, password);
|
||||
if (!user) {
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't create the missing user '{}' using the auth module because the user already exists as a role.",
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
|
||||
"https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
UpdatePassword(*user, password);
|
||||
}
|
||||
if (FLAGS_auth_module_manage_roles) {
|
||||
if (!rolename.empty()) {
|
||||
auto role = GetRole(rolename);
|
||||
if (!role) {
|
||||
if (FLAGS_auth_module_create_missing_role) {
|
||||
role = AddRole(rolename);
|
||||
if (!role) {
|
||||
spdlog::warn(
|
||||
utils::MessageWithLink("Couldn't authenticate user '{}' using the auth module because the user's "
|
||||
"role '{}' already exists as a user.",
|
||||
username, rolename, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
SaveRole(*role);
|
||||
} else {
|
||||
spdlog::warn(utils::MessageWithLink(
|
||||
"Couldn't authenticate user '{}' using the auth module because the user's role '{}' doesn't exist.",
|
||||
username, rolename, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
user->SetRole(*role);
|
||||
} else {
|
||||
user->ClearRole();
|
||||
}
|
||||
}
|
||||
SaveUser(*user);
|
||||
}
|
||||
return user;
|
||||
} else {
|
||||
auto user = GetUser(username);
|
||||
if (!user) {
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
||||
"https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!user->CheckPassword(password)) {
|
||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (user->UpgradeHash(password)) {
|
||||
SaveUser(*user);
|
||||
}
|
||||
|
||||
return user;
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
void Auth::LinkUser(User &user) const {
|
||||
auto link = storage_.Get(kLinkPrefix + user.username());
|
||||
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
||||
auto username = utils::ToLowerCase(username_orig);
|
||||
auto existing_user = storage_.Get(kUserPrefix + username);
|
||||
if (!existing_user) return std::nullopt;
|
||||
|
||||
nlohmann::json data;
|
||||
try {
|
||||
data = nlohmann::json::parse(*existing_user);
|
||||
} catch (const nlohmann::json::parse_error &e) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
|
||||
auto user = User::Deserialize(data);
|
||||
auto link = storage_.Get(kLinkPrefix + username);
|
||||
|
||||
if (link) {
|
||||
auto role = GetRole(*link);
|
||||
if (role) {
|
||||
user.SetRole(*role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
||||
if (module_.IsUsed()) return std::nullopt; // User's are not supported when using module
|
||||
auto username = utils::ToLowerCase(username_orig);
|
||||
auto existing_user = storage_.Get(kUserPrefix + username);
|
||||
if (!existing_user) return std::nullopt;
|
||||
|
||||
auto user = User::Deserialize(ParseJson(*existing_user));
|
||||
LinkUser(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||
DisableIfModuleUsed();
|
||||
bool success = false;
|
||||
if (const auto *role = user.role(); role != nullptr) {
|
||||
success = storage_.PutMultiple(
|
||||
@@ -334,10 +338,6 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||
if (!success) {
|
||||
throw AuthException("Couldn't save user '{}'!", user.username());
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// All changes to the user end up calling this function, so no need to add a delta anywhere else
|
||||
if (system_tx) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -347,7 +347,6 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||
}
|
||||
|
||||
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
|
||||
DisableIfModuleUsed();
|
||||
// Check if null
|
||||
if (!password) {
|
||||
if (!config_.password_permit_null) {
|
||||
@@ -379,7 +378,6 @@ void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &pa
|
||||
|
||||
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password,
|
||||
system::Transaction *system_tx) {
|
||||
DisableIfModuleUsed();
|
||||
if (!NameRegexMatch(username)) {
|
||||
throw AuthException("Invalid user name.");
|
||||
}
|
||||
@@ -394,17 +392,12 @@ std::optional<User> Auth::AddUser(const std::string &username, const std::option
|
||||
}
|
||||
|
||||
bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) {
|
||||
DisableIfModuleUsed();
|
||||
auto username = utils::ToLowerCase(username_orig);
|
||||
if (!storage_.Get(kUserPrefix + username)) return false;
|
||||
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove user '{}'!", username);
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// Handling drop user delta
|
||||
if (system_tx) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -419,12 +412,9 @@ std::vector<auth::User> Auth::AllUsers() const {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
if (username != utils::ToLowerCase(username)) continue;
|
||||
try {
|
||||
User user = auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
LinkUser(user);
|
||||
ret.emplace_back(std::move(user));
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
auto user = GetUser(username);
|
||||
if (user) {
|
||||
ret.push_back(std::move(*user));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -435,12 +425,9 @@ std::vector<std::string> Auth::AllUsernames() const {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
if (username != utils::ToLowerCase(username)) continue;
|
||||
try {
|
||||
// Check if serialized correctly
|
||||
memgraph::auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
ret.emplace_back(std::move(username));
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
auto user = GetUser(username);
|
||||
if (user) {
|
||||
ret.push_back(username);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -448,24 +435,25 @@ std::vector<std::string> Auth::AllUsernames() const {
|
||||
|
||||
bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); }
|
||||
|
||||
bool Auth::AccessControlled() const { return HasUsers() || module_.IsUsed(); }
|
||||
|
||||
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
||||
auto rolename = utils::ToLowerCase(rolename_orig);
|
||||
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
||||
if (!existing_role) return std::nullopt;
|
||||
|
||||
return Role::Deserialize(ParseJson(*existing_role));
|
||||
nlohmann::json data;
|
||||
try {
|
||||
data = nlohmann::json::parse(*existing_role);
|
||||
} catch (const nlohmann::json::parse_error &e) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
|
||||
return Role::Deserialize(data);
|
||||
}
|
||||
|
||||
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
|
||||
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
||||
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// All changes to the role end up calling this function, so no need to add a delta anywhere else
|
||||
if (system_tx) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -498,10 +486,6 @@ bool Auth::RemoveRole(const std::string &rolename_orig, system::Transaction *sys
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove role '{}'!", rolename);
|
||||
}
|
||||
|
||||
// Durability updated -> new epoch
|
||||
UpdateEpoch();
|
||||
|
||||
// Handling drop role delta
|
||||
if (system_tx) {
|
||||
#ifdef MG_ENTERPRISE
|
||||
@@ -516,8 +500,11 @@ std::vector<auth::Role> Auth::AllRoles() const {
|
||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||
auto rolename = it->first.substr(kRolePrefix.size());
|
||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||
Role role = memgraph::auth::Role::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||
ret.emplace_back(std::move(role));
|
||||
if (auto role = GetRole(rolename)) {
|
||||
ret.push_back(*role);
|
||||
} else {
|
||||
throw AuthException("Couldn't load role '{}'!", rolename);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -527,19 +514,14 @@ std::vector<std::string> Auth::AllRolenames() const {
|
||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||
auto rolename = it->first.substr(kRolePrefix.size());
|
||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||
try {
|
||||
// Check that the data is serialized correctly
|
||||
memgraph::auth::Role::Deserialize(ParseJson(it->second));
|
||||
ret.emplace_back(std::move(rolename));
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
if (auto role = GetRole(rolename)) {
|
||||
ret.push_back(rolename);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
||||
DisableIfModuleUsed();
|
||||
const auto rolename = utils::ToLowerCase(rolename_orig);
|
||||
std::vector<auth::User> ret;
|
||||
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
||||
@@ -558,176 +540,51 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
Auth::Result Auth::GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
GrantDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_ROLE;
|
||||
}
|
||||
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
if (auto user = GetUser(name)) {
|
||||
GrantDatabase(db, *user, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (auto role = GetRole(name)) {
|
||||
GrantDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_USER_ROLE;
|
||||
}
|
||||
|
||||
void Auth::GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
user.db_access().GrantAll();
|
||||
} else {
|
||||
user.db_access().Grant(db);
|
||||
}
|
||||
SaveUser(user, system_tx);
|
||||
}
|
||||
|
||||
void Auth::GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
role.db_access().GrantAll();
|
||||
} else {
|
||||
role.db_access().Grant(db);
|
||||
}
|
||||
SaveRole(role, system_tx);
|
||||
}
|
||||
|
||||
Auth::Result Auth::DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
DenyDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().GrantAll();
|
||||
} else {
|
||||
user->db_access().Add(db);
|
||||
}
|
||||
return NO_ROLE;
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
if (auto user = GetUser(name)) {
|
||||
DenyDatabase(db, *user, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (auto role = GetRole(name)) {
|
||||
DenyDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_USER_ROLE;
|
||||
}
|
||||
|
||||
void Auth::DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
user.db_access().DenyAll();
|
||||
} else {
|
||||
user.db_access().Deny(db);
|
||||
}
|
||||
SaveUser(user, system_tx);
|
||||
}
|
||||
|
||||
void Auth::DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
role.db_access().DenyAll();
|
||||
} else {
|
||||
role.db_access().Deny(db);
|
||||
}
|
||||
SaveRole(role, system_tx);
|
||||
}
|
||||
|
||||
Auth::Result Auth::RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
RevokeDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
if (db == kAllDatabases) {
|
||||
user->db_access().DenyAll();
|
||||
} else {
|
||||
user->db_access().Remove(db);
|
||||
}
|
||||
return NO_ROLE;
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
}
|
||||
if (auto user = GetUser(name)) {
|
||||
RevokeDatabase(db, *user, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (auto role = GetRole(name)) {
|
||||
RevokeDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_USER_ROLE;
|
||||
}
|
||||
|
||||
void Auth::RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
user.db_access().RevokeAll();
|
||||
} else {
|
||||
user.db_access().Revoke(db);
|
||||
}
|
||||
SaveUser(user, system_tx);
|
||||
}
|
||||
|
||||
void Auth::RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||
if (db == kAllDatabases) {
|
||||
role.db_access().RevokeAll();
|
||||
} else {
|
||||
role.db_access().Revoke(db);
|
||||
}
|
||||
SaveRole(role, system_tx);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
|
||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||
auto username = it->first.substr(kUserPrefix.size());
|
||||
try {
|
||||
User user = auth::User::Deserialize(ParseJson(it->second));
|
||||
LinkUser(user);
|
||||
user.db_access().Revoke(db);
|
||||
SaveUser(user, system_tx);
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||
auto rolename = it->first.substr(kRolePrefix.size());
|
||||
try {
|
||||
auto role = memgraph::auth::Role::Deserialize(ParseJson(it->second));
|
||||
role.db_access().Revoke(db);
|
||||
SaveRole(role, system_tx);
|
||||
} catch (AuthException &) {
|
||||
continue;
|
||||
if (auto user = GetUser(username)) {
|
||||
user->db_access().Delete(db);
|
||||
SaveUser(*user, system_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Auth::Result Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
||||
using enum Auth::Result;
|
||||
if (module_.IsUsed()) {
|
||||
if (auto role = GetRole(name)) {
|
||||
SetMainDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_ROLE;
|
||||
}
|
||||
bool Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
||||
if (auto user = GetUser(name)) {
|
||||
SetMainDatabase(db, *user, system_tx);
|
||||
return SUCCESS;
|
||||
if (!user->db_access().SetDefault(db)) {
|
||||
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
||||
}
|
||||
SaveUser(*user, system_tx);
|
||||
return true;
|
||||
}
|
||||
if (auto role = GetRole(name)) {
|
||||
SetMainDatabase(db, *role, system_tx);
|
||||
return SUCCESS;
|
||||
}
|
||||
return NO_USER_ROLE;
|
||||
}
|
||||
|
||||
void Auth::SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx) {
|
||||
if (!user.db_access().SetMain(db)) {
|
||||
throw AuthException("Couldn't set default database '{}' for '{}'!", db, user.username());
|
||||
}
|
||||
SaveUser(user, system_tx);
|
||||
}
|
||||
|
||||
void Auth::SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx) {
|
||||
if (!role.db_access().SetMain(db)) {
|
||||
throw AuthException("Couldn't set default database '{}' for '{}'!", db, role.rolename());
|
||||
}
|
||||
SaveRole(role, system_tx);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -29,18 +29,6 @@ using SynchedAuth = memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph
|
||||
|
||||
static const constexpr char *const kAllDatabases = "*";
|
||||
|
||||
struct RoleWUsername : Role {
|
||||
template <typename... Args>
|
||||
RoleWUsername(std::string_view username, Args &&...args) : Role{std::forward<Args>(args)...}, username_{username} {}
|
||||
|
||||
std::string username() { return username_; }
|
||||
const std::string &username() const { return username_; }
|
||||
|
||||
private:
|
||||
std::string username_;
|
||||
};
|
||||
using UserOrRole = std::variant<User, RoleWUsername>;
|
||||
|
||||
/**
|
||||
* This class serves as the main Authentication/Authorization storage.
|
||||
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
||||
@@ -73,25 +61,6 @@ class Auth final {
|
||||
std::regex password_regex{password_regex_str};
|
||||
};
|
||||
|
||||
struct Epoch {
|
||||
Epoch() : epoch_{0} {}
|
||||
Epoch(unsigned e) : epoch_{e} {}
|
||||
|
||||
Epoch operator++() { return ++epoch_; }
|
||||
bool operator==(const Epoch &rhs) const = default;
|
||||
|
||||
private:
|
||||
unsigned epoch_;
|
||||
};
|
||||
|
||||
static const Epoch kStartEpoch;
|
||||
|
||||
enum class Result {
|
||||
SUCCESS,
|
||||
NO_USER_ROLE,
|
||||
NO_ROLE,
|
||||
};
|
||||
|
||||
explicit Auth(std::string storage_directory, Config config);
|
||||
|
||||
/**
|
||||
@@ -120,7 +89,7 @@ class Auth final {
|
||||
* @return a user when the username and password match, nullopt otherwise
|
||||
* @throw AuthException if unable to authenticate for whatever reason.
|
||||
*/
|
||||
std::optional<UserOrRole> Authenticate(const std::string &username, const std::string &password);
|
||||
std::optional<User> Authenticate(const std::string &username, const std::string &password);
|
||||
|
||||
/**
|
||||
* Gets a user from the storage.
|
||||
@@ -132,8 +101,6 @@ class Auth final {
|
||||
*/
|
||||
std::optional<User> GetUser(const std::string &username) const;
|
||||
|
||||
void LinkUser(User &user) const;
|
||||
|
||||
/**
|
||||
* Saves a user object to the storage.
|
||||
*
|
||||
@@ -196,13 +163,6 @@ class Auth final {
|
||||
*/
|
||||
bool HasUsers() const;
|
||||
|
||||
/**
|
||||
* Returns whether the access is controlled by authentication/authorization.
|
||||
*
|
||||
* @return `true` if auth needs to run
|
||||
*/
|
||||
bool AccessControlled() const;
|
||||
|
||||
/**
|
||||
* Gets a role from the storage.
|
||||
*
|
||||
@@ -213,37 +173,6 @@ class Auth final {
|
||||
*/
|
||||
std::optional<Role> GetRole(const std::string &rolename) const;
|
||||
|
||||
std::optional<UserOrRole> GetUserOrRole(const std::optional<std::string> &username,
|
||||
const std::optional<std::string> &rolename) const {
|
||||
auto expect = [](bool condition, std::string &&msg) {
|
||||
if (!condition) throw AuthException(std::move(msg));
|
||||
};
|
||||
// Special case if we are using a module; we must find the specified role
|
||||
if (module_.IsUsed()) {
|
||||
expect(username && rolename, "When using a module, a role needs to be connected to a username.");
|
||||
const auto role = GetRole(*rolename);
|
||||
expect(role != std::nullopt, "No role named " + *rolename);
|
||||
return UserOrRole(auth::RoleWUsername{*username, *role});
|
||||
}
|
||||
|
||||
// First check if we need to find a role
|
||||
if (username && rolename) {
|
||||
const auto role = GetRole(*rolename);
|
||||
expect(role != std::nullopt, "No role named " + *rolename);
|
||||
return UserOrRole(auth::RoleWUsername{*username, *role});
|
||||
}
|
||||
|
||||
// We are only looking for a user
|
||||
if (username) {
|
||||
const auto user = GetUser(*username);
|
||||
expect(user != std::nullopt, "No user named " + *username);
|
||||
return *user;
|
||||
}
|
||||
|
||||
// No user or role
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a role object to the storage.
|
||||
*
|
||||
@@ -300,6 +229,16 @@ class Auth final {
|
||||
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/**
|
||||
* @brief Revoke access to individual database for a user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
bool RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Grant access to individual database for a user.
|
||||
*
|
||||
@@ -308,33 +247,7 @@ class Auth final {
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
Result GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
void GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||
void GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Revoke access to individual database for a user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
Result DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
void DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||
void DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Revoke access to individual database for a user.
|
||||
*
|
||||
* @param db name of the database to revoke
|
||||
* @param name user's username
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
Result RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
void RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||
void RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||
bool GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Delete a database from all users.
|
||||
@@ -352,17 +265,9 @@ class Auth final {
|
||||
* @return true on success
|
||||
* @throw AuthException if unable to find or update the user
|
||||
*/
|
||||
Result SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
void SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx = nullptr);
|
||||
void SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx = nullptr);
|
||||
bool SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||
#endif
|
||||
|
||||
bool UpToDate(Epoch &e) const {
|
||||
bool res = e == epoch_;
|
||||
e = epoch_;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief
|
||||
@@ -373,18 +278,11 @@ class Auth final {
|
||||
*/
|
||||
bool NameRegexMatch(const std::string &user_or_role) const;
|
||||
|
||||
void UpdateEpoch() { ++epoch_; }
|
||||
|
||||
void DisableIfModuleUsed() const {
|
||||
if (module_.IsUsed()) throw AuthException("Operation not permited when using an authentication module.");
|
||||
}
|
||||
|
||||
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
|
||||
// Auth is not thread-safe because modifying users and roles might require
|
||||
// more than one operation on the storage.
|
||||
kvstore::KVStore storage_;
|
||||
auth::Module module_;
|
||||
Config config_;
|
||||
Epoch epoch_{kStartEpoch};
|
||||
};
|
||||
} // namespace memgraph::auth
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <json/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <json/json.hpp>
|
||||
|
||||
namespace memgraph::auth {
|
||||
/// Need to be stable, auth durability depends on this
|
||||
enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 };
|
||||
|
||||
@@ -425,11 +425,10 @@ Role::Role(const std::string &rolename, const Permissions &permissions)
|
||||
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
||||
#ifdef MG_ENTERPRISE
|
||||
Role::Role(const std::string &rolename, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
|
||||
FineGrainedAccessHandler fine_grained_access_handler)
|
||||
: rolename_(utils::ToLowerCase(rolename)),
|
||||
permissions_(permissions),
|
||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
|
||||
db_access_(std::move(db_access)) {}
|
||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {}
|
||||
#endif
|
||||
|
||||
const std::string &Role::rolename() const { return rolename_; }
|
||||
@@ -455,10 +454,8 @@ nlohmann::json Role::Serialize() const {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||
data[kDatabases] = db_access_.Serialize();
|
||||
} else {
|
||||
data[kFineGrainedAccessHandler] = {};
|
||||
data[kDatabases] = {};
|
||||
}
|
||||
#endif
|
||||
return data;
|
||||
@@ -474,21 +471,12 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
Databases db_access;
|
||||
if (data[kDatabases].is_structured()) {
|
||||
db_access = Databases::Deserialize(data[kDatabases]);
|
||||
} else {
|
||||
// Back-compatibility
|
||||
spdlog::warn("Role without specified database access. Given access to the default database.");
|
||||
db_access.Grant(dbms::kDefaultDB);
|
||||
db_access.SetMain(dbms::kDefaultDB);
|
||||
}
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||
}
|
||||
return {data[kRoleName], permissions, std::move(fine_grained_access_handler), std::move(db_access)};
|
||||
return {data[kRoleName], permissions, std::move(fine_grained_access_handler)};
|
||||
}
|
||||
#endif
|
||||
return {data[kRoleName], permissions};
|
||||
@@ -505,7 +493,7 @@ bool operator==(const Role &first, const Role &second) {
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
void Databases::Grant(std::string_view db) {
|
||||
void Databases::Add(std::string_view db) {
|
||||
if (allow_all_) {
|
||||
grants_dbs_.clear();
|
||||
allow_all_ = false;
|
||||
@@ -514,19 +502,19 @@ void Databases::Grant(std::string_view db) {
|
||||
denies_dbs_.erase(std::string{db}); // TODO: C++23 use transparent key compare
|
||||
}
|
||||
|
||||
void Databases::Deny(const std::string &db) {
|
||||
void Databases::Remove(const std::string &db) {
|
||||
denies_dbs_.emplace(db);
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
|
||||
void Databases::Revoke(const std::string &db) {
|
||||
void Databases::Delete(const std::string &db) {
|
||||
denies_dbs_.erase(db);
|
||||
if (!allow_all_) {
|
||||
grants_dbs_.erase(db);
|
||||
}
|
||||
// Reset if default deleted
|
||||
if (main_db_ == db) {
|
||||
main_db_ = "";
|
||||
if (default_db_ == db) {
|
||||
default_db_ = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,16 +530,9 @@ void Databases::DenyAll() {
|
||||
denies_dbs_.clear();
|
||||
}
|
||||
|
||||
void Databases::RevokeAll() {
|
||||
allow_all_ = false;
|
||||
grants_dbs_.clear();
|
||||
denies_dbs_.clear();
|
||||
main_db_ = "";
|
||||
}
|
||||
|
||||
bool Databases::SetMain(std::string_view db) {
|
||||
bool Databases::SetDefault(std::string_view db) {
|
||||
if (!Contains(db)) return false;
|
||||
main_db_ = db;
|
||||
default_db_ = db;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -559,11 +540,11 @@ bool Databases::SetMain(std::string_view db) {
|
||||
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
||||
}
|
||||
|
||||
const std::string &Databases::GetMain() const {
|
||||
if (!Contains(main_db_)) {
|
||||
throw AuthException("No access to the set default database \"{}\".", main_db_);
|
||||
const std::string &Databases::GetDefault() const {
|
||||
if (!Contains(default_db_)) {
|
||||
throw AuthException("No access to the set default database \"{}\".", default_db_);
|
||||
}
|
||||
return main_db_;
|
||||
return default_db_;
|
||||
}
|
||||
|
||||
nlohmann::json Databases::Serialize() const {
|
||||
@@ -571,7 +552,7 @@ nlohmann::json Databases::Serialize() const {
|
||||
data[kGrants] = grants_dbs_;
|
||||
data[kDenies] = denies_dbs_;
|
||||
data[kAllowAll] = allow_all_;
|
||||
data[kDefault] = main_db_;
|
||||
data[kDefault] = default_db_;
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -738,16 +719,15 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
} else {
|
||||
// Back-compatibility
|
||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||
db_access.Grant(dbms::kDefaultDB);
|
||||
db_access.SetMain(dbms::kDefaultDB);
|
||||
db_access.Add(dbms::kDefaultDB);
|
||||
db_access.SetDefault(dbms::kDefaultDB);
|
||||
}
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||
}
|
||||
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler),
|
||||
std::move(db_access)};
|
||||
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler), db_access};
|
||||
}
|
||||
#endif
|
||||
return {data[kUsername], std::move(password_hash), permissions};
|
||||
|
||||
@@ -205,96 +205,7 @@ class FineGrainedAccessHandler final {
|
||||
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
class Databases final {
|
||||
public:
|
||||
Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), main_db_(dbms::kDefaultDB) {}
|
||||
|
||||
Databases(const Databases &) = default;
|
||||
Databases &operator=(const Databases &) = default;
|
||||
Databases(Databases &&) noexcept = default;
|
||||
Databases &operator=(Databases &&) noexcept = default;
|
||||
~Databases() = default;
|
||||
|
||||
/**
|
||||
* @brief Add database to the list of granted access. @note allow_all_ will be false after execution
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Grant(std::string_view db);
|
||||
|
||||
/**
|
||||
* @brief Remove database to the list of granted access.
|
||||
* @note if allow_all_ is set, the flag will remain set and the
|
||||
* database will be added to the set of denied databases.
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Deny(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
|
||||
* @note allow_all_ is not changed
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Revoke(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to true and clears grants and denied sets.
|
||||
*/
|
||||
void GrantAll();
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||
*/
|
||||
void DenyAll();
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||
*/
|
||||
void RevokeAll();
|
||||
|
||||
/**
|
||||
* @brief Set the default database.
|
||||
*/
|
||||
bool SetMain(std::string_view db);
|
||||
|
||||
/**
|
||||
* @brief Checks if access is grated to the database.
|
||||
*
|
||||
* @param db name of the database
|
||||
* @return true if allow_all and not denied or granted
|
||||
*/
|
||||
bool Contains(std::string_view db) const;
|
||||
bool Denies(std::string_view db_name) const { return denies_dbs_.contains(db_name); }
|
||||
bool Grants(std::string_view db_name) const { return allow_all_ || grants_dbs_.contains(db_name); }
|
||||
|
||||
bool GetAllowAll() const { return allow_all_; }
|
||||
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
|
||||
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
|
||||
const std::string &GetMain() const;
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
static Databases Deserialize(const nlohmann::json &data);
|
||||
|
||||
private:
|
||||
Databases(bool allow_all, std::set<std::string, std::less<>> grant, std::set<std::string, std::less<>> deny,
|
||||
std::string default_db = std::string{dbms::kDefaultDB})
|
||||
: grants_dbs_(std::move(grant)),
|
||||
denies_dbs_(std::move(deny)),
|
||||
allow_all_(allow_all),
|
||||
main_db_(std::move(default_db)) {}
|
||||
|
||||
std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access
|
||||
std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access
|
||||
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
|
||||
std::string main_db_; //!< user's default database
|
||||
};
|
||||
#endif
|
||||
|
||||
class Role {
|
||||
class Role final {
|
||||
public:
|
||||
Role() = default;
|
||||
|
||||
@@ -302,7 +213,7 @@ class Role {
|
||||
Role(const std::string &rolename, const Permissions &permissions);
|
||||
#ifdef MG_ENTERPRISE
|
||||
Role(const std::string &rolename, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
|
||||
FineGrainedAccessHandler fine_grained_access_handler);
|
||||
#endif
|
||||
Role(const Role &) = default;
|
||||
Role &operator=(const Role &) = default;
|
||||
@@ -313,23 +224,12 @@ class Role {
|
||||
const std::string &rolename() const;
|
||||
const Permissions &permissions() const;
|
||||
Permissions &permissions();
|
||||
Permissions GetPermissions() const { return permissions_; }
|
||||
#ifdef MG_ENTERPRISE
|
||||
const FineGrainedAccessHandler &fine_grained_access_handler() const;
|
||||
FineGrainedAccessHandler &fine_grained_access_handler();
|
||||
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
|
||||
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
Databases &db_access() { return db_access_; }
|
||||
const Databases &db_access() const { return db_access_; }
|
||||
|
||||
bool DeniesDB(std::string_view db_name) const { return db_access_.Denies(db_name); }
|
||||
bool GrantsDB(std::string_view db_name) const { return db_access_.Grants(db_name); }
|
||||
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
|
||||
#endif
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
@@ -342,12 +242,93 @@ class Role {
|
||||
Permissions permissions_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||
Databases db_access_;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool operator==(const Role &first, const Role &second);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
class Databases final {
|
||||
public:
|
||||
Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), default_db_(dbms::kDefaultDB) {}
|
||||
|
||||
Databases(const Databases &) = default;
|
||||
Databases &operator=(const Databases &) = default;
|
||||
Databases(Databases &&) noexcept = default;
|
||||
Databases &operator=(Databases &&) noexcept = default;
|
||||
~Databases() = default;
|
||||
|
||||
/**
|
||||
* @brief Add database to the list of granted access. @note allow_all_ will be false after execution
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Add(std::string_view db);
|
||||
|
||||
/**
|
||||
* @brief Remove database to the list of granted access.
|
||||
* @note if allow_all_ is set, the flag will remain set and the
|
||||
* database will be added to the set of denied databases.
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Remove(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
|
||||
* @note allow_all_ is not changed
|
||||
*
|
||||
* @param db name of the database to grant access to
|
||||
*/
|
||||
void Delete(const std::string &db);
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to true and clears grants and denied sets.
|
||||
*/
|
||||
void GrantAll();
|
||||
|
||||
/**
|
||||
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||
*/
|
||||
void DenyAll();
|
||||
|
||||
/**
|
||||
* @brief Set the default database.
|
||||
*/
|
||||
bool SetDefault(std::string_view db);
|
||||
|
||||
/**
|
||||
* @brief Checks if access is grated to the database.
|
||||
*
|
||||
* @param db name of the database
|
||||
* @return true if allow_all and not denied or granted
|
||||
*/
|
||||
bool Contains(std::string_view db) const;
|
||||
|
||||
bool GetAllowAll() const { return allow_all_; }
|
||||
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
|
||||
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
|
||||
const std::string &GetDefault() const;
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
static Databases Deserialize(const nlohmann::json &data);
|
||||
|
||||
private:
|
||||
Databases(bool allow_all, std::set<std::string, std::less<>> grant, std::set<std::string, std::less<>> deny,
|
||||
std::string default_db = std::string{dbms::kDefaultDB})
|
||||
: grants_dbs_(std::move(grant)),
|
||||
denies_dbs_(std::move(deny)),
|
||||
allow_all_(allow_all),
|
||||
default_db_(std::move(default_db)) {}
|
||||
|
||||
std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access
|
||||
std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access
|
||||
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
|
||||
std::string default_db_; //!< user's default database
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO (mferencevic): Implement password expiry.
|
||||
class User final {
|
||||
public:
|
||||
@@ -407,18 +388,6 @@ class User final {
|
||||
#ifdef MG_ENTERPRISE
|
||||
Databases &db_access() { return database_access_; }
|
||||
const Databases &db_access() const { return database_access_; }
|
||||
|
||||
bool DeniesDB(std::string_view db_name) const {
|
||||
bool denies = database_access_.Denies(db_name);
|
||||
if (role_) denies |= role_->DeniesDB(db_name);
|
||||
return denies;
|
||||
}
|
||||
bool GrantsDB(std::string_view db_name) const {
|
||||
bool grants = database_access_.Grants(db_name);
|
||||
if (role_) grants |= role_->GrantsDB(db_name);
|
||||
return grants;
|
||||
}
|
||||
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
|
||||
#endif
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
@@ -434,7 +403,7 @@ class User final {
|
||||
Permissions permissions_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||
Databases database_access_{};
|
||||
Databases database_access_;
|
||||
#endif
|
||||
std::optional<Role> role_;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -403,7 +403,7 @@ nlohmann::json Module::Call(const nlohmann::json ¶ms, int timeout_millisec)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Module::IsUsed() const { return !module_executable_path_.empty(); }
|
||||
bool Module::IsUsed() { return !module_executable_path_.empty(); }
|
||||
|
||||
void Module::Shutdown() {
|
||||
if (pid_ == -1) return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -49,7 +49,7 @@ class Module final {
|
||||
/// specified executable path and can thus be used.
|
||||
///
|
||||
/// @return boolean indicating whether the module can be used
|
||||
bool IsUsed() const;
|
||||
bool IsUsed();
|
||||
|
||||
~Module();
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
#include "utils/enum.hpp"
|
||||
|
||||
namespace memgraph::slk {
|
||||
// Serialize code for auth::Role
|
||||
void Save(const auth::Role &self, Builder *builder) { memgraph::slk::Save(self.Serialize().dump(), builder); }
|
||||
|
||||
// Serialize code for auth::Role
|
||||
void Save(const auth::Role &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.Serialize().dump(), builder);
|
||||
}
|
||||
namespace {
|
||||
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
||||
std::string tmp;
|
||||
|
||||
@@ -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
|
||||
@@ -15,9 +15,6 @@
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
#include "communication/bolt/v1/fmt.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04};
|
||||
constexpr uint8_t kEmptyBoltVersion[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if FMT_VERSION > 90000
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
|
||||
template <>
|
||||
class fmt::formatter<memgraph::communication::bolt::Value> : public fmt::ostream_formatter {};
|
||||
|
||||
template <>
|
||||
class fmt::formatter<std::vector<memgraph::communication::bolt::Value>> : public fmt::ostream_formatter {};
|
||||
|
||||
template <>
|
||||
class fmt::formatter<std::map<std::string, memgraph::communication::bolt::Value>> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if FMT_VERSION > 90000
|
||||
#include <fmt/ostream.h>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
template <>
|
||||
class fmt::formatter<boost::asio::ip::tcp::endpoint> : public fmt::ostream_formatter {};
|
||||
#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
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "communication/http/session.hpp"
|
||||
#include "utils/spin_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
@@ -83,7 +82,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("HTTP server is listening on {}", endpoint);
|
||||
spdlog::info("HTTP server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||
}
|
||||
|
||||
void DoAccept() {
|
||||
|
||||
@@ -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
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "communication/session.hpp"
|
||||
#include "io/network/epoll.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/signals.hpp"
|
||||
|
||||
@@ -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
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include "communication/init.hpp"
|
||||
#include "communication/listener.hpp"
|
||||
#include "io/network/fmt.hpp"
|
||||
#include "io/network/socket.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/message.hpp"
|
||||
|
||||
@@ -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
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "communication/init.hpp"
|
||||
#include "communication/v2/listener.hpp"
|
||||
#include "communication/v2/pool.hpp"
|
||||
@@ -130,7 +129,7 @@ bool Server<TSession, TSessionContext>::Start() {
|
||||
listener_->Start();
|
||||
|
||||
spdlog::info("{} server is fully armed and operational", service_name_);
|
||||
spdlog::info("{} listening on {}", service_name_, endpoint_);
|
||||
spdlog::info("{} listening on {}", service_name_, endpoint_.address());
|
||||
context_thread_pool_.Run();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -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
|
||||
@@ -47,7 +47,6 @@
|
||||
#include "communication/buffer.hpp"
|
||||
#include "communication/context.hpp"
|
||||
#include "communication/exceptions.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "utils/event_counter.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
@@ -213,11 +212,14 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
||||
session_.Execute();
|
||||
DoRead();
|
||||
} catch (const SessionClosedException &e) {
|
||||
spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
|
||||
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
DoClose();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
||||
remote_endpoint_);
|
||||
spdlog::error(
|
||||
"Exception was thrown while processing event in {} session "
|
||||
"associated with {}:{}",
|
||||
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||
spdlog::debug("Exception message: {}", e.what());
|
||||
DoClose();
|
||||
}
|
||||
@@ -374,7 +376,8 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
socket.lowest_layer().non_blocking(false);
|
||||
});
|
||||
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
|
||||
spdlog::info("Accepted a connection from {}: {}", service_name_, remote_endpoint_);
|
||||
spdlog::info("Accepted a connection from {}: {}:{}", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
}
|
||||
|
||||
void DoRead() {
|
||||
@@ -434,11 +437,14 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
||||
session_.Execute();
|
||||
DoRead();
|
||||
} catch (const SessionClosedException &e) {
|
||||
spdlog::info("{} client {} closed the connection.", service_name_, remote_endpoint_);
|
||||
spdlog::info("{} client {}:{} closed the connection.", service_name_, remote_endpoint_.address(),
|
||||
remote_endpoint_.port());
|
||||
DoShutdown();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
||||
remote_endpoint_);
|
||||
spdlog::error(
|
||||
"Exception was thrown while processing event in {} session "
|
||||
"associated with {}:{}",
|
||||
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||
spdlog::debug("Exception message: {}", e.what());
|
||||
DoShutdown();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -12,44 +12,19 @@
|
||||
#include "communication/websocket/auth.hpp"
|
||||
|
||||
#include <string>
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
namespace memgraph::communication::websocket {
|
||||
|
||||
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
||||
user_or_role_ = auth_->Lock()->Authenticate(username, password);
|
||||
return user_or_role_.has_value();
|
||||
return auth_->Lock()->Authenticate(username, password).has_value();
|
||||
}
|
||||
|
||||
bool SafeAuth::HasPermission(const auth::Permission permission) const {
|
||||
auto locked_auth = auth_->ReadLock();
|
||||
// Update if cache invalidated
|
||||
if (!locked_auth->UpToDate(auth_epoch_) && user_or_role_) {
|
||||
bool success = true;
|
||||
std::visit(utils::Overloaded{[&](auth::User &user) {
|
||||
auto tmp = locked_auth->GetUser(user.username());
|
||||
if (!tmp) success = false;
|
||||
user = std::move(*tmp);
|
||||
},
|
||||
[&](auth::Role &role) {
|
||||
auto tmp = locked_auth->GetRole(role.rolename());
|
||||
if (!tmp) success = false;
|
||||
role = std::move(*tmp);
|
||||
}},
|
||||
*user_or_role_);
|
||||
// Missing user/role; delete from cache
|
||||
if (!success) user_or_role_.reset();
|
||||
bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permission permission) const {
|
||||
if (const auto user = auth_->ReadLock()->GetUser(username); user) {
|
||||
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||
}
|
||||
// Check permissions
|
||||
if (user_or_role_) {
|
||||
return std::visit(utils::Overloaded{[&](auto &user_or_role) {
|
||||
return user_or_role.GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||
}},
|
||||
*user_or_role_);
|
||||
}
|
||||
// NOTE: websocket authenticates only if there is a user, so no need to check if access controlled
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeAuth::AccessControlled() const { return auth_->ReadLock()->AccessControlled(); }
|
||||
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
|
||||
} // namespace memgraph::communication::websocket
|
||||
|
||||
@@ -21,9 +21,9 @@ class AuthenticationInterface {
|
||||
public:
|
||||
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
|
||||
|
||||
virtual bool HasPermission(auth::Permission permission) const = 0;
|
||||
virtual bool HasUserPermission(const std::string &username, auth::Permission permission) const = 0;
|
||||
|
||||
virtual bool AccessControlled() const = 0;
|
||||
virtual bool HasAnyUsers() const = 0;
|
||||
};
|
||||
|
||||
class SafeAuth : public AuthenticationInterface {
|
||||
@@ -32,13 +32,11 @@ class SafeAuth : public AuthenticationInterface {
|
||||
|
||||
bool Authenticate(const std::string &username, const std::string &password) const override;
|
||||
|
||||
bool HasPermission(auth::Permission permission) const override;
|
||||
bool HasUserPermission(const std::string &username, auth::Permission permission) const override;
|
||||
|
||||
bool AccessControlled() const override;
|
||||
bool HasAnyUsers() const override;
|
||||
|
||||
private:
|
||||
auth::SynchedAuth *auth_;
|
||||
mutable std::optional<auth::UserOrRole> user_or_role_;
|
||||
mutable auth::Auth::Epoch auth_epoch_{};
|
||||
};
|
||||
} // namespace memgraph::communication::websocket
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -10,7 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "communication/websocket/listener.hpp"
|
||||
#include "communication/fmt.hpp"
|
||||
|
||||
namespace memgraph::communication::websocket {
|
||||
namespace {
|
||||
@@ -62,7 +61,7 @@ Listener::Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::en
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("WebSocket server is listening on {}", endpoint);
|
||||
spdlog::info("WebSocket server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||
}
|
||||
|
||||
void Listener::DoAccept() {
|
||||
|
||||
@@ -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
|
||||
@@ -80,7 +80,7 @@ bool Session::Run() {
|
||||
return false;
|
||||
}
|
||||
|
||||
authenticated_ = !auth_.AccessControlled();
|
||||
authenticated_ = !auth_.HasAnyUsers();
|
||||
connected_.store(true, std::memory_order_relaxed);
|
||||
|
||||
// run on the strand
|
||||
@@ -162,7 +162,7 @@ utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds)
|
||||
return {"Authentication failed!"};
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (!auth_.HasPermission(auth::Permission::WEBSOCKET)) {
|
||||
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) {
|
||||
return {"Authorization failed!"};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -9,12 +9,13 @@ target_sources(mg-coordination
|
||||
include/coordination/coordinator_config.hpp
|
||||
include/coordination/coordinator_exceptions.hpp
|
||||
include/coordination/coordinator_slk.hpp
|
||||
include/coordination/coordinator_instance.hpp
|
||||
include/coordination/coordinator_data.hpp
|
||||
include/coordination/constants.hpp
|
||||
include/coordination/coordinator_cluster_config.hpp
|
||||
include/coordination/coordinator_handlers.hpp
|
||||
include/coordination/coordinator_instance.hpp
|
||||
include/coordination/instance_status.hpp
|
||||
include/coordination/replication_instance.hpp
|
||||
include/coordination/raft_state.hpp
|
||||
include/coordination/rpc_errors.hpp
|
||||
|
||||
include/nuraft/coordinator_log_store.hpp
|
||||
include/nuraft/coordinator_state_machine.hpp
|
||||
@@ -25,10 +26,10 @@ target_sources(mg-coordination
|
||||
coordinator_state.cpp
|
||||
coordinator_rpc.cpp
|
||||
coordinator_server.cpp
|
||||
coordinator_handlers.cpp
|
||||
coordinator_data.cpp
|
||||
coordinator_instance.cpp
|
||||
coordinator_handlers.cpp
|
||||
replication_instance.cpp
|
||||
raft_state.cpp
|
||||
|
||||
coordinator_log_store.cpp
|
||||
coordinator_state_machine.cpp
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_rpc.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
@@ -29,38 +28,25 @@ auto CreateClientContext(memgraph::coordination::CoordinatorClientConfig const &
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CoordinatorClient::CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
||||
CoordinatorClient::CoordinatorClient(CoordinatorData *coord_data, CoordinatorClientConfig config,
|
||||
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||
: rpc_context_{CreateClientContext(config)},
|
||||
rpc_client_{io::network::Endpoint(io::network::Endpoint::needs_resolving, config.ip_address, config.port),
|
||||
&rpc_context_},
|
||||
config_{std::move(config)},
|
||||
coord_instance_{coord_instance},
|
||||
coord_data_{coord_data},
|
||||
succ_cb_{std::move(succ_cb)},
|
||||
fail_cb_{std::move(fail_cb)} {}
|
||||
|
||||
auto CoordinatorClient::InstanceName() const -> std::string { return config_.instance_name; }
|
||||
auto CoordinatorClient::SocketAddress() const -> std::string { return rpc_client_.Endpoint().SocketAddress(); }
|
||||
|
||||
auto CoordinatorClient::InstanceDownTimeoutSec() const -> std::chrono::seconds {
|
||||
return config_.instance_down_timeout_sec;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::InstanceGetUUIDFrequencySec() const -> std::chrono::seconds {
|
||||
return config_.instance_get_uuid_frequency_sec;
|
||||
}
|
||||
|
||||
void CoordinatorClient::StartFrequentCheck() {
|
||||
if (instance_checker_.IsRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MG_ASSERT(config_.instance_health_check_frequency_sec > std::chrono::seconds(0),
|
||||
MG_ASSERT(config_.health_check_frequency_sec > std::chrono::seconds(0),
|
||||
"Health check frequency must be greater than 0");
|
||||
|
||||
instance_checker_.Run(
|
||||
config_.instance_name, config_.instance_health_check_frequency_sec,
|
||||
[this, instance_name = config_.instance_name] {
|
||||
config_.instance_name, config_.health_check_frequency_sec, [this, instance_name = config_.instance_name] {
|
||||
try {
|
||||
spdlog::trace("Sending frequent heartbeat to machine {} on {}", instance_name,
|
||||
rpc_client_.Endpoint().SocketAddress());
|
||||
@@ -68,9 +54,9 @@ void CoordinatorClient::StartFrequentCheck() {
|
||||
auto stream{rpc_client_.Stream<memgraph::replication_coordination_glue::FrequentHeartbeatRpc>()};
|
||||
stream.AwaitResponse();
|
||||
}
|
||||
succ_cb_(coord_instance_, instance_name);
|
||||
succ_cb_(coord_data_, instance_name);
|
||||
} catch (rpc::RpcFailedException const &) {
|
||||
fail_cb_(coord_instance_, instance_name);
|
||||
fail_cb_(coord_data_, instance_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -131,45 +117,5 @@ auto CoordinatorClient::SendSwapMainUUIDRpc(const utils::UUID &uuid) const -> bo
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::SendUnregisterReplicaRpc(std::string const &instance_name) const -> bool {
|
||||
try {
|
||||
auto stream{rpc_client_.Stream<UnregisterReplicaRpc>(instance_name)};
|
||||
if (!stream.AwaitResponse().success) {
|
||||
spdlog::error("Failed to receive successful RPC response for unregistering replica!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (rpc::RpcFailedException const &) {
|
||||
spdlog::error("Failed to unregister replica!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::SendGetInstanceUUIDRpc() const
|
||||
-> utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>> {
|
||||
try {
|
||||
auto stream{rpc_client_.Stream<GetInstanceUUIDRpc>()};
|
||||
auto res = stream.AwaitResponse();
|
||||
return res.uuid;
|
||||
} catch (const rpc::RpcFailedException &) {
|
||||
spdlog::error("RPC error occured while sending GetInstance UUID RPC");
|
||||
return GetInstanceUUIDError::RPC_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorClient::SendEnableWritingOnMainRpc() const -> bool {
|
||||
try {
|
||||
auto stream{rpc_client_.Stream<EnableWritingOnMainRpc>()};
|
||||
if (!stream.AwaitResponse().success) {
|
||||
spdlog::error("Failed to receive successful RPC response for enabling writing on main!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (rpc::RpcFailedException const &) {
|
||||
spdlog::error("Failed to enable writing on main!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
||||
282
src/coordination/coordinator_data.cpp
Normal file
282
src/coordination/coordinator_data.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_data.hpp"
|
||||
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "coordination/replication_instance.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
#include <range/v3/view.hpp>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using nuraft::ptr;
|
||||
using nuraft::srv_config;
|
||||
|
||||
CoordinatorData::CoordinatorData() {
|
||||
auto find_instance = [](CoordinatorData *coord_data, std::string_view instance_name) -> ReplicationInstance & {
|
||||
auto instance = std::ranges::find_if(
|
||||
coord_data->repl_instances_,
|
||||
[instance_name](ReplicationInstance const &instance) { return instance.InstanceName() == instance_name; });
|
||||
|
||||
MG_ASSERT(instance != coord_data->repl_instances_.end(), "Instance {} not found during callback!", instance_name);
|
||||
return *instance;
|
||||
};
|
||||
|
||||
replica_succ_cb_ = [this, find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing replica successful callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
|
||||
if (!instance.GetMainUUID().has_value() || main_uuid_ != instance.GetMainUUID().value()) {
|
||||
if (!instance.SendSwapAndUpdateUUID(main_uuid_)) {
|
||||
spdlog::error(
|
||||
fmt::format("Failed to swap uuid for replica instance {} which is alive", instance.InstanceName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
instance.OnSuccessPing();
|
||||
};
|
||||
|
||||
replica_fail_cb_ = [find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing replica failure callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
instance.OnFailPing();
|
||||
// We need to restart main uuid from instance since it was "down" at least a second
|
||||
// There is slight delay, if we choose to use isAlive, instance can be down and back up in less than
|
||||
// our isAlive time difference, which would lead to instance setting UUID to nullopt and stopping accepting any
|
||||
// incoming RPCs from valid main
|
||||
// TODO(antoniofilipovic) this needs here more complex logic
|
||||
// We need to get id of main replica is listening to on successful ping
|
||||
// and swap it to correct uuid if it failed
|
||||
instance.SetNewMainUUID();
|
||||
};
|
||||
|
||||
main_succ_cb_ = [this, find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing main successful callback", instance_name);
|
||||
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
|
||||
if (instance.IsAlive()) {
|
||||
instance.OnSuccessPing();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &instance_uuid = instance.GetMainUUID();
|
||||
MG_ASSERT(instance_uuid.has_value(), "Instance must have uuid set");
|
||||
if (main_uuid_ == instance_uuid.value()) {
|
||||
instance.OnSuccessPing();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(antoniof) make demoteToReplica idempotent since main can be demoted to replica but
|
||||
// swapUUID can fail
|
||||
bool const demoted = instance.DemoteToReplica(coord_data->replica_succ_cb_, coord_data->replica_fail_cb_);
|
||||
if (demoted) {
|
||||
instance.OnSuccessPing();
|
||||
spdlog::info("Instance {} demoted to replica", instance_name);
|
||||
} else {
|
||||
spdlog::error("Instance {} failed to become replica", instance_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instance.SendSwapAndUpdateUUID(main_uuid_)) {
|
||||
spdlog::error(fmt::format("Failed to swap uuid for demoted main instance {}", instance.InstanceName()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
main_fail_cb_ = [this, find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing main failure callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
instance.OnFailPing();
|
||||
const auto &instance_uuid = instance.GetMainUUID();
|
||||
MG_ASSERT(instance_uuid.has_value(), "Instance must have uuid set");
|
||||
|
||||
if (!instance.IsAlive() && main_uuid_ == instance_uuid.value()) {
|
||||
spdlog::info("Cluster without main instance, trying automatic failover");
|
||||
coord_data->TryFailover();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
auto CoordinatorData::TryFailover() -> void {
|
||||
auto alive_replicas = repl_instances_ | ranges::views::filter(&ReplicationInstance::IsReplica) |
|
||||
ranges::views::filter(&ReplicationInstance::IsAlive);
|
||||
|
||||
if (ranges::empty(alive_replicas)) {
|
||||
spdlog::warn("Failover failed since all replicas are down!");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Smarter choice
|
||||
auto chosen_replica_instance = ranges::begin(alive_replicas);
|
||||
|
||||
chosen_replica_instance->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&chosen_replica_instance] { chosen_replica_instance->ResumeFrequentCheck(); }};
|
||||
|
||||
auto const potential_new_main_uuid = utils::UUID{};
|
||||
|
||||
auto const is_not_chosen_replica_instance = [&chosen_replica_instance](ReplicationInstance &instance) {
|
||||
return instance != *chosen_replica_instance;
|
||||
};
|
||||
|
||||
// If for some replicas swap fails, for others on successful ping we will revert back on next change
|
||||
// or we will do failover first again and then it will be consistent again
|
||||
for (auto &other_replica_instance : alive_replicas | ranges::views::filter(is_not_chosen_replica_instance)) {
|
||||
if (!other_replica_instance.SendSwapAndUpdateUUID(potential_new_main_uuid)) {
|
||||
spdlog::error(fmt::format("Failed to swap uuid for instance {} which is alive, aborting failover",
|
||||
other_replica_instance.InstanceName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ReplClientInfo> repl_clients_info;
|
||||
repl_clients_info.reserve(repl_instances_.size() - 1);
|
||||
std::ranges::transform(repl_instances_ | ranges::views::filter(is_not_chosen_replica_instance),
|
||||
std::back_inserter(repl_clients_info), &ReplicationInstance::ReplicationClientInfo);
|
||||
|
||||
if (!chosen_replica_instance->PromoteToMain(potential_new_main_uuid, std::move(repl_clients_info), main_succ_cb_,
|
||||
main_fail_cb_)) {
|
||||
spdlog::warn("Failover failed since promoting replica to main failed!");
|
||||
return;
|
||||
}
|
||||
chosen_replica_instance->SetNewMainUUID(potential_new_main_uuid);
|
||||
main_uuid_ = potential_new_main_uuid;
|
||||
|
||||
spdlog::info("Failover successful! Instance {} promoted to main.", chosen_replica_instance->InstanceName());
|
||||
}
|
||||
|
||||
auto CoordinatorData::ShowInstances() const -> std::vector<InstanceStatus> {
|
||||
auto const coord_instances = self_.GetAllCoordinators();
|
||||
|
||||
std::vector<InstanceStatus> instances_status;
|
||||
instances_status.reserve(repl_instances_.size() + coord_instances.size());
|
||||
|
||||
auto const stringify_repl_role = [](ReplicationInstance const &instance) -> std::string {
|
||||
if (!instance.IsAlive()) return "unknown";
|
||||
if (instance.IsMain()) return "main";
|
||||
return "replica";
|
||||
};
|
||||
|
||||
auto const repl_instance_to_status = [&stringify_repl_role](ReplicationInstance const &instance) -> InstanceStatus {
|
||||
return {.instance_name = instance.InstanceName(),
|
||||
.coord_socket_address = instance.SocketAddress(),
|
||||
.cluster_role = stringify_repl_role(instance),
|
||||
.is_alive = instance.IsAlive()};
|
||||
};
|
||||
|
||||
auto const coord_instance_to_status = [](ptr<srv_config> const &instance) -> InstanceStatus {
|
||||
return {.instance_name = "coordinator_" + std::to_string(instance->get_id()),
|
||||
.raft_socket_address = instance->get_endpoint(),
|
||||
.cluster_role = "coordinator",
|
||||
.is_alive = true}; // TODO: (andi) Get this info from RAFT and test it or when we will move
|
||||
// CoordinatorState to every instance, we can be smarter about this using our RPC.
|
||||
};
|
||||
|
||||
std::ranges::transform(coord_instances, std::back_inserter(instances_status), coord_instance_to_status);
|
||||
|
||||
{
|
||||
auto lock = std::shared_lock{coord_data_lock_};
|
||||
std::ranges::transform(repl_instances_, std::back_inserter(instances_status), repl_instance_to_status);
|
||||
}
|
||||
|
||||
return instances_status;
|
||||
}
|
||||
|
||||
// TODO: (andi) Make sure you cannot put coordinator instance to the main
|
||||
auto CoordinatorData::SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_data_lock_};
|
||||
|
||||
auto const is_new_main = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == instance_name;
|
||||
};
|
||||
auto new_main = std::ranges::find_if(repl_instances_, is_new_main);
|
||||
|
||||
if (new_main == repl_instances_.end()) {
|
||||
spdlog::error("Instance {} not registered. Please register it using REGISTER INSTANCE {}", instance_name,
|
||||
instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::NO_INSTANCE_WITH_NAME;
|
||||
}
|
||||
|
||||
new_main->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&new_main] { new_main->ResumeFrequentCheck(); }};
|
||||
|
||||
ReplicationClientsInfo repl_clients_info;
|
||||
repl_clients_info.reserve(repl_instances_.size() - 1);
|
||||
|
||||
auto const is_not_new_main = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() != instance_name;
|
||||
};
|
||||
|
||||
auto potential_new_main_uuid = utils::UUID{};
|
||||
spdlog::trace("Generated potential new main uuid");
|
||||
|
||||
for (auto &other_instance : repl_instances_ | ranges::views::filter(is_not_new_main)) {
|
||||
if (!other_instance.SendSwapAndUpdateUUID(potential_new_main_uuid)) {
|
||||
spdlog::error(
|
||||
fmt::format("Failed to swap uuid for instance {}, aborting failover", other_instance.InstanceName()));
|
||||
return SetInstanceToMainCoordinatorStatus::SWAP_UUID_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
std::ranges::transform(repl_instances_ | ranges::views::filter(is_not_new_main),
|
||||
std::back_inserter(repl_clients_info),
|
||||
[](const ReplicationInstance &instance) { return instance.ReplicationClientInfo(); });
|
||||
|
||||
if (!new_main->PromoteToMain(potential_new_main_uuid, std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
|
||||
return SetInstanceToMainCoordinatorStatus::COULD_NOT_PROMOTE_TO_MAIN;
|
||||
}
|
||||
|
||||
new_main->SetNewMainUUID(potential_new_main_uuid);
|
||||
main_uuid_ = potential_new_main_uuid;
|
||||
spdlog::info("Instance {} promoted to main", instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorData::RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_data_lock_};
|
||||
if (std::ranges::any_of(repl_instances_, [&config](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == config.instance_name;
|
||||
})) {
|
||||
return RegisterInstanceCoordinatorStatus::NAME_EXISTS;
|
||||
}
|
||||
|
||||
if (std::ranges::any_of(repl_instances_, [&config](ReplicationInstance const &instance) {
|
||||
return instance.SocketAddress() == config.SocketAddress();
|
||||
})) {
|
||||
return RegisterInstanceCoordinatorStatus::ENDPOINT_EXISTS;
|
||||
}
|
||||
|
||||
try {
|
||||
repl_instances_.emplace_back(this, std::move(config), replica_succ_cb_, replica_fail_cb_);
|
||||
return RegisterInstanceCoordinatorStatus::SUCCESS;
|
||||
|
||||
} catch (CoordinatorRegisterInstanceException const &) {
|
||||
return RegisterInstanceCoordinatorStatus::RPC_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorData::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||
-> void {
|
||||
self_.AddCoordinatorInstance(raft_server_id, raft_port, std::move(raft_address));
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -39,24 +39,6 @@ void CoordinatorHandlers::Register(memgraph::coordination::CoordinatorServer &se
|
||||
spdlog::info("Received SwapMainUUIDRPC on coordinator server");
|
||||
CoordinatorHandlers::SwapMainUUIDHandler(replication_handler, req_reader, res_builder);
|
||||
});
|
||||
|
||||
server.Register<coordination::UnregisterReplicaRpc>(
|
||||
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
||||
spdlog::info("Received UnregisterReplicaRpc on coordinator server");
|
||||
CoordinatorHandlers::UnregisterReplicaHandler(replication_handler, req_reader, res_builder);
|
||||
});
|
||||
|
||||
server.Register<coordination::EnableWritingOnMainRpc>(
|
||||
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
||||
spdlog::info("Received EnableWritingOnMainRpc on coordinator server");
|
||||
CoordinatorHandlers::EnableWritingOnMainHandler(replication_handler, req_reader, res_builder);
|
||||
});
|
||||
|
||||
server.Register<coordination::GetInstanceUUIDRpc>(
|
||||
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
||||
spdlog::info("Received GetInstanceUUIDRpc on coordinator server");
|
||||
CoordinatorHandlers::GetInstanceUUIDHandler(replication_handler, req_reader, res_builder);
|
||||
});
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler,
|
||||
@@ -80,6 +62,12 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
|
||||
slk::Reader *req_reader, slk::Builder *res_builder) {
|
||||
spdlog::info("Executing DemoteMainToReplicaHandler");
|
||||
|
||||
if (!replication_handler.IsMain()) {
|
||||
spdlog::error("Setting to replica must be performed on main.");
|
||||
slk::Save(coordination::DemoteMainToReplicaRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
coordination::DemoteMainToReplicaReq req;
|
||||
slk::Load(&req, req_reader);
|
||||
|
||||
@@ -89,18 +77,11 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
|
||||
|
||||
if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) {
|
||||
spdlog::error("Demoting main to replica failed!");
|
||||
slk::Save(coordination::DemoteMainToReplicaRes{false}, res_builder);
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
slk::Save(coordination::DemoteMainToReplicaRes{true}, res_builder);
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::GetInstanceUUIDHandler(replication::ReplicationHandler &replication_handler,
|
||||
slk::Reader * /*req_reader*/, slk::Builder *res_builder) {
|
||||
spdlog::info("Executing GetInstanceUUIDHandler");
|
||||
|
||||
slk::Save(coordination::GetInstanceUUIDRes{replication_handler.GetReplicaUUID()}, res_builder);
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder);
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHandler &replication_handler,
|
||||
@@ -132,7 +113,7 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
|
||||
|
||||
// registering replicas
|
||||
for (auto const &config : req.replication_clients_info | ranges::views::transform(converter)) {
|
||||
auto instance_client = replication_handler.RegisterReplica(config);
|
||||
auto instance_client = replication_handler.RegisterReplica(config, false);
|
||||
if (instance_client.HasError()) {
|
||||
using enum memgraph::replication::RegisterReplicaError;
|
||||
switch (instance_client.GetError()) {
|
||||
@@ -161,58 +142,9 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
|
||||
}
|
||||
}
|
||||
}
|
||||
spdlog::info("Promote replica to main was success {}", std::string(req.main_uuid_));
|
||||
spdlog::error(fmt::format("FICO : Promote replica to main was success {}", std::string(req.main_uuid_)));
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder);
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::UnregisterReplicaHandler(replication::ReplicationHandler &replication_handler,
|
||||
slk::Reader *req_reader, slk::Builder *res_builder) {
|
||||
if (!replication_handler.IsMain()) {
|
||||
spdlog::error("Unregistering replica must be performed on main.");
|
||||
slk::Save(coordination::UnregisterReplicaRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
coordination::UnregisterReplicaReq req;
|
||||
slk::Load(&req, req_reader);
|
||||
|
||||
auto res = replication_handler.UnregisterReplica(req.instance_name);
|
||||
switch (res) {
|
||||
using enum memgraph::query::UnregisterReplicaResult;
|
||||
case SUCCESS:
|
||||
slk::Save(coordination::UnregisterReplicaRes{true}, res_builder);
|
||||
break;
|
||||
case NOT_MAIN:
|
||||
spdlog::error("Unregistering replica must be performed on main.");
|
||||
slk::Save(coordination::UnregisterReplicaRes{false}, res_builder);
|
||||
break;
|
||||
case CAN_NOT_UNREGISTER:
|
||||
spdlog::error("Could not unregister replica.");
|
||||
slk::Save(coordination::UnregisterReplicaRes{false}, res_builder);
|
||||
break;
|
||||
case COULD_NOT_BE_PERSISTED:
|
||||
spdlog::error("Could not persist replica unregistration.");
|
||||
slk::Save(coordination::UnregisterReplicaRes{false}, res_builder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::EnableWritingOnMainHandler(replication::ReplicationHandler &replication_handler,
|
||||
slk::Reader * /*req_reader*/, slk::Builder *res_builder) {
|
||||
if (!replication_handler.IsMain()) {
|
||||
spdlog::error("Enable writing on main must be performed on main!");
|
||||
slk::Save(coordination::EnableWritingOnMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!replication_handler.GetReplState().EnableWritingOnMain()) {
|
||||
spdlog::error("Enabling writing on main failed!");
|
||||
slk::Save(coordination::EnableWritingOnMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
slk::Save(coordination::EnableWritingOnMainRes{true}, res_builder);
|
||||
}
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
#endif
|
||||
|
||||
@@ -14,334 +14,85 @@
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "coordination/fmt.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "nuraft/coordinator_state_manager.hpp"
|
||||
#include "utils/counter.hpp"
|
||||
#include "utils/functional.hpp"
|
||||
|
||||
#include <range/v3/view.hpp>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using nuraft::asio_service;
|
||||
using nuraft::cmd_result;
|
||||
using nuraft::cs_new;
|
||||
using nuraft::ptr;
|
||||
using nuraft::raft_params;
|
||||
using nuraft::srv_config;
|
||||
using raft_result = cmd_result<ptr<buffer>>;
|
||||
|
||||
CoordinatorInstance::CoordinatorInstance()
|
||||
: raft_state_(RaftState::MakeRaftState(
|
||||
[this] { std::ranges::for_each(repl_instances_, &ReplicationInstance::StartFrequentCheck); },
|
||||
[this] { std::ranges::for_each(repl_instances_, &ReplicationInstance::StopFrequentCheck); })) {
|
||||
auto find_repl_instance = [](CoordinatorInstance *self,
|
||||
std::string_view repl_instance_name) -> ReplicationInstance & {
|
||||
auto repl_instance =
|
||||
std::ranges::find_if(self->repl_instances_, [repl_instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == repl_instance_name;
|
||||
});
|
||||
: raft_server_id_(FLAGS_raft_server_id), raft_port_(FLAGS_raft_server_port), raft_address_("127.0.0.1") {
|
||||
auto raft_endpoint = raft_address_ + ":" + std::to_string(raft_port_);
|
||||
state_manager_ = cs_new<CoordinatorStateManager>(raft_server_id_, raft_endpoint);
|
||||
state_machine_ = cs_new<CoordinatorStateMachine>();
|
||||
logger_ = nullptr;
|
||||
|
||||
MG_ASSERT(repl_instance != self->repl_instances_.end(), "Instance {} not found during callback!",
|
||||
repl_instance_name);
|
||||
return *repl_instance;
|
||||
};
|
||||
// ASIO options
|
||||
asio_service::options asio_opts;
|
||||
asio_opts.thread_pool_size_ = 1; // TODO: (andi) Improve this
|
||||
|
||||
replica_succ_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
||||
spdlog::trace("Instance {} performing replica successful callback", repl_instance_name);
|
||||
auto &repl_instance = find_repl_instance(self, repl_instance_name);
|
||||
// RAFT parameters. Heartbeat every 100ms, election timeout between 200ms and 400ms.
|
||||
raft_params params;
|
||||
params.heart_beat_interval_ = 100;
|
||||
params.election_timeout_lower_bound_ = 200;
|
||||
params.election_timeout_upper_bound_ = 400;
|
||||
// 5 logs are preserved before the last snapshot
|
||||
params.reserved_log_items_ = 5;
|
||||
// Create snapshot for every 5 log appends
|
||||
params.snapshot_distance_ = 5;
|
||||
params.client_req_timeout_ = 3000;
|
||||
params.return_method_ = raft_params::blocking;
|
||||
|
||||
// We need to get replicas UUID from time to time to ensure replica is listening to correct main
|
||||
// and that it didn't go down for less time than we could notice
|
||||
// We need to get id of main replica is listening to
|
||||
// and swap if necessary
|
||||
if (!repl_instance.EnsureReplicaHasCorrectMainUUID(self->GetMainUUID())) {
|
||||
spdlog::error("Failed to swap uuid for replica instance {} which is alive", repl_instance.InstanceName());
|
||||
return;
|
||||
}
|
||||
raft_server_ =
|
||||
launcher_.init(state_machine_, state_manager_, logger_, static_cast<int>(raft_port_), asio_opts, params);
|
||||
|
||||
repl_instance.OnSuccessPing();
|
||||
};
|
||||
if (!raft_server_) {
|
||||
throw RaftServerStartException("Failed to launch raft server on {}", raft_endpoint);
|
||||
}
|
||||
|
||||
replica_fail_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
||||
spdlog::trace("Instance {} performing replica failure callback", repl_instance_name);
|
||||
auto &repl_instance = find_repl_instance(self, repl_instance_name);
|
||||
repl_instance.OnFailPing();
|
||||
};
|
||||
auto maybe_stop = utils::ResettableCounter<20>();
|
||||
while (!raft_server_->is_initialized() && !maybe_stop()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
main_succ_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
||||
spdlog::trace("Instance {} performing main successful callback", repl_instance_name);
|
||||
if (!raft_server_->is_initialized()) {
|
||||
throw RaftServerStartException("Failed to initialize raft server on {}", raft_endpoint);
|
||||
}
|
||||
|
||||
auto &repl_instance = find_repl_instance(self, repl_instance_name);
|
||||
|
||||
if (repl_instance.IsAlive()) {
|
||||
repl_instance.OnSuccessPing();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &repl_instance_uuid = repl_instance.GetMainUUID();
|
||||
MG_ASSERT(repl_instance_uuid.has_value(), "Instance must have uuid set.");
|
||||
|
||||
auto const curr_main_uuid = self->GetMainUUID();
|
||||
if (curr_main_uuid == repl_instance_uuid.value()) {
|
||||
if (!repl_instance.EnableWritingOnMain()) {
|
||||
spdlog::error("Failed to enable writing on main instance {}", repl_instance_name);
|
||||
return;
|
||||
}
|
||||
|
||||
repl_instance.OnSuccessPing();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(antoniof) make demoteToReplica idempotent since main can be demoted to replica but
|
||||
// swapUUID can fail
|
||||
if (repl_instance.DemoteToReplica(self->replica_succ_cb_, self->replica_fail_cb_)) {
|
||||
repl_instance.OnSuccessPing();
|
||||
spdlog::info("Instance {} demoted to replica", repl_instance_name);
|
||||
} else {
|
||||
spdlog::error("Instance {} failed to become replica", repl_instance_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!repl_instance.SendSwapAndUpdateUUID(curr_main_uuid)) {
|
||||
spdlog::error(fmt::format("Failed to swap uuid for demoted main instance {}", repl_instance.InstanceName()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
main_fail_cb_ = [find_repl_instance](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
||||
spdlog::trace("Instance {} performing main failure callback", repl_instance_name);
|
||||
auto &repl_instance = find_repl_instance(self, repl_instance_name);
|
||||
repl_instance.OnFailPing();
|
||||
const auto &repl_instance_uuid = repl_instance.GetMainUUID();
|
||||
MG_ASSERT(repl_instance_uuid.has_value(), "Instance must have uuid set");
|
||||
|
||||
if (!repl_instance.IsAlive() && self->GetMainUUID() == repl_instance_uuid.value()) {
|
||||
spdlog::info("Cluster without main instance, trying automatic failover");
|
||||
self->TryFailover(); // TODO: (andi) Initiate failover
|
||||
}
|
||||
};
|
||||
spdlog::info("Raft server started on {}", raft_endpoint);
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> {
|
||||
auto const coord_instances = raft_state_.GetAllCoordinators();
|
||||
|
||||
auto const stringify_repl_role = [](ReplicationInstance const &instance) -> std::string {
|
||||
if (!instance.IsAlive()) return "unknown";
|
||||
if (instance.IsMain()) return "main";
|
||||
return "replica";
|
||||
};
|
||||
|
||||
auto const repl_instance_to_status = [&stringify_repl_role](ReplicationInstance const &instance) -> InstanceStatus {
|
||||
return {.instance_name = instance.InstanceName(),
|
||||
.coord_socket_address = instance.SocketAddress(),
|
||||
.cluster_role = stringify_repl_role(instance),
|
||||
.is_alive = instance.IsAlive()};
|
||||
};
|
||||
|
||||
auto const coord_instance_to_status = [](ptr<srv_config> const &instance) -> InstanceStatus {
|
||||
return {.instance_name = "coordinator_" + std::to_string(instance->get_id()),
|
||||
.raft_socket_address = instance->get_endpoint(),
|
||||
.cluster_role = "coordinator",
|
||||
.is_alive = true}; // TODO: (andi) Get this info from RAFT and test it or when we will move
|
||||
// CoordinatorState to every instance, we can be smarter about this using our RPC.
|
||||
};
|
||||
|
||||
auto instances_status = utils::fmap(coord_instance_to_status, coord_instances);
|
||||
{
|
||||
auto lock = std::shared_lock{coord_instance_lock_};
|
||||
std::ranges::transform(repl_instances_, std::back_inserter(instances_status), repl_instance_to_status);
|
||||
}
|
||||
|
||||
return instances_status;
|
||||
auto CoordinatorInstance::InstanceName() const -> std::string {
|
||||
return "coordinator_" + std::to_string(raft_server_id_);
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::TryFailover() -> void {
|
||||
auto alive_replicas = repl_instances_ | ranges::views::filter(&ReplicationInstance::IsReplica) |
|
||||
ranges::views::filter(&ReplicationInstance::IsAlive);
|
||||
|
||||
if (ranges::empty(alive_replicas)) {
|
||||
spdlog::warn("Failover failed since all replicas are down!");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Smarter choice
|
||||
auto new_main = ranges::begin(alive_replicas);
|
||||
|
||||
new_main->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&new_main] { new_main->ResumeFrequentCheck(); }};
|
||||
|
||||
auto const is_not_new_main = [&new_main](ReplicationInstance &instance) {
|
||||
return instance.InstanceName() != new_main->InstanceName();
|
||||
};
|
||||
|
||||
auto const new_main_uuid = utils::UUID{};
|
||||
// If for some replicas swap fails, for others on successful ping we will revert back on next change
|
||||
// or we will do failover first again and then it will be consistent again
|
||||
for (auto &other_replica_instance : alive_replicas | ranges::views::filter(is_not_new_main)) {
|
||||
if (!other_replica_instance.SendSwapAndUpdateUUID(new_main_uuid)) {
|
||||
spdlog::error(fmt::format("Failed to swap uuid for instance {} which is alive, aborting failover",
|
||||
other_replica_instance.InstanceName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto repl_clients_info = repl_instances_ | ranges::views::filter(is_not_new_main) |
|
||||
ranges::views::transform(&ReplicationInstance::ReplicationClientInfo) |
|
||||
ranges::to<ReplicationClientsInfo>();
|
||||
|
||||
if (!new_main->PromoteToMain(new_main_uuid, std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
|
||||
spdlog::warn("Failover failed since promoting replica to main failed!");
|
||||
return;
|
||||
}
|
||||
// TODO: (andi) This should be replicated across all coordinator instances with Raft log
|
||||
SetMainUUID(new_main_uuid);
|
||||
spdlog::info("Failover successful! Instance {} promoted to main.", new_main->InstanceName());
|
||||
}
|
||||
|
||||
// TODO: (andi) Make sure you cannot put coordinator instance to the main
|
||||
auto CoordinatorInstance::SetReplicationInstanceToMain(std::string instance_name)
|
||||
-> SetInstanceToMainCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_instance_lock_};
|
||||
|
||||
if (std::ranges::any_of(repl_instances_, &ReplicationInstance::IsMain)) {
|
||||
return SetInstanceToMainCoordinatorStatus::MAIN_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
auto const is_new_main = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == instance_name;
|
||||
};
|
||||
auto new_main = std::ranges::find_if(repl_instances_, is_new_main);
|
||||
|
||||
if (new_main == repl_instances_.end()) {
|
||||
spdlog::error("Instance {} not registered. Please register it using REGISTER INSTANCE {}", instance_name,
|
||||
instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::NO_INSTANCE_WITH_NAME;
|
||||
}
|
||||
|
||||
new_main->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&new_main] { new_main->ResumeFrequentCheck(); }};
|
||||
|
||||
auto const is_not_new_main = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() != instance_name;
|
||||
};
|
||||
|
||||
auto const new_main_uuid = utils::UUID{};
|
||||
|
||||
for (auto &other_instance : repl_instances_ | ranges::views::filter(is_not_new_main)) {
|
||||
if (!other_instance.SendSwapAndUpdateUUID(new_main_uuid)) {
|
||||
spdlog::error(
|
||||
fmt::format("Failed to swap uuid for instance {}, aborting failover", other_instance.InstanceName()));
|
||||
return SetInstanceToMainCoordinatorStatus::SWAP_UUID_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
ReplicationClientsInfo repl_clients_info;
|
||||
repl_clients_info.reserve(repl_instances_.size() - 1);
|
||||
std::ranges::transform(repl_instances_ | ranges::views::filter(is_not_new_main),
|
||||
std::back_inserter(repl_clients_info), &ReplicationInstance::ReplicationClientInfo);
|
||||
|
||||
if (!new_main->PromoteToMain(new_main_uuid, std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
|
||||
return SetInstanceToMainCoordinatorStatus::COULD_NOT_PROMOTE_TO_MAIN;
|
||||
}
|
||||
|
||||
// TODO: (andi) This should be replicated across all coordinator instances with Raft log
|
||||
SetMainUUID(new_main_uuid);
|
||||
spdlog::info("Instance {} promoted to main", instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::RegisterReplicationInstance(CoordinatorClientConfig config)
|
||||
-> RegisterInstanceCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_instance_lock_};
|
||||
|
||||
auto instance_name = config.instance_name;
|
||||
|
||||
auto const name_matches = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == instance_name;
|
||||
};
|
||||
|
||||
if (std::ranges::any_of(repl_instances_, name_matches)) {
|
||||
return RegisterInstanceCoordinatorStatus::NAME_EXISTS;
|
||||
}
|
||||
|
||||
auto const socket_address_matches = [&config](ReplicationInstance const &instance) {
|
||||
return instance.SocketAddress() == config.SocketAddress();
|
||||
};
|
||||
|
||||
if (std::ranges::any_of(repl_instances_, socket_address_matches)) {
|
||||
return RegisterInstanceCoordinatorStatus::ENDPOINT_EXISTS;
|
||||
}
|
||||
|
||||
if (!raft_state_.RequestLeadership()) {
|
||||
return RegisterInstanceCoordinatorStatus::NOT_LEADER;
|
||||
}
|
||||
|
||||
auto const res = raft_state_.AppendRegisterReplicationInstance(instance_name);
|
||||
if (!res->get_accepted()) {
|
||||
spdlog::error(
|
||||
"Failed to accept request for registering instance {}. Most likely the reason is that the instance is not "
|
||||
"the "
|
||||
"leader.",
|
||||
config.instance_name);
|
||||
return RegisterInstanceCoordinatorStatus::RAFT_COULD_NOT_ACCEPT;
|
||||
}
|
||||
|
||||
spdlog::info("Request for registering instance {} accepted", instance_name);
|
||||
try {
|
||||
repl_instances_.emplace_back(this, std::move(config), replica_succ_cb_, replica_fail_cb_);
|
||||
} catch (CoordinatorRegisterInstanceException const &) {
|
||||
return RegisterInstanceCoordinatorStatus::RPC_FAILED;
|
||||
}
|
||||
|
||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
||||
spdlog::error("Failed to register instance {} with error code {}", instance_name, res->get_result_code());
|
||||
return RegisterInstanceCoordinatorStatus::RAFT_COULD_NOT_APPEND;
|
||||
}
|
||||
|
||||
spdlog::info("Instance {} registered", instance_name);
|
||||
return RegisterInstanceCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::UnregisterReplicationInstance(std::string instance_name)
|
||||
-> UnregisterInstanceCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_instance_lock_};
|
||||
|
||||
auto const name_matches = [&instance_name](ReplicationInstance const &instance) {
|
||||
return instance.InstanceName() == instance_name;
|
||||
};
|
||||
|
||||
auto inst_to_remove = std::ranges::find_if(repl_instances_, name_matches);
|
||||
if (inst_to_remove == repl_instances_.end()) {
|
||||
return UnregisterInstanceCoordinatorStatus::NO_INSTANCE_WITH_NAME;
|
||||
}
|
||||
|
||||
if (inst_to_remove->IsMain() && inst_to_remove->IsAlive()) {
|
||||
return UnregisterInstanceCoordinatorStatus::IS_MAIN;
|
||||
}
|
||||
|
||||
inst_to_remove->StopFrequentCheck();
|
||||
auto curr_main = std::ranges::find_if(repl_instances_, &ReplicationInstance::IsMain);
|
||||
MG_ASSERT(curr_main != repl_instances_.end(), "There must be a main instance when unregistering a replica");
|
||||
if (!curr_main->SendUnregisterReplicaRpc(instance_name)) {
|
||||
inst_to_remove->StartFrequentCheck();
|
||||
return UnregisterInstanceCoordinatorStatus::RPC_FAILED;
|
||||
}
|
||||
std::erase_if(repl_instances_, name_matches);
|
||||
|
||||
return UnregisterInstanceCoordinatorStatus::SUCCESS;
|
||||
auto CoordinatorInstance::RaftSocketAddress() const -> std::string {
|
||||
return raft_address_ + ":" + std::to_string(raft_port_);
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||
-> void {
|
||||
raft_state_.AddCoordinatorInstance(raft_server_id, raft_port, std::move(raft_address));
|
||||
auto const endpoint = raft_address + ":" + std::to_string(raft_port);
|
||||
srv_config const srv_config_to_add(static_cast<int>(raft_server_id), endpoint);
|
||||
if (!raft_server_->add_srv(srv_config_to_add)->get_accepted()) {
|
||||
throw RaftAddServerException("Failed to add server {} to the cluster", endpoint);
|
||||
}
|
||||
spdlog::info("Request to add server {} to the cluster accepted", endpoint);
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::GetMainUUID() const -> utils::UUID { return main_uuid_; }
|
||||
|
||||
// TODO: (andi) Add to the RAFT log.
|
||||
auto CoordinatorInstance::SetMainUUID(utils::UUID new_uuid) -> void { main_uuid_ = new_uuid; }
|
||||
auto CoordinatorInstance::GetAllCoordinators() const -> std::vector<ptr<srv_config>> {
|
||||
std::vector<ptr<srv_config>> all_srv_configs;
|
||||
raft_server_->get_srv_config_all(all_srv_configs);
|
||||
return all_srv_configs;
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
||||
@@ -13,149 +13,214 @@
|
||||
|
||||
#include "nuraft/coordinator_log_store.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using nuraft::cs_new;
|
||||
using nuraft::timer_helper;
|
||||
|
||||
namespace {
|
||||
|
||||
ptr<log_entry> MakeClone(const ptr<log_entry> &entry) {
|
||||
return cs_new<log_entry>(entry->get_term(), buffer::clone(entry->get_buf()), entry->get_val_type(),
|
||||
entry->get_timestamp());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CoordinatorLogStore::CoordinatorLogStore() : start_idx_(1) {
|
||||
ptr<buffer> buf = buffer::alloc(sizeof(uint64_t));
|
||||
CoordinatorLogStore::CoordinatorLogStore()
|
||||
: start_idx_(1),
|
||||
raft_server_bwd_pointer_(nullptr),
|
||||
disk_emul_delay(0),
|
||||
disk_emul_thread_(nullptr),
|
||||
disk_emul_thread_stop_signal_(false),
|
||||
disk_emul_last_durable_index_(0) {
|
||||
// Dummy entry for index 0.
|
||||
ptr<buffer> buf = buffer::alloc(sz_ulong);
|
||||
logs_[0] = cs_new<log_entry>(0, buf);
|
||||
}
|
||||
|
||||
CoordinatorLogStore::~CoordinatorLogStore() {}
|
||||
|
||||
auto CoordinatorLogStore::FindOrDefault_(uint64_t index) const -> ptr<log_entry> {
|
||||
auto entry = logs_.find(index);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
CoordinatorLogStore::~CoordinatorLogStore() {
|
||||
if (disk_emul_thread_) {
|
||||
disk_emul_thread_stop_signal_ = true;
|
||||
// disk_emul_ea_.invoke();
|
||||
if (disk_emul_thread_->joinable()) {
|
||||
disk_emul_thread_->join();
|
||||
}
|
||||
}
|
||||
return entry->second;
|
||||
}
|
||||
|
||||
uint64_t CoordinatorLogStore::next_slot() const {
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
ptr<log_entry> CoordinatorLogStore::MakeClone(const ptr<log_entry> &entry) {
|
||||
// NOTE:
|
||||
// Timestamp is used only when `replicate_log_timestamp_` option is on.
|
||||
// Otherwise, log store does not need to store or load it.
|
||||
ptr<log_entry> clone = cs_new<log_entry>(entry->get_term(), buffer::clone(entry->get_buf()), entry->get_val_type(),
|
||||
entry->get_timestamp());
|
||||
return clone;
|
||||
}
|
||||
|
||||
ulong CoordinatorLogStore::next_slot() const {
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
// Exclude the dummy entry.
|
||||
return start_idx_ + logs_.size() - 1;
|
||||
}
|
||||
|
||||
uint64_t CoordinatorLogStore::start_index() const { return start_idx_; }
|
||||
ulong CoordinatorLogStore::start_index() const { return start_idx_; }
|
||||
|
||||
ptr<log_entry> CoordinatorLogStore::last_entry() const {
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
|
||||
uint64_t const last_idx = start_idx_ + logs_.size() - 1;
|
||||
auto const last_src = FindOrDefault_(last_idx - 1);
|
||||
|
||||
return MakeClone(last_src);
|
||||
}
|
||||
|
||||
uint64_t CoordinatorLogStore::append(ptr<log_entry> &entry) {
|
||||
ptr<log_entry> clone = MakeClone(entry);
|
||||
uint64_t next_slot{0};
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
next_slot = start_idx_ + logs_.size() - 1;
|
||||
logs_[next_slot] = clone;
|
||||
ulong next_idx = next_slot();
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.find(next_idx - 1);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
}
|
||||
return next_slot;
|
||||
|
||||
return MakeClone(entry->second);
|
||||
}
|
||||
|
||||
void CoordinatorLogStore::write_at(uint64_t index, ptr<log_entry> &entry) {
|
||||
ulong CoordinatorLogStore::append(ptr<log_entry> &entry) {
|
||||
ptr<log_entry> clone = MakeClone(entry);
|
||||
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
size_t idx = start_idx_ + logs_.size() - 1;
|
||||
logs_[idx] = clone;
|
||||
|
||||
if (disk_emul_delay) {
|
||||
uint64_t cur_time = timer_helper::get_timeofday_us();
|
||||
disk_emul_logs_being_written_[cur_time + disk_emul_delay * 1000] = idx;
|
||||
// disk_emul_ea_.invoke();
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void CoordinatorLogStore::write_at(ulong index, ptr<log_entry> &entry) {
|
||||
ptr<log_entry> clone = MakeClone(entry);
|
||||
|
||||
// Discard all logs equal to or greater than `index.
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
auto itr = logs_.lower_bound(index);
|
||||
while (itr != logs_.end()) {
|
||||
itr = logs_.erase(itr);
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto itr = logs_.lower_bound(index);
|
||||
while (itr != logs_.end()) {
|
||||
itr = logs_.erase(itr);
|
||||
}
|
||||
logs_[index] = clone;
|
||||
|
||||
if (disk_emul_delay) {
|
||||
uint64_t cur_time = timer_helper::get_timeofday_us();
|
||||
disk_emul_logs_being_written_[cur_time + disk_emul_delay * 1000] = index;
|
||||
|
||||
// Remove entries greater than `index`.
|
||||
auto entry = disk_emul_logs_being_written_.begin();
|
||||
while (entry != disk_emul_logs_being_written_.end()) {
|
||||
if (entry->second > index) {
|
||||
entry = disk_emul_logs_being_written_.erase(entry);
|
||||
} else {
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
logs_[index] = clone;
|
||||
// disk_emul_ea_.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
ptr<std::vector<ptr<log_entry>>> CoordinatorLogStore::log_entries(uint64_t start, uint64_t end) {
|
||||
auto ret = cs_new<std::vector<ptr<log_entry>>>();
|
||||
ret->resize(end - start);
|
||||
ptr<std::vector<ptr<log_entry>>> CoordinatorLogStore::log_entries(ulong start, ulong end) {
|
||||
ptr<std::vector<ptr<log_entry>>> ret = cs_new<std::vector<ptr<log_entry>>>();
|
||||
|
||||
for (uint64_t i = start, curr_index = 0; i < end; ++i, ++curr_index) {
|
||||
ret->resize(end - start);
|
||||
ulong cc = 0;
|
||||
for (ulong ii = start; ii < end; ++ii) {
|
||||
ptr<log_entry> src = nullptr;
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
if (auto const entry = logs_.find(i); entry != logs_.end()) {
|
||||
src = entry->second;
|
||||
} else {
|
||||
throw RaftCouldNotFindEntryException("Could not find entry at index {}", i);
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.find(ii);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
assert(0);
|
||||
}
|
||||
src = entry->second;
|
||||
}
|
||||
(*ret)[curr_index] = MakeClone(src);
|
||||
(*ret)[cc++] = MakeClone(src);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptr<log_entry> CoordinatorLogStore::entry_at(uint64_t index) {
|
||||
// NOLINTNEXTLINE(google-default-arguments)
|
||||
ptr<std::vector<ptr<log_entry>>> CoordinatorLogStore::log_entries_ext(ulong start, ulong end,
|
||||
int64 batch_size_hint_in_bytes) {
|
||||
ptr<std::vector<ptr<log_entry>>> ret = cs_new<std::vector<ptr<log_entry>>>();
|
||||
|
||||
if (batch_size_hint_in_bytes < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t accum_size = 0;
|
||||
for (ulong ii = start; ii < end; ++ii) {
|
||||
ptr<log_entry> src = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.find(ii);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
assert(0);
|
||||
}
|
||||
src = entry->second;
|
||||
}
|
||||
ret->push_back(MakeClone(src));
|
||||
accum_size += src->get_buf().size();
|
||||
if (batch_size_hint_in_bytes && accum_size >= (ulong)batch_size_hint_in_bytes) break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptr<log_entry> CoordinatorLogStore::entry_at(ulong index) {
|
||||
ptr<log_entry> src = nullptr;
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
src = FindOrDefault_(index);
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.find(index);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
}
|
||||
src = entry->second;
|
||||
}
|
||||
return MakeClone(src);
|
||||
}
|
||||
|
||||
uint64_t CoordinatorLogStore::term_at(uint64_t index) {
|
||||
uint64_t term = 0;
|
||||
ulong CoordinatorLogStore::term_at(ulong index) {
|
||||
ulong term = 0;
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
term = FindOrDefault_(index)->get_term();
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.find(index);
|
||||
if (entry == logs_.end()) {
|
||||
entry = logs_.find(0);
|
||||
}
|
||||
term = entry->second->get_term();
|
||||
}
|
||||
return term;
|
||||
}
|
||||
|
||||
ptr<buffer> CoordinatorLogStore::pack(uint64_t index, int32 cnt) {
|
||||
ptr<buffer> CoordinatorLogStore::pack(ulong index, int32 cnt) {
|
||||
std::vector<ptr<buffer>> logs;
|
||||
|
||||
size_t size_total = 0;
|
||||
uint64_t const end_index = index + cnt;
|
||||
for (uint64_t i = index; i < end_index; ++i) {
|
||||
for (ulong ii = index; ii < index + cnt; ++ii) {
|
||||
ptr<log_entry> le = nullptr;
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
le = logs_[i];
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
le = logs_[ii];
|
||||
}
|
||||
MG_ASSERT(le.get(), "Could not find log entry at index {}", i);
|
||||
auto buf = le->serialize();
|
||||
assert(le.get());
|
||||
ptr<buffer> buf = le->serialize();
|
||||
size_total += buf->size();
|
||||
logs.push_back(buf);
|
||||
}
|
||||
|
||||
auto buf_out = buffer::alloc(sizeof(int32) + cnt * sizeof(int32) + size_total);
|
||||
ptr<buffer> buf_out = buffer::alloc(sizeof(int32) + cnt * sizeof(int32) + size_total);
|
||||
buf_out->pos(0);
|
||||
buf_out->put((int32)cnt);
|
||||
|
||||
for (auto &entry : logs) {
|
||||
buf_out->put(static_cast<int32>(entry->size()));
|
||||
buf_out->put(*entry);
|
||||
ptr<buffer> &bb = entry;
|
||||
buf_out->put((int32)bb->size());
|
||||
buf_out->put(*bb);
|
||||
}
|
||||
return buf_out;
|
||||
}
|
||||
|
||||
void CoordinatorLogStore::apply_pack(uint64_t index, buffer &pack) {
|
||||
void CoordinatorLogStore::apply_pack(ulong index, buffer &pack) {
|
||||
pack.pos(0);
|
||||
int32 const num_logs = pack.get_int();
|
||||
int32 num_logs = pack.get_int();
|
||||
|
||||
for (int32 i = 0; i < num_logs; ++i) {
|
||||
uint64_t cur_idx = index + i;
|
||||
for (int32 ii = 0; ii < num_logs; ++ii) {
|
||||
ulong cur_idx = index + ii;
|
||||
int32 buf_size = pack.get_int();
|
||||
|
||||
ptr<buffer> buf_local = buffer::alloc(buf_size);
|
||||
@@ -163,14 +228,14 @@ void CoordinatorLogStore::apply_pack(uint64_t index, buffer &pack) {
|
||||
|
||||
ptr<log_entry> le = log_entry::deserialize(*buf_local);
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
logs_[cur_idx] = le;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
auto const entry = logs_.upper_bound(0);
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
auto entry = logs_.upper_bound(0);
|
||||
if (entry != logs_.end()) {
|
||||
start_idx_ = entry->first;
|
||||
} else {
|
||||
@@ -179,23 +244,88 @@ void CoordinatorLogStore::apply_pack(uint64_t index, buffer &pack) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Remove all logs up to given 'last_log_index' (inclusive).
|
||||
bool CoordinatorLogStore::compact(uint64_t last_log_index) {
|
||||
auto lock = std::lock_guard{logs_lock_};
|
||||
for (uint64_t ii = start_idx_; ii <= last_log_index; ++ii) {
|
||||
auto const entry = logs_.find(ii);
|
||||
bool CoordinatorLogStore::compact(ulong last_log_index) {
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
for (ulong ii = start_idx_; ii <= last_log_index; ++ii) {
|
||||
auto entry = logs_.find(ii);
|
||||
if (entry != logs_.end()) {
|
||||
logs_.erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING:
|
||||
// Even though nothing has been erased,
|
||||
// we should set `start_idx_` to new index.
|
||||
if (start_idx_ <= last_log_index) {
|
||||
start_idx_ = last_log_index + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoordinatorLogStore::flush() { return true; }
|
||||
bool CoordinatorLogStore::flush() {
|
||||
disk_emul_last_durable_index_ = next_slot() - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ulong CoordinatorLogStore::last_durable_index() {
|
||||
uint64_t last_log = next_slot() - 1;
|
||||
if (!disk_emul_delay) {
|
||||
return last_log;
|
||||
}
|
||||
|
||||
return disk_emul_last_durable_index_;
|
||||
}
|
||||
|
||||
void CoordinatorLogStore::DiskEmulLoop() {
|
||||
// This thread mimics async disk writes.
|
||||
|
||||
// uint32_t next_sleep_us = 100 * 1000;
|
||||
while (!disk_emul_thread_stop_signal_) {
|
||||
// disk_emul_ea_.wait_us(next_sleep_us);
|
||||
// disk_emul_ea_.reset();
|
||||
if (disk_emul_thread_stop_signal_) break;
|
||||
|
||||
uint64_t cur_time = timer_helper::get_timeofday_us();
|
||||
// next_sleep_us = 100 * 1000;
|
||||
|
||||
bool call_notification = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(logs_lock_);
|
||||
// Remove all timestamps equal to or smaller than `cur_time`,
|
||||
// and pick the greatest one among them.
|
||||
auto entry = disk_emul_logs_being_written_.begin();
|
||||
while (entry != disk_emul_logs_being_written_.end()) {
|
||||
if (entry->first <= cur_time) {
|
||||
disk_emul_last_durable_index_ = entry->second;
|
||||
entry = disk_emul_logs_being_written_.erase(entry);
|
||||
call_notification = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entry = disk_emul_logs_being_written_.begin();
|
||||
if (entry != disk_emul_logs_being_written_.end()) {
|
||||
// next_sleep_us = entry->first - cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
if (call_notification) {
|
||||
raft_server_bwd_pointer_->notify_log_append_completion(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CoordinatorLogStore::Close() {}
|
||||
|
||||
void CoordinatorLogStore::SetDiskDelay(raft_server *raft, size_t delay_ms) {
|
||||
disk_emul_delay = delay_ms;
|
||||
raft_server_bwd_pointer_ = raft;
|
||||
|
||||
if (!disk_emul_thread_) {
|
||||
disk_emul_thread_ = std::make_unique<std::thread>(&CoordinatorLogStore::DiskEmulLoop, this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
||||
@@ -52,51 +52,6 @@ void DemoteMainToReplicaRes::Load(DemoteMainToReplicaRes *self, memgraph::slk::R
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void UnregisterReplicaReq::Save(UnregisterReplicaReq const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void UnregisterReplicaReq::Load(UnregisterReplicaReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void UnregisterReplicaRes::Save(UnregisterReplicaRes const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void UnregisterReplicaRes::Load(UnregisterReplicaRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void EnableWritingOnMainRes::Save(EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void EnableWritingOnMainRes::Load(EnableWritingOnMainRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void EnableWritingOnMainReq::Save(EnableWritingOnMainReq const &self, memgraph::slk::Builder *builder) {}
|
||||
|
||||
void EnableWritingOnMainReq::Load(EnableWritingOnMainReq *self, memgraph::slk::Reader *reader) {}
|
||||
|
||||
// GetInstanceUUID
|
||||
void GetInstanceUUIDReq::Save(const GetInstanceUUIDReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void GetInstanceUUIDReq::Load(GetInstanceUUIDReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void GetInstanceUUIDRes::Save(const GetInstanceUUIDRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void GetInstanceUUIDRes::Load(GetInstanceUUIDRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
} // namespace coordination
|
||||
|
||||
constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ,
|
||||
@@ -109,31 +64,10 @@ constexpr utils::TypeInfo coordination::DemoteMainToReplicaReq::kType{utils::Typ
|
||||
"CoordDemoteToReplicaReq", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::DemoteMainToReplicaRes::kType{utils::TypeId::COORD_SET_REPL_MAIN_RES,
|
||||
|
||||
"CoordDemoteToReplicaRes", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::UnregisterReplicaReq::kType{utils::TypeId::COORD_UNREGISTER_REPLICA_REQ,
|
||||
"UnregisterReplicaReq", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::UnregisterReplicaRes::kType{utils::TypeId::COORD_UNREGISTER_REPLICA_RES,
|
||||
"UnregisterReplicaRes", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::EnableWritingOnMainReq::kType{utils::TypeId::COORD_ENABLE_WRITING_ON_MAIN_REQ,
|
||||
"CoordEnableWritingOnMainReq", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::EnableWritingOnMainRes::kType{utils::TypeId::COORD_ENABLE_WRITING_ON_MAIN_RES,
|
||||
"CoordEnableWritingOnMainRes", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::GetInstanceUUIDReq::kType{utils::TypeId::COORD_GET_UUID_REQ, "CoordGetUUIDReq",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::GetInstanceUUIDRes::kType{utils::TypeId::COORD_GET_UUID_RES, "CoordGetUUIDRes",
|
||||
nullptr};
|
||||
|
||||
namespace slk {
|
||||
|
||||
// PromoteReplicaToMainRpc
|
||||
|
||||
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.success, builder);
|
||||
}
|
||||
@@ -152,7 +86,6 @@ void Load(memgraph::coordination::PromoteReplicaToMainReq *self, memgraph::slk::
|
||||
memgraph::slk::Load(&self->replication_clients_info, reader);
|
||||
}
|
||||
|
||||
// DemoteMainToReplicaRpc
|
||||
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.replication_client_info, builder);
|
||||
}
|
||||
@@ -169,50 +102,6 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
|
||||
memgraph::slk::Load(&self->success, reader);
|
||||
}
|
||||
|
||||
// UnregisterReplicaRpc
|
||||
|
||||
void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.instance_name, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::UnregisterReplicaReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->instance_name, reader);
|
||||
}
|
||||
|
||||
void Save(memgraph::coordination::UnregisterReplicaRes const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.success, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::UnregisterReplicaRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->success, reader);
|
||||
}
|
||||
|
||||
void Save(memgraph::coordination::EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.success, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->success, reader);
|
||||
}
|
||||
|
||||
// GetInstanceUUIDRpc
|
||||
|
||||
void Save(const memgraph::coordination::GetInstanceUUIDReq & /*self*/, memgraph::slk::Builder * /*builder*/) {
|
||||
/* nothing to serialize*/
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::GetInstanceUUIDReq * /*self*/, memgraph::slk::Reader * /*reader*/) {
|
||||
/* nothing to serialize*/
|
||||
}
|
||||
|
||||
void Save(const memgraph::coordination::GetInstanceUUIDRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.uuid, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::GetInstanceUUIDRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->uuid, reader);
|
||||
}
|
||||
|
||||
} // namespace slk
|
||||
|
||||
} // namespace memgraph
|
||||
|
||||
@@ -41,53 +41,37 @@ CoordinatorState::CoordinatorState() {
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorState::RegisterReplicationInstance(CoordinatorClientConfig config)
|
||||
-> RegisterInstanceCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
auto CoordinatorState::RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return RegisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[config](CoordinatorInstance &coordinator_instance) {
|
||||
return coordinator_instance.RegisterReplicationInstance(config);
|
||||
}},
|
||||
memgraph::utils::Overloaded{
|
||||
[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return RegisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[config](CoordinatorData &coordinator_data) { return coordinator_data.RegisterInstance(config); }},
|
||||
data_);
|
||||
}
|
||||
|
||||
auto CoordinatorState::UnregisterReplicationInstance(std::string instance_name) -> UnregisterInstanceCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
"Coordinator cannot unregister instance since variant holds wrong alternative");
|
||||
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return UnregisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[&instance_name](CoordinatorInstance &coordinator_instance) {
|
||||
return coordinator_instance.UnregisterReplicationInstance(instance_name);
|
||||
}},
|
||||
data_);
|
||||
}
|
||||
|
||||
auto CoordinatorState::SetReplicationInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
auto CoordinatorState::SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return SetInstanceToMainCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[&instance_name](CoordinatorInstance &coordinator_instance) {
|
||||
return coordinator_instance.SetReplicationInstanceToMain(instance_name);
|
||||
[&instance_name](CoordinatorData &coordinator_data) {
|
||||
return coordinator_data.SetInstanceToMain(instance_name);
|
||||
}},
|
||||
data_);
|
||||
}
|
||||
|
||||
auto CoordinatorState::ShowInstances() const -> std::vector<InstanceStatus> {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Can't call show instances on data_, as variant holds wrong alternative");
|
||||
return std::get<CoordinatorInstance>(data_).ShowInstances();
|
||||
return std::get<CoordinatorData>(data_).ShowInstances();
|
||||
}
|
||||
|
||||
auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
||||
@@ -98,9 +82,9 @@ auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
||||
|
||||
auto CoordinatorState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||
-> void {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
return std::get<CoordinatorInstance>(data_).AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
||||
return std::get<CoordinatorData>(data_).AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
@@ -15,19 +15,6 @@
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
auto CoordinatorStateMachine::EncodeRegisterReplicationInstance(const std::string &name) -> ptr<buffer> {
|
||||
std::string str_log = name + "_replica";
|
||||
ptr<buffer> log = buffer::alloc(sizeof(uint32_t) + str_log.size());
|
||||
buffer_serializer bs(log);
|
||||
bs.put_str(str_log);
|
||||
return log;
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::DecodeRegisterReplicationInstance(buffer &data) -> std::string {
|
||||
buffer_serializer bs(data);
|
||||
return bs.get_str();
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::pre_commit(ulong const log_idx, buffer &data) -> ptr<buffer> {
|
||||
buffer_serializer bs(data);
|
||||
std::string str = bs.get_str();
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if FMT_VERSION > 90000
|
||||
#include <fmt/ostream.h>
|
||||
#include <string>
|
||||
|
||||
#include <libnuraft/nuraft.hxx>
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
inline std::string ToString(const nuraft::cmd_result_code &code) {
|
||||
switch (code) {
|
||||
case nuraft::cmd_result_code::OK:
|
||||
return "OK";
|
||||
case nuraft::cmd_result_code::FAILED:
|
||||
return "FAILED";
|
||||
case nuraft::cmd_result_code::RESULT_NOT_EXIST_YET:
|
||||
return "RESULT_NOT_EXIST_YET";
|
||||
case nuraft::cmd_result_code::TERM_MISMATCH:
|
||||
return "TERM_MISMATCH";
|
||||
case nuraft::cmd_result_code::SERVER_IS_LEAVING:
|
||||
return "SERVER_IS_LEAVING";
|
||||
case nuraft::cmd_result_code::CANNOT_REMOVE_LEADER:
|
||||
return "CANNOT_REMOVE_LEADER";
|
||||
case nuraft::cmd_result_code::SERVER_NOT_FOUND:
|
||||
return "SERVER_NOT_FOUND";
|
||||
case nuraft::cmd_result_code::SERVER_IS_JOINING:
|
||||
return "SERVER_IS_JOINING";
|
||||
case nuraft::cmd_result_code::CONFIG_CHANGING:
|
||||
return "CONFIG_CHANGING";
|
||||
case nuraft::cmd_result_code::SERVER_ALREADY_EXISTS:
|
||||
return "SERVER_ALREADY_EXISTS";
|
||||
case nuraft::cmd_result_code::BAD_REQUEST:
|
||||
return "BAD_REQUEST";
|
||||
case nuraft::cmd_result_code::NOT_LEADER:
|
||||
return "NOT_LEADER";
|
||||
case nuraft::cmd_result_code::TIMEOUT:
|
||||
return "TIMEOUT";
|
||||
case nuraft::cmd_result_code::CANCELLED:
|
||||
return "CANCELLED";
|
||||
}
|
||||
LOG_FATAL("ToString of a nuraft::cmd_result_code -> check missing switch case");
|
||||
}
|
||||
inline std::ostream &operator<<(std::ostream &os, const nuraft::cmd_result_code &code) {
|
||||
os << ToString(code);
|
||||
return os;
|
||||
}
|
||||
template <>
|
||||
class fmt::formatter<nuraft::cmd_result_code> : public fmt::ostream_formatter {};
|
||||
#endif
|
||||
@@ -9,6 +9,14 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::coordination {
|
||||
enum class GetInstanceUUIDError { NO_RESPONSE, RPC_EXCEPTION };
|
||||
|
||||
#ifdef MG_EXPERIMENTAL_HIGH_AVAILABILITY
|
||||
constexpr bool allow_ha = true;
|
||||
#else
|
||||
constexpr bool allow_ha = false;
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
@@ -11,25 +11,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/uuid.hpp"
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "rpc/client.hpp"
|
||||
#include "rpc_errors.hpp"
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/scheduler.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorInstance;
|
||||
using HealthCheckCallback = std::function<void(CoordinatorInstance *, std::string_view)>;
|
||||
class CoordinatorData;
|
||||
using HealthCheckCallback = std::function<void(CoordinatorData *, std::string_view)>;
|
||||
using ReplicationClientsInfo = std::vector<ReplClientInfo>;
|
||||
|
||||
class CoordinatorClient {
|
||||
public:
|
||||
explicit CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
||||
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb);
|
||||
explicit CoordinatorClient(CoordinatorData *coord_data_, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||
HealthCheckCallback fail_cb);
|
||||
|
||||
~CoordinatorClient() = default;
|
||||
|
||||
@@ -48,28 +46,17 @@ class CoordinatorClient {
|
||||
auto SocketAddress() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto DemoteToReplica() const -> bool;
|
||||
|
||||
auto SendPromoteReplicaToMainRpc(const utils::UUID &uuid, ReplicationClientsInfo replication_clients_info) const
|
||||
-> bool;
|
||||
|
||||
auto SendSwapMainUUIDRpc(const utils::UUID &uuid) const -> bool;
|
||||
|
||||
auto SendUnregisterReplicaRpc(std::string const &instance_name) const -> bool;
|
||||
|
||||
auto SendEnableWritingOnMainRpc() const -> bool;
|
||||
|
||||
auto SendGetInstanceUUIDRpc() const -> memgraph::utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>>;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
|
||||
auto SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void;
|
||||
|
||||
auto RpcClient() -> rpc::Client & { return rpc_client_; }
|
||||
|
||||
auto InstanceDownTimeoutSec() const -> std::chrono::seconds;
|
||||
|
||||
auto InstanceGetUUIDFrequencySec() const -> std::chrono::seconds;
|
||||
|
||||
friend bool operator==(CoordinatorClient const &first, CoordinatorClient const &second) {
|
||||
return first.config_ == second.config_;
|
||||
}
|
||||
@@ -77,11 +64,12 @@ class CoordinatorClient {
|
||||
private:
|
||||
utils::Scheduler instance_checker_;
|
||||
|
||||
// TODO: (andi) Pimpl?
|
||||
communication::ClientContext rpc_context_;
|
||||
mutable rpc::Client rpc_client_;
|
||||
|
||||
CoordinatorClientConfig config_;
|
||||
CoordinatorInstance *coord_instance_;
|
||||
CoordinatorData *coord_data_;
|
||||
HealthCheckCallback succ_cb_;
|
||||
HealthCheckCallback fail_cb_;
|
||||
};
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if FMT_VERSION > 90000
|
||||
#include <fmt/ostream.h>
|
||||
#ifdef MG_ENTERPRISE
|
||||
namespace memgraph::coordination {
|
||||
|
||||
#include "io/network/endpoint.hpp"
|
||||
struct CoordinatorClusterConfig {
|
||||
static constexpr int alive_response_time_difference_sec_{5};
|
||||
};
|
||||
|
||||
template <>
|
||||
class fmt::formatter<memgraph::io::network::Endpoint> : public fmt::ostream_formatter {};
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -28,9 +28,7 @@ struct CoordinatorClientConfig {
|
||||
std::string instance_name;
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
std::chrono::seconds instance_health_check_frequency_sec{1};
|
||||
std::chrono::seconds instance_down_timeout_sec{5};
|
||||
std::chrono::seconds instance_get_uuid_frequency_sec{10};
|
||||
std::chrono::seconds health_check_frequency_sec{1};
|
||||
|
||||
auto SocketAddress() const -> std::string { return ip_address + ":" + std::to_string(port); }
|
||||
|
||||
|
||||
61
src/coordination/include/coordination/coordinator_data.hpp
Normal file
61
src/coordination/include/coordination/coordinator_data.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "coordination/instance_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "coordination/replication_instance.hpp"
|
||||
#include "replication_coordination_glue/handler.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/thread_pool.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
class CoordinatorData {
|
||||
public:
|
||||
CoordinatorData();
|
||||
|
||||
// TODO: (andi) Probably rename to RegisterReplicationInstance
|
||||
[[nodiscard]] auto RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
|
||||
[[nodiscard]] auto SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
||||
|
||||
auto TryFailover() -> void;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||
|
||||
private:
|
||||
HealthCheckCallback main_succ_cb_, main_fail_cb_, replica_succ_cb_, replica_fail_cb_;
|
||||
|
||||
// NOTE: Must be std::list because we rely on pointer stability
|
||||
std::list<ReplicationInstance> repl_instances_;
|
||||
mutable utils::RWLock coord_data_lock_{utils::RWLock::Priority::READ};
|
||||
|
||||
CoordinatorInstance self_;
|
||||
|
||||
utils::UUID main_uuid_;
|
||||
};
|
||||
|
||||
struct CoordinatorMainReplicaData {
|
||||
std::unique_ptr<CoordinatorServer> coordinator_server_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -50,38 +50,5 @@ class RaftAddServerException final : public utils::BasicException {
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(RaftAddServerException)
|
||||
};
|
||||
|
||||
class RaftBecomeLeaderException final : public utils::BasicException {
|
||||
public:
|
||||
explicit RaftBecomeLeaderException(std::string_view what) noexcept : BasicException(what) {}
|
||||
|
||||
template <class... Args>
|
||||
explicit RaftBecomeLeaderException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
||||
: RaftBecomeLeaderException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(RaftBecomeLeaderException)
|
||||
};
|
||||
|
||||
class RaftCouldNotFindEntryException final : public utils::BasicException {
|
||||
public:
|
||||
explicit RaftCouldNotFindEntryException(std::string_view what) noexcept : BasicException(what) {}
|
||||
|
||||
template <class... Args>
|
||||
explicit RaftCouldNotFindEntryException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
||||
: RaftCouldNotFindEntryException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(RaftCouldNotFindEntryException)
|
||||
};
|
||||
|
||||
class RaftCouldNotParseFlagsException final : public utils::BasicException {
|
||||
public:
|
||||
explicit RaftCouldNotParseFlagsException(std::string_view what) noexcept : BasicException(what) {}
|
||||
|
||||
template <class... Args>
|
||||
explicit RaftCouldNotParseFlagsException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
||||
: RaftCouldNotParseFlagsException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(RaftCouldNotParseFlagsException)
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
||||
@@ -33,14 +33,6 @@ class CoordinatorHandlers {
|
||||
slk::Builder *res_builder);
|
||||
static void SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder);
|
||||
|
||||
static void UnregisterReplicaHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder);
|
||||
static void EnableWritingOnMainHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder);
|
||||
|
||||
static void GetInstanceUUIDHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder);
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
@@ -13,47 +13,45 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "coordination/instance_status.hpp"
|
||||
#include "coordination/raft_state.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "coordination/replication_instance.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/thread_pool.hpp"
|
||||
#include <flags/replication.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <libnuraft/nuraft.hxx>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using nuraft::logger;
|
||||
using nuraft::ptr;
|
||||
using nuraft::raft_launcher;
|
||||
using nuraft::raft_server;
|
||||
using nuraft::srv_config;
|
||||
using nuraft::state_machine;
|
||||
using nuraft::state_mgr;
|
||||
|
||||
class CoordinatorInstance {
|
||||
public:
|
||||
CoordinatorInstance();
|
||||
CoordinatorInstance(CoordinatorInstance const &other) = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance const &other) = delete;
|
||||
CoordinatorInstance(CoordinatorInstance &&other) noexcept = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance &&other) noexcept = delete;
|
||||
~CoordinatorInstance() = default;
|
||||
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string instance_name) -> UnregisterInstanceCoordinatorStatus;
|
||||
|
||||
[[nodiscard]] auto SetReplicationInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
||||
|
||||
auto TryFailover() -> void;
|
||||
|
||||
auto InstanceName() const -> std::string;
|
||||
auto RaftSocketAddress() const -> std::string;
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||
|
||||
auto GetMainUUID() const -> utils::UUID;
|
||||
|
||||
auto SetMainUUID(utils::UUID new_uuid) -> void;
|
||||
auto GetAllCoordinators() const -> std::vector<ptr<srv_config>>;
|
||||
|
||||
private:
|
||||
HealthCheckCallback main_succ_cb_, main_fail_cb_, replica_succ_cb_, replica_fail_cb_;
|
||||
ptr<state_machine> state_machine_;
|
||||
ptr<state_mgr> state_manager_;
|
||||
ptr<raft_server> raft_server_;
|
||||
ptr<logger> logger_;
|
||||
raft_launcher launcher_;
|
||||
|
||||
// NOTE: Must be std::list because we rely on pointer stability
|
||||
std::list<ReplicationInstance> repl_instances_;
|
||||
mutable utils::RWLock coord_instance_lock_{utils::RWLock::Priority::READ};
|
||||
|
||||
utils::UUID main_uuid_;
|
||||
|
||||
RaftState raft_state_;
|
||||
// TODO: (andi) I think variables below can be abstracted
|
||||
uint32_t raft_server_id_;
|
||||
uint32_t raft_port_;
|
||||
std::string raft_address_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
@@ -82,85 +82,6 @@ struct DemoteMainToReplicaRes {
|
||||
|
||||
using DemoteMainToReplicaRpc = rpc::RequestResponse<DemoteMainToReplicaReq, DemoteMainToReplicaRes>;
|
||||
|
||||
struct UnregisterReplicaReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(UnregisterReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(UnregisterReplicaReq const &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit UnregisterReplicaReq(std::string instance_name) : instance_name(std::move(instance_name)) {}
|
||||
|
||||
UnregisterReplicaReq() = default;
|
||||
|
||||
std::string instance_name;
|
||||
};
|
||||
|
||||
struct UnregisterReplicaRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(UnregisterReplicaRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const UnregisterReplicaRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit UnregisterReplicaRes(bool success) : success(success) {}
|
||||
UnregisterReplicaRes() = default;
|
||||
|
||||
bool success;
|
||||
};
|
||||
|
||||
using UnregisterReplicaRpc = rpc::RequestResponse<UnregisterReplicaReq, UnregisterReplicaRes>;
|
||||
|
||||
struct EnableWritingOnMainReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(EnableWritingOnMainReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(EnableWritingOnMainReq const &self, memgraph::slk::Builder *builder);
|
||||
|
||||
EnableWritingOnMainReq() = default;
|
||||
};
|
||||
|
||||
struct EnableWritingOnMainRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(EnableWritingOnMainRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit EnableWritingOnMainRes(bool success) : success(success) {}
|
||||
EnableWritingOnMainRes() = default;
|
||||
|
||||
bool success;
|
||||
};
|
||||
|
||||
using EnableWritingOnMainRpc = rpc::RequestResponse<EnableWritingOnMainReq, EnableWritingOnMainRes>;
|
||||
|
||||
struct GetInstanceUUIDReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(GetInstanceUUIDReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const GetInstanceUUIDReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
GetInstanceUUIDReq() = default;
|
||||
};
|
||||
|
||||
struct GetInstanceUUIDRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(GetInstanceUUIDRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const GetInstanceUUIDRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit GetInstanceUUIDRes(std::optional<utils::UUID> uuid) : uuid(uuid) {}
|
||||
GetInstanceUUIDRes() = default;
|
||||
|
||||
std::optional<utils::UUID> uuid;
|
||||
};
|
||||
|
||||
using GetInstanceUUIDRpc = rpc::RequestResponse<GetInstanceUUIDReq, GetInstanceUUIDRes>;
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
// SLK serialization declarations
|
||||
@@ -178,19 +99,6 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
|
||||
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::DemoteMainToReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
|
||||
// GetInstanceUUIDRpc
|
||||
void Save(const memgraph::coordination::GetInstanceUUIDReq &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::GetInstanceUUIDReq *self, memgraph::slk::Reader *reader);
|
||||
void Save(const memgraph::coordination::GetInstanceUUIDRes &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::GetInstanceUUIDRes *self, memgraph::slk::Reader *reader);
|
||||
// UnregisterReplicaRpc
|
||||
void Save(memgraph::coordination::UnregisterReplicaRes const &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::UnregisterReplicaRes *self, memgraph::slk::Reader *reader);
|
||||
void Save(memgraph::coordination::UnregisterReplicaReq const &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::UnregisterReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
|
||||
void Save(memgraph::coordination::EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::Reader *reader);
|
||||
|
||||
} // namespace memgraph::slk
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
#include "coordination/coordinator_data.hpp"
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "coordination/instance_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
@@ -33,24 +33,19 @@ class CoordinatorState {
|
||||
CoordinatorState(CoordinatorState &&) noexcept = delete;
|
||||
CoordinatorState &operator=(CoordinatorState &&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string instance_name) -> UnregisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
|
||||
[[nodiscard]] auto SetReplicationInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
[[nodiscard]] auto SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||
|
||||
// NOTE: The client code must check that the server exists before calling this method.
|
||||
// The client code must check that the server exists before calling this method.
|
||||
auto GetCoordinatorServer() const -> CoordinatorServer &;
|
||||
|
||||
private:
|
||||
struct CoordinatorMainReplicaData {
|
||||
std::unique_ptr<CoordinatorServer> coordinator_server_;
|
||||
};
|
||||
|
||||
std::variant<CoordinatorInstance, CoordinatorMainReplicaData> data_;
|
||||
std::variant<CoordinatorData, CoordinatorMainReplicaData> data_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include <flags/replication.hpp>
|
||||
|
||||
#include <libnuraft/nuraft.hxx>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using BecomeLeaderCb = std::function<void()>;
|
||||
using BecomeFollowerCb = std::function<void()>;
|
||||
|
||||
using nuraft::buffer;
|
||||
using nuraft::logger;
|
||||
using nuraft::ptr;
|
||||
using nuraft::raft_launcher;
|
||||
using nuraft::raft_server;
|
||||
using nuraft::srv_config;
|
||||
using nuraft::state_machine;
|
||||
using nuraft::state_mgr;
|
||||
using raft_result = nuraft::cmd_result<ptr<buffer>>;
|
||||
|
||||
class RaftState {
|
||||
private:
|
||||
explicit RaftState(BecomeLeaderCb become_leader_cb, BecomeFollowerCb become_follower_cb, uint32_t raft_server_id,
|
||||
uint32_t raft_port, std::string raft_address);
|
||||
|
||||
auto InitRaftServer() -> void;
|
||||
|
||||
public:
|
||||
RaftState() = delete;
|
||||
RaftState(RaftState const &other) = default;
|
||||
RaftState &operator=(RaftState const &other) = default;
|
||||
RaftState(RaftState &&other) noexcept = default;
|
||||
RaftState &operator=(RaftState &&other) noexcept = default;
|
||||
~RaftState();
|
||||
|
||||
static auto MakeRaftState(BecomeLeaderCb become_leader_cb, BecomeFollowerCb become_follower_cb) -> RaftState;
|
||||
|
||||
auto InstanceName() const -> std::string;
|
||||
auto RaftSocketAddress() const -> std::string;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||
auto GetAllCoordinators() const -> std::vector<ptr<srv_config>>;
|
||||
|
||||
auto RequestLeadership() -> bool;
|
||||
auto IsLeader() const -> bool;
|
||||
|
||||
auto AppendRegisterReplicationInstance(std::string const &instance) -> ptr<raft_result>;
|
||||
|
||||
// TODO: (andi) I think variables below can be abstracted
|
||||
uint32_t raft_server_id_;
|
||||
uint32_t raft_port_;
|
||||
std::string raft_address_;
|
||||
|
||||
ptr<state_machine> state_machine_;
|
||||
ptr<state_mgr> state_manager_;
|
||||
ptr<raft_server> raft_server_;
|
||||
ptr<logger> logger_;
|
||||
raft_launcher launcher_;
|
||||
|
||||
BecomeLeaderCb become_leader_cb_;
|
||||
BecomeFollowerCb become_follower_cb_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -22,24 +22,11 @@ enum class RegisterInstanceCoordinatorStatus : uint8_t {
|
||||
ENDPOINT_EXISTS,
|
||||
NOT_COORDINATOR,
|
||||
RPC_FAILED,
|
||||
NOT_LEADER,
|
||||
RAFT_COULD_NOT_ACCEPT,
|
||||
RAFT_COULD_NOT_APPEND,
|
||||
SUCCESS
|
||||
};
|
||||
|
||||
enum class UnregisterInstanceCoordinatorStatus : uint8_t {
|
||||
NO_INSTANCE_WITH_NAME,
|
||||
IS_MAIN,
|
||||
NOT_COORDINATOR,
|
||||
NOT_LEADER,
|
||||
RPC_FAILED,
|
||||
SUCCESS,
|
||||
};
|
||||
|
||||
enum class SetInstanceToMainCoordinatorStatus : uint8_t {
|
||||
NO_INSTANCE_WITH_NAME,
|
||||
MAIN_ALREADY_EXISTS,
|
||||
NOT_COORDINATOR,
|
||||
SUCCESS,
|
||||
COULD_NOT_PROMOTE_TO_MAIN,
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_client.hpp"
|
||||
#include "coordination/coordinator_cluster_config.hpp"
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
|
||||
#include <libnuraft/nuraft.hxx>
|
||||
#include "utils/result.hpp"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorInstance;
|
||||
class CoordinatorData;
|
||||
|
||||
class ReplicationInstance {
|
||||
public:
|
||||
ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||
ReplicationInstance(CoordinatorData *data, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||
HealthCheckCallback fail_cb);
|
||||
|
||||
ReplicationInstance(ReplicationInstance const &other) = delete;
|
||||
@@ -38,9 +38,6 @@ class ReplicationInstance {
|
||||
|
||||
auto OnSuccessPing() -> void;
|
||||
auto OnFailPing() -> bool;
|
||||
auto IsReadyForUUIDPing() -> bool;
|
||||
|
||||
void UpdateReplicaLastResponseUUID();
|
||||
|
||||
auto IsAlive() const -> bool;
|
||||
|
||||
@@ -54,34 +51,22 @@ class ReplicationInstance {
|
||||
HealthCheckCallback main_fail_cb) -> bool;
|
||||
auto DemoteToReplica(HealthCheckCallback replica_succ_cb, HealthCheckCallback replica_fail_cb) -> bool;
|
||||
|
||||
auto StartFrequentCheck() -> void;
|
||||
auto StopFrequentCheck() -> void;
|
||||
auto PauseFrequentCheck() -> void;
|
||||
auto ResumeFrequentCheck() -> void;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
|
||||
auto EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool;
|
||||
|
||||
auto SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool;
|
||||
auto SendUnregisterReplicaRpc(std::string const &instance_name) -> bool;
|
||||
|
||||
|
||||
auto SendGetInstanceUUID() -> utils::BasicResult<coordination::GetInstanceUUIDError, std::optional<utils::UUID>>;
|
||||
auto SendSwapAndUpdateUUID(const utils::UUID &main_uuid) -> bool;
|
||||
auto GetClient() -> CoordinatorClient &;
|
||||
|
||||
auto EnableWritingOnMain() -> bool;
|
||||
|
||||
auto SetNewMainUUID(utils::UUID const &main_uuid) -> void;
|
||||
auto ResetMainUUID() -> void;
|
||||
auto GetMainUUID() const -> const std::optional<utils::UUID> &;
|
||||
void SetNewMainUUID(const std::optional<utils::UUID> &main_uuid = std::nullopt);
|
||||
auto GetMainUUID() -> const std::optional<utils::UUID> &;
|
||||
|
||||
private:
|
||||
CoordinatorClient client_;
|
||||
replication_coordination_glue::ReplicationRole replication_role_;
|
||||
std::chrono::system_clock::time_point last_response_time_{};
|
||||
bool is_alive_{false};
|
||||
std::chrono::system_clock::time_point last_check_of_uuid_{};
|
||||
|
||||
// for replica this is main uuid of current main
|
||||
// for "main" main this same as in CoordinatorData
|
||||
|
||||
@@ -46,6 +46,9 @@ class CoordinatorLogStore : public log_store {
|
||||
|
||||
ptr<std::vector<ptr<log_entry>>> log_entries(ulong start, ulong end) override;
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
ptr<std::vector<ptr<log_entry>>> log_entries_ext(ulong start, ulong end, int64 batch_size_hint_in_bytes = 0) override;
|
||||
|
||||
ptr<log_entry> entry_at(ulong index) override;
|
||||
|
||||
ulong term_at(ulong index) override;
|
||||
@@ -58,12 +61,67 @@ class CoordinatorLogStore : public log_store {
|
||||
|
||||
bool flush() override;
|
||||
|
||||
private:
|
||||
auto FindOrDefault_(ulong index) const -> ptr<log_entry>;
|
||||
ulong last_durable_index() override;
|
||||
|
||||
void Close();
|
||||
|
||||
void SetDiskDelay(raft_server *raft, size_t delay_ms);
|
||||
|
||||
private:
|
||||
static ptr<log_entry> MakeClone(ptr<log_entry> const &entry);
|
||||
|
||||
void DiskEmulLoop();
|
||||
|
||||
/**
|
||||
* Map of <log index, log data>.
|
||||
*/
|
||||
std::map<ulong, ptr<log_entry>> logs_;
|
||||
|
||||
/**
|
||||
* Lock for `logs_`.
|
||||
*/
|
||||
mutable std::mutex logs_lock_;
|
||||
|
||||
/**
|
||||
* The index of the first log.
|
||||
*/
|
||||
std::atomic<ulong> start_idx_;
|
||||
|
||||
/**
|
||||
* Backward pointer to Raft server.
|
||||
*/
|
||||
raft_server *raft_server_bwd_pointer_;
|
||||
|
||||
// Testing purpose --------------- BEGIN
|
||||
|
||||
/**
|
||||
* If non-zero, this log store will emulate the disk write delay.
|
||||
*/
|
||||
std::atomic<size_t> disk_emul_delay;
|
||||
|
||||
/**
|
||||
* Map of <timestamp, log index>, emulating logs that is being written to disk.
|
||||
* Log index will be regarded as "durable" after the corresponding timestamp.
|
||||
*/
|
||||
std::map<uint64_t, uint64_t> disk_emul_logs_being_written_;
|
||||
|
||||
/**
|
||||
* Thread that will update `last_durable_index_` and call
|
||||
* `notify_log_append_completion` at proper time.
|
||||
*/
|
||||
std::unique_ptr<std::thread> disk_emul_thread_;
|
||||
|
||||
/**
|
||||
* Flag to terminate the thread.
|
||||
*/
|
||||
std::atomic<bool> disk_emul_thread_stop_signal_;
|
||||
|
||||
/**
|
||||
* Last written log index.
|
||||
*/
|
||||
std::atomic<uint64_t> disk_emul_last_durable_index_;
|
||||
|
||||
// Testing purpose --------------- END
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
@@ -36,10 +36,6 @@ class CoordinatorStateMachine : public state_machine {
|
||||
CoordinatorStateMachine &operator=(CoordinatorStateMachine &&) = delete;
|
||||
~CoordinatorStateMachine() override {}
|
||||
|
||||
static auto EncodeRegisterReplicationInstance(const std::string &name) -> ptr<buffer>;
|
||||
|
||||
static auto DecodeRegisterReplicationInstance(buffer &data) -> std::string;
|
||||
|
||||
auto pre_commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
||||
|
||||
auto commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
// 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
|
||||
// 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.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/raft_state.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "nuraft/coordinator_state_manager.hpp"
|
||||
#include "utils/counter.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using nuraft::asio_service;
|
||||
using nuraft::cb_func;
|
||||
using nuraft::CbReturnCode;
|
||||
using nuraft::cmd_result;
|
||||
using nuraft::cs_new;
|
||||
using nuraft::ptr;
|
||||
using nuraft::raft_params;
|
||||
using nuraft::raft_server;
|
||||
using nuraft::srv_config;
|
||||
using raft_result = cmd_result<ptr<buffer>>;
|
||||
|
||||
RaftState::RaftState(BecomeLeaderCb become_leader_cb, BecomeFollowerCb become_follower_cb, uint32_t raft_server_id,
|
||||
uint32_t raft_port, std::string raft_address)
|
||||
: raft_server_id_(raft_server_id),
|
||||
raft_port_(raft_port),
|
||||
raft_address_(std::move(raft_address)),
|
||||
state_machine_(cs_new<CoordinatorStateMachine>()),
|
||||
state_manager_(
|
||||
cs_new<CoordinatorStateManager>(raft_server_id_, raft_address_ + ":" + std::to_string(raft_port_))),
|
||||
logger_(nullptr),
|
||||
become_leader_cb_(std::move(become_leader_cb)),
|
||||
become_follower_cb_(std::move(become_follower_cb)) {}
|
||||
|
||||
auto RaftState::InitRaftServer() -> void {
|
||||
asio_service::options asio_opts;
|
||||
asio_opts.thread_pool_size_ = 1; // TODO: (andi) Improve this
|
||||
|
||||
raft_params params;
|
||||
params.heart_beat_interval_ = 100;
|
||||
params.election_timeout_lower_bound_ = 200;
|
||||
params.election_timeout_upper_bound_ = 400;
|
||||
// 5 logs are preserved before the last snapshot
|
||||
params.reserved_log_items_ = 5;
|
||||
// Create snapshot for every 5 log appends
|
||||
params.snapshot_distance_ = 5;
|
||||
params.client_req_timeout_ = 3000;
|
||||
params.return_method_ = raft_params::blocking;
|
||||
|
||||
raft_server::init_options init_opts;
|
||||
init_opts.raft_callback_ = [this](cb_func::Type event_type, cb_func::Param *param) -> nuraft::CbReturnCode {
|
||||
if (event_type == cb_func::BecomeLeader) {
|
||||
spdlog::info("Node {} became leader", param->leaderId);
|
||||
become_leader_cb_();
|
||||
} else if (event_type == cb_func::BecomeFollower) {
|
||||
spdlog::info("Node {} became follower", param->myId);
|
||||
become_follower_cb_();
|
||||
}
|
||||
return CbReturnCode::Ok;
|
||||
};
|
||||
|
||||
raft_launcher launcher;
|
||||
|
||||
raft_server_ = launcher.init(state_machine_, state_manager_, logger_, static_cast<int>(raft_port_), asio_opts, params,
|
||||
init_opts);
|
||||
|
||||
if (!raft_server_) {
|
||||
throw RaftServerStartException("Failed to launch raft server on {}:{}", raft_address_, raft_port_);
|
||||
}
|
||||
|
||||
auto maybe_stop = utils::ResettableCounter<20>();
|
||||
do {
|
||||
if (raft_server_->is_initialized()) {
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
} while (!maybe_stop());
|
||||
|
||||
throw RaftServerStartException("Failed to initialize raft server on {}:{}", raft_address_, raft_port_);
|
||||
}
|
||||
|
||||
auto RaftState::MakeRaftState(BecomeLeaderCb become_leader_cb, BecomeFollowerCb become_follower_cb) -> RaftState {
|
||||
uint32_t raft_server_id{0};
|
||||
uint32_t raft_port{0};
|
||||
try {
|
||||
raft_server_id = FLAGS_raft_server_id;
|
||||
raft_port = FLAGS_raft_server_port;
|
||||
} catch (std::exception const &e) {
|
||||
throw RaftCouldNotParseFlagsException("Failed to parse flags: {}", e.what());
|
||||
}
|
||||
|
||||
auto raft_state =
|
||||
RaftState(std::move(become_leader_cb), std::move(become_follower_cb), raft_server_id, raft_port, "127.0.0.1");
|
||||
raft_state.InitRaftServer();
|
||||
return raft_state;
|
||||
}
|
||||
|
||||
RaftState::~RaftState() { launcher_.shutdown(); }
|
||||
|
||||
auto RaftState::InstanceName() const -> std::string { return "coordinator_" + std::to_string(raft_server_id_); }
|
||||
|
||||
auto RaftState::RaftSocketAddress() const -> std::string { return raft_address_ + ":" + std::to_string(raft_port_); }
|
||||
|
||||
auto RaftState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void {
|
||||
auto const endpoint = raft_address + ":" + std::to_string(raft_port);
|
||||
srv_config const srv_config_to_add(static_cast<int>(raft_server_id), endpoint);
|
||||
if (!raft_server_->add_srv(srv_config_to_add)->get_accepted()) {
|
||||
throw RaftAddServerException("Failed to add server {} to the cluster", endpoint);
|
||||
}
|
||||
spdlog::info("Request to add server {} to the cluster accepted", endpoint);
|
||||
}
|
||||
|
||||
auto RaftState::GetAllCoordinators() const -> std::vector<ptr<srv_config>> {
|
||||
std::vector<ptr<srv_config>> all_srv_configs;
|
||||
raft_server_->get_srv_config_all(all_srv_configs);
|
||||
return all_srv_configs;
|
||||
}
|
||||
|
||||
auto RaftState::IsLeader() const -> bool { return raft_server_->is_leader(); }
|
||||
|
||||
auto RaftState::RequestLeadership() -> bool { return raft_server_->is_leader() || raft_server_->request_leadership(); }
|
||||
|
||||
auto RaftState::AppendRegisterReplicationInstance(std::string const &instance) -> ptr<raft_result> {
|
||||
auto new_log = CoordinatorStateMachine::EncodeRegisterReplicationInstance(instance);
|
||||
return raft_server_->append_entries({new_log});
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -14,18 +14,17 @@
|
||||
#include "coordination/replication_instance.hpp"
|
||||
|
||||
#include "replication_coordination_glue/handler.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
ReplicationInstance::ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config,
|
||||
ReplicationInstance::ReplicationInstance(CoordinatorData *data, CoordinatorClientConfig config,
|
||||
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||
: client_(peer, std::move(config), std::move(succ_cb), std::move(fail_cb)),
|
||||
replication_role_(replication_coordination_glue::ReplicationRole::REPLICA) {
|
||||
: client_(data, std::move(config), std::move(succ_cb), std::move(fail_cb)),
|
||||
replication_role_(replication_coordination_glue::ReplicationRole::REPLICA),
|
||||
is_alive_(true) {
|
||||
if (!client_.DemoteToReplica()) {
|
||||
throw CoordinatorRegisterInstanceException("Failed to demote instance {} to replica", client_.InstanceName());
|
||||
}
|
||||
|
||||
client_.StartFrequentCheck();
|
||||
}
|
||||
|
||||
@@ -35,16 +34,12 @@ auto ReplicationInstance::OnSuccessPing() -> void {
|
||||
}
|
||||
|
||||
auto ReplicationInstance::OnFailPing() -> bool {
|
||||
auto elapsed_time = std::chrono::system_clock::now() - last_response_time_;
|
||||
is_alive_ = elapsed_time < client_.InstanceDownTimeoutSec();
|
||||
is_alive_ =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - last_response_time_).count() <
|
||||
CoordinatorClusterConfig::alive_response_time_difference_sec_;
|
||||
return is_alive_;
|
||||
}
|
||||
|
||||
auto ReplicationInstance::IsReadyForUUIDPing() -> bool {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - last_check_of_uuid_) >
|
||||
client_.InstanceGetUUIDFrequencySec();
|
||||
}
|
||||
|
||||
auto ReplicationInstance::InstanceName() const -> std::string { return client_.InstanceName(); }
|
||||
auto ReplicationInstance::SocketAddress() const -> std::string { return client_.SocketAddress(); }
|
||||
auto ReplicationInstance::IsAlive() const -> bool { return is_alive_; }
|
||||
@@ -56,14 +51,13 @@ auto ReplicationInstance::IsMain() const -> bool {
|
||||
return replication_role_ == replication_coordination_glue::ReplicationRole::MAIN;
|
||||
}
|
||||
|
||||
auto ReplicationInstance::PromoteToMain(utils::UUID new_uuid, ReplicationClientsInfo repl_clients_info,
|
||||
auto ReplicationInstance::PromoteToMain(utils::UUID uuid, ReplicationClientsInfo repl_clients_info,
|
||||
HealthCheckCallback main_succ_cb, HealthCheckCallback main_fail_cb) -> bool {
|
||||
if (!client_.SendPromoteReplicaToMainRpc(new_uuid, std::move(repl_clients_info))) {
|
||||
if (!client_.SendPromoteReplicaToMainRpc(uuid, std::move(repl_clients_info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
replication_role_ = replication_coordination_glue::ReplicationRole::MAIN;
|
||||
main_uuid_ = new_uuid;
|
||||
client_.SetCallbacks(std::move(main_succ_cb), std::move(main_fail_cb));
|
||||
|
||||
return true;
|
||||
@@ -81,8 +75,6 @@ auto ReplicationInstance::DemoteToReplica(HealthCheckCallback replica_succ_cb, H
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ReplicationInstance::StartFrequentCheck() -> void { client_.StartFrequentCheck(); }
|
||||
auto ReplicationInstance::StopFrequentCheck() -> void { client_.StopFrequentCheck(); }
|
||||
auto ReplicationInstance::PauseFrequentCheck() -> void { client_.PauseFrequentCheck(); }
|
||||
auto ReplicationInstance::ResumeFrequentCheck() -> void { client_.ResumeFrequentCheck(); }
|
||||
|
||||
@@ -91,48 +83,16 @@ auto ReplicationInstance::ReplicationClientInfo() const -> CoordinatorClientConf
|
||||
}
|
||||
|
||||
auto ReplicationInstance::GetClient() -> CoordinatorClient & { return client_; }
|
||||
void ReplicationInstance::SetNewMainUUID(const std::optional<utils::UUID> &main_uuid) { main_uuid_ = main_uuid; }
|
||||
auto ReplicationInstance::GetMainUUID() -> const std::optional<utils::UUID> & { return main_uuid_; }
|
||||
|
||||
auto ReplicationInstance::SetNewMainUUID(utils::UUID const &main_uuid) -> void { main_uuid_ = main_uuid; }
|
||||
auto ReplicationInstance::ResetMainUUID() -> void { main_uuid_ = std::nullopt; }
|
||||
auto ReplicationInstance::GetMainUUID() const -> std::optional<utils::UUID> const & { return main_uuid_; }
|
||||
|
||||
auto ReplicationInstance::EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool {
|
||||
if (!IsReadyForUUIDPing()) {
|
||||
return true;
|
||||
}
|
||||
auto res = SendGetInstanceUUID();
|
||||
if (res.HasError()) {
|
||||
auto ReplicationInstance::SendSwapAndUpdateUUID(const utils::UUID &main_uuid) -> bool {
|
||||
if (!replication_coordination_glue::SendSwapMainUUIDRpc(client_.RpcClient(), main_uuid)) {
|
||||
return false;
|
||||
}
|
||||
UpdateReplicaLastResponseUUID();
|
||||
|
||||
if (res.GetValue().has_value() && res.GetValue().value() == curr_main_uuid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return SendSwapAndUpdateUUID(curr_main_uuid);
|
||||
}
|
||||
|
||||
auto ReplicationInstance::SendSwapAndUpdateUUID(const utils::UUID &new_main_uuid) -> bool {
|
||||
if (!replication_coordination_glue::SendSwapMainUUIDRpc(client_.RpcClient(), new_main_uuid)) {
|
||||
return false;
|
||||
}
|
||||
SetNewMainUUID(new_main_uuid);
|
||||
SetNewMainUUID(main_uuid_);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ReplicationInstance::SendUnregisterReplicaRpc(std::string const &instance_name) -> bool {
|
||||
return client_.SendUnregisterReplicaRpc(instance_name);
|
||||
}
|
||||
|
||||
auto ReplicationInstance::EnableWritingOnMain() -> bool { return client_.SendEnableWritingOnMainRpc(); }
|
||||
|
||||
auto ReplicationInstance::SendGetInstanceUUID()
|
||||
-> utils::BasicResult<coordination::GetInstanceUUIDError, std::optional<utils::UUID>> {
|
||||
return client_.SendGetInstanceUUIDRpc();
|
||||
}
|
||||
|
||||
void ReplicationInstance::UpdateReplicaLastResponseUUID() { last_check_of_uuid_ = std::chrono::system_clock::now(); }
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
||||
@@ -16,4 +16,10 @@ namespace memgraph::dbms {
|
||||
constexpr std::string_view kDefaultDB = "memgraph"; //!< Name of the default database
|
||||
constexpr std::string_view kMultiTenantDir = "databases"; //!< Name of the multi-tenant directory
|
||||
|
||||
#ifdef MG_EXPERIMENTAL_REPLICATION_MULTITENANCY
|
||||
constexpr bool allow_mt_repl = true;
|
||||
#else
|
||||
constexpr bool allow_mt_repl = false;
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
@@ -20,19 +20,14 @@ namespace memgraph::dbms {
|
||||
CoordinatorHandler::CoordinatorHandler(coordination::CoordinatorState &coordinator_state)
|
||||
: coordinator_state_(coordinator_state) {}
|
||||
|
||||
auto CoordinatorHandler::RegisterReplicationInstance(memgraph::coordination::CoordinatorClientConfig config)
|
||||
auto CoordinatorHandler::RegisterInstance(memgraph::coordination::CoordinatorClientConfig config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus {
|
||||
return coordinator_state_.RegisterReplicationInstance(config);
|
||||
return coordinator_state_.RegisterInstance(config);
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::UnregisterReplicationInstance(std::string instance_name)
|
||||
-> coordination::UnregisterInstanceCoordinatorStatus {
|
||||
return coordinator_state_.UnregisterReplicationInstance(std::move(instance_name));
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::SetReplicationInstanceToMain(std::string instance_name)
|
||||
auto CoordinatorHandler::SetInstanceToMain(std::string instance_name)
|
||||
-> coordination::SetInstanceToMainCoordinatorStatus {
|
||||
return coordinator_state_.SetReplicationInstanceToMain(std::move(instance_name));
|
||||
return coordinator_state_.SetInstanceToMain(std::move(instance_name));
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::ShowInstances() const -> std::vector<coordination::InstanceStatus> {
|
||||
|
||||
@@ -28,14 +28,10 @@ class CoordinatorHandler {
|
||||
public:
|
||||
explicit CoordinatorHandler(coordination::CoordinatorState &coordinator_state);
|
||||
|
||||
// TODO: (andi) When moving coordinator state on same instances, rename from RegisterReplicationInstance to
|
||||
// RegisterInstance
|
||||
auto RegisterReplicationInstance(coordination::CoordinatorClientConfig config)
|
||||
auto RegisterInstance(coordination::CoordinatorClientConfig config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus;
|
||||
|
||||
auto UnregisterReplicationInstance(std::string instance_name) -> coordination::UnregisterInstanceCoordinatorStatus;
|
||||
|
||||
auto SetReplicationInstanceToMain(std::string instance_name) -> coordination::SetInstanceToMainCoordinatorStatus;
|
||||
auto SetInstanceToMain(std::string instance_name) -> coordination::SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<coordination::InstanceStatus>;
|
||||
|
||||
|
||||
@@ -110,9 +110,9 @@ class Database {
|
||||
* @param force_directory Use the configured directory, do not try to decipher the multi-db version
|
||||
* @return DatabaseInfo
|
||||
*/
|
||||
DatabaseInfo GetInfo(replication_coordination_glue::ReplicationRole replication_role) const {
|
||||
DatabaseInfo GetInfo(bool force_directory, replication_coordination_glue::ReplicationRole replication_role) const {
|
||||
DatabaseInfo info;
|
||||
info.storage_info = storage_->GetInfo(replication_role);
|
||||
info.storage_info = storage_->GetInfo(force_directory, replication_role);
|
||||
info.triggers = trigger_store_.GetTriggerInfo().size();
|
||||
info.streams = streams_.GetStreamInfo().size();
|
||||
return info;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "dbms/constants.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "flags/experimental.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "system/include/system/system.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
@@ -159,9 +158,9 @@ struct Durability {
|
||||
}
|
||||
};
|
||||
|
||||
DbmsHandler::DbmsHandler(storage::Config config, replication::ReplicationState &repl_state, auth::SynchedAuth &auth,
|
||||
bool recovery_on_startup)
|
||||
: default_config_{std::move(config)}, auth_{auth}, repl_state_{repl_state} {
|
||||
DbmsHandler::DbmsHandler(storage::Config config, memgraph::system::System &system,
|
||||
replication::ReplicationState &repl_state, auth::SynchedAuth &auth, bool recovery_on_startup)
|
||||
: default_config_{std::move(config)}, auth_{auth}, repl_state_{repl_state}, system_{&system} {
|
||||
// TODO: Decouple storage config from dbms config
|
||||
// TODO: Save individual db configs inside the kvstore and restore from there
|
||||
|
||||
@@ -420,10 +419,7 @@ void DbmsHandler::UpdateDurability(const storage::Config &config, std::optional<
|
||||
#endif
|
||||
|
||||
void DbmsHandler::RecoverStorageReplication(DatabaseAccess db_acc, replication::RoleMainData &role_main_data) {
|
||||
using enum memgraph::flags::Experiments;
|
||||
auto const is_enterprise = license::global_license_checker.IsEnterpriseValidFast();
|
||||
auto experimental_system_replication = flags::AreExperimentsEnabled(SYSTEM_REPLICATION);
|
||||
if ((is_enterprise && experimental_system_replication) || db_acc->name() == dbms::kDefaultDB) {
|
||||
if (allow_mt_repl || db_acc->name() == dbms::kDefaultDB) {
|
||||
// Handle global replication state
|
||||
spdlog::info("Replication configuration will be stored and will be automatically restored in case of a crash.");
|
||||
// RECOVER REPLICA CONNECTIONS
|
||||
|
||||
@@ -107,7 +107,8 @@ class DbmsHandler {
|
||||
* @param auth pointer to the global authenticator
|
||||
* @param recovery_on_startup restore databases (and its content) and authentication data
|
||||
*/
|
||||
DbmsHandler(storage::Config config, replication::ReplicationState &repl_state, auth::SynchedAuth &auth,
|
||||
DbmsHandler(storage::Config config, memgraph::system::System &system, replication::ReplicationState &repl_state,
|
||||
auth::SynchedAuth &auth,
|
||||
bool recovery_on_startup); // TODO If more arguments are added use a config struct
|
||||
#else
|
||||
/**
|
||||
@@ -115,8 +116,9 @@ class DbmsHandler {
|
||||
*
|
||||
* @param configs storage configuration
|
||||
*/
|
||||
DbmsHandler(storage::Config config, replication::ReplicationState &repl_state)
|
||||
DbmsHandler(storage::Config config, memgraph::system::System &system, replication::ReplicationState &repl_state)
|
||||
: repl_state_{repl_state},
|
||||
system_{&system},
|
||||
db_gatekeeper_{[&] {
|
||||
config.salient.name = kDefaultDB;
|
||||
return std::move(config);
|
||||
@@ -270,20 +272,6 @@ class DbmsHandler {
|
||||
// coordination::CoordinatorState &CoordinatorState() { return coordinator_state_; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return all active databases.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
*/
|
||||
auto Count() const -> std::size_t {
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::shared_lock<LockT> rd(lock_);
|
||||
return db_handler_.size();
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the statistics all databases.
|
||||
*
|
||||
@@ -302,14 +290,14 @@ class DbmsHandler {
|
||||
auto db_acc_opt = db_gk.access();
|
||||
if (db_acc_opt) {
|
||||
auto &db_acc = *db_acc_opt;
|
||||
const auto &info = db_acc->GetInfo(replication_role);
|
||||
const auto &info = db_acc->GetInfo(false, replication_role);
|
||||
const auto &storage_info = info.storage_info;
|
||||
stats.num_vertex += storage_info.vertex_count;
|
||||
stats.num_edges += storage_info.edge_count;
|
||||
stats.triggers += info.triggers;
|
||||
stats.streams += info.streams;
|
||||
++stats.num_databases;
|
||||
stats.indices += storage_info.label_indices + storage_info.label_property_indices;
|
||||
stats.indices += storage_info.label_indices + storage_info.label_property_indices + storage_info.text_indices;
|
||||
stats.constraints += storage_info.existence_constraints + storage_info.unique_constraints;
|
||||
++stats.storage_modes[(int)storage_info.storage_mode];
|
||||
++stats.isolation_levels[(int)storage_info.isolation_level];
|
||||
@@ -338,7 +326,7 @@ class DbmsHandler {
|
||||
auto db_acc_opt = db_gk.access();
|
||||
if (db_acc_opt) {
|
||||
auto &db_acc = *db_acc_opt;
|
||||
res.push_back(db_acc->GetInfo(replication_role));
|
||||
res.push_back(db_acc->GetInfo(false, replication_role));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -599,6 +587,9 @@ class DbmsHandler {
|
||||
// current replication role. TODO: make Database Access explicit about the role and remove this from
|
||||
// dbms stuff
|
||||
replication::ReplicationState &repl_state_; //!< Ref to global replication state
|
||||
public:
|
||||
// TODO fix to be non public/remove from dbms....maybe
|
||||
system::System *system_;
|
||||
|
||||
#ifndef MG_ENTERPRISE
|
||||
mutable utils::Gatekeeper<Database> db_gatekeeper_; //!< Single databases gatekeeper
|
||||
|
||||
@@ -144,8 +144,6 @@ class Handler {
|
||||
auto cbegin() const { return items_.cbegin(); }
|
||||
auto cend() const { return items_.cend(); }
|
||||
|
||||
auto size() const { return items_.size(); }
|
||||
|
||||
struct string_hash {
|
||||
using is_transparent = void;
|
||||
[[nodiscard]] size_t operator()(const char *s) const { return std::hash<std::string_view>{}(s); }
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "storage/v2/durability/durability.hpp"
|
||||
#include "storage/v2/durability/snapshot.hpp"
|
||||
#include "storage/v2/durability/version.hpp"
|
||||
#include "storage/v2/fmt.hpp"
|
||||
#include "storage/v2/indices/label_index_stats.hpp"
|
||||
#include "storage/v2/inmemory/storage.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
@@ -156,12 +155,6 @@ void InMemoryReplicationHandlers::HeartbeatHandler(dbms::DbmsHandler *dbms_handl
|
||||
return;
|
||||
}
|
||||
// TODO: this handler is agnostic of InMemory, move to be reused by on-disk
|
||||
if (!db_acc.has_value()) {
|
||||
spdlog::warn("No database accessor");
|
||||
storage::replication::HeartbeatRes res{false, 0, ""};
|
||||
slk::Save(res, res_builder);
|
||||
return;
|
||||
}
|
||||
auto const *storage = db_acc->get()->storage();
|
||||
storage::replication::HeartbeatRes res{true, storage->repl_storage_state_.last_commit_timestamp_.load(),
|
||||
std::string{storage->repl_storage_state_.epoch_.id()}};
|
||||
@@ -470,6 +463,7 @@ void InMemoryReplicationHandlers::TimestampHandler(dbms::DbmsHandler *dbms_handl
|
||||
slk::Save(res, res_builder);
|
||||
}
|
||||
|
||||
/////// AF how does this work, does it get all deltas at once or what?
|
||||
uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage *storage,
|
||||
storage::durability::BaseDecoder *decoder,
|
||||
const uint64_t version) {
|
||||
@@ -540,6 +534,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
auto vertex = transaction->FindVertex(delta.vertex_add_remove_label.gid, View::NEW);
|
||||
if (!vertex)
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
// NOTE: Phase 1 of the text search feature doesn't have replication in scope
|
||||
auto ret = vertex->AddLabel(transaction->NameToLabel(delta.vertex_add_remove_label.label));
|
||||
if (ret.HasError() || !ret.GetValue())
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
@@ -552,6 +547,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
auto vertex = transaction->FindVertex(delta.vertex_add_remove_label.gid, View::NEW);
|
||||
if (!vertex)
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
// NOTE: Phase 1 of the text search feature doesn't have replication in scope
|
||||
auto ret = vertex->RemoveLabel(transaction->NameToLabel(delta.vertex_add_remove_label.label));
|
||||
if (ret.HasError() || !ret.GetValue())
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
@@ -564,6 +560,7 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
auto vertex = transaction->FindVertex(delta.vertex_edge_set_property.gid, View::NEW);
|
||||
if (!vertex)
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
// NOTE: Phase 1 of the text search feature doesn't have replication in scope
|
||||
auto ret = vertex->SetProperty(transaction->NameToProperty(delta.vertex_edge_set_property.property),
|
||||
delta.vertex_edge_set_property.value);
|
||||
if (ret.HasError())
|
||||
@@ -764,6 +761,14 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
transaction->DeleteLabelPropertyIndexStats(storage->NameToLabel(info.label));
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::TEXT_INDEX_CREATE: {
|
||||
// NOTE: Phase 1 of the text search feature doesn't have replication in scope
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::TEXT_INDEX_DROP: {
|
||||
// NOTE: Phase 1 of the text search feature doesn't have replication in scope
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE: {
|
||||
spdlog::trace(" Create existence constraint on :{} ({})", delta.operation_label_property.label,
|
||||
delta.operation_label_property.property);
|
||||
|
||||
0
src/dbms/replication_client.cpp
Normal file
0
src/dbms/replication_client.cpp
Normal file
0
src/dbms/replication_handler.cpp
Normal file
0
src/dbms/replication_handler.cpp
Normal file
0
src/dbms/replication_handler.hpp
Normal file
0
src/dbms/replication_handler.hpp
Normal file
@@ -57,7 +57,6 @@ namespace slk {
|
||||
// Serialize code for CreateDatabaseReq
|
||||
|
||||
void Save(const memgraph::storage::replication::CreateDatabaseReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.main_uuid, builder);
|
||||
memgraph::slk::Save(self.epoch_id, builder);
|
||||
memgraph::slk::Save(self.expected_group_timestamp, builder);
|
||||
memgraph::slk::Save(self.new_group_timestamp, builder);
|
||||
@@ -65,7 +64,6 @@ void Save(const memgraph::storage::replication::CreateDatabaseReq &self, memgrap
|
||||
}
|
||||
|
||||
void Load(memgraph::storage::replication::CreateDatabaseReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->main_uuid, reader);
|
||||
memgraph::slk::Load(&self->epoch_id, reader);
|
||||
memgraph::slk::Load(&self->expected_group_timestamp, reader);
|
||||
memgraph::slk::Load(&self->new_group_timestamp, reader);
|
||||
@@ -89,7 +87,6 @@ void Load(memgraph::storage::replication::CreateDatabaseRes *self, memgraph::slk
|
||||
// Serialize code for DropDatabaseReq
|
||||
|
||||
void Save(const memgraph::storage::replication::DropDatabaseReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.main_uuid, builder);
|
||||
memgraph::slk::Save(self.epoch_id, builder);
|
||||
memgraph::slk::Save(self.expected_group_timestamp, builder);
|
||||
memgraph::slk::Save(self.new_group_timestamp, builder);
|
||||
@@ -97,7 +94,6 @@ void Save(const memgraph::storage::replication::DropDatabaseReq &self, memgraph:
|
||||
}
|
||||
|
||||
void Load(memgraph::storage::replication::DropDatabaseReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->main_uuid, reader);
|
||||
memgraph::slk::Load(&self->epoch_id, reader);
|
||||
memgraph::slk::Load(&self->expected_group_timestamp, reader);
|
||||
memgraph::slk::Load(&self->new_group_timestamp, reader);
|
||||
|
||||
0
src/dbms/utils.hpp
Normal file
0
src/dbms/utils.hpp
Normal file
@@ -10,7 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user