Compare commits
1 Commits
install-de
...
detach-del
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a98b8f046 |
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
|
about: Create a report to help us improve
|
||||||
title: ""
|
title: ""
|
||||||
labels: bug
|
labels: bug
|
||||||
|
assignees: gitbuda
|
||||||
---
|
---
|
||||||
|
|
||||||
**Memgraph version**
|
**Memgraph version**
|
||||||
|
|||||||
22
.github/pull_request_template.md
vendored
22
.github/pull_request_template.md
vendored
@@ -1,28 +1,14 @@
|
|||||||
### Description
|
|
||||||
|
|
||||||
Please briefly explain the changes you made here.
|
|
||||||
|
|
||||||
|
|
||||||
Please delete either the [master < EPIC] or [master < Task] part, depending on what are your needs.
|
|
||||||
|
|
||||||
[master < Epic] PR
|
[master < Epic] PR
|
||||||
|
- [ ] Check, and update documentation if necessary
|
||||||
- [ ] Write E2E tests
|
- [ ] Write E2E tests
|
||||||
- [ ] Compare the [benchmarking results](https://bench-graph.memgraph.com/) between the master branch and the Epic branch
|
- [ ] Compare the [benchmarking results](https://bench-graph.memgraph.com/) between the master branch and the Epic branch
|
||||||
- [ ] Provide the full content or a guide for the final git message
|
- [ ] Provide the full content or a guide for the final git message
|
||||||
- [FINAL GIT MESSAGE]
|
|
||||||
|
|
||||||
[master < Task] PR
|
[master < Task] PR
|
||||||
|
- [ ] Check, and update documentation if necessary
|
||||||
- [ ] Provide the full content or a guide for the final git message
|
- [ ] Provide the full content or a guide for the final git message
|
||||||
- **[FINAL GIT MESSAGE]**
|
|
||||||
|
|
||||||
|
|
||||||
### Documentation checklist
|
To keep docs changelog up to date, one more thing to do:
|
||||||
- [ ] Add the documentation label tag
|
- [ ] Write a release note here, including added/changed clauses
|
||||||
- [ ] Add the bug / feature label tag
|
|
||||||
- [ ] Add the milestone for which this feature is intended
|
|
||||||
- If not known, set for a later milestone
|
|
||||||
- [ ] Write a release note, including added/changed clauses
|
|
||||||
- **[Release note text]**
|
|
||||||
- [ ] Link the documentation PR here
|
|
||||||
- **[Documentation PR link]**
|
|
||||||
- [ ] Tag someone from docs team in the comments
|
- [ ] Tag someone from docs team in the comments
|
||||||
|
|||||||
115
.github/workflows/diff.yaml
vendored
115
.github/workflows/diff.yaml
vendored
@@ -268,6 +268,7 @@ jobs:
|
|||||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||||
|
|
||||||
- name: Ensure Kafka and Pulsar are up
|
- name: Ensure Kafka and Pulsar are up
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
cd tests/e2e/streams/kafka
|
cd tests/e2e/streams/kafka
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
@@ -275,6 +276,7 @@ jobs:
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
- name: Run e2e tests
|
- name: Run e2e tests
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
cd tests
|
cd tests
|
||||||
./setup.sh /opt/toolchain-v4/activate
|
./setup.sh /opt/toolchain-v4/activate
|
||||||
@@ -283,6 +285,7 @@ jobs:
|
|||||||
./run.sh
|
./run.sh
|
||||||
|
|
||||||
- name: Ensure Kafka and Pulsar are down
|
- name: Ensure Kafka and Pulsar are down
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
cd tests/e2e/streams/kafka
|
cd tests/e2e/streams/kafka
|
||||||
docker-compose down
|
docker-compose down
|
||||||
@@ -336,6 +339,118 @@ jobs:
|
|||||||
# multiple paths could be defined
|
# multiple paths could be defined
|
||||||
build/logs
|
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:
|
release_jepsen_test:
|
||||||
name: "Release Jepsen Test"
|
name: "Release Jepsen Test"
|
||||||
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
|
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
|
||||||
|
|||||||
208
.github/workflows/release_build_test.yaml
vendored
208
.github/workflows/release_build_test.yaml
vendored
@@ -1,208 +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 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"
|
|
||||||
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
|
|
||||||
- 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 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"
|
|
||||||
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
|
|
||||||
- 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: 150
|
|
||||||
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
|
|
||||||
|
|
||||||
PushToS3Ubuntu20_04_ARM:
|
|
||||||
if: github.ref_type == 'tag'
|
|
||||||
needs: [PackageUbuntu20_04_ARM]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download package
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ubuntu-22.04-aarch64
|
|
||||||
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 }}/"
|
|
||||||
|
|
||||||
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 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"
|
|
||||||
DEST_DIR: "memgraph-unofficial/${{ github.ref_name }}/"
|
|
||||||
- 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: 150
|
|
||||||
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
|
|
||||||
|
|
||||||
PushToS3Debian11_ARM:
|
|
||||||
if: github.ref_type == 'tag'
|
|
||||||
needs: [PackageDebian11_ARM]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download package
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: debian-11-aarch64
|
|
||||||
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
|
name: Release Debian 10
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
build_type:
|
|
||||||
type: string
|
|
||||||
description: "Memgraph Build type. Default value is Release."
|
|
||||||
default: 'Release'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
build_type:
|
build_type:
|
||||||
@@ -17,8 +11,10 @@ on:
|
|||||||
- Release
|
- Release
|
||||||
- RelWithDebInfo
|
- RelWithDebInfo
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: "0 22 * * *"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OS: "Debian10"
|
|
||||||
THREADS: 24
|
THREADS: 24
|
||||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||||
@@ -115,7 +111,7 @@ jobs:
|
|||||||
- name: Save code coverage
|
- name: Save code coverage
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
name: "Code coverage(Coverage build)"
|
||||||
path: tools/github/generated/code_coverage.tar.gz
|
path: tools/github/generated/code_coverage.tar.gz
|
||||||
|
|
||||||
debug_build:
|
debug_build:
|
||||||
@@ -169,7 +165,7 @@ jobs:
|
|||||||
- name: Save cppcheck and clang-format errors
|
- name: Save cppcheck and clang-format errors
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
name: "Code coverage(Debug build)"
|
||||||
path: tools/github/cppcheck_and_clang_format.txt
|
path: tools/github/cppcheck_and_clang_format.txt
|
||||||
|
|
||||||
debug_integration_test:
|
debug_integration_test:
|
||||||
@@ -246,7 +242,7 @@ jobs:
|
|||||||
- name: Save enterprise DEB package
|
- name: Save enterprise DEB package
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Enterprise DEB package-${{ env.OS}}"
|
name: "Enterprise DEB package"
|
||||||
path: build/output/memgraph*.deb
|
path: build/output/memgraph*.deb
|
||||||
|
|
||||||
- name: Run GQL Behave tests
|
- name: Run GQL Behave tests
|
||||||
@@ -259,7 +255,7 @@ jobs:
|
|||||||
- name: Save quality assurance status
|
- name: Save quality assurance status
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "GQL Behave Status-${{ env.OS }}"
|
name: "GQL Behave Status"
|
||||||
path: |
|
path: |
|
||||||
tests/gql_behave/gql_behave_status.csv
|
tests/gql_behave/gql_behave_status.csv
|
||||||
tests/gql_behave/gql_behave_status.html
|
tests/gql_behave/gql_behave_status.html
|
||||||
@@ -325,6 +321,7 @@ jobs:
|
|||||||
--no-strict
|
--no-strict
|
||||||
|
|
||||||
release_e2e_test:
|
release_e2e_test:
|
||||||
|
if: false
|
||||||
name: "Release End-to-end Test"
|
name: "Release End-to-end Test"
|
||||||
runs-on: [self-hosted, Linux, X64, Debian10]
|
runs-on: [self-hosted, Linux, X64, Debian10]
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
@@ -459,5 +456,5 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: "Jepsen Report-${{ env.OS }}"
|
name: "Jepsen Report"
|
||||||
path: tests/jepsen/Jepsen.tar.gz
|
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
|
name: Release Ubuntu 20.04
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
build_type:
|
|
||||||
type: string
|
|
||||||
description: "Memgraph Build type. Default value is Release."
|
|
||||||
default: 'Release'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
build_type:
|
build_type:
|
||||||
@@ -17,8 +11,10 @@ on:
|
|||||||
- Release
|
- Release
|
||||||
- RelWithDebInfo
|
- RelWithDebInfo
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: "0 22 * * *"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OS: "Ubuntu 20.04"
|
|
||||||
THREADS: 24
|
THREADS: 24
|
||||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||||
@@ -111,7 +107,7 @@ jobs:
|
|||||||
- name: Save code coverage
|
- name: Save code coverage
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Code coverage(Coverage build)-${{ env.OS }}"
|
name: "Code coverage(Coverage build)"
|
||||||
path: tools/github/generated/code_coverage.tar.gz
|
path: tools/github/generated/code_coverage.tar.gz
|
||||||
|
|
||||||
debug_build:
|
debug_build:
|
||||||
@@ -165,7 +161,7 @@ jobs:
|
|||||||
- name: Save cppcheck and clang-format errors
|
- name: Save cppcheck and clang-format errors
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Code coverage(Debug build)-${{ env.OS }}"
|
name: "Code coverage(Debug build)"
|
||||||
path: tools/github/cppcheck_and_clang_format.txt
|
path: tools/github/cppcheck_and_clang_format.txt
|
||||||
|
|
||||||
debug_integration_test:
|
debug_integration_test:
|
||||||
@@ -242,7 +238,7 @@ jobs:
|
|||||||
- name: Save enterprise DEB package
|
- name: Save enterprise DEB package
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "Enterprise DEB package-${{ env.OS }}"
|
name: "Enterprise DEB package"
|
||||||
path: build/output/memgraph*.deb
|
path: build/output/memgraph*.deb
|
||||||
|
|
||||||
- name: Run GQL Behave tests
|
- name: Run GQL Behave tests
|
||||||
@@ -255,7 +251,7 @@ jobs:
|
|||||||
- name: Save quality assurance status
|
- name: Save quality assurance status
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "GQL Behave Status-${{ env.OS }}"
|
name: "GQL Behave Status"
|
||||||
path: |
|
path: |
|
||||||
tests/gql_behave/gql_behave_status.csv
|
tests/gql_behave/gql_behave_status.csv
|
||||||
tests/gql_behave/gql_behave_status.html
|
tests/gql_behave/gql_behave_status.html
|
||||||
@@ -321,6 +317,7 @@ jobs:
|
|||||||
--no-strict
|
--no-strict
|
||||||
|
|
||||||
release_e2e_test:
|
release_e2e_test:
|
||||||
|
if: false
|
||||||
name: "Release End-to-end Test"
|
name: "Release End-to-end Test"
|
||||||
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
|
runs-on: [self-hosted, Linux, X64, Ubuntu20.04]
|
||||||
timeout-minutes: 60
|
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
|
name: Stress test large
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -13,10 +10,7 @@ on:
|
|||||||
options:
|
options:
|
||||||
- Release
|
- Release
|
||||||
- RelWithDebInfo
|
- RelWithDebInfo
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*.*.*-rc*"
|
|
||||||
- "v*.*-rc*"
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 22 * * *"
|
- cron: "0 22 * * *"
|
||||||
|
|
||||||
|
|||||||
@@ -211,13 +211,8 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
|||||||
# ** Static linking is allowed only for executables! **
|
# ** Static linking is allowed only for executables! **
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
|
|
||||||
# Use lld linker to speedup build and use less memory.
|
# Use lld linker to speedup build
|
||||||
add_link_options(-fuse-ld=lld)
|
add_link_options(-fuse-ld=lld) # TODO: use mold linker
|
||||||
# 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})
|
|
||||||
|
|
||||||
# release flags
|
# release flags
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||||
@@ -276,6 +271,18 @@ endif()
|
|||||||
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
|
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
|
||||||
add_subdirectory(libs EXCLUDE_FROM_ALL)
|
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(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF)
|
||||||
option(TOOLS "Build tools binaries" ON)
|
option(TOOLS "Build tools binaries" ON)
|
||||||
option(QUERY_MODULES "Build query modules containing custom procedures" 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)
|
option(UBSAN "Build with Undefined Behaviour Sanitizer" OFF)
|
||||||
|
|
||||||
# Build feature flags
|
# 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)
|
if (TEST_COVERAGE)
|
||||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -7,7 +9,7 @@ check_operating_system "amzn-2"
|
|||||||
check_architecture "x86_64"
|
check_architecture "x86_64"
|
||||||
|
|
||||||
TOOLCHAIN_BUILD_DEPS=(
|
TOOLCHAIN_BUILD_DEPS=(
|
||||||
git gcc gcc-c++ make # generic build tools
|
gcc gcc-c++ make # generic build tools
|
||||||
wget # used for archive download
|
wget # used for archive download
|
||||||
gnupg2 # used for archive signature verification
|
gnupg2 # used for archive signature verification
|
||||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||||
@@ -45,7 +47,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
readline-devel # for memgraph console
|
readline-devel # for memgraph console
|
||||||
python3-devel # for query modules
|
python3-devel # for query modules
|
||||||
openssl-devel
|
openssl-devel
|
||||||
openssl
|
|
||||||
libseccomp-devel
|
libseccomp-devel
|
||||||
python3 python3-pip nmap-ncat # for tests
|
python3 python3-pip nmap-ncat # for tests
|
||||||
#
|
#
|
||||||
@@ -62,8 +63,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
cyrus-sasl-devel
|
cyrus-sasl-devel
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp
|
logrotate openssl python3 libseccomp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -43,7 +45,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
readline-devel # for memgraph console
|
readline-devel # for memgraph console
|
||||||
python3-devel # for query modules
|
python3-devel # for query modules
|
||||||
openssl-devel
|
openssl-devel
|
||||||
openssl
|
|
||||||
libseccomp-devel
|
libseccomp-devel
|
||||||
python3 python-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
|
python3 python-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
|
||||||
#
|
#
|
||||||
@@ -62,8 +63,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
cyrus-sasl-devel
|
cyrus-sasl-devel
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp
|
logrotate openssl python3 libseccomp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -7,10 +9,8 @@ check_operating_system "centos-9"
|
|||||||
check_architecture "x86_64"
|
check_architecture "x86_64"
|
||||||
|
|
||||||
TOOLCHAIN_BUILD_DEPS=(
|
TOOLCHAIN_BUILD_DEPS=(
|
||||||
wget # used for archive download
|
|
||||||
coreutils-common gcc gcc-c++ make # generic build tools
|
coreutils-common gcc gcc-c++ make # generic build tools
|
||||||
# NOTE: Pure libcurl conflicts with libcurl-minimal
|
wget # used for archive download
|
||||||
libcurl-devel # cmake build requires it
|
|
||||||
gnupg2 # used for archive signature verification
|
gnupg2 # used for archive signature verification
|
||||||
tar gzip bzip2 xz unzip # used for archive unpacking
|
tar gzip bzip2 xz unzip # used for archive unpacking
|
||||||
zlib-devel # zlib library used for all builds
|
zlib-devel # zlib library used for all builds
|
||||||
@@ -64,8 +64,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
cyrus-sasl-devel
|
cyrus-sasl-devel
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp
|
logrotate openssl python3 libseccomp
|
||||||
)
|
)
|
||||||
@@ -125,9 +123,7 @@ install() {
|
|||||||
else
|
else
|
||||||
echo "NOTE: export LANG=en_US.utf8"
|
echo "NOTE: export LANG=en_US.utf8"
|
||||||
fi
|
fi
|
||||||
# --nobest is used because of libipt because we install custom versions
|
yum update -y
|
||||||
# because libipt-devel is not available on CentOS 9 Stream
|
|
||||||
yum update -y --nobest
|
|
||||||
yum install -y wget git python3 python3-pip
|
yum install -y wget git python3 python3-pip
|
||||||
|
|
||||||
for pkg in $1; do
|
for pkg in $1; do
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
|
||||||
|
|
||||||
check_operating_system "debian-10"
|
check_operating_system "debian-10"
|
||||||
check_architecture "x86_64"
|
check_architecture "x86_64"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
|
||||||
|
|
||||||
check_operating_system "debian-11"
|
check_operating_system "debian-11"
|
||||||
check_architecture "arm64" "aarch64"
|
check_architecture "arm64" "aarch64"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -59,8 +61,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
libsasl2-dev
|
libsasl2-dev
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp
|
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
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
|
||||||
|
|
||||||
check_operating_system "fedora-36"
|
check_operating_system "fedora-36"
|
||||||
check_architecture "x86_64"
|
check_architecture "x86_64"
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
|||||||
libipt libipt-devel # intel
|
libipt libipt-devel # intel
|
||||||
patch
|
patch
|
||||||
perl # for openssl
|
perl # for openssl
|
||||||
git
|
|
||||||
)
|
)
|
||||||
|
|
||||||
TOOLCHAIN_RUN_DEPS=(
|
TOOLCHAIN_RUN_DEPS=(
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -25,7 +27,6 @@ TOOLCHAIN_BUILD_DEPS=(
|
|||||||
libipt libipt-devel # intel
|
libipt libipt-devel # intel
|
||||||
patch
|
patch
|
||||||
perl # for openssl
|
perl # for openssl
|
||||||
git
|
|
||||||
)
|
)
|
||||||
|
|
||||||
TOOLCHAIN_RUN_DEPS=(
|
TOOLCHAIN_RUN_DEPS=(
|
||||||
@@ -57,16 +58,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
libtool # for protobuf 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() {
|
list() {
|
||||||
echo "$1"
|
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,140 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Initialize a variable to track if the --build-deps or --toolchain-deps flag is passed
|
|
||||||
build_deps_flag=false
|
|
||||||
toolchain_deps_flag=false
|
|
||||||
|
|
||||||
# Display help if no arguments are passed
|
|
||||||
if [ "$#" -eq 0 ]; then
|
|
||||||
cat <<EOF
|
|
||||||
Please specify one or both of these config flags:
|
|
||||||
--build-deps for installing Memgraph build dependencies
|
|
||||||
--toolchain-deps for installing toolchain runtime dependencies
|
|
||||||
|
|
||||||
If you run into issues with this script, please run corresponding commands manually and open a github issue:
|
|
||||||
sudo ./environment/os/debian-10.sh install TOOLCHAIN_RUN_DEPS
|
|
||||||
sudo ./environment/os/debian-10.sh install MEMGRAPH_BUILD_DEPS
|
|
||||||
|
|
||||||
Link to create new github issue: https://github.com/memgraph/memgraph/issues/new?title=install-deps.sh%20...&assignee=gitbuda&body=%0A%0A%0A---%0AI%27m+a+human.+Please+be+nice.
|
|
||||||
EOF
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse command-line arguments
|
|
||||||
while [[ "$#" -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--build-deps) build_deps_flag=true ;;
|
|
||||||
--toolchain-deps) toolchain_deps_flag=true ;;
|
|
||||||
*) echo "Unknown parameter passed: $1"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Function to check and install dependencies for a given distribution script
|
|
||||||
run_script() {
|
|
||||||
local distro_script=$1
|
|
||||||
echo "Running script for $distro_script"
|
|
||||||
if [ "$toolchain_deps_flag" = true ]; then
|
|
||||||
sudo ./environment/os/$distro_script check TOOLCHAIN_RUN_DEPS
|
|
||||||
sudo ./environment/os/$distro_script install TOOLCHAIN_RUN_DEPS
|
|
||||||
fi
|
|
||||||
if [ "$build_deps_flag" = true ]; then
|
|
||||||
sudo ./environment/os/$distro_script install MEMGRAPH_BUILD_DEPS
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Detect OS, version, and architecture
|
|
||||||
if [ -f /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
OS=$ID
|
|
||||||
VER=$VERSION_ID
|
|
||||||
elif type lsb_release >/dev/null 2>&1; then
|
|
||||||
OS=$(lsb_release -si)
|
|
||||||
VER=$(lsb_release -sr)
|
|
||||||
elif [ -f /etc/lsb-release ]; then
|
|
||||||
. /etc/lsb-release
|
|
||||||
OS=$DISTRIB_ID
|
|
||||||
VER=$DISTRIB_RELEASE
|
|
||||||
elif [ -f /etc/debian_version ]; then
|
|
||||||
OS=Debian
|
|
||||||
VER=$(cat /etc/debian_version)
|
|
||||||
else
|
|
||||||
echo "OS not identified"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
case $ARCH in
|
|
||||||
arm*|aarch64)
|
|
||||||
ARCH="arm"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
ARCH=""
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Normalize OS name to lowercase
|
|
||||||
OS=$(echo "$OS" | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
# Map OS, version, and architecture to script name
|
|
||||||
case "$OS" in
|
|
||||||
debian)
|
|
||||||
case "$VER" in
|
|
||||||
10) run_script "debian-10.sh" ;;
|
|
||||||
11)
|
|
||||||
if [ "$ARCH" = "arm" ]; then
|
|
||||||
run_script "debian-11-arm.sh"
|
|
||||||
else
|
|
||||||
run_script "debian-11.sh"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
12)
|
|
||||||
if [ "$ARCH" = "arm" ]; then
|
|
||||||
run_script "debian-12-arm.sh"
|
|
||||||
else
|
|
||||||
run_script "debian-12.sh"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*) echo "Unsupported Debian version: $VER"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
ubuntu)
|
|
||||||
case "$VER" in
|
|
||||||
18.04) run_script "ubuntu-18.04.sh" ;;
|
|
||||||
20.04) run_script "ubuntu-20.04.sh" ;;
|
|
||||||
22.04)
|
|
||||||
if [ "$ARCH" = "arm" ]; then
|
|
||||||
run_script "ubuntu-22.04-arm.sh"
|
|
||||||
else
|
|
||||||
run_script "ubuntu-22.04.sh"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*) echo "Unsupported Ubuntu version: $VER"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
centos)
|
|
||||||
case "$VER" in
|
|
||||||
7) run_script "centos-7.sh" ;;
|
|
||||||
9) run_script "centos-9.sh" ;;
|
|
||||||
*) echo "Unsupported CentOS version: $VER"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
fedora)
|
|
||||||
case "$VER" in
|
|
||||||
36) run_script "fedora-36.sh" ;;
|
|
||||||
38) run_script "fedora-38.sh" ;;
|
|
||||||
39) run_script "fedora-39.sh" ;;
|
|
||||||
*) echo "Unsupported Fedora version: $VER"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
"amzn")
|
|
||||||
case "$VER" in
|
|
||||||
2) run_script "amzn-2.sh" ;;
|
|
||||||
*) echo "Unsupported Amazon Linux version: $VER"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unsupported OS: $OS"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -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
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -18,10 +20,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
pkg
|
pkg
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS=(
|
|
||||||
pkg
|
|
||||||
)
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
pkg
|
pkg
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
# IMPORTANT: Deprecated since memgraph v2.12.0.
|
|
||||||
|
|
||||||
check_operating_system "ubuntu-18.04"
|
check_operating_system "ubuntu-18.04"
|
||||||
check_architecture "x86_64"
|
check_architecture "x86_64"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
libsasl2-dev
|
libsasl2-dev
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp2
|
logrotate openssl python3 libseccomp2
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
libsasl2-dev
|
libsasl2-dev
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp2
|
logrotate openssl python3 libseccomp2
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
source "$DIR/../util.sh"
|
source "$DIR/../util.sh"
|
||||||
|
|
||||||
@@ -58,8 +60,6 @@ MEMGRAPH_BUILD_DEPS=(
|
|||||||
libsasl2-dev
|
libsasl2-dev
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMGRAPH_TEST_DEPS="${MEMGRAPH_BUILD_DEPS[*]}"
|
|
||||||
|
|
||||||
MEMGRAPH_RUN_DEPS=(
|
MEMGRAPH_RUN_DEPS=(
|
||||||
logrotate openssl python3 libseccomp2
|
logrotate openssl python3 libseccomp2
|
||||||
)
|
)
|
||||||
|
|||||||
1
environment/toolchain/.gitignore
vendored
1
environment/toolchain/.gitignore
vendored
@@ -2,4 +2,3 @@ archives
|
|||||||
build
|
build
|
||||||
output
|
output
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
tmp_build.sh
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
# NOTE: Copy this under memgraph/environment/toolchain/vN/tmp_build.sh, edit and test.
|
|
||||||
|
|
||||||
pushd () { command pushd "$@" > /dev/null; }
|
|
||||||
popd () { command popd "$@" > /dev/null; }
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
||||||
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 ""
|
|
||||||
echo "#### $1 ####"
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# HERE: Remove/clear dependencies from a given toolchain.
|
|
||||||
|
|
||||||
mkdir -p archives && pushd archives
|
|
||||||
# HERE: Download dependencies here.
|
|
||||||
popd
|
|
||||||
|
|
||||||
mkdir -p build
|
|
||||||
pushd build
|
|
||||||
source $PREFIX/activate
|
|
||||||
export CC=$PREFIX/bin/clang
|
|
||||||
export CXX=$PREFIX/bin/clang++
|
|
||||||
export CFLAGS="$CFLAGS -fPIC"
|
|
||||||
export PATH=$PREFIX/bin:$PATH
|
|
||||||
export LD_LIBRARY_PATH=$PREFIX/lib64
|
|
||||||
COMMON_CMAKE_FLAGS="-DCMAKE_INSTALL_PREFIX=$PREFIX
|
|
||||||
-DCMAKE_PREFIX_PATH=$PREFIX
|
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
|
||||||
-DCMAKE_C_COMPILER=$CC
|
|
||||||
-DCMAKE_CXX_COMPILER=$CXX
|
|
||||||
-DBUILD_SHARED_LIBS=OFF
|
|
||||||
-DCMAKE_CXX_STANDARD=20
|
|
||||||
-DBUILD_TESTING=OFF
|
|
||||||
-DCMAKE_REQUIRED_INCLUDES=$PREFIX/include
|
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
|
|
||||||
|
|
||||||
# HERE: Add dependencies to test below.
|
|
||||||
@@ -307,7 +307,7 @@ if [ ! -f $PREFIX/bin/ld.gold ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log_tool_name "GDB $GDB_VERSION"
|
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
|
if [ -d gdb-$GDB_VERSION ]; then
|
||||||
rm -rf gdb-$GDB_VERSION
|
rm -rf gdb-$GDB_VERSION
|
||||||
fi
|
fi
|
||||||
@@ -671,6 +671,7 @@ PROXYGEN_SHA256=5360a8ccdfb2f5a6c7b3eed331ec7ab0e2c792d579c6fff499c85c516c11fe14
|
|||||||
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
WANGLE_SHA256=1002e9c32b6f4837f6a760016e3b3e22f3509880ef3eaad191c80dc92655f23f
|
||||||
# WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532
|
# WANGLE_SHA256=0e493c03572bb27fe9ca03a9da5023e52fde99c95abdcaa919bb6190e7e69532
|
||||||
|
|
||||||
|
FLEX_VERSION=2.6.4
|
||||||
FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b
|
FMT_SHA256=78b8c0a72b1c35e4443a7e308df52498252d1cefc2b08c9a97bc9ee6cfe61f8b
|
||||||
FMT_VERSION=10.1.1
|
FMT_VERSION=10.1.1
|
||||||
# NOTE: spdlog depends on exact fmt versions -> UPGRADE fmt and spdlog TOGETHER.
|
# 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_SHA256=75c1fbb3d618dd3a0483bff0e26d0a92b495bbe5059c8b4f1c962b478b6e06e7
|
||||||
SNAPPY_VERSION=1.1.9
|
SNAPPY_VERSION=1.1.9
|
||||||
XZ_VERSION=5.2.5 # for LZMA
|
XZ_VERSION=5.2.5 # for LZMA
|
||||||
ZLIB_VERSION=1.3.1
|
ZLIB_VERSION=1.3
|
||||||
ZSTD_VERSION=1.5.5
|
ZSTD_VERSION=1.5.0
|
||||||
|
|
||||||
pushd archives
|
pushd archives
|
||||||
if [ ! -f boost_$BOOST_VERSION_UNDERSCORES.tar.gz ]; then
|
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
|
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
|
fi
|
||||||
if [ ! -f bzip2-$BZIP2_VERSION.tar.gz ]; then
|
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
|
fi
|
||||||
if [ ! -f double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz ]; then
|
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
|
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
|
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
|
wget https://github.com/facebookincubator/fizz/releases/download/v$FBLIBS_VERSION/fizz-v$FBLIBS_VERSION.tar.gz -O fizz-$FBLIBS_VERSION.tar.gz
|
||||||
fi
|
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
|
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
|
wget https://github.com/fmtlib/fmt/archive/refs/tags/$FMT_VERSION.tar.gz -O fmt-$FMT_VERSION.tar.gz
|
||||||
fi
|
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
|
echo "$DOUBLE_CONVERSION_SHA256 double-conversion-$DOUBLE_CONVERSION_VERSION.tar.gz" | sha256sum -c
|
||||||
# verify fizz
|
# verify fizz
|
||||||
echo "$FIZZ_SHA256 fizz-$FBLIBS_VERSION.tar.gz" | sha256sum -c
|
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
|
# verify fmt
|
||||||
echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c
|
echo "$FMT_SHA256 fmt-$FMT_VERSION.tar.gz" | sha256sum -c
|
||||||
# verify spdlog
|
# verify spdlog
|
||||||
@@ -1014,6 +1025,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
|||||||
if [ -d gflags ]; then
|
if [ -d gflags ]; then
|
||||||
rm -rf gflags
|
rm -rf gflags
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git clone https://github.com/memgraph/gflags.git gflags
|
git clone https://github.com/memgraph/gflags.git gflags
|
||||||
pushd gflags
|
pushd gflags
|
||||||
git checkout $GFLAGS_COMMIT_HASH
|
git checkout $GFLAGS_COMMIT_HASH
|
||||||
@@ -1022,7 +1034,7 @@ if [ ! -d $PREFIX/include/gflags ]; then
|
|||||||
cmake .. $COMMON_CMAKE_FLAGS \
|
cmake .. $COMMON_CMAKE_FLAGS \
|
||||||
-DREGISTER_INSTALL_PREFIX=OFF \
|
-DREGISTER_INSTALL_PREFIX=OFF \
|
||||||
-DBUILD_gflags_nothreads_LIB=OFF \
|
-DBUILD_gflags_nothreads_LIB=OFF \
|
||||||
-DGFLAGS_NO_FILENAMES=1
|
-DGFLAGS_NO_FILENAMES=0
|
||||||
make -j$CPUS install
|
make -j$CPUS install
|
||||||
popd && popd
|
popd && popd
|
||||||
fi
|
fi
|
||||||
@@ -1220,6 +1232,18 @@ if false; then
|
|||||||
fi
|
fi
|
||||||
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
|
popd
|
||||||
# NOTE: It's important/clean (e.g., easier upload to S3) to have a separated
|
# NOTE: It's important/clean (e.g., easier upload to S3) to have a separated
|
||||||
# folder to the output archive.
|
# folder to the output archive.
|
||||||
|
|||||||
@@ -20,18 +20,14 @@ if [ ! -f "$INPUT" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} BEGIN and COMMIT are required because variables share the same name (e.g. row)"
|
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} BEGIN and COMMIT are required because variables share the same name (e.g. row)"
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} CONSTRAINTS are just skipped -> ${COLOR_RED}please create constraints manually if needed${COLOR_NULL}"
|
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} CONSTRAINTS are just skipped -> ${COLOR_RED}please create consraints manually if needed${COLOR_NULL}"
|
||||||
|
|
||||||
echo 'CREATE INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' > "$OUTPUT"
|
|
||||||
|
|
||||||
sed -e 's/^:begin/BEGIN/g; s/^BEGIN$/BEGIN;/g;' \
|
sed -e 's/^:begin/BEGIN/g; s/^BEGIN$/BEGIN;/g;' \
|
||||||
-e 's/^:commit/COMMIT/g; s/^COMMIT$/COMMIT;/g;' \
|
-e 's/^:commit/COMMIT/g; s/^COMMIT$/COMMIT;/g;' \
|
||||||
-e '/^CALL/d; /^SCHEMA AWAIT/d;' \
|
-e '/^CALL/d; /^SCHEMA AWAIT/d;' \
|
||||||
-e 's/CREATE RANGE INDEX FOR (n:/CREATE INDEX ON :/g;' \
|
-e 's/CREATE RANGE INDEX FOR (n:/CREATE INDEX ON :/g;' \
|
||||||
-e 's/) ON (n./(/g;' \
|
-e 's/) ON (n./(/g;' \
|
||||||
-e '/^CREATE CONSTRAINT/d; /^DROP CONSTRAINT/d;' "$INPUT" >> "$OUTPUT"
|
-e '/^CREATE CONSTRAINT/d; /^DROP CONSTRAINT/d;' "$INPUT" > "$OUTPUT"
|
||||||
|
|
||||||
echo 'DROP INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' >> "$OUTPUT"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${COLOR_GREEN}DONE!${COLOR_NULL} Please find Memgraph compatible cypherl|.cypher file under $OUTPUT"
|
echo -e "${COLOR_GREEN}DONE!${COLOR_NULL} Please find Memgraph compatible cypherl|.cypher file under $OUTPUT"
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
COLOR_ORANGE="\e[38;5;208m"
|
|
||||||
COLOR_GREEN="\e[38;5;35m"
|
|
||||||
COLOR_RED="\e[0;31m"
|
|
||||||
COLOR_NULL="\e[0m"
|
|
||||||
|
|
||||||
print_help() {
|
|
||||||
echo -e "${COLOR_ORANGE}HOW TO RUN:${COLOR_NULL} $0 input_file_schema_path input_file_nodes_path input_file_relationships_path input_file_cleanup_path output_file_path"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$#" -ne 5 ]; then
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
INPUT_SCHEMA="$1"
|
|
||||||
INPUT_NODES="$2"
|
|
||||||
INPUT_RELATIONSHIPS="$3"
|
|
||||||
INPUT_CLEANUP="$4"
|
|
||||||
OUTPUT="$5"
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_SCHEMA" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_NODES" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_RELATIONSHIPS" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_CLEANUP" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} BEGIN and COMMIT are required because variables share the same name (e.g. row)"
|
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} CONSTRAINTS are just skipped -> ${COLOR_RED}please create constraints manually if needed${COLOR_NULL}"
|
|
||||||
|
|
||||||
|
|
||||||
echo 'CREATE INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' > "$OUTPUT"
|
|
||||||
|
|
||||||
sed -e 's/CREATE RANGE INDEX FOR (n:/CREATE INDEX ON :/g;' \
|
|
||||||
-e 's/) ON (n./(/g;' \
|
|
||||||
-e '/^CREATE CONSTRAINT/d' $INPUT_SCHEMA >> "$OUTPUT"
|
|
||||||
|
|
||||||
cat "$INPUT_NODES" >> "$OUTPUT"
|
|
||||||
cat "$INPUT_RELATIONSHIPS" >> "$OUTPUT"
|
|
||||||
|
|
||||||
sed -e '/^DROP CONSTRAINT/d' "$INPUT_CLEANUP" >> "$OUTPUT"
|
|
||||||
|
|
||||||
echo 'DROP INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' >> "$OUTPUT"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${COLOR_GREEN}DONE!${COLOR_NULL} Please find Memgraph compatible cypherl|.cypher file under $OUTPUT"
|
|
||||||
echo ""
|
|
||||||
echo "Please import data by executing => \`cat $OUTPUT | mgconsole\`"
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
COLOR_ORANGE="\e[38;5;208m"
|
|
||||||
COLOR_GREEN="\e[38;5;35m"
|
|
||||||
COLOR_RED="\e[0;31m"
|
|
||||||
COLOR_NULL="\e[0m"
|
|
||||||
|
|
||||||
print_help() {
|
|
||||||
echo -e "${COLOR_ORANGE}HOW TO RUN:${COLOR_NULL} $0 input_file_schema_path input_file_nodes_path input_file_relationships_path input_file_cleanup_path output_file_schema_path output_file_nodes_path output_file_relationships_path output_file_cleanup_path"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$#" -ne 8 ]; then
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
INPUT_SCHEMA="$1"
|
|
||||||
INPUT_NODES="$2"
|
|
||||||
INPUT_RELATIONSHIPS="$3"
|
|
||||||
INPUT_CLEANUP="$4"
|
|
||||||
OUTPUT_SCHEMA="$5"
|
|
||||||
OUTPUT_NODES="$6"
|
|
||||||
OUTPUT_RELATIONSHIPS="$7"
|
|
||||||
OUTPUT_CLEANUP="$8"
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_SCHEMA" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_NODES" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_RELATIONSHIPS" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$INPUT_CLEANUP" ]; then
|
|
||||||
echo -e "${COLOR_RED}ERROR:${COLOR_NULL} input_file_path is not a file!"
|
|
||||||
print_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} BEGIN and COMMIT are required because variables share the same name (e.g. row)"
|
|
||||||
echo -e "${COLOR_ORANGE}NOTE:${COLOR_NULL} CONSTRAINTS are just skipped -> ${COLOR_RED}please create constraints manually if needed${COLOR_NULL}"
|
|
||||||
|
|
||||||
|
|
||||||
echo 'CREATE INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' > "$OUTPUT_SCHEMA"
|
|
||||||
|
|
||||||
sed -e 's/CREATE RANGE INDEX FOR (n:/CREATE INDEX ON :/g;' \
|
|
||||||
-e 's/) ON (n./(/g;' \
|
|
||||||
-e '/^CREATE CONSTRAINT/d' $INPUT_SCHEMA >> "$OUTPUT_SCHEMA"
|
|
||||||
|
|
||||||
cat "$INPUT_NODES" > "$OUTPUT_NODES"
|
|
||||||
cat "$INPUT_RELATIONSHIPS" > "$OUTPUT_RELATIONSHIPS"
|
|
||||||
|
|
||||||
sed -e '/^DROP CONSTRAINT/d' "$INPUT_CLEANUP" >> "$OUTPUT_CLEANUP"
|
|
||||||
|
|
||||||
echo 'DROP INDEX ON :`UNIQUE IMPORT LABEL`(`UNIQUE IMPORT ID`);' >> "$OUTPUT_CLEANUP"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${COLOR_GREEN}DONE!${COLOR_NULL} Please find Memgraph compatible cypherl|.cypher files under $OUTPUT_SCHEMA, $OUTPUT_NODES, $OUTPUT_RELATIONSHIPS and $OUTPUT_CLEANUP"
|
|
||||||
echo ""
|
|
||||||
echo "Please import data by executing => \`cat $OUTPUT_SCHEMA | mgconsole\`, \`cat $OUTPUT_NODES | mgconsole\`, \`cat $OUTPUT_RELATIONSHIPS | mgconsole\` and \`cat $OUTPUT_CLEANUP | mgconsole\`"
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Memgraph Ltd.
|
// Copyright 2023 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@@ -283,7 +283,7 @@ inline mgp_list *list_all_unique_constraints(mgp_graph *graph, mgp_memory *memor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mgp_graph
|
// mgp_graph
|
||||||
|
|
||||||
inline bool graph_is_transactional(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_transactional, graph); }
|
inline bool graph_is_transactional(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_transactional, graph); }
|
||||||
|
|
||||||
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
|
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
|
||||||
|
|||||||
1
libs/.gitignore
vendored
1
libs/.gitignore
vendored
@@ -7,4 +7,3 @@
|
|||||||
!pulsar.patch
|
!pulsar.patch
|
||||||
!antlr4.10.1.patch
|
!antlr4.10.1.patch
|
||||||
!rocksdb8.1.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.
|
# NOTE: config/generate.py depends on the gflags help XML format.
|
||||||
find_package(gflags REQUIRED)
|
find_package(gflags REQUIRED)
|
||||||
find_package(fmt 8.0.1 REQUIRED)
|
find_package(fmt 8.0.1)
|
||||||
find_package(ZLIB 1.2.11 REQUIRED)
|
find_package(ZLIB 1.2.11 REQUIRED)
|
||||||
|
|
||||||
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ index ee9b58c..31359a9 100644
|
|||||||
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
|
@@ -48,7 +48,7 @@ option(LIBRDTSC_USE_PMU "Enables PMU usage on ARM platforms" OFF)
|
||||||
# | Library Build and Install Properties |
|
# | Library Build and Install Properties |
|
||||||
# +--------------------------------------------------------+
|
# +--------------------------------------------------------+
|
||||||
|
|
||||||
-add_library(rdtsc SHARED
|
-add_library(rdtsc SHARED
|
||||||
+add_library(rdtsc
|
+add_library(rdtsc
|
||||||
src/cycles.c
|
src/cycles.c
|
||||||
@@ -14,7 +14,7 @@ index ee9b58c..31359a9 100644
|
|||||||
@@ -72,15 +72,6 @@ target_include_directories(rdtsc
|
@@ -72,15 +72,6 @@ target_include_directories(rdtsc
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
)
|
)
|
||||||
|
|
||||||
-# Install directory changes depending on build mode
|
-# Install directory changes depending on build mode
|
||||||
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
-if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
||||||
- # During debug, the library will be installed into a local directory
|
- # 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)
|
# Specifying what to export when installing (GNUInstallDirs required)
|
||||||
install(TARGETS rdtsc
|
install(TARGETS rdtsc
|
||||||
EXPORT librstsc-config
|
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}"
|
||||||
@@ -168,11 +168,12 @@ pushd antlr4
|
|||||||
git apply ../antlr4.10.1.patch
|
git apply ../antlr4.10.1.patch
|
||||||
popd
|
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"
|
repo_clone_try_double "${primary_urls[cppitertools]}" "${secondary_urls[cppitertools]}" "cppitertools" "$cppitertools_ref"
|
||||||
|
|
||||||
# rapidcheck
|
# 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"
|
repo_clone_try_double "${primary_urls[rapidcheck]}" "${secondary_urls[rapidcheck]}" "rapidcheck" "$rapidcheck_tag"
|
||||||
|
|
||||||
# google benchmark
|
# google benchmark
|
||||||
@@ -220,7 +221,7 @@ repo_clone_try_double "${primary_urls[pymgclient]}" "${secondary_urls[pymgclient
|
|||||||
mgconsole_tag="v1.4.0" # (2023-05-21)
|
mgconsole_tag="v1.4.0" # (2023-05-21)
|
||||||
repo_clone_try_double "${primary_urls[mgconsole]}" "${secondary_urls[mgconsole]}" "mgconsole" "$mgconsole_tag" true
|
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
|
repo_clone_try_double "${primary_urls[spdlog]}" "${secondary_urls[spdlog]}" "spdlog" "$spdlog_tag" true
|
||||||
|
|
||||||
# librdkafka
|
# librdkafka
|
||||||
@@ -285,6 +286,5 @@ repo_clone_try_double "${primary_urls[range-v3]}" "${secondary_urls[range-v3]}"
|
|||||||
nuraft_tag="v2.1.0"
|
nuraft_tag="v2.1.0"
|
||||||
repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true
|
repo_clone_try_double "${primary_urls[nuraft]}" "${secondary_urls[nuraft]}" "nuraft" "$nuraft_tag" true
|
||||||
pushd nuraft
|
pushd nuraft
|
||||||
git apply ../nuraft2.1.0.patch
|
|
||||||
./prepare.sh
|
./prepare.sh
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Memgraph Ltd.
|
// Copyright 2023 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@@ -9,11 +9,10 @@
|
|||||||
// by the Apache License, Version 2.0, included in the file
|
// by the Apache License, Version 2.0, included in the file
|
||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
#include <mgp.hpp>
|
#include <mgp.hpp>
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
#include <unordered_set>
|
#include <optional>
|
||||||
|
|
||||||
namespace Schema {
|
namespace Schema {
|
||||||
|
|
||||||
@@ -38,7 +37,6 @@ constexpr std::string_view kParameterIndices = "indices";
|
|||||||
constexpr std::string_view kParameterUniqueConstraints = "unique_constraints";
|
constexpr std::string_view kParameterUniqueConstraints = "unique_constraints";
|
||||||
constexpr std::string_view kParameterExistenceConstraints = "existence_constraints";
|
constexpr std::string_view kParameterExistenceConstraints = "existence_constraints";
|
||||||
constexpr std::string_view kParameterDropExisting = "drop_existing";
|
constexpr std::string_view kParameterDropExisting = "drop_existing";
|
||||||
constexpr int kInitialNumberOfPropertyOccurances = 1;
|
|
||||||
|
|
||||||
std::string TypeOf(const mgp::Type &type);
|
std::string TypeOf(const mgp::Type &type);
|
||||||
|
|
||||||
@@ -110,79 +108,83 @@ void Schema::ProcessPropertiesRel(mgp::Record &record, const std::string_view &t
|
|||||||
record.Insert(std::string(kReturnMandatory).c_str(), mandatory);
|
record.Insert(std::string(kReturnMandatory).c_str(), mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PropertyInfo {
|
struct Property {
|
||||||
std::unordered_set<std::string> property_types; // property types
|
std::string name;
|
||||||
int64_t number_of_property_occurrences = 0;
|
mgp::Value value;
|
||||||
|
|
||||||
PropertyInfo() = default;
|
Property(const std::string &name, mgp::Value &&value) : name(name), value(std::move(value)) {}
|
||||||
explicit PropertyInfo(std::string &&property_type)
|
|
||||||
: property_types({std::move(property_type)}),
|
|
||||||
number_of_property_occurrences(Schema::kInitialNumberOfPropertyOccurances) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LabelsInfo {
|
|
||||||
std::unordered_map<std::string, PropertyInfo> properties; // key is a property name
|
|
||||||
int64_t number_of_label_occurrences = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LabelsHash {
|
struct LabelsHash {
|
||||||
std::size_t operator()(const std::set<std::string> &s) const { return boost::hash_range(s.begin(), s.end()); }
|
std::size_t operator()(const std::set<std::string> &set) const {
|
||||||
|
std::size_t seed = set.size();
|
||||||
|
for (const auto &i : set) {
|
||||||
|
seed ^= std::hash<std::string>{}(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LabelsComparator {
|
struct LabelsComparator {
|
||||||
bool operator()(const std::set<std::string> &lhs, const std::set<std::string> &rhs) const { return lhs == rhs; }
|
bool operator()(const std::set<std::string> &lhs, const std::set<std::string> &rhs) const { return lhs == rhs; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PropertyComparator {
|
||||||
|
bool operator()(const Property &lhs, const Property &rhs) const { return lhs.name < rhs.name; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropertyInfo {
|
||||||
|
std::set<Property, PropertyComparator> properties;
|
||||||
|
bool mandatory;
|
||||||
|
};
|
||||||
|
|
||||||
void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result,
|
void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result,
|
||||||
mgp_memory *memory) {
|
mgp_memory *memory) {
|
||||||
mgp::MemoryDispatcherGuard guard{memory};
|
mgp::MemoryDispatcherGuard guard{memory};
|
||||||
const auto record_factory = mgp::RecordFactory(result);
|
const auto record_factory = mgp::RecordFactory(result);
|
||||||
try {
|
try {
|
||||||
std::unordered_map<std::set<std::string>, LabelsInfo, LabelsHash, LabelsComparator> node_types_properties;
|
std::unordered_map<std::set<std::string>, PropertyInfo, LabelsHash, LabelsComparator> node_types_properties;
|
||||||
|
|
||||||
for (const auto node : mgp::Graph(memgraph_graph).Nodes()) {
|
for (auto node : mgp::Graph(memgraph_graph).Nodes()) {
|
||||||
std::set<std::string> labels_set = {};
|
std::set<std::string> labels_set = {};
|
||||||
for (const auto label : node.Labels()) {
|
for (auto label : node.Labels()) {
|
||||||
labels_set.emplace(label);
|
labels_set.emplace(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
node_types_properties[labels_set].number_of_label_occurrences++;
|
if (node_types_properties.find(labels_set) == node_types_properties.end()) {
|
||||||
|
node_types_properties[labels_set] = PropertyInfo{std::set<Property, PropertyComparator>(), true};
|
||||||
|
}
|
||||||
|
|
||||||
if (node.Properties().empty()) {
|
if (node.Properties().empty()) {
|
||||||
|
node_types_properties[labels_set].mandatory = false; // if there is node with no property, it is not mandatory
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &labels_info = node_types_properties.at(labels_set);
|
auto &property_info = node_types_properties.at(labels_set);
|
||||||
for (const auto &[key, prop] : node.Properties()) {
|
for (auto &[key, prop] : node.Properties()) {
|
||||||
auto prop_type = TypeOf(prop.Type());
|
property_info.properties.emplace(key, std::move(prop));
|
||||||
if (labels_info.properties.find(key) == labels_info.properties.end()) {
|
if (property_info.mandatory) {
|
||||||
labels_info.properties[key] = PropertyInfo{std::move(prop_type)};
|
property_info.mandatory =
|
||||||
} else {
|
property_info.properties.size() == 1; // if there is only one property, it is mandatory
|
||||||
labels_info.properties[key].property_types.emplace(prop_type);
|
|
||||||
labels_info.properties[key].number_of_property_occurrences++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &[node_type, labels_info] : node_types_properties) { // node type is a set of labels
|
for (auto &[labels, property_info] : node_types_properties) {
|
||||||
std::string label_type;
|
std::string label_type;
|
||||||
auto labels_list = mgp::List();
|
mgp::List labels_list = mgp::List();
|
||||||
for (const auto &label : node_type) {
|
for (auto const &label : labels) {
|
||||||
label_type += ":`" + std::string(label) + "`";
|
label_type += ":`" + std::string(label) + "`";
|
||||||
labels_list.AppendExtend(mgp::Value(label));
|
labels_list.AppendExtend(mgp::Value(label));
|
||||||
}
|
}
|
||||||
for (const auto &prop : labels_info.properties) {
|
for (auto const &prop : property_info.properties) {
|
||||||
auto prop_types = mgp::List();
|
|
||||||
for (const auto &prop_type : prop.second.property_types) {
|
|
||||||
prop_types.AppendExtend(mgp::Value(prop_type));
|
|
||||||
}
|
|
||||||
bool mandatory = prop.second.number_of_property_occurrences == labels_info.number_of_label_occurrences;
|
|
||||||
auto record = record_factory.NewRecord();
|
auto record = record_factory.NewRecord();
|
||||||
ProcessPropertiesNode(record, label_type, labels_list, prop.first, prop_types, mandatory);
|
ProcessPropertiesNode(record, label_type, labels_list, prop.name, TypeOf(prop.value.Type()),
|
||||||
|
property_info.mandatory);
|
||||||
}
|
}
|
||||||
if (labels_info.properties.empty()) {
|
if (property_info.properties.empty()) {
|
||||||
auto record = record_factory.NewRecord();
|
auto record = record_factory.NewRecord();
|
||||||
ProcessPropertiesNode<mgp::List>(record, label_type, labels_list, "", mgp::List(), false);
|
ProcessPropertiesNode<std::string>(record, label_type, labels_list, "", "", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,45 +197,40 @@ void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph,
|
|||||||
void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
|
||||||
mgp::MemoryDispatcherGuard guard{memory};
|
mgp::MemoryDispatcherGuard guard{memory};
|
||||||
|
|
||||||
std::unordered_map<std::string, LabelsInfo> rel_types_properties;
|
std::unordered_map<std::string, PropertyInfo> rel_types_properties;
|
||||||
const auto record_factory = mgp::RecordFactory(result);
|
const auto record_factory = mgp::RecordFactory(result);
|
||||||
try {
|
try {
|
||||||
const auto graph = mgp::Graph(memgraph_graph);
|
const mgp::Graph graph = mgp::Graph(memgraph_graph);
|
||||||
for (const auto rel : graph.Relationships()) {
|
for (auto rel : graph.Relationships()) {
|
||||||
std::string rel_type = std::string(rel.Type());
|
std::string rel_type = std::string(rel.Type());
|
||||||
|
if (rel_types_properties.find(rel_type) == rel_types_properties.end()) {
|
||||||
rel_types_properties[rel_type].number_of_label_occurrences++;
|
rel_types_properties[rel_type] = PropertyInfo{std::set<Property, PropertyComparator>(), true};
|
||||||
|
}
|
||||||
|
|
||||||
if (rel.Properties().empty()) {
|
if (rel.Properties().empty()) {
|
||||||
|
rel_types_properties[rel_type].mandatory = false; // if there is rel with no property, it is not mandatory
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &labels_info = rel_types_properties.at(rel_type);
|
auto &property_info = rel_types_properties.at(rel_type);
|
||||||
for (auto &[key, prop] : rel.Properties()) {
|
for (auto &[key, prop] : rel.Properties()) {
|
||||||
auto prop_type = TypeOf(prop.Type());
|
property_info.properties.emplace(key, std::move(prop));
|
||||||
if (labels_info.properties.find(key) == labels_info.properties.end()) {
|
if (property_info.mandatory) {
|
||||||
labels_info.properties[key] = PropertyInfo{std::move(prop_type)};
|
property_info.mandatory =
|
||||||
} else {
|
property_info.properties.size() == 1; // if there is only one property, it is mandatory
|
||||||
labels_info.properties[key].property_types.emplace(prop_type);
|
|
||||||
labels_info.properties[key].number_of_property_occurrences++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &[rel_type, labels_info] : rel_types_properties) {
|
for (auto &[type, property_info] : rel_types_properties) {
|
||||||
std::string type_str = ":`" + std::string(rel_type) + "`";
|
std::string type_str = ":`" + std::string(type) + "`";
|
||||||
for (const auto &prop : labels_info.properties) {
|
for (auto const &prop : property_info.properties) {
|
||||||
auto prop_types = mgp::List();
|
|
||||||
for (const auto &prop_type : prop.second.property_types) {
|
|
||||||
prop_types.AppendExtend(mgp::Value(prop_type));
|
|
||||||
}
|
|
||||||
bool mandatory = prop.second.number_of_property_occurrences == labels_info.number_of_label_occurrences;
|
|
||||||
auto record = record_factory.NewRecord();
|
auto record = record_factory.NewRecord();
|
||||||
ProcessPropertiesRel(record, type_str, prop.first, prop_types, mandatory);
|
ProcessPropertiesRel(record, type_str, prop.name, TypeOf(prop.value.Type()), property_info.mandatory);
|
||||||
}
|
}
|
||||||
if (labels_info.properties.empty()) {
|
if (property_info.properties.empty()) {
|
||||||
auto record = record_factory.NewRecord();
|
auto record = record_factory.NewRecord();
|
||||||
ProcessPropertiesRel<mgp::List>(record, type_str, "", mgp::List(), false);
|
ProcessPropertiesRel<std::string>(record, type_str, "", "", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,42 +35,16 @@ DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth m
|
|||||||
}
|
}
|
||||||
return true;
|
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,
|
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
|
||||||
"Timeout (in milliseconds) used when waiting for a "
|
"Timeout (in milliseconds) used when waiting for a "
|
||||||
"response from the auth module.",
|
"response from the auth module.",
|
||||||
FLAG_IN_RANGE(100, 1800000));
|
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 {
|
namespace memgraph::auth {
|
||||||
|
|
||||||
const Auth::Epoch Auth::kStartEpoch = 1;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
/**
|
/**
|
||||||
@@ -218,17 +192,6 @@ void MigrateVersions(kvstore::KVStore &store) {
|
|||||||
version_str = kVersionV1;
|
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
|
}; // namespace
|
||||||
|
|
||||||
Auth::Auth(std::string storage_directory, Config config)
|
Auth::Auth(std::string storage_directory, Config config)
|
||||||
@@ -236,11 +199,8 @@ Auth::Auth(std::string storage_directory, Config config)
|
|||||||
MigrateVersions(storage_);
|
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()) {
|
if (module_.IsUsed()) {
|
||||||
/*
|
|
||||||
* MODULE AUTH STORAGE
|
|
||||||
*/
|
|
||||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||||
if (license_check_result.HasError()) {
|
if (license_check_result.HasError()) {
|
||||||
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
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>();
|
auto is_authenticated = ret_authenticated.get<bool>();
|
||||||
const auto &rolename = ret_role.get<std::string>();
|
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.
|
// Authenticate the user.
|
||||||
if (!is_authenticated) return std::nullopt;
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
// Find or create the user and return it.
|
||||||
* LOCAL AUTH STORAGE
|
auto user = GetUser(username);
|
||||||
*/
|
if (!user) {
|
||||||
auto user = GetUser(username);
|
if (FLAGS_auth_module_create_missing_user) {
|
||||||
if (!user) {
|
user = AddUser(username, password);
|
||||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
if (!user) {
|
||||||
"https://memgr.ph/auth"));
|
spdlog::warn(utils::MessageWithLink(
|
||||||
return std::nullopt;
|
"Couldn't create the missing user '{}' using the auth module because the user already exists as a role.",
|
||||||
}
|
username, "https://memgr.ph/auth"));
|
||||||
if (!user->CheckPassword(password)) {
|
return std::nullopt;
|
||||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
}
|
||||||
username, "https://memgr.ph/auth"));
|
} else {
|
||||||
return std::nullopt;
|
spdlog::warn(utils::MessageWithLink(
|
||||||
}
|
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
|
||||||
if (user->UpgradeHash(password)) {
|
"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);
|
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 {
|
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
||||||
auto link = storage_.Get(kLinkPrefix + user.username());
|
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) {
|
if (link) {
|
||||||
auto role = GetRole(*link);
|
auto role = GetRole(*link);
|
||||||
if (role) {
|
if (role) {
|
||||||
user.SetRole(*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;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||||
DisableIfModuleUsed();
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (const auto *role = user.role(); role != nullptr) {
|
if (const auto *role = user.role(); role != nullptr) {
|
||||||
success = storage_.PutMultiple(
|
success = storage_.PutMultiple(
|
||||||
@@ -334,10 +338,6 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
throw AuthException("Couldn't save user '{}'!", user.username());
|
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
|
// All changes to the user end up calling this function, so no need to add a delta anywhere else
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#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) {
|
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
|
||||||
DisableIfModuleUsed();
|
|
||||||
// Check if null
|
// Check if null
|
||||||
if (!password) {
|
if (!password) {
|
||||||
if (!config_.password_permit_null) {
|
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,
|
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password,
|
||||||
system::Transaction *system_tx) {
|
system::Transaction *system_tx) {
|
||||||
DisableIfModuleUsed();
|
|
||||||
if (!NameRegexMatch(username)) {
|
if (!NameRegexMatch(username)) {
|
||||||
throw AuthException("Invalid user name.");
|
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) {
|
bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) {
|
||||||
DisableIfModuleUsed();
|
|
||||||
auto username = utils::ToLowerCase(username_orig);
|
auto username = utils::ToLowerCase(username_orig);
|
||||||
if (!storage_.Get(kUserPrefix + username)) return false;
|
if (!storage_.Get(kUserPrefix + username)) return false;
|
||||||
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
||||||
if (!storage_.DeleteMultiple(keys)) {
|
if (!storage_.DeleteMultiple(keys)) {
|
||||||
throw AuthException("Couldn't remove user '{}'!", username);
|
throw AuthException("Couldn't remove user '{}'!", username);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durability updated -> new epoch
|
|
||||||
UpdateEpoch();
|
|
||||||
|
|
||||||
// Handling drop user delta
|
// Handling drop user delta
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#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) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
if (username != utils::ToLowerCase(username)) continue;
|
if (username != utils::ToLowerCase(username)) continue;
|
||||||
try {
|
auto user = GetUser(username);
|
||||||
User user = auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
if (user) {
|
||||||
LinkUser(user);
|
ret.push_back(std::move(*user));
|
||||||
ret.emplace_back(std::move(user));
|
|
||||||
} catch (AuthException &) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -435,12 +425,9 @@ std::vector<std::string> Auth::AllUsernames() const {
|
|||||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
if (username != utils::ToLowerCase(username)) continue;
|
if (username != utils::ToLowerCase(username)) continue;
|
||||||
try {
|
auto user = GetUser(username);
|
||||||
// Check if serialized correctly
|
if (user) {
|
||||||
memgraph::auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
ret.push_back(username);
|
||||||
ret.emplace_back(std::move(username));
|
|
||||||
} catch (AuthException &) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
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::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 {
|
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
||||||
auto rolename = utils::ToLowerCase(rolename_orig);
|
auto rolename = utils::ToLowerCase(rolename_orig);
|
||||||
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
||||||
if (!existing_role) return std::nullopt;
|
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) {
|
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
|
||||||
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
||||||
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
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
|
// All changes to the role end up calling this function, so no need to add a delta anywhere else
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@@ -498,10 +486,6 @@ bool Auth::RemoveRole(const std::string &rolename_orig, system::Transaction *sys
|
|||||||
if (!storage_.DeleteMultiple(keys)) {
|
if (!storage_.DeleteMultiple(keys)) {
|
||||||
throw AuthException("Couldn't remove role '{}'!", rolename);
|
throw AuthException("Couldn't remove role '{}'!", rolename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durability updated -> new epoch
|
|
||||||
UpdateEpoch();
|
|
||||||
|
|
||||||
// Handling drop role delta
|
// Handling drop role delta
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#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) {
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||||
auto rolename = it->first.substr(kRolePrefix.size());
|
auto rolename = it->first.substr(kRolePrefix.size());
|
||||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||||
Role role = memgraph::auth::Role::Deserialize(ParseJson(it->second)); // Will throw on failure
|
if (auto role = GetRole(rolename)) {
|
||||||
ret.emplace_back(std::move(role));
|
ret.push_back(*role);
|
||||||
|
} else {
|
||||||
|
throw AuthException("Couldn't load role '{}'!", rolename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -527,19 +514,14 @@ std::vector<std::string> Auth::AllRolenames() const {
|
|||||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||||
auto rolename = it->first.substr(kRolePrefix.size());
|
auto rolename = it->first.substr(kRolePrefix.size());
|
||||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||||
try {
|
if (auto role = GetRole(rolename)) {
|
||||||
// Check that the data is serialized correctly
|
ret.push_back(rolename);
|
||||||
memgraph::auth::Role::Deserialize(ParseJson(it->second));
|
|
||||||
ret.emplace_back(std::move(rolename));
|
|
||||||
} catch (AuthException &) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
||||||
DisableIfModuleUsed();
|
|
||||||
const auto rolename = utils::ToLowerCase(rolename_orig);
|
const auto rolename = utils::ToLowerCase(rolename_orig);
|
||||||
std::vector<auth::User> ret;
|
std::vector<auth::User> ret;
|
||||||
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
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
|
#ifdef MG_ENTERPRISE
|
||||||
Auth::Result Auth::GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
bool Auth::GrantDatabaseToUser(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;
|
|
||||||
}
|
|
||||||
if (auto user = GetUser(name)) {
|
if (auto user = GetUser(name)) {
|
||||||
GrantDatabase(db, *user, system_tx);
|
if (db == kAllDatabases) {
|
||||||
return SUCCESS;
|
user->db_access().GrantAll();
|
||||||
}
|
} else {
|
||||||
if (auto role = GetRole(name)) {
|
user->db_access().Add(db);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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)) {
|
if (auto user = GetUser(name)) {
|
||||||
DenyDatabase(db, *user, system_tx);
|
if (db == kAllDatabases) {
|
||||||
return SUCCESS;
|
user->db_access().DenyAll();
|
||||||
}
|
} else {
|
||||||
if (auto role = GetRole(name)) {
|
user->db_access().Remove(db);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return NO_ROLE;
|
SaveUser(*user, system_tx);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (auto user = GetUser(name)) {
|
return false;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
|
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
|
||||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
try {
|
if (auto user = GetUser(username)) {
|
||||||
User user = auth::User::Deserialize(ParseJson(it->second));
|
user->db_access().Delete(db);
|
||||||
LinkUser(user);
|
SaveUser(*user, system_tx);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Auth::Result Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
bool 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;
|
|
||||||
}
|
|
||||||
if (auto user = GetUser(name)) {
|
if (auto user = GetUser(name)) {
|
||||||
SetMainDatabase(db, *user, system_tx);
|
if (!user->db_access().SetDefault(db)) {
|
||||||
return SUCCESS;
|
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
||||||
|
}
|
||||||
|
SaveUser(*user, system_tx);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (auto role = GetRole(name)) {
|
return false;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -29,18 +29,6 @@ using SynchedAuth = memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph
|
|||||||
|
|
||||||
static const constexpr char *const kAllDatabases = "*";
|
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.
|
* This class serves as the main Authentication/Authorization storage.
|
||||||
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
||||||
@@ -73,25 +61,6 @@ class Auth final {
|
|||||||
std::regex password_regex{password_regex_str};
|
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);
|
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
|
* @return a user when the username and password match, nullopt otherwise
|
||||||
* @throw AuthException if unable to authenticate for whatever reason.
|
* @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.
|
* Gets a user from the storage.
|
||||||
@@ -132,8 +101,6 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
std::optional<User> GetUser(const std::string &username) const;
|
std::optional<User> GetUser(const std::string &username) const;
|
||||||
|
|
||||||
void LinkUser(User &user) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a user object to the storage.
|
* Saves a user object to the storage.
|
||||||
*
|
*
|
||||||
@@ -196,13 +163,6 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
bool HasUsers() const;
|
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.
|
* Gets a role from the storage.
|
||||||
*
|
*
|
||||||
@@ -213,37 +173,6 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
std::optional<Role> GetRole(const std::string &rolename) const;
|
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.
|
* Saves a role object to the storage.
|
||||||
*
|
*
|
||||||
@@ -300,6 +229,16 @@ class Auth final {
|
|||||||
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#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.
|
* @brief Grant access to individual database for a user.
|
||||||
*
|
*
|
||||||
@@ -308,33 +247,7 @@ class Auth final {
|
|||||||
* @return true on success
|
* @return true on success
|
||||||
* @throw AuthException if unable to find or update the user
|
* @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);
|
bool GrantDatabaseToUser(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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete a database from all users.
|
* @brief Delete a database from all users.
|
||||||
@@ -352,17 +265,9 @@ class Auth final {
|
|||||||
* @return true on success
|
* @return true on success
|
||||||
* @throw AuthException if unable to find or update the user
|
* @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);
|
bool 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);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool UpToDate(Epoch &e) const {
|
|
||||||
bool res = e == epoch_;
|
|
||||||
e = epoch_;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
@@ -373,18 +278,11 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
bool NameRegexMatch(const std::string &user_or_role) const;
|
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,
|
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
|
||||||
// Auth is not thread-safe because modifying users and roles might require
|
// Auth is not thread-safe because modifying users and roles might require
|
||||||
// more than one operation on the storage.
|
// more than one operation on the storage.
|
||||||
kvstore::KVStore storage_;
|
kvstore::KVStore storage_;
|
||||||
auth::Module module_;
|
auth::Module module_;
|
||||||
Config config_;
|
Config config_;
|
||||||
Epoch epoch_{kStartEpoch};
|
|
||||||
};
|
};
|
||||||
} // namespace memgraph::auth
|
} // namespace memgraph::auth
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <json/json.hpp>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <json/json.hpp>
|
|
||||||
|
|
||||||
namespace memgraph::auth {
|
namespace memgraph::auth {
|
||||||
/// Need to be stable, auth durability depends on this
|
/// Need to be stable, auth durability depends on this
|
||||||
enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 };
|
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) {}
|
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
Role::Role(const std::string &rolename, const Permissions &permissions,
|
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)),
|
: rolename_(utils::ToLowerCase(rolename)),
|
||||||
permissions_(permissions),
|
permissions_(permissions),
|
||||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
|
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {}
|
||||||
db_access_(std::move(db_access)) {}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::string &Role::rolename() const { return rolename_; }
|
const std::string &Role::rolename() const { return rolename_; }
|
||||||
@@ -455,10 +454,8 @@ nlohmann::json Role::Serialize() const {
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||||
data[kDatabases] = db_access_.Serialize();
|
|
||||||
} else {
|
} else {
|
||||||
data[kFineGrainedAccessHandler] = {};
|
data[kFineGrainedAccessHandler] = {};
|
||||||
data[kDatabases] = {};
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return data;
|
return data;
|
||||||
@@ -474,21 +471,12 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
|||||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
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;
|
FineGrainedAccessHandler fine_grained_access_handler;
|
||||||
// We can have an empty fine_grained if the user was created without a valid license
|
// We can have an empty fine_grained if the user was created without a valid license
|
||||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
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
|
#endif
|
||||||
return {data[kRoleName], permissions};
|
return {data[kRoleName], permissions};
|
||||||
@@ -505,7 +493,7 @@ bool operator==(const Role &first, const Role &second) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
void Databases::Grant(std::string_view db) {
|
void Databases::Add(std::string_view db) {
|
||||||
if (allow_all_) {
|
if (allow_all_) {
|
||||||
grants_dbs_.clear();
|
grants_dbs_.clear();
|
||||||
allow_all_ = false;
|
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
|
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);
|
denies_dbs_.emplace(db);
|
||||||
grants_dbs_.erase(db);
|
grants_dbs_.erase(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Databases::Revoke(const std::string &db) {
|
void Databases::Delete(const std::string &db) {
|
||||||
denies_dbs_.erase(db);
|
denies_dbs_.erase(db);
|
||||||
if (!allow_all_) {
|
if (!allow_all_) {
|
||||||
grants_dbs_.erase(db);
|
grants_dbs_.erase(db);
|
||||||
}
|
}
|
||||||
// Reset if default deleted
|
// Reset if default deleted
|
||||||
if (main_db_ == db) {
|
if (default_db_ == db) {
|
||||||
main_db_ = "";
|
default_db_ = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,16 +530,9 @@ void Databases::DenyAll() {
|
|||||||
denies_dbs_.clear();
|
denies_dbs_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Databases::RevokeAll() {
|
bool Databases::SetDefault(std::string_view db) {
|
||||||
allow_all_ = false;
|
|
||||||
grants_dbs_.clear();
|
|
||||||
denies_dbs_.clear();
|
|
||||||
main_db_ = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Databases::SetMain(std::string_view db) {
|
|
||||||
if (!Contains(db)) return false;
|
if (!Contains(db)) return false;
|
||||||
main_db_ = db;
|
default_db_ = db;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,11 +540,11 @@ bool Databases::SetMain(std::string_view db) {
|
|||||||
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &Databases::GetMain() const {
|
const std::string &Databases::GetDefault() const {
|
||||||
if (!Contains(main_db_)) {
|
if (!Contains(default_db_)) {
|
||||||
throw AuthException("No access to the set default database \"{}\".", main_db_);
|
throw AuthException("No access to the set default database \"{}\".", default_db_);
|
||||||
}
|
}
|
||||||
return main_db_;
|
return default_db_;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json Databases::Serialize() const {
|
nlohmann::json Databases::Serialize() const {
|
||||||
@@ -571,7 +552,7 @@ nlohmann::json Databases::Serialize() const {
|
|||||||
data[kGrants] = grants_dbs_;
|
data[kGrants] = grants_dbs_;
|
||||||
data[kDenies] = denies_dbs_;
|
data[kDenies] = denies_dbs_;
|
||||||
data[kAllowAll] = allow_all_;
|
data[kAllowAll] = allow_all_;
|
||||||
data[kDefault] = main_db_;
|
data[kDefault] = default_db_;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,16 +719,15 @@ User User::Deserialize(const nlohmann::json &data) {
|
|||||||
} else {
|
} else {
|
||||||
// Back-compatibility
|
// Back-compatibility
|
||||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||||
db_access.Grant(dbms::kDefaultDB);
|
db_access.Add(dbms::kDefaultDB);
|
||||||
db_access.SetMain(dbms::kDefaultDB);
|
db_access.SetDefault(dbms::kDefaultDB);
|
||||||
}
|
}
|
||||||
FineGrainedAccessHandler fine_grained_access_handler;
|
FineGrainedAccessHandler fine_grained_access_handler;
|
||||||
// We can have an empty fine_grained if the user was created without a valid license
|
// We can have an empty fine_grained if the user was created without a valid license
|
||||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||||
}
|
}
|
||||||
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler),
|
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler), db_access};
|
||||||
std::move(db_access)};
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return {data[kUsername], std::move(password_hash), permissions};
|
return {data[kUsername], std::move(password_hash), permissions};
|
||||||
|
|||||||
@@ -205,96 +205,7 @@ class FineGrainedAccessHandler final {
|
|||||||
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
class Role final {
|
||||||
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 {
|
|
||||||
public:
|
public:
|
||||||
Role() = default;
|
Role() = default;
|
||||||
|
|
||||||
@@ -302,7 +213,7 @@ class Role {
|
|||||||
Role(const std::string &rolename, const Permissions &permissions);
|
Role(const std::string &rolename, const Permissions &permissions);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
Role(const std::string &rolename, const Permissions &permissions,
|
Role(const std::string &rolename, const Permissions &permissions,
|
||||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
|
FineGrainedAccessHandler fine_grained_access_handler);
|
||||||
#endif
|
#endif
|
||||||
Role(const Role &) = default;
|
Role(const Role &) = default;
|
||||||
Role &operator=(const Role &) = default;
|
Role &operator=(const Role &) = default;
|
||||||
@@ -313,23 +224,12 @@ class Role {
|
|||||||
const std::string &rolename() const;
|
const std::string &rolename() const;
|
||||||
const Permissions &permissions() const;
|
const Permissions &permissions() const;
|
||||||
Permissions &permissions();
|
Permissions &permissions();
|
||||||
Permissions GetPermissions() const { return permissions_; }
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
const FineGrainedAccessHandler &fine_grained_access_handler() const;
|
const FineGrainedAccessHandler &fine_grained_access_handler() const;
|
||||||
FineGrainedAccessHandler &fine_grained_access_handler();
|
FineGrainedAccessHandler &fine_grained_access_handler();
|
||||||
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
|
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
|
||||||
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
|
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
|
||||||
#endif
|
#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;
|
nlohmann::json Serialize() const;
|
||||||
|
|
||||||
/// @throw AuthException if unable to deserialize.
|
/// @throw AuthException if unable to deserialize.
|
||||||
@@ -342,12 +242,93 @@ class Role {
|
|||||||
Permissions permissions_;
|
Permissions permissions_;
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||||
Databases db_access_;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const Role &first, const Role &second);
|
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.
|
// TODO (mferencevic): Implement password expiry.
|
||||||
class User final {
|
class User final {
|
||||||
public:
|
public:
|
||||||
@@ -407,18 +388,6 @@ class User final {
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
Databases &db_access() { return database_access_; }
|
Databases &db_access() { return database_access_; }
|
||||||
const Databases &db_access() const { 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
|
#endif
|
||||||
|
|
||||||
nlohmann::json Serialize() const;
|
nlohmann::json Serialize() const;
|
||||||
@@ -434,7 +403,7 @@ class User final {
|
|||||||
Permissions permissions_;
|
Permissions permissions_;
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||||
Databases database_access_{};
|
Databases database_access_;
|
||||||
#endif
|
#endif
|
||||||
std::optional<Role> role_;
|
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
|
// 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
|
// 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Module::IsUsed() const { return !module_executable_path_.empty(); }
|
bool Module::IsUsed() { return !module_executable_path_.empty(); }
|
||||||
|
|
||||||
void Module::Shutdown() {
|
void Module::Shutdown() {
|
||||||
if (pid_ == -1) return;
|
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
|
// 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
|
// 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.
|
/// specified executable path and can thus be used.
|
||||||
///
|
///
|
||||||
/// @return boolean indicating whether the module can be used
|
/// @return boolean indicating whether the module can be used
|
||||||
bool IsUsed() const;
|
bool IsUsed();
|
||||||
|
|
||||||
~Module();
|
~Module();
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,11 @@
|
|||||||
#include "utils/enum.hpp"
|
#include "utils/enum.hpp"
|
||||||
|
|
||||||
namespace memgraph::slk {
|
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 {
|
namespace {
|
||||||
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
||||||
std::string tmp;
|
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
|
// 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
|
// 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 "communication/bolt/v1/value.hpp"
|
||||||
#include "utils/logging.hpp"
|
#include "utils/logging.hpp"
|
||||||
|
|
||||||
#include "communication/bolt/v1/fmt.hpp"
|
|
||||||
#include "io/network/fmt.hpp"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04};
|
constexpr uint8_t kBoltV43Version[4] = {0x00, 0x00, 0x03, 0x04};
|
||||||
constexpr uint8_t kEmptyBoltVersion[4] = {0x00, 0x00, 0x00, 0x00};
|
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
|
// 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
|
// 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 <boost/beast/core.hpp>
|
||||||
|
|
||||||
#include "communication/context.hpp"
|
#include "communication/context.hpp"
|
||||||
#include "communication/fmt.hpp"
|
|
||||||
#include "communication/http/session.hpp"
|
#include "communication/http/session.hpp"
|
||||||
#include "utils/spin_lock.hpp"
|
#include "utils/spin_lock.hpp"
|
||||||
#include "utils/synchronized.hpp"
|
#include "utils/synchronized.hpp"
|
||||||
@@ -83,7 +82,7 @@ class Listener final : public std::enable_shared_from_this<Listener<TRequestHand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::info("HTTP server is listening on {}", endpoint);
|
spdlog::info("HTTP server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoAccept() {
|
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
|
// 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
|
// 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 "communication/session.hpp"
|
||||||
#include "io/network/epoll.hpp"
|
#include "io/network/epoll.hpp"
|
||||||
#include "io/network/fmt.hpp"
|
|
||||||
#include "io/network/socket.hpp"
|
#include "io/network/socket.hpp"
|
||||||
#include "utils/logging.hpp"
|
#include "utils/logging.hpp"
|
||||||
#include "utils/signals.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
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Memgraph Ltd.
|
// Copyright 2023 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// 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/init.hpp"
|
||||||
#include "communication/listener.hpp"
|
#include "communication/listener.hpp"
|
||||||
#include "io/network/fmt.hpp"
|
|
||||||
#include "io/network/socket.hpp"
|
#include "io/network/socket.hpp"
|
||||||
#include "utils/logging.hpp"
|
#include "utils/logging.hpp"
|
||||||
#include "utils/message.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
|
// 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
|
// 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 <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
#include "communication/context.hpp"
|
#include "communication/context.hpp"
|
||||||
#include "communication/fmt.hpp"
|
|
||||||
#include "communication/init.hpp"
|
#include "communication/init.hpp"
|
||||||
#include "communication/v2/listener.hpp"
|
#include "communication/v2/listener.hpp"
|
||||||
#include "communication/v2/pool.hpp"
|
#include "communication/v2/pool.hpp"
|
||||||
@@ -130,7 +129,7 @@ bool Server<TSession, TSessionContext>::Start() {
|
|||||||
listener_->Start();
|
listener_->Start();
|
||||||
|
|
||||||
spdlog::info("{} server is fully armed and operational", service_name_);
|
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();
|
context_thread_pool_.Run();
|
||||||
|
|
||||||
return true;
|
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
|
// 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
|
// 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/buffer.hpp"
|
||||||
#include "communication/context.hpp"
|
#include "communication/context.hpp"
|
||||||
#include "communication/exceptions.hpp"
|
#include "communication/exceptions.hpp"
|
||||||
#include "communication/fmt.hpp"
|
|
||||||
#include "dbms/global.hpp"
|
#include "dbms/global.hpp"
|
||||||
#include "utils/event_counter.hpp"
|
#include "utils/event_counter.hpp"
|
||||||
#include "utils/logging.hpp"
|
#include "utils/logging.hpp"
|
||||||
@@ -213,11 +212,14 @@ class WebsocketSession : public std::enable_shared_from_this<WebsocketSession<TS
|
|||||||
session_.Execute();
|
session_.Execute();
|
||||||
DoRead();
|
DoRead();
|
||||||
} catch (const SessionClosedException &e) {
|
} 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();
|
DoClose();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
spdlog::error(
|
||||||
remote_endpoint_);
|
"Exception was thrown while processing event in {} session "
|
||||||
|
"associated with {}:{}",
|
||||||
|
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||||
spdlog::debug("Exception message: {}", e.what());
|
spdlog::debug("Exception message: {}", e.what());
|
||||||
DoClose();
|
DoClose();
|
||||||
}
|
}
|
||||||
@@ -374,7 +376,8 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
|||||||
socket.lowest_layer().non_blocking(false);
|
socket.lowest_layer().non_blocking(false);
|
||||||
});
|
});
|
||||||
timeout_timer_.expires_at(boost::asio::steady_timer::time_point::max());
|
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() {
|
void DoRead() {
|
||||||
@@ -434,11 +437,14 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
|
|||||||
session_.Execute();
|
session_.Execute();
|
||||||
DoRead();
|
DoRead();
|
||||||
} catch (const SessionClosedException &e) {
|
} 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();
|
DoShutdown();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
spdlog::error("Exception was thrown while processing event in {} session associated with {}", service_name_,
|
spdlog::error(
|
||||||
remote_endpoint_);
|
"Exception was thrown while processing event in {} session "
|
||||||
|
"associated with {}:{}",
|
||||||
|
service_name_, remote_endpoint_.address(), remote_endpoint_.port());
|
||||||
spdlog::debug("Exception message: {}", e.what());
|
spdlog::debug("Exception message: {}", e.what());
|
||||||
DoShutdown();
|
DoShutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Memgraph Ltd.
|
// Copyright 2022 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// 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 "communication/websocket/auth.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "utils/variant_helpers.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::communication::websocket {
|
namespace memgraph::communication::websocket {
|
||||||
|
|
||||||
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
||||||
user_or_role_ = auth_->Lock()->Authenticate(username, password);
|
return auth_->Lock()->Authenticate(username, password).has_value();
|
||||||
return user_or_role_.has_value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SafeAuth::HasPermission(const auth::Permission permission) const {
|
bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permission permission) const {
|
||||||
auto locked_auth = auth_->ReadLock();
|
if (const auto user = auth_->ReadLock()->GetUser(username); user) {
|
||||||
// Update if cache invalidated
|
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SafeAuth::AccessControlled() const { return auth_->ReadLock()->AccessControlled(); }
|
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
|
||||||
} // namespace memgraph::communication::websocket
|
} // namespace memgraph::communication::websocket
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ class AuthenticationInterface {
|
|||||||
public:
|
public:
|
||||||
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
|
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 {
|
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 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:
|
private:
|
||||||
auth::SynchedAuth *auth_;
|
auth::SynchedAuth *auth_;
|
||||||
mutable std::optional<auth::UserOrRole> user_or_role_;
|
|
||||||
mutable auth::Auth::Epoch auth_epoch_{};
|
|
||||||
};
|
};
|
||||||
} // namespace memgraph::communication::websocket
|
} // 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
|
// 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
|
// 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.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
#include "communication/websocket/listener.hpp"
|
#include "communication/websocket/listener.hpp"
|
||||||
#include "communication/fmt.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::communication::websocket {
|
namespace memgraph::communication::websocket {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -62,7 +61,7 @@ Listener::Listener(boost::asio::io_context &ioc, ServerContext *context, tcp::en
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::info("WebSocket server is listening on {}", endpoint);
|
spdlog::info("WebSocket server is listening on {}:{}", endpoint.address(), endpoint.port());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Listener::DoAccept() {
|
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
|
// 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
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticated_ = !auth_.AccessControlled();
|
authenticated_ = !auth_.HasAnyUsers();
|
||||||
connected_.store(true, std::memory_order_relaxed);
|
connected_.store(true, std::memory_order_relaxed);
|
||||||
|
|
||||||
// run on the strand
|
// run on the strand
|
||||||
@@ -162,7 +162,7 @@ utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds)
|
|||||||
return {"Authentication failed!"};
|
return {"Authentication failed!"};
|
||||||
}
|
}
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (!auth_.HasPermission(auth::Permission::WEBSOCKET)) {
|
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) {
|
||||||
return {"Authorization failed!"};
|
return {"Authorization failed!"};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,34 +9,31 @@ target_sources(mg-coordination
|
|||||||
include/coordination/coordinator_config.hpp
|
include/coordination/coordinator_config.hpp
|
||||||
include/coordination/coordinator_exceptions.hpp
|
include/coordination/coordinator_exceptions.hpp
|
||||||
include/coordination/coordinator_slk.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_handlers.hpp
|
||||||
|
include/coordination/coordinator_instance.hpp
|
||||||
include/coordination/instance_status.hpp
|
include/coordination/instance_status.hpp
|
||||||
include/coordination/replication_instance.hpp
|
include/coordination/replication_instance.hpp
|
||||||
include/coordination/raft_state.hpp
|
|
||||||
include/coordination/rpc_errors.hpp
|
|
||||||
|
|
||||||
include/nuraft/raft_log_action.hpp
|
|
||||||
include/nuraft/coordinator_cluster_state.hpp
|
|
||||||
include/nuraft/coordinator_log_store.hpp
|
include/nuraft/coordinator_log_store.hpp
|
||||||
include/nuraft/coordinator_state_machine.hpp
|
include/nuraft/coordinator_state_machine.hpp
|
||||||
include/nuraft/coordinator_state_manager.hpp
|
include/nuraft/coordinator_state_manager.hpp
|
||||||
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
coordinator_config.cpp
|
|
||||||
coordinator_client.cpp
|
coordinator_client.cpp
|
||||||
coordinator_state.cpp
|
coordinator_state.cpp
|
||||||
coordinator_rpc.cpp
|
coordinator_rpc.cpp
|
||||||
coordinator_server.cpp
|
coordinator_server.cpp
|
||||||
coordinator_handlers.cpp
|
coordinator_data.cpp
|
||||||
coordinator_instance.cpp
|
coordinator_instance.cpp
|
||||||
|
coordinator_handlers.cpp
|
||||||
replication_instance.cpp
|
replication_instance.cpp
|
||||||
raft_state.cpp
|
|
||||||
|
|
||||||
coordinator_log_store.cpp
|
coordinator_log_store.cpp
|
||||||
coordinator_state_machine.cpp
|
coordinator_state_machine.cpp
|
||||||
coordinator_state_manager.cpp
|
coordinator_state_manager.cpp
|
||||||
coordinator_cluster_state.cpp
|
|
||||||
)
|
)
|
||||||
target_include_directories(mg-coordination PUBLIC include)
|
target_include_directories(mg-coordination PUBLIC include)
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
#include "coordination/coordinator_config.hpp"
|
||||||
#include "coordination/coordinator_rpc.hpp"
|
#include "coordination/coordinator_rpc.hpp"
|
||||||
#include "replication_coordination_glue/common.hpp"
|
|
||||||
#include "replication_coordination_glue/messages.hpp"
|
#include "replication_coordination_glue/messages.hpp"
|
||||||
#include "utils/result.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
@@ -30,54 +28,35 @@ auto CreateClientContext(memgraph::coordination::CoordinatorClientConfig const &
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CoordinatorClient::CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
CoordinatorClient::CoordinatorClient(CoordinatorData *coord_data, CoordinatorClientConfig config,
|
||||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb)
|
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||||
: rpc_context_{CreateClientContext(config)},
|
: rpc_context_{CreateClientContext(config)},
|
||||||
rpc_client_{io::network::Endpoint(io::network::Endpoint::needs_resolving, config.ip_address, config.port),
|
rpc_client_{io::network::Endpoint(io::network::Endpoint::needs_resolving, config.ip_address, config.port),
|
||||||
&rpc_context_},
|
&rpc_context_},
|
||||||
config_{std::move(config)},
|
config_{std::move(config)},
|
||||||
coord_instance_{coord_instance},
|
coord_data_{coord_data},
|
||||||
succ_cb_{std::move(succ_cb)},
|
succ_cb_{std::move(succ_cb)},
|
||||||
fail_cb_{std::move(fail_cb)} {}
|
fail_cb_{std::move(fail_cb)} {}
|
||||||
|
|
||||||
auto CoordinatorClient::InstanceName() const -> std::string { return config_.instance_name; }
|
auto CoordinatorClient::InstanceName() const -> std::string { return config_.instance_name; }
|
||||||
|
auto CoordinatorClient::SocketAddress() const -> std::string { return rpc_client_.Endpoint().SocketAddress(); }
|
||||||
auto CoordinatorClient::CoordinatorSocketAddress() const -> std::string { return config_.CoordinatorSocketAddress(); }
|
|
||||||
auto CoordinatorClient::ReplicationSocketAddress() const -> std::string { return config_.ReplicationSocketAddress(); }
|
|
||||||
|
|
||||||
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() {
|
void CoordinatorClient::StartFrequentCheck() {
|
||||||
if (instance_checker_.IsRunning()) {
|
MG_ASSERT(config_.health_check_frequency_sec > std::chrono::seconds(0),
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MG_ASSERT(config_.instance_health_check_frequency_sec > std::chrono::seconds(0),
|
|
||||||
"Health check frequency must be greater than 0");
|
"Health check frequency must be greater than 0");
|
||||||
|
|
||||||
instance_checker_.Run(
|
instance_checker_.Run(
|
||||||
config_.instance_name, config_.instance_health_check_frequency_sec,
|
config_.instance_name, config_.health_check_frequency_sec, [this, instance_name = config_.instance_name] {
|
||||||
[this, instance_name = config_.instance_name] {
|
|
||||||
try {
|
try {
|
||||||
spdlog::trace("Sending frequent heartbeat to machine {} on {}", instance_name,
|
spdlog::trace("Sending frequent heartbeat to machine {} on {}", instance_name,
|
||||||
config_.CoordinatorSocketAddress());
|
rpc_client_.Endpoint().SocketAddress());
|
||||||
{ // NOTE: This is intentionally scoped so that stream lock could get released.
|
{ // NOTE: This is intentionally scoped so that stream lock could get released.
|
||||||
auto stream{rpc_client_.Stream<memgraph::replication_coordination_glue::FrequentHeartbeatRpc>()};
|
auto stream{rpc_client_.Stream<memgraph::replication_coordination_glue::FrequentHeartbeatRpc>()};
|
||||||
stream.AwaitResponse();
|
stream.AwaitResponse();
|
||||||
}
|
}
|
||||||
// Subtle race condition:
|
succ_cb_(coord_data_, instance_name);
|
||||||
// acquiring of lock needs to happen before function call, as function callback can be changed
|
|
||||||
// for instance after lock is already acquired
|
|
||||||
// (failover case when instance is promoted to MAIN)
|
|
||||||
succ_cb_(coord_instance_, instance_name);
|
|
||||||
} catch (rpc::RpcFailedException const &) {
|
} catch (rpc::RpcFailedException const &) {
|
||||||
fail_cb_(coord_instance_, instance_name);
|
fail_cb_(coord_data_, instance_name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -86,6 +65,11 @@ void CoordinatorClient::StopFrequentCheck() { instance_checker_.Stop(); }
|
|||||||
void CoordinatorClient::PauseFrequentCheck() { instance_checker_.Pause(); }
|
void CoordinatorClient::PauseFrequentCheck() { instance_checker_.Pause(); }
|
||||||
void CoordinatorClient::ResumeFrequentCheck() { instance_checker_.Resume(); }
|
void CoordinatorClient::ResumeFrequentCheck() { instance_checker_.Resume(); }
|
||||||
|
|
||||||
|
auto CoordinatorClient::SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void {
|
||||||
|
succ_cb_ = std::move(succ_cb);
|
||||||
|
fail_cb_ = std::move(fail_cb);
|
||||||
|
}
|
||||||
|
|
||||||
auto CoordinatorClient::ReplicationClientInfo() const -> ReplClientInfo { return config_.replication_client_info; }
|
auto CoordinatorClient::ReplicationClientInfo() const -> ReplClientInfo { return config_.replication_client_info; }
|
||||||
|
|
||||||
auto CoordinatorClient::SendPromoteReplicaToMainRpc(const utils::UUID &uuid,
|
auto CoordinatorClient::SendPromoteReplicaToMainRpc(const utils::UUID &uuid,
|
||||||
@@ -119,7 +103,7 @@ auto CoordinatorClient::DemoteToReplica() const -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorClient::SendSwapMainUUIDRpc(utils::UUID const &uuid) const -> bool {
|
auto CoordinatorClient::SendSwapMainUUIDRpc(const utils::UUID &uuid) const -> bool {
|
||||||
try {
|
try {
|
||||||
auto stream{rpc_client_.Stream<replication_coordination_glue::SwapMainUUIDRpc>(uuid)};
|
auto stream{rpc_client_.Stream<replication_coordination_glue::SwapMainUUIDRpc>(uuid)};
|
||||||
if (!stream.AwaitResponse().success) {
|
if (!stream.AwaitResponse().success) {
|
||||||
@@ -133,57 +117,5 @@ auto CoordinatorClient::SendSwapMainUUIDRpc(utils::UUID const &uuid) const -> bo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorClient::SendUnregisterReplicaRpc(std::string_view 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClient::SendGetInstanceTimestampsRpc() const
|
|
||||||
-> utils::BasicResult<GetInstanceUUIDError, replication_coordination_glue::DatabaseHistories> {
|
|
||||||
try {
|
|
||||||
auto stream{rpc_client_.Stream<coordination::GetDatabaseHistoriesRpc>()};
|
|
||||||
return stream.AwaitResponse().database_histories;
|
|
||||||
|
|
||||||
} catch (const rpc::RpcFailedException &) {
|
|
||||||
spdlog::error("RPC error occured while sending GetInstance UUID RPC");
|
|
||||||
return GetInstanceUUIDError::RPC_EXCEPTION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,147 +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 "nuraft/coordinator_cluster_state.hpp"
|
|
||||||
#include "utils/logging.hpp"
|
|
||||||
|
|
||||||
#include <shared_mutex>
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, InstanceState const &instance_state) {
|
|
||||||
j = nlohmann::json{{"config", instance_state.config}, {"status", instance_state.status}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(nlohmann::json const &j, InstanceState &instance_state) {
|
|
||||||
j.at("config").get_to(instance_state.config);
|
|
||||||
j.at("status").get_to(instance_state.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
CoordinatorClusterState::CoordinatorClusterState(std::map<std::string, InstanceState, std::less<>> instances)
|
|
||||||
: instances_{std::move(instances)} {}
|
|
||||||
|
|
||||||
CoordinatorClusterState::CoordinatorClusterState(CoordinatorClusterState const &other) : instances_{other.instances_} {}
|
|
||||||
|
|
||||||
CoordinatorClusterState &CoordinatorClusterState::operator=(CoordinatorClusterState const &other) {
|
|
||||||
if (this == &other) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
instances_ = other.instances_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoordinatorClusterState::CoordinatorClusterState(CoordinatorClusterState &&other) noexcept
|
|
||||||
: instances_{std::move(other.instances_)} {}
|
|
||||||
|
|
||||||
CoordinatorClusterState &CoordinatorClusterState::operator=(CoordinatorClusterState &&other) noexcept {
|
|
||||||
if (this == &other) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
instances_ = std::move(other.instances_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::MainExists() const -> bool {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
return std::ranges::any_of(instances_,
|
|
||||||
[](auto const &entry) { return entry.second.status == ReplicationRole::MAIN; });
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::IsMain(std::string_view instance_name) const -> bool {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
auto const it = instances_.find(instance_name);
|
|
||||||
return it != instances_.end() && it->second.status == ReplicationRole::MAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::IsReplica(std::string_view instance_name) const -> bool {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
auto const it = instances_.find(instance_name);
|
|
||||||
return it != instances_.end() && it->second.status == ReplicationRole::REPLICA;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::InsertInstance(std::string instance_name, InstanceState instance_state) -> void {
|
|
||||||
auto lock = std::lock_guard{log_lock_};
|
|
||||||
instances_.insert_or_assign(std::move(instance_name), std::move(instance_state));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::DoAction(TRaftLog log_entry, RaftLogAction log_action) -> void {
|
|
||||||
auto lock = std::lock_guard{log_lock_};
|
|
||||||
switch (log_action) {
|
|
||||||
case RaftLogAction::REGISTER_REPLICATION_INSTANCE: {
|
|
||||||
auto const &config = std::get<CoordinatorClientConfig>(log_entry);
|
|
||||||
instances_[config.instance_name] = InstanceState{config, ReplicationRole::REPLICA};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RaftLogAction::UNREGISTER_REPLICATION_INSTANCE: {
|
|
||||||
auto const instance_name = std::get<std::string>(log_entry);
|
|
||||||
instances_.erase(instance_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RaftLogAction::SET_INSTANCE_AS_MAIN: {
|
|
||||||
auto const instance_name = std::get<std::string>(log_entry);
|
|
||||||
auto it = instances_.find(instance_name);
|
|
||||||
MG_ASSERT(it != instances_.end(), "Instance does not exist as part of raft state!");
|
|
||||||
it->second.status = ReplicationRole::MAIN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RaftLogAction::SET_INSTANCE_AS_REPLICA: {
|
|
||||||
auto const instance_name = std::get<std::string>(log_entry);
|
|
||||||
auto it = instances_.find(instance_name);
|
|
||||||
MG_ASSERT(it != instances_.end(), "Instance does not exist as part of raft state!");
|
|
||||||
it->second.status = ReplicationRole::REPLICA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RaftLogAction::UPDATE_UUID: {
|
|
||||||
uuid_ = std::get<utils::UUID>(log_entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::Serialize(ptr<buffer> &data) -> void {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
|
|
||||||
auto const log = nlohmann::json(instances_).dump();
|
|
||||||
|
|
||||||
data = buffer::alloc(sizeof(uint32_t) + log.size());
|
|
||||||
buffer_serializer bs(data);
|
|
||||||
bs.put_str(log);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::Deserialize(buffer &data) -> CoordinatorClusterState {
|
|
||||||
buffer_serializer bs(data);
|
|
||||||
auto const j = nlohmann::json::parse(bs.get_str());
|
|
||||||
auto instances = j.get<std::map<std::string, InstanceState, std::less<>>>();
|
|
||||||
|
|
||||||
return CoordinatorClusterState{std::move(instances)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::GetInstances() const -> std::vector<InstanceState> {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
return instances_ | ranges::views::values | ranges::to<std::vector<InstanceState>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::GetUUID() const -> utils::UUID { return uuid_; }
|
|
||||||
|
|
||||||
auto CoordinatorClusterState::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
|
||||||
auto lock = std::shared_lock{log_lock_};
|
|
||||||
auto const it =
|
|
||||||
std::ranges::find_if(instances_, [](auto const &entry) { return entry.second.status == ReplicationRole::MAIN; });
|
|
||||||
if (it == instances_.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return it->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
|
||||||
#endif
|
|
||||||
@@ -1,54 +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/coordinator_config.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, ReplClientInfo const &config) {
|
|
||||||
j = nlohmann::json{{"instance_name", config.instance_name},
|
|
||||||
{"replication_mode", config.replication_mode},
|
|
||||||
{"replication_ip_address", config.replication_ip_address},
|
|
||||||
{"replication_port", config.replication_port}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(nlohmann::json const &j, ReplClientInfo &config) {
|
|
||||||
config.instance_name = j.at("instance_name").get<std::string>();
|
|
||||||
config.replication_mode = j.at("replication_mode").get<replication_coordination_glue::ReplicationMode>();
|
|
||||||
config.replication_ip_address = j.at("replication_ip_address").get<std::string>();
|
|
||||||
config.replication_port = j.at("replication_port").get<uint16_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, CoordinatorClientConfig const &config) {
|
|
||||||
j = nlohmann::json{{"instance_name", config.instance_name},
|
|
||||||
{"ip_address", config.ip_address},
|
|
||||||
{"port", config.port},
|
|
||||||
{"instance_health_check_frequency_sec", config.instance_health_check_frequency_sec.count()},
|
|
||||||
{"instance_down_timeout_sec", config.instance_down_timeout_sec.count()},
|
|
||||||
{"instance_get_uuid_frequency_sec", config.instance_get_uuid_frequency_sec.count()},
|
|
||||||
{"replication_client_info", config.replication_client_info}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(nlohmann::json const &j, CoordinatorClientConfig &config) {
|
|
||||||
config.instance_name = j.at("instance_name").get<std::string>();
|
|
||||||
config.ip_address = j.at("ip_address").get<std::string>();
|
|
||||||
config.port = j.at("port").get<uint16_t>();
|
|
||||||
config.instance_health_check_frequency_sec =
|
|
||||||
std::chrono::seconds{j.at("instance_health_check_frequency_sec").get<int>()};
|
|
||||||
config.instance_down_timeout_sec = std::chrono::seconds{j.at("instance_down_timeout_sec").get<int>()};
|
|
||||||
config.instance_get_uuid_frequency_sec = std::chrono::seconds{j.at("instance_get_uuid_frequency_sec").get<int>()};
|
|
||||||
config.replication_client_info = j.at("replication_client_info").get<ReplClientInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // 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,35 +39,6 @@ void CoordinatorHandlers::Register(memgraph::coordination::CoordinatorServer &se
|
|||||||
spdlog::info("Received SwapMainUUIDRPC on coordinator server");
|
spdlog::info("Received SwapMainUUIDRPC on coordinator server");
|
||||||
CoordinatorHandlers::SwapMainUUIDHandler(replication_handler, req_reader, res_builder);
|
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.Register<coordination::GetDatabaseHistoriesRpc>(
|
|
||||||
[&replication_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
|
||||||
spdlog::info("Received GetDatabasesHistoryRpc on coordinator server");
|
|
||||||
CoordinatorHandlers::GetDatabaseHistoriesHandler(replication_handler, req_reader, res_builder);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinatorHandlers::GetDatabaseHistoriesHandler(replication::ReplicationHandler &replication_handler,
|
|
||||||
slk::Reader * /*req_reader*/, slk::Builder *res_builder) {
|
|
||||||
slk::Save(coordination::GetDatabaseHistoriesRes{replication_handler.GetDatabasesHistories()}, res_builder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoordinatorHandlers::SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler,
|
void CoordinatorHandlers::SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler,
|
||||||
@@ -91,6 +62,12 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
|
|||||||
slk::Reader *req_reader, slk::Builder *res_builder) {
|
slk::Reader *req_reader, slk::Builder *res_builder) {
|
||||||
spdlog::info("Executing DemoteMainToReplicaHandler");
|
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;
|
coordination::DemoteMainToReplicaReq req;
|
||||||
slk::Load(&req, req_reader);
|
slk::Load(&req, req_reader);
|
||||||
|
|
||||||
@@ -100,18 +77,11 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
|
|||||||
|
|
||||||
if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) {
|
if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) {
|
||||||
spdlog::error("Demoting main to replica failed!");
|
spdlog::error("Demoting main to replica failed!");
|
||||||
slk::Save(coordination::DemoteMainToReplicaRes{false}, res_builder);
|
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
slk::Save(coordination::DemoteMainToReplicaRes{true}, res_builder);
|
slk::Save(coordination::PromoteReplicaToMainRes{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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHandler &replication_handler,
|
void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHandler &replication_handler,
|
||||||
@@ -143,7 +113,7 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
|
|||||||
|
|
||||||
// registering replicas
|
// registering replicas
|
||||||
for (auto const &config : req.replication_clients_info | ranges::views::transform(converter)) {
|
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()) {
|
if (instance_client.HasError()) {
|
||||||
using enum memgraph::replication::RegisterReplicaError;
|
using enum memgraph::replication::RegisterReplicaError;
|
||||||
switch (instance_client.GetError()) {
|
switch (instance_client.GetError()) {
|
||||||
@@ -172,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);
|
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
|
} // namespace memgraph::dbms
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,547 +14,84 @@
|
|||||||
#include "coordination/coordinator_instance.hpp"
|
#include "coordination/coordinator_instance.hpp"
|
||||||
|
|
||||||
#include "coordination/coordinator_exceptions.hpp"
|
#include "coordination/coordinator_exceptions.hpp"
|
||||||
#include "coordination/fmt.hpp"
|
|
||||||
#include "dbms/constants.hpp"
|
|
||||||
#include "nuraft/coordinator_state_machine.hpp"
|
#include "nuraft/coordinator_state_machine.hpp"
|
||||||
#include "nuraft/coordinator_state_manager.hpp"
|
#include "nuraft/coordinator_state_manager.hpp"
|
||||||
#include "utils/counter.hpp"
|
#include "utils/counter.hpp"
|
||||||
#include "utils/functional.hpp"
|
|
||||||
#include "utils/resource_lock.hpp"
|
|
||||||
|
|
||||||
#include <range/v3/view.hpp>
|
|
||||||
#include <shared_mutex>
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
|
using nuraft::asio_service;
|
||||||
|
using nuraft::cmd_result;
|
||||||
|
using nuraft::cs_new;
|
||||||
using nuraft::ptr;
|
using nuraft::ptr;
|
||||||
|
using nuraft::raft_params;
|
||||||
using nuraft::srv_config;
|
using nuraft::srv_config;
|
||||||
|
using raft_result = cmd_result<ptr<buffer>>;
|
||||||
|
|
||||||
CoordinatorInstance::CoordinatorInstance()
|
CoordinatorInstance::CoordinatorInstance()
|
||||||
: raft_state_(RaftState::MakeRaftState(
|
: raft_server_id_(FLAGS_raft_server_id), raft_port_(FLAGS_raft_server_port), raft_address_("127.0.0.1") {
|
||||||
[this]() {
|
auto raft_endpoint = raft_address_ + ":" + std::to_string(raft_port_);
|
||||||
spdlog::info("Leader changed, starting all replication instances!");
|
state_manager_ = cs_new<CoordinatorStateManager>(raft_server_id_, raft_endpoint);
|
||||||
auto const instances = raft_state_.GetInstances();
|
state_machine_ = cs_new<CoordinatorStateMachine>();
|
||||||
auto replicas = instances | ranges::views::filter([](auto const &instance) {
|
logger_ = nullptr;
|
||||||
return instance.status == ReplicationRole::REPLICA;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::ranges::for_each(replicas, [this](auto &replica) {
|
// ASIO options
|
||||||
spdlog::info("Started pinging replication instance {}", replica.config.instance_name);
|
asio_service::options asio_opts;
|
||||||
repl_instances_.emplace_back(this, replica.config, client_succ_cb_, client_fail_cb_,
|
asio_opts.thread_pool_size_ = 1; // TODO: (andi) Improve this
|
||||||
&CoordinatorInstance::ReplicaSuccessCallback,
|
|
||||||
&CoordinatorInstance::ReplicaFailCallback);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto main = instances | ranges::views::filter(
|
// RAFT parameters. Heartbeat every 100ms, election timeout between 200ms and 400ms.
|
||||||
[](auto const &instance) { return instance.status == ReplicationRole::MAIN; });
|
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;
|
||||||
|
|
||||||
std::ranges::for_each(main, [this](auto &main_instance) {
|
raft_server_ =
|
||||||
spdlog::info("Started pinging main instance {}", main_instance.config.instance_name);
|
launcher_.init(state_machine_, state_manager_, logger_, static_cast<int>(raft_port_), asio_opts, params);
|
||||||
repl_instances_.emplace_back(this, main_instance.config, client_succ_cb_, client_fail_cb_,
|
|
||||||
&CoordinatorInstance::MainSuccessCallback,
|
|
||||||
&CoordinatorInstance::MainFailCallback);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::ranges::for_each(repl_instances_, [this](auto &instance) {
|
if (!raft_server_) {
|
||||||
instance.SetNewMainUUID(raft_state_.GetUUID());
|
throw RaftServerStartException("Failed to launch raft server on {}", raft_endpoint);
|
||||||
instance.StartFrequentCheck();
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
[this]() {
|
|
||||||
spdlog::info("Leader changed, stopping all replication instances!");
|
|
||||||
repl_instances_.clear();
|
|
||||||
})) {
|
|
||||||
client_succ_cb_ = [](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
|
||||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
|
||||||
auto &repl_instance = self->FindReplicationInstance(repl_instance_name);
|
|
||||||
std::invoke(repl_instance.GetSuccessCallback(), self, repl_instance_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
client_fail_cb_ = [](CoordinatorInstance *self, std::string_view repl_instance_name) -> void {
|
auto maybe_stop = utils::ResettableCounter<20>();
|
||||||
auto lock = std::lock_guard{self->coord_instance_lock_};
|
while (!raft_server_->is_initialized() && !maybe_stop()) {
|
||||||
auto &repl_instance = self->FindReplicationInstance(repl_instance_name);
|
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||||
std::invoke(repl_instance.GetFailCallback(), self, repl_instance_name);
|
}
|
||||||
};
|
|
||||||
|
if (!raft_server_->is_initialized()) {
|
||||||
|
throw RaftServerStartException("Failed to initialize raft server on {}", raft_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("Raft server started on {}", raft_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorInstance::FindReplicationInstance(std::string_view replication_instance_name) -> ReplicationInstance & {
|
auto CoordinatorInstance::InstanceName() const -> std::string {
|
||||||
auto repl_instance =
|
return "coordinator_" + std::to_string(raft_server_id_);
|
||||||
std::ranges::find_if(repl_instances_, [replication_instance_name](ReplicationInstance const &instance) {
|
|
||||||
return instance.InstanceName() == replication_instance_name;
|
|
||||||
});
|
|
||||||
|
|
||||||
MG_ASSERT(repl_instance != repl_instances_.end(), "Instance {} not found during callback!",
|
|
||||||
replication_instance_name);
|
|
||||||
return *repl_instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> {
|
auto CoordinatorInstance::RaftSocketAddress() const -> std::string {
|
||||||
auto const coord_instance_to_status = [](ptr<srv_config> const &instance) -> InstanceStatus {
|
return raft_address_ + ":" + std::to_string(raft_port_);
|
||||||
return {.instance_name = "coordinator_" + std::to_string(instance->get_id()),
|
|
||||||
.raft_socket_address = instance->get_endpoint(),
|
|
||||||
.cluster_role = "coordinator",
|
|
||||||
.health = "unknown"}; // TODO: (andi) Get this info from RAFT and test it or when we will move
|
|
||||||
};
|
|
||||||
auto instances_status = utils::fmap(raft_state_.GetAllCoordinators(), coord_instance_to_status);
|
|
||||||
|
|
||||||
if (raft_state_.IsLeader()) {
|
|
||||||
auto const stringify_repl_role = [this](ReplicationInstance const &instance) -> std::string {
|
|
||||||
if (!instance.IsAlive()) return "unknown";
|
|
||||||
if (raft_state_.IsMain(instance.InstanceName())) return "main";
|
|
||||||
return "replica";
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const stringify_repl_health = [](ReplicationInstance const &instance) -> std::string {
|
|
||||||
return instance.IsAlive() ? "up" : "down";
|
|
||||||
};
|
|
||||||
|
|
||||||
auto process_repl_instance_as_leader =
|
|
||||||
[&stringify_repl_role, &stringify_repl_health](ReplicationInstance const &instance) -> InstanceStatus {
|
|
||||||
return {.instance_name = instance.InstanceName(),
|
|
||||||
.coord_socket_address = instance.CoordinatorSocketAddress(),
|
|
||||||
.cluster_role = stringify_repl_role(instance),
|
|
||||||
.health = stringify_repl_health(instance)};
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
auto lock = std::shared_lock{coord_instance_lock_};
|
|
||||||
std::ranges::transform(repl_instances_, std::back_inserter(instances_status), process_repl_instance_as_leader);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto const stringify_inst_status = [](ReplicationRole status) -> std::string {
|
|
||||||
return status == ReplicationRole::MAIN ? "main" : "replica";
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: (andi) Add capability that followers can also return socket addresses
|
|
||||||
auto process_repl_instance_as_follower = [&stringify_inst_status](auto const &instance) -> InstanceStatus {
|
|
||||||
return {.instance_name = instance.config.instance_name,
|
|
||||||
.cluster_role = stringify_inst_status(instance.status),
|
|
||||||
.health = "unknown"};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ranges::transform(raft_state_.GetInstances(), std::back_inserter(instances_status),
|
|
||||||
process_repl_instance_as_follower);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instances_status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorInstance::TryFailover() -> void {
|
auto CoordinatorInstance::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||||
auto const is_replica = [this](ReplicationInstance const &instance) { return IsReplica(instance.InstanceName()); };
|
-> void {
|
||||||
|
auto const endpoint = raft_address + ":" + std::to_string(raft_port);
|
||||||
auto alive_replicas =
|
srv_config const srv_config_to_add(static_cast<int>(raft_server_id), endpoint);
|
||||||
repl_instances_ | ranges::views::filter(is_replica) | ranges::views::filter(&ReplicationInstance::IsAlive);
|
if (!raft_server_->add_srv(srv_config_to_add)->get_accepted()) {
|
||||||
|
throw RaftAddServerException("Failed to add server {} to the cluster", endpoint);
|
||||||
if (ranges::empty(alive_replicas)) {
|
|
||||||
spdlog::warn("Failover failed since all replicas are down!");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
spdlog::info("Request to add server {} to the cluster accepted", endpoint);
|
||||||
if (!raft_state_.RequestLeadership()) {
|
|
||||||
spdlog::error("Failover failed since the instance is not the leader!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const get_ts = [](ReplicationInstance &replica) { return replica.GetClient().SendGetInstanceTimestampsRpc(); };
|
|
||||||
|
|
||||||
auto maybe_instance_db_histories = alive_replicas | ranges::views::transform(get_ts) | ranges::to<std::vector>();
|
|
||||||
|
|
||||||
auto const ts_has_error = [](auto const &res) -> bool { return res.HasError(); };
|
|
||||||
|
|
||||||
if (std::ranges::any_of(maybe_instance_db_histories, ts_has_error)) {
|
|
||||||
spdlog::error("Aborting failover as at least one instance didn't provide per database history.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto transform_to_pairs = ranges::views::transform([](auto const &zipped) {
|
|
||||||
auto &[replica, res] = zipped;
|
|
||||||
return std::make_pair(replica.InstanceName(), res.GetValue());
|
|
||||||
});
|
|
||||||
|
|
||||||
auto instance_db_histories =
|
|
||||||
ranges::views::zip(alive_replicas, maybe_instance_db_histories) | transform_to_pairs | ranges::to<std::vector>();
|
|
||||||
|
|
||||||
auto [most_up_to_date_instance, latest_epoch, latest_commit_timestamp] =
|
|
||||||
ChooseMostUpToDateInstance(instance_db_histories);
|
|
||||||
|
|
||||||
spdlog::trace("The most up to date instance is {} with epoch {} and {} latest commit timestamp",
|
|
||||||
most_up_to_date_instance, latest_epoch, latest_commit_timestamp); // NOLINT
|
|
||||||
|
|
||||||
auto *new_main = &FindReplicationInstance(most_up_to_date_instance);
|
|
||||||
|
|
||||||
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{};
|
|
||||||
|
|
||||||
auto const failed_to_swap = [&new_main_uuid](ReplicationInstance &instance) {
|
|
||||||
return !instance.SendSwapAndUpdateUUID(new_main_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
|
|
||||||
if (std::ranges::any_of(alive_replicas | ranges::views::filter(is_not_new_main), failed_to_swap)) {
|
|
||||||
spdlog::error("Failed to swap uuid for all instances");
|
|
||||||
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), &CoordinatorInstance::MainSuccessCallback,
|
|
||||||
&CoordinatorInstance::MainFailCallback)) {
|
|
||||||
spdlog::warn("Failover failed since promoting replica to main failed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.AppendUpdateUUIDLog(new_main_uuid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const new_main_instance_name = new_main->InstanceName();
|
|
||||||
|
|
||||||
if (!raft_state_.AppendSetInstanceAsMainLog(new_main_instance_name)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Failover successful! Instance {} promoted to main.", new_main->InstanceName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorInstance::SetReplicationInstanceToMain(std::string_view instance_name)
|
auto CoordinatorInstance::GetAllCoordinators() const -> std::vector<ptr<srv_config>> {
|
||||||
-> SetInstanceToMainCoordinatorStatus {
|
std::vector<ptr<srv_config>> all_srv_configs;
|
||||||
auto lock = std::lock_guard{coord_instance_lock_};
|
raft_server_->get_srv_config_all(all_srv_configs);
|
||||||
|
return all_srv_configs;
|
||||||
if (raft_state_.MainExists()) {
|
|
||||||
return SetInstanceToMainCoordinatorStatus::MAIN_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.RequestLeadership()) {
|
|
||||||
return SetInstanceToMainCoordinatorStatus::NOT_LEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
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{};
|
|
||||||
|
|
||||||
auto const failed_to_swap = [&new_main_uuid](ReplicationInstance &instance) {
|
|
||||||
return !instance.SendSwapAndUpdateUUID(new_main_uuid);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (std::ranges::any_of(repl_instances_ | ranges::views::filter(is_not_new_main), failed_to_swap)) {
|
|
||||||
spdlog::error("Failed to swap uuid for all instances");
|
|
||||||
return SetInstanceToMainCoordinatorStatus::SWAP_UUID_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
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), &CoordinatorInstance::MainSuccessCallback,
|
|
||||||
&CoordinatorInstance::MainFailCallback)) {
|
|
||||||
return SetInstanceToMainCoordinatorStatus::COULD_NOT_PROMOTE_TO_MAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.AppendUpdateUUIDLog(new_main_uuid)) {
|
|
||||||
return SetInstanceToMainCoordinatorStatus::RAFT_LOG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.AppendSetInstanceAsMainLog(instance_name)) {
|
|
||||||
return SetInstanceToMainCoordinatorStatus::RAFT_LOG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Instance {} promoted to main on leader", instance_name);
|
|
||||||
return SetInstanceToMainCoordinatorStatus::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
|
||||||
-> RegisterInstanceCoordinatorStatus {
|
|
||||||
auto lock = std::lock_guard{coord_instance_lock_};
|
|
||||||
|
|
||||||
if (std::ranges::any_of(repl_instances_, [instance_name = config.instance_name](ReplicationInstance const &instance) {
|
|
||||||
return instance.InstanceName() == instance_name;
|
|
||||||
})) {
|
|
||||||
return RegisterInstanceCoordinatorStatus::NAME_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::ranges::any_of(repl_instances_, [&config](ReplicationInstance const &instance) {
|
|
||||||
return instance.CoordinatorSocketAddress() == config.CoordinatorSocketAddress();
|
|
||||||
})) {
|
|
||||||
return RegisterInstanceCoordinatorStatus::COORD_ENDPOINT_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::ranges::any_of(repl_instances_, [&config](ReplicationInstance const &instance) {
|
|
||||||
return instance.ReplicationSocketAddress() == config.ReplicationSocketAddress();
|
|
||||||
})) {
|
|
||||||
return RegisterInstanceCoordinatorStatus::REPL_ENDPOINT_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.RequestLeadership()) {
|
|
||||||
return RegisterInstanceCoordinatorStatus::NOT_LEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const undo_action_ = [this]() { repl_instances_.pop_back(); };
|
|
||||||
|
|
||||||
auto *new_instance = &repl_instances_.emplace_back(this, config, client_succ_cb_, client_fail_cb_,
|
|
||||||
&CoordinatorInstance::ReplicaSuccessCallback,
|
|
||||||
&CoordinatorInstance::ReplicaFailCallback);
|
|
||||||
|
|
||||||
if (!new_instance->SendDemoteToReplicaRpc()) {
|
|
||||||
spdlog::error("Failed to send demote to replica rpc for instance {}", config.instance_name);
|
|
||||||
undo_action_();
|
|
||||||
return RegisterInstanceCoordinatorStatus::RPC_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.AppendRegisterReplicationInstanceLog(config)) {
|
|
||||||
undo_action_();
|
|
||||||
return RegisterInstanceCoordinatorStatus::RAFT_LOG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_instance->StartFrequentCheck();
|
|
||||||
|
|
||||||
spdlog::info("Instance {} registered", config.instance_name);
|
|
||||||
return RegisterInstanceCoordinatorStatus::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::UnregisterReplicationInstance(std::string_view instance_name)
|
|
||||||
-> UnregisterInstanceCoordinatorStatus {
|
|
||||||
auto lock = std::lock_guard{coord_instance_lock_};
|
|
||||||
|
|
||||||
if (!raft_state_.RequestLeadership()) {
|
|
||||||
return UnregisterInstanceCoordinatorStatus::NOT_LEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const is_main = [this](ReplicationInstance const &instance) {
|
|
||||||
return IsMain(instance.InstanceName()) && instance.GetMainUUID() == raft_state_.GetUUID() && instance.IsAlive();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (is_main(*inst_to_remove)) {
|
|
||||||
return UnregisterInstanceCoordinatorStatus::IS_MAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
inst_to_remove->StopFrequentCheck();
|
|
||||||
|
|
||||||
auto curr_main = std::ranges::find_if(repl_instances_, is_main);
|
|
||||||
|
|
||||||
if (curr_main != repl_instances_.end() && curr_main->IsAlive()) {
|
|
||||||
if (!curr_main->SendUnregisterReplicaRpc(instance_name)) {
|
|
||||||
inst_to_remove->StartFrequentCheck();
|
|
||||||
return UnregisterInstanceCoordinatorStatus::RPC_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::erase_if(repl_instances_, name_matches);
|
|
||||||
|
|
||||||
if (!raft_state_.AppendUnregisterReplicationInstanceLog(instance_name)) {
|
|
||||||
return UnregisterInstanceCoordinatorStatus::RAFT_LOG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return UnregisterInstanceCoordinatorStatus::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
|
||||||
std::string_view raft_address) -> void {
|
|
||||||
raft_state_.AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinatorInstance::MainFailCallback(std::string_view repl_instance_name) {
|
|
||||||
spdlog::trace("Instance {} performing main fail callback", repl_instance_name);
|
|
||||||
auto &repl_instance = FindReplicationInstance(repl_instance_name);
|
|
||||||
repl_instance.OnFailPing();
|
|
||||||
const auto &repl_instance_uuid = repl_instance.GetMainUUID();
|
|
||||||
MG_ASSERT(repl_instance_uuid.has_value(), "Replication instance must have uuid set");
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
|
||||||
if (!repl_instance.IsAlive() && raft_state_.GetUUID() == repl_instance_uuid.value()) {
|
|
||||||
spdlog::info("Cluster without main instance, trying automatic failover");
|
|
||||||
TryFailover();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinatorInstance::MainSuccessCallback(std::string_view repl_instance_name) {
|
|
||||||
spdlog::trace("Instance {} performing main successful callback", repl_instance_name);
|
|
||||||
auto &repl_instance = FindReplicationInstance(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.");
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
|
||||||
if (raft_state_.GetUUID() == 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.RequestLeadership()) {
|
|
||||||
spdlog::error("Demoting main instance {} to replica failed since the instance is not the leader!",
|
|
||||||
repl_instance_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repl_instance.DemoteToReplica(&CoordinatorInstance::ReplicaSuccessCallback,
|
|
||||||
&CoordinatorInstance::ReplicaFailCallback)) {
|
|
||||||
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(raft_state_.GetUUID())) {
|
|
||||||
spdlog::error("Failed to swap uuid for demoted main instance {}", repl_instance_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!raft_state_.AppendSetInstanceAsReplicaLog(repl_instance_name)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinatorInstance::ReplicaSuccessCallback(std::string_view repl_instance_name) {
|
|
||||||
spdlog::trace("Instance {} performing replica successful callback", repl_instance_name);
|
|
||||||
auto &repl_instance = FindReplicationInstance(repl_instance_name);
|
|
||||||
|
|
||||||
if (!IsReplica(repl_instance_name)) {
|
|
||||||
spdlog::error("Aborting replica callback since instance {} is not replica anymore", repl_instance_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 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(raft_state_.GetUUID())) {
|
|
||||||
spdlog::error("Failed to swap uuid for replica instance {} which is alive", repl_instance.InstanceName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
repl_instance.OnSuccessPing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinatorInstance::ReplicaFailCallback(std::string_view repl_instance_name) {
|
|
||||||
spdlog::trace("Instance {} performing replica failure callback", repl_instance_name);
|
|
||||||
auto &repl_instance = FindReplicationInstance(repl_instance_name);
|
|
||||||
|
|
||||||
if (!IsReplica(repl_instance_name)) {
|
|
||||||
spdlog::error("Aborting replica fail callback since instance {} is not replica anymore", repl_instance_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
repl_instance.OnFailPing();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
|
|
||||||
-> NewMainRes {
|
|
||||||
std::optional<NewMainRes> new_main_res;
|
|
||||||
std::for_each(
|
|
||||||
instance_database_histories.begin(), instance_database_histories.end(),
|
|
||||||
[&new_main_res](const InstanceNameDbHistories &instance_res_pair) {
|
|
||||||
const auto &[instance_name, instance_db_histories] = instance_res_pair;
|
|
||||||
|
|
||||||
// Find default db for instance and its history
|
|
||||||
auto default_db_history_data = std::ranges::find_if(
|
|
||||||
instance_db_histories, [default_db = memgraph::dbms::kDefaultDB](
|
|
||||||
const replication_coordination_glue::DatabaseHistory &db_timestamps) {
|
|
||||||
return db_timestamps.name == default_db;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::ranges::for_each(
|
|
||||||
instance_db_histories,
|
|
||||||
[&instance_name = instance_name](const replication_coordination_glue::DatabaseHistory &db_history) {
|
|
||||||
spdlog::debug("Instance {}: name {}, default db {}", instance_name, db_history.name,
|
|
||||||
memgraph::dbms::kDefaultDB);
|
|
||||||
});
|
|
||||||
|
|
||||||
MG_ASSERT(default_db_history_data != instance_db_histories.end(), "No history for instance");
|
|
||||||
|
|
||||||
const auto &instance_default_db_history = default_db_history_data->history;
|
|
||||||
|
|
||||||
std::ranges::for_each(instance_default_db_history | ranges::views::reverse,
|
|
||||||
[&instance_name = instance_name](const auto &epoch_history_it) {
|
|
||||||
spdlog::debug("Instance {}: epoch {}, last_commit_timestamp: {}", instance_name,
|
|
||||||
std::get<0>(epoch_history_it), std::get<1>(epoch_history_it));
|
|
||||||
});
|
|
||||||
|
|
||||||
// get latest epoch
|
|
||||||
// get latest timestamp
|
|
||||||
|
|
||||||
if (!new_main_res) {
|
|
||||||
const auto &[epoch, timestamp] = *instance_default_db_history.crbegin();
|
|
||||||
new_main_res = std::make_optional<NewMainRes>({instance_name, epoch, timestamp});
|
|
||||||
spdlog::debug("Currently the most up to date instance is {} with epoch {} and {} latest commit timestamp",
|
|
||||||
instance_name, epoch, timestamp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found_same_point{false};
|
|
||||||
std::string last_most_up_to_date_epoch{new_main_res->latest_epoch};
|
|
||||||
for (auto [epoch, timestamp] : ranges::reverse_view(instance_default_db_history)) {
|
|
||||||
if (new_main_res->latest_commit_timestamp < timestamp) {
|
|
||||||
new_main_res = std::make_optional<NewMainRes>({instance_name, epoch, timestamp});
|
|
||||||
spdlog::trace("Found the new most up to date instance {} with epoch {} and {} latest commit timestamp",
|
|
||||||
instance_name, epoch, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we found point at which they were same
|
|
||||||
if (epoch == last_most_up_to_date_epoch) {
|
|
||||||
found_same_point = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found_same_point) {
|
|
||||||
spdlog::error("Didn't find same history epoch {} for instance {} and instance {}", last_most_up_to_date_epoch,
|
|
||||||
new_main_res->most_up_to_date_instance, instance_name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return std::move(*new_main_res);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::IsMain(std::string_view instance_name) const -> bool {
|
|
||||||
return raft_state_.IsMain(instance_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorInstance::IsReplica(std::string_view instance_name) const -> bool {
|
|
||||||
return raft_state_.IsReplica(instance_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -13,141 +13,214 @@
|
|||||||
|
|
||||||
#include "nuraft/coordinator_log_store.hpp"
|
#include "nuraft/coordinator_log_store.hpp"
|
||||||
|
|
||||||
#include "coordination/coordinator_exceptions.hpp"
|
|
||||||
#include "utils/logging.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
using nuraft::cs_new;
|
using nuraft::cs_new;
|
||||||
|
using nuraft::timer_helper;
|
||||||
|
|
||||||
namespace {
|
CoordinatorLogStore::CoordinatorLogStore()
|
||||||
|
: start_idx_(1),
|
||||||
ptr<log_entry> MakeClone(const ptr<log_entry> &entry) {
|
raft_server_bwd_pointer_(nullptr),
|
||||||
return cs_new<log_entry>(entry->get_term(), buffer::clone(entry->get_buf()), entry->get_val_type(),
|
disk_emul_delay(0),
|
||||||
entry->get_timestamp());
|
disk_emul_thread_(nullptr),
|
||||||
}
|
disk_emul_thread_stop_signal_(false),
|
||||||
|
disk_emul_last_durable_index_(0) {
|
||||||
} // namespace
|
// Dummy entry for index 0.
|
||||||
|
ptr<buffer> buf = buffer::alloc(sz_ulong);
|
||||||
CoordinatorLogStore::CoordinatorLogStore() : start_idx_(1) {
|
|
||||||
ptr<buffer> buf = buffer::alloc(sizeof(uint64_t));
|
|
||||||
logs_[0] = cs_new<log_entry>(0, buf);
|
logs_[0] = cs_new<log_entry>(0, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoordinatorLogStore::~CoordinatorLogStore() {}
|
CoordinatorLogStore::~CoordinatorLogStore() {
|
||||||
|
if (disk_emul_thread_) {
|
||||||
auto CoordinatorLogStore::FindOrDefault_(uint64_t index) const -> ptr<log_entry> {
|
disk_emul_thread_stop_signal_ = true;
|
||||||
auto entry = logs_.find(index);
|
// disk_emul_ea_.invoke();
|
||||||
if (entry == logs_.end()) {
|
if (disk_emul_thread_->joinable()) {
|
||||||
entry = logs_.find(0);
|
disk_emul_thread_->join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return entry->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CoordinatorLogStore::next_slot() const {
|
ptr<log_entry> CoordinatorLogStore::MakeClone(const ptr<log_entry> &entry) {
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
// 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;
|
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 {
|
ptr<log_entry> CoordinatorLogStore::last_entry() const {
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t const last_idx = start_idx_ + logs_.size() - 1;
|
return MakeClone(entry->second);
|
||||||
auto const last_src = FindOrDefault_(last_idx - 1);
|
|
||||||
|
|
||||||
return MakeClone(last_src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CoordinatorLogStore::append(ptr<log_entry> &entry) {
|
ulong CoordinatorLogStore::append(ptr<log_entry> &entry) {
|
||||||
ptr<log_entry> clone = MakeClone(entry);
|
ptr<log_entry> clone = MakeClone(entry);
|
||||||
|
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
std::lock_guard<std::mutex> l(logs_lock_);
|
||||||
uint64_t next_slot = start_idx_ + logs_.size() - 1;
|
size_t idx = start_idx_ + logs_.size() - 1;
|
||||||
logs_[next_slot] = clone;
|
logs_[idx] = clone;
|
||||||
|
|
||||||
return next_slot;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: (andi) I think this is used for resolving conflicts inside NuRaft, check...
|
void CoordinatorLogStore::write_at(ulong index, ptr<log_entry> &entry) {
|
||||||
// different compared to in_memory_log_store.cxx
|
|
||||||
void CoordinatorLogStore::write_at(uint64_t index, ptr<log_entry> &entry) {
|
|
||||||
ptr<log_entry> clone = MakeClone(entry);
|
ptr<log_entry> clone = MakeClone(entry);
|
||||||
|
|
||||||
// Discard all logs equal to or greater than `index.
|
// Discard all logs equal to or greater than `index.
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
std::lock_guard<std::mutex> l(logs_lock_);
|
||||||
auto itr = logs_.lower_bound(index);
|
auto itr = logs_.lower_bound(index);
|
||||||
while (itr != logs_.end()) {
|
while (itr != logs_.end()) {
|
||||||
itr = logs_.erase(itr);
|
itr = logs_.erase(itr);
|
||||||
}
|
}
|
||||||
logs_[index] = clone;
|
logs_[index] = clone;
|
||||||
}
|
|
||||||
|
|
||||||
ptr<std::vector<ptr<log_entry>>> CoordinatorLogStore::log_entries(uint64_t start, uint64_t end) {
|
if (disk_emul_delay) {
|
||||||
auto ret = cs_new<std::vector<ptr<log_entry>>>();
|
uint64_t cur_time = timer_helper::get_timeofday_us();
|
||||||
ret->resize(end - start);
|
disk_emul_logs_being_written_[cur_time + disk_emul_delay * 1000] = index;
|
||||||
|
|
||||||
for (uint64_t i = start, curr_index = 0; i < end; i++, curr_index++) {
|
// Remove entries greater than `index`.
|
||||||
ptr<log_entry> src = nullptr;
|
auto entry = disk_emul_logs_being_written_.begin();
|
||||||
{
|
while (entry != disk_emul_logs_being_written_.end()) {
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
if (entry->second > index) {
|
||||||
if (auto const entry = logs_.find(i); entry != logs_.end()) {
|
entry = disk_emul_logs_being_written_.erase(entry);
|
||||||
src = entry->second;
|
|
||||||
} else {
|
} else {
|
||||||
throw RaftCouldNotFindEntryException("Could not find entry at index {}", i);
|
entry++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*ret)[curr_index] = MakeClone(src);
|
// disk_emul_ea_.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>>>();
|
||||||
|
|
||||||
|
ret->resize(end - start);
|
||||||
|
ulong cc = 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)[cc++] = MakeClone(src);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<log_entry> CoordinatorLogStore::entry_at(uint64_t index) {
|
// NOLINTNEXTLINE(google-default-arguments)
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
ptr<std::vector<ptr<log_entry>>> CoordinatorLogStore::log_entries_ext(ulong start, ulong end,
|
||||||
ptr<log_entry> src = FindOrDefault_(index);
|
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;
|
||||||
|
{
|
||||||
|
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);
|
return MakeClone(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CoordinatorLogStore::term_at(uint64_t index) {
|
ulong CoordinatorLogStore::term_at(ulong index) {
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
ulong term = 0;
|
||||||
return 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;
|
std::vector<ptr<buffer>> logs;
|
||||||
|
|
||||||
size_t size_total = 0;
|
size_t size_total = 0;
|
||||||
uint64_t const end_index = index + cnt;
|
for (ulong ii = index; ii < index + cnt; ++ii) {
|
||||||
for (uint64_t i = index; i < end_index; ++i) {
|
|
||||||
ptr<log_entry> le = nullptr;
|
ptr<log_entry> le = nullptr;
|
||||||
{
|
{
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
std::lock_guard<std::mutex> l(logs_lock_);
|
||||||
le = logs_[i];
|
le = logs_[ii];
|
||||||
}
|
}
|
||||||
MG_ASSERT(le.get(), "Could not find log entry at index {}", i);
|
assert(le.get());
|
||||||
auto buf = le->serialize();
|
ptr<buffer> buf = le->serialize();
|
||||||
size_total += buf->size();
|
size_total += buf->size();
|
||||||
logs.push_back(buf);
|
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->pos(0);
|
||||||
buf_out->put((int32)cnt);
|
buf_out->put((int32)cnt);
|
||||||
|
|
||||||
for (auto &entry : logs) {
|
for (auto &entry : logs) {
|
||||||
buf_out->put(static_cast<int32>(entry->size()));
|
ptr<buffer> &bb = entry;
|
||||||
buf_out->put(*entry);
|
buf_out->put((int32)bb->size());
|
||||||
|
buf_out->put(*bb);
|
||||||
}
|
}
|
||||||
return buf_out;
|
return buf_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoordinatorLogStore::apply_pack(uint64_t index, buffer &pack) {
|
void CoordinatorLogStore::apply_pack(ulong index, buffer &pack) {
|
||||||
pack.pos(0);
|
pack.pos(0);
|
||||||
int32 const num_logs = pack.get_int();
|
int32 num_logs = pack.get_int();
|
||||||
|
|
||||||
for (int32 i = 0; i < num_logs; ++i) {
|
for (int32 ii = 0; ii < num_logs; ++ii) {
|
||||||
uint64_t cur_idx = index + i;
|
ulong cur_idx = index + ii;
|
||||||
int32 buf_size = pack.get_int();
|
int32 buf_size = pack.get_int();
|
||||||
|
|
||||||
ptr<buffer> buf_local = buffer::alloc(buf_size);
|
ptr<buffer> buf_local = buffer::alloc(buf_size);
|
||||||
@@ -155,14 +228,14 @@ void CoordinatorLogStore::apply_pack(uint64_t index, buffer &pack) {
|
|||||||
|
|
||||||
ptr<log_entry> le = log_entry::deserialize(*buf_local);
|
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;
|
logs_[cur_idx] = le;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
std::lock_guard<std::mutex> l(logs_lock_);
|
||||||
auto const entry = logs_.upper_bound(0);
|
auto entry = logs_.upper_bound(0);
|
||||||
if (entry != logs_.end()) {
|
if (entry != logs_.end()) {
|
||||||
start_idx_ = entry->first;
|
start_idx_ = entry->first;
|
||||||
} else {
|
} else {
|
||||||
@@ -171,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(ulong last_log_index) {
|
||||||
bool CoordinatorLogStore::compact(uint64_t last_log_index) {
|
std::lock_guard<std::mutex> l(logs_lock_);
|
||||||
auto lock = std::lock_guard{logs_lock_};
|
for (ulong ii = start_idx_; ii <= last_log_index; ++ii) {
|
||||||
for (uint64_t ii = start_idx_; ii <= last_log_index; ++ii) {
|
auto entry = logs_.find(ii);
|
||||||
auto const entry = logs_.find(ii);
|
|
||||||
if (entry != logs_.end()) {
|
if (entry != logs_.end()) {
|
||||||
logs_.erase(entry);
|
logs_.erase(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WARNING:
|
||||||
|
// Even though nothing has been erased,
|
||||||
|
// we should set `start_idx_` to new index.
|
||||||
if (start_idx_ <= last_log_index) {
|
if (start_idx_ <= last_log_index) {
|
||||||
start_idx_ = last_log_index + 1;
|
start_idx_ = last_log_index + 1;
|
||||||
}
|
}
|
||||||
return true;
|
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
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -52,69 +52,6 @@ void DemoteMainToReplicaRes::Load(DemoteMainToReplicaRes *self, memgraph::slk::R
|
|||||||
memgraph::slk::Load(self, reader);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDatabaseHistoriesRpc
|
|
||||||
|
|
||||||
void GetDatabaseHistoriesReq::Save(const GetDatabaseHistoriesReq & /*self*/, memgraph::slk::Builder * /*builder*/) {
|
|
||||||
/* nothing to serialize */
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetDatabaseHistoriesReq::Load(GetDatabaseHistoriesReq * /*self*/, memgraph::slk::Reader * /*reader*/) {
|
|
||||||
/* nothing to serialize */
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetDatabaseHistoriesRes::Save(const GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder) {
|
|
||||||
memgraph::slk::Save(self, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetDatabaseHistoriesRes::Load(GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader) {
|
|
||||||
memgraph::slk::Load(self, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace coordination
|
} // namespace coordination
|
||||||
|
|
||||||
constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ,
|
constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ,
|
||||||
@@ -127,37 +64,10 @@ constexpr utils::TypeInfo coordination::DemoteMainToReplicaReq::kType{utils::Typ
|
|||||||
"CoordDemoteToReplicaReq", nullptr};
|
"CoordDemoteToReplicaReq", nullptr};
|
||||||
|
|
||||||
constexpr utils::TypeInfo coordination::DemoteMainToReplicaRes::kType{utils::TypeId::COORD_SET_REPL_MAIN_RES,
|
constexpr utils::TypeInfo coordination::DemoteMainToReplicaRes::kType{utils::TypeId::COORD_SET_REPL_MAIN_RES,
|
||||||
|
|
||||||
"CoordDemoteToReplicaRes", nullptr};
|
"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};
|
|
||||||
|
|
||||||
constexpr utils::TypeInfo coordination::GetDatabaseHistoriesReq::kType{utils::TypeId::COORD_GET_INSTANCE_DATABASES_REQ,
|
|
||||||
"GetInstanceDatabasesReq", nullptr};
|
|
||||||
|
|
||||||
constexpr utils::TypeInfo coordination::GetDatabaseHistoriesRes::kType{utils::TypeId::COORD_GET_INSTANCE_DATABASES_RES,
|
|
||||||
"GetInstanceDatabasesRes", nullptr};
|
|
||||||
|
|
||||||
namespace slk {
|
namespace slk {
|
||||||
|
|
||||||
// PromoteReplicaToMainRpc
|
|
||||||
|
|
||||||
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
|
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
|
||||||
memgraph::slk::Save(self.success, builder);
|
memgraph::slk::Save(self.success, builder);
|
||||||
}
|
}
|
||||||
@@ -176,7 +86,6 @@ void Load(memgraph::coordination::PromoteReplicaToMainReq *self, memgraph::slk::
|
|||||||
memgraph::slk::Load(&self->replication_clients_info, reader);
|
memgraph::slk::Load(&self->replication_clients_info, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DemoteMainToReplicaRpc
|
|
||||||
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder) {
|
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder) {
|
||||||
memgraph::slk::Save(self.replication_client_info, builder);
|
memgraph::slk::Save(self.replication_client_info, builder);
|
||||||
}
|
}
|
||||||
@@ -193,60 +102,6 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
|
|||||||
memgraph::slk::Load(&self->success, reader);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInstanceTimestampsReq
|
|
||||||
|
|
||||||
void Save(const memgraph::coordination::GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder) {
|
|
||||||
memgraph::slk::Save(self.database_histories, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Load(memgraph::coordination::GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader) {
|
|
||||||
memgraph::slk::Load(&self->database_histories, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace slk
|
} // namespace slk
|
||||||
|
|
||||||
} // namespace memgraph
|
} // namespace memgraph
|
||||||
|
|||||||
@@ -41,55 +41,37 @@ CoordinatorState::CoordinatorState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorState::RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
auto CoordinatorState::RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus {
|
||||||
-> RegisterInstanceCoordinatorStatus {
|
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
|
||||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||||
|
|
||||||
return std::visit(
|
return std::visit(
|
||||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
memgraph::utils::Overloaded{
|
||||||
return RegisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||||
},
|
return RegisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
||||||
[config](CoordinatorInstance &coordinator_instance) {
|
},
|
||||||
return coordinator_instance.RegisterReplicationInstance(config);
|
[config](CoordinatorData &coordinator_data) { return coordinator_data.RegisterInstance(config); }},
|
||||||
}},
|
|
||||||
data_);
|
data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorState::UnregisterReplicationInstance(std::string_view instance_name)
|
auto CoordinatorState::SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||||
-> UnregisterInstanceCoordinatorStatus {
|
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||||
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_view instance_name)
|
|
||||||
-> SetInstanceToMainCoordinatorStatus {
|
|
||||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
|
||||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||||
|
|
||||||
return std::visit(
|
return std::visit(
|
||||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||||
return SetInstanceToMainCoordinatorStatus::NOT_COORDINATOR;
|
return SetInstanceToMainCoordinatorStatus::NOT_COORDINATOR;
|
||||||
},
|
},
|
||||||
[&instance_name](CoordinatorInstance &coordinator_instance) {
|
[&instance_name](CoordinatorData &coordinator_data) {
|
||||||
return coordinator_instance.SetReplicationInstanceToMain(instance_name);
|
return coordinator_data.SetInstanceToMain(instance_name);
|
||||||
}},
|
}},
|
||||||
data_);
|
data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorState::ShowInstances() const -> std::vector<InstanceStatus> {
|
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");
|
"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 & {
|
auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
||||||
@@ -98,11 +80,11 @@ auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
|||||||
return *std::get<CoordinatorMainReplicaData>(data_).coordinator_server_;
|
return *std::get<CoordinatorMainReplicaData>(data_).coordinator_server_;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
auto CoordinatorState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||||
std::string_view raft_address) -> void {
|
-> void {
|
||||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
"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
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -12,204 +12,87 @@
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "nuraft/coordinator_state_machine.hpp"
|
#include "nuraft/coordinator_state_machine.hpp"
|
||||||
#include "utils/logging.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr int MAX_SNAPSHOTS = 3;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
auto CoordinatorStateMachine::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
auto CoordinatorStateMachine::pre_commit(ulong const log_idx, buffer &data) -> ptr<buffer> {
|
||||||
return cluster_state_.FindCurrentMainInstanceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::MainExists() const -> bool { return cluster_state_.MainExists(); }
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::IsMain(std::string_view instance_name) const -> bool {
|
|
||||||
return cluster_state_.IsMain(instance_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::IsReplica(std::string_view instance_name) const -> bool {
|
|
||||||
return cluster_state_.IsReplica(instance_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::CreateLog(nlohmann::json &&log) -> ptr<buffer> {
|
|
||||||
auto const log_dump = log.dump();
|
|
||||||
ptr<buffer> log_buf = buffer::alloc(sizeof(uint32_t) + log_dump.size());
|
|
||||||
buffer_serializer bs(log_buf);
|
|
||||||
bs.put_str(log_dump);
|
|
||||||
return log_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::SerializeRegisterInstance(CoordinatorClientConfig const &config) -> ptr<buffer> {
|
|
||||||
return CreateLog({{"action", RaftLogAction::REGISTER_REPLICATION_INSTANCE}, {"info", config}});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::SerializeUnregisterInstance(std::string_view instance_name) -> ptr<buffer> {
|
|
||||||
return CreateLog({{"action", RaftLogAction::UNREGISTER_REPLICATION_INSTANCE}, {"info", instance_name}});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::SerializeSetInstanceAsMain(std::string_view instance_name) -> ptr<buffer> {
|
|
||||||
return CreateLog({{"action", RaftLogAction::SET_INSTANCE_AS_MAIN}, {"info", instance_name}});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::SerializeSetInstanceAsReplica(std::string_view instance_name) -> ptr<buffer> {
|
|
||||||
return CreateLog({{"action", RaftLogAction::SET_INSTANCE_AS_REPLICA}, {"info", instance_name}});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::SerializeUpdateUUID(utils::UUID const &uuid) -> ptr<buffer> {
|
|
||||||
return CreateLog({{"action", RaftLogAction::UPDATE_UUID}, {"info", uuid}});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::DecodeLog(buffer &data) -> std::pair<TRaftLog, RaftLogAction> {
|
|
||||||
buffer_serializer bs(data);
|
buffer_serializer bs(data);
|
||||||
auto const json = nlohmann::json::parse(bs.get_str());
|
std::string str = bs.get_str();
|
||||||
|
|
||||||
auto const action = json["action"].get<RaftLogAction>();
|
spdlog::info("pre_commit {} : {}", log_idx, str);
|
||||||
auto const &info = json["info"];
|
return nullptr;
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case RaftLogAction::REGISTER_REPLICATION_INSTANCE:
|
|
||||||
return {info.get<CoordinatorClientConfig>(), action};
|
|
||||||
case RaftLogAction::UPDATE_UUID:
|
|
||||||
return {info.get<utils::UUID>(), action};
|
|
||||||
case RaftLogAction::UNREGISTER_REPLICATION_INSTANCE:
|
|
||||||
case RaftLogAction::SET_INSTANCE_AS_MAIN:
|
|
||||||
[[fallthrough]];
|
|
||||||
case RaftLogAction::SET_INSTANCE_AS_REPLICA:
|
|
||||||
return {info.get<std::string>(), action};
|
|
||||||
}
|
|
||||||
throw std::runtime_error("Unknown action");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::pre_commit(ulong const /*log_idx*/, buffer & /*data*/) -> ptr<buffer> { return nullptr; }
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::commit(ulong const log_idx, buffer &data) -> ptr<buffer> {
|
auto CoordinatorStateMachine::commit(ulong const log_idx, buffer &data) -> ptr<buffer> {
|
||||||
spdlog::debug("Commit: log_idx={}, data.size()={}", log_idx, data.size());
|
buffer_serializer bs(data);
|
||||||
auto const [parsed_data, log_action] = DecodeLog(data);
|
std::string str = bs.get_str();
|
||||||
cluster_state_.DoAction(parsed_data, log_action);
|
|
||||||
last_committed_idx_ = log_idx;
|
|
||||||
|
|
||||||
// Return raft log number
|
spdlog::info("commit {} : {}", log_idx, str);
|
||||||
ptr<buffer> ret = buffer::alloc(sizeof(log_idx));
|
|
||||||
buffer_serializer bs_ret(ret);
|
last_committed_idx_ = log_idx;
|
||||||
bs_ret.put_u64(log_idx);
|
return nullptr;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::commit_config(ulong const log_idx, ptr<cluster_config> & /*new_conf*/) -> void {
|
auto CoordinatorStateMachine::commit_config(ulong const log_idx, ptr<cluster_config> & /*new_conf*/) -> void {
|
||||||
last_committed_idx_ = log_idx;
|
last_committed_idx_ = log_idx;
|
||||||
spdlog::debug("Commit config: log_idx={}", log_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::rollback(ulong const log_idx, buffer &data) -> void {
|
auto CoordinatorStateMachine::rollback(ulong const log_idx, buffer &data) -> void {
|
||||||
// NOTE: Nothing since we don't do anything in pre_commit
|
buffer_serializer bs(data);
|
||||||
spdlog::debug("Rollback: log_idx={}, data.size()={}", log_idx, data.size());
|
std::string str = bs.get_str();
|
||||||
|
|
||||||
|
spdlog::info("rollback {} : {}", log_idx, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::read_logical_snp_obj(snapshot &snapshot, void *& /*user_snp_ctx*/, ulong obj_id,
|
auto CoordinatorStateMachine::read_logical_snp_obj(snapshot & /*snapshot*/, void *& /*user_snp_ctx*/, ulong /*obj_id*/,
|
||||||
ptr<buffer> &data_out, bool &is_last_obj) -> int {
|
ptr<buffer> &data_out, bool &is_last_obj) -> int {
|
||||||
spdlog::debug("read logical snapshot object, obj_id: {}", obj_id);
|
// Put dummy data.
|
||||||
|
data_out = buffer::alloc(sizeof(int32));
|
||||||
ptr<SnapshotCtx> ctx = nullptr;
|
buffer_serializer bs(data_out);
|
||||||
{
|
bs.put_i32(0);
|
||||||
auto ll = std::lock_guard{snapshots_lock_};
|
|
||||||
auto entry = snapshots_.find(snapshot.get_last_log_idx());
|
|
||||||
if (entry == snapshots_.end()) {
|
|
||||||
data_out = nullptr;
|
|
||||||
is_last_obj = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ctx = entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj_id == 0) {
|
|
||||||
// Object ID == 0: first object, put dummy data.
|
|
||||||
data_out = buffer::alloc(sizeof(int32));
|
|
||||||
buffer_serializer bs(data_out);
|
|
||||||
bs.put_i32(0);
|
|
||||||
is_last_obj = false;
|
|
||||||
} else {
|
|
||||||
// Object ID > 0: second object, put actual value.
|
|
||||||
ctx->cluster_state_.Serialize(data_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
is_last_obj = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::save_logical_snp_obj(snapshot &snapshot, ulong &obj_id, buffer &data, bool is_first_obj,
|
auto CoordinatorStateMachine::save_logical_snp_obj(snapshot &s, ulong &obj_id, buffer & /*data*/, bool /*is_first_obj*/,
|
||||||
bool is_last_obj) -> void {
|
bool /*is_last_obj*/) -> void {
|
||||||
spdlog::debug("save logical snapshot object, obj_id: {}, is_first_obj: {}, is_last_obj: {}", obj_id, is_first_obj,
|
spdlog::info("save snapshot {} term {} object ID", s.get_last_log_idx(), s.get_last_log_term(), obj_id);
|
||||||
is_last_obj);
|
// Request next object.
|
||||||
|
obj_id++;
|
||||||
if (obj_id == 0) {
|
|
||||||
ptr<buffer> snp_buf = snapshot.serialize();
|
|
||||||
auto ss = snapshot::deserialize(*snp_buf);
|
|
||||||
create_snapshot_internal(ss);
|
|
||||||
} else {
|
|
||||||
auto cluster_state = CoordinatorClusterState::Deserialize(data);
|
|
||||||
|
|
||||||
auto ll = std::lock_guard{snapshots_lock_};
|
|
||||||
auto entry = snapshots_.find(snapshot.get_last_log_idx());
|
|
||||||
DMG_ASSERT(entry != snapshots_.end());
|
|
||||||
entry->second->cluster_state_ = cluster_state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::apply_snapshot(snapshot &s) -> bool {
|
auto CoordinatorStateMachine::apply_snapshot(snapshot &s) -> bool {
|
||||||
auto ll = std::lock_guard{snapshots_lock_};
|
spdlog::info("apply snapshot {} term {}", s.get_last_log_idx(), s.get_last_log_term());
|
||||||
spdlog::debug("apply snapshot, last_log_idx: {}", s.get_last_log_idx());
|
{
|
||||||
|
auto lock = std::lock_guard{last_snapshot_lock_};
|
||||||
auto entry = snapshots_.find(s.get_last_log_idx());
|
ptr<buffer> snp_buf = s.serialize();
|
||||||
if (entry == snapshots_.end()) return false;
|
last_snapshot_ = snapshot::deserialize(*snp_buf);
|
||||||
|
}
|
||||||
cluster_state_ = entry->second->cluster_state_;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::free_user_snp_ctx(void *&user_snp_ctx) -> void {}
|
auto CoordinatorStateMachine::free_user_snp_ctx(void *&user_snp_ctx) -> void {}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::last_snapshot() -> ptr<snapshot> {
|
auto CoordinatorStateMachine::last_snapshot() -> ptr<snapshot> {
|
||||||
auto ll = std::lock_guard{snapshots_lock_};
|
auto lock = std::lock_guard{last_snapshot_lock_};
|
||||||
spdlog::debug("last_snapshot");
|
return last_snapshot_;
|
||||||
auto entry = snapshots_.rbegin();
|
|
||||||
if (entry == snapshots_.rend()) return nullptr;
|
|
||||||
|
|
||||||
ptr<SnapshotCtx> ctx = entry->second;
|
|
||||||
return ctx->snapshot_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::last_commit_index() -> ulong { return last_committed_idx_; }
|
auto CoordinatorStateMachine::last_commit_index() -> ulong { return last_committed_idx_; }
|
||||||
|
|
||||||
auto CoordinatorStateMachine::create_snapshot(snapshot &s, async_result<bool>::handler_type &when_done) -> void {
|
auto CoordinatorStateMachine::create_snapshot(snapshot &s, async_result<bool>::handler_type &when_done) -> void {
|
||||||
spdlog::debug("create_snapshot, last_log_idx: {}", s.get_last_log_idx());
|
spdlog::info("create snapshot {} term {}", s.get_last_log_idx(), s.get_last_log_term());
|
||||||
ptr<buffer> snp_buf = s.serialize();
|
// Clone snapshot from `s`.
|
||||||
ptr<snapshot> ss = snapshot::deserialize(*snp_buf);
|
{
|
||||||
create_snapshot_internal(ss);
|
auto lock = std::lock_guard{last_snapshot_lock_};
|
||||||
|
ptr<buffer> snp_buf = s.serialize();
|
||||||
|
last_snapshot_ = snapshot::deserialize(*snp_buf);
|
||||||
|
}
|
||||||
ptr<std::exception> except(nullptr);
|
ptr<std::exception> except(nullptr);
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
when_done(ret, except);
|
when_done(ret, except);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorStateMachine::create_snapshot_internal(ptr<snapshot> snapshot) -> void {
|
|
||||||
auto ll = std::lock_guard{snapshots_lock_};
|
|
||||||
spdlog::debug("create_snapshot_internal, last_log_idx: {}", snapshot->get_last_log_idx());
|
|
||||||
|
|
||||||
auto ctx = cs_new<SnapshotCtx>(snapshot, cluster_state_);
|
|
||||||
snapshots_[snapshot->get_last_log_idx()] = ctx;
|
|
||||||
|
|
||||||
while (snapshots_.size() > MAX_SNAPSHOTS) {
|
|
||||||
snapshots_.erase(snapshots_.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::GetInstances() const -> std::vector<InstanceState> {
|
|
||||||
return cluster_state_.GetInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorStateMachine::GetUUID() const -> utils::UUID { return cluster_state_.GetUUID(); }
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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,7 +9,14 @@
|
|||||||
// by the Apache License, Version 2.0, included in the file
|
// by the Apache License, Version 2.0, included in the file
|
||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
enum class GetInstanceUUIDError { NO_RESPONSE, RPC_EXCEPTION };
|
|
||||||
enum class GetInstanceTimestampsError { NO_RESPONSE, RPC_EXCEPTION };
|
#ifdef MG_EXPERIMENTAL_HIGH_AVAILABILITY
|
||||||
|
constexpr bool allow_ha = true;
|
||||||
|
#else
|
||||||
|
constexpr bool allow_ha = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
@@ -11,26 +11,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils/uuid.hpp"
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
#include "coordination/coordinator_config.hpp"
|
||||||
#include "replication_coordination_glue/common.hpp"
|
|
||||||
#include "rpc/client.hpp"
|
#include "rpc/client.hpp"
|
||||||
#include "rpc_errors.hpp"
|
|
||||||
#include "utils/result.hpp"
|
|
||||||
#include "utils/scheduler.hpp"
|
#include "utils/scheduler.hpp"
|
||||||
#include "utils/uuid.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
class CoordinatorInstance;
|
class CoordinatorData;
|
||||||
using HealthCheckClientCallback = std::function<void(CoordinatorInstance *, std::string_view)>;
|
using HealthCheckCallback = std::function<void(CoordinatorData *, std::string_view)>;
|
||||||
using ReplicationClientsInfo = std::vector<ReplClientInfo>;
|
using ReplicationClientsInfo = std::vector<ReplClientInfo>;
|
||||||
|
|
||||||
class CoordinatorClient {
|
class CoordinatorClient {
|
||||||
public:
|
public:
|
||||||
explicit CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
explicit CoordinatorClient(CoordinatorData *coord_data_, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb);
|
HealthCheckCallback fail_cb);
|
||||||
|
|
||||||
~CoordinatorClient() = default;
|
~CoordinatorClient() = default;
|
||||||
|
|
||||||
@@ -46,33 +43,20 @@ class CoordinatorClient {
|
|||||||
void ResumeFrequentCheck();
|
void ResumeFrequentCheck();
|
||||||
|
|
||||||
auto InstanceName() const -> std::string;
|
auto InstanceName() const -> std::string;
|
||||||
auto CoordinatorSocketAddress() const -> std::string;
|
auto SocketAddress() const -> std::string;
|
||||||
auto ReplicationSocketAddress() const -> std::string;
|
|
||||||
|
|
||||||
[[nodiscard]] auto DemoteToReplica() const -> bool;
|
[[nodiscard]] auto DemoteToReplica() const -> bool;
|
||||||
|
auto SendPromoteReplicaToMainRpc(const utils::UUID &uuid, ReplicationClientsInfo replication_clients_info) const
|
||||||
auto SendPromoteReplicaToMainRpc(utils::UUID const &uuid, ReplicationClientsInfo replication_clients_info) const
|
|
||||||
-> bool;
|
-> bool;
|
||||||
|
|
||||||
auto SendSwapMainUUIDRpc(utils::UUID const &uuid) const -> bool;
|
auto SendSwapMainUUIDRpc(const utils::UUID &uuid) const -> bool;
|
||||||
|
|
||||||
auto SendUnregisterReplicaRpc(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
auto SendEnableWritingOnMainRpc() const -> bool;
|
|
||||||
|
|
||||||
auto SendGetInstanceUUIDRpc() const -> memgraph::utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>>;
|
|
||||||
|
|
||||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||||
|
|
||||||
auto SendGetInstanceTimestampsRpc() const
|
auto SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void;
|
||||||
-> utils::BasicResult<GetInstanceUUIDError, replication_coordination_glue::DatabaseHistories>;
|
|
||||||
|
|
||||||
auto RpcClient() -> rpc::Client & { return rpc_client_; }
|
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) {
|
friend bool operator==(CoordinatorClient const &first, CoordinatorClient const &second) {
|
||||||
return first.config_ == second.config_;
|
return first.config_ == second.config_;
|
||||||
}
|
}
|
||||||
@@ -80,13 +64,14 @@ class CoordinatorClient {
|
|||||||
private:
|
private:
|
||||||
utils::Scheduler instance_checker_;
|
utils::Scheduler instance_checker_;
|
||||||
|
|
||||||
|
// TODO: (andi) Pimpl?
|
||||||
communication::ClientContext rpc_context_;
|
communication::ClientContext rpc_context_;
|
||||||
mutable rpc::Client rpc_client_;
|
mutable rpc::Client rpc_client_;
|
||||||
|
|
||||||
CoordinatorClientConfig config_;
|
CoordinatorClientConfig config_;
|
||||||
CoordinatorInstance *coord_instance_;
|
CoordinatorData *coord_data_;
|
||||||
HealthCheckClientCallback succ_cb_;
|
HealthCheckCallback succ_cb_;
|
||||||
HealthCheckClientCallback fail_cb_;
|
HealthCheckCallback fail_cb_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -11,11 +11,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if FMT_VERSION > 90000
|
#ifdef MG_ENTERPRISE
|
||||||
#include <fmt/ostream.h>
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
#include "io/network/endpoint.hpp"
|
struct CoordinatorClusterConfig {
|
||||||
|
static constexpr int alive_response_time_difference_sec_{5};
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
} // namespace memgraph::coordination
|
||||||
class fmt::formatter<memgraph::io::network::Endpoint> : public fmt::ostream_formatter {};
|
|
||||||
#endif
|
#endif
|
||||||
@@ -14,16 +14,12 @@
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "replication_coordination_glue/mode.hpp"
|
#include "replication_coordination_glue/mode.hpp"
|
||||||
#include "utils/string.hpp"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "json/json.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
inline constexpr auto *kDefaultReplicationServerIp = "0.0.0.0";
|
inline constexpr auto *kDefaultReplicationServerIp = "0.0.0.0";
|
||||||
@@ -32,15 +28,9 @@ struct CoordinatorClientConfig {
|
|||||||
std::string instance_name;
|
std::string instance_name;
|
||||||
std::string ip_address;
|
std::string ip_address;
|
||||||
uint16_t port{};
|
uint16_t port{};
|
||||||
std::chrono::seconds instance_health_check_frequency_sec{1};
|
std::chrono::seconds health_check_frequency_sec{1};
|
||||||
std::chrono::seconds instance_down_timeout_sec{5};
|
|
||||||
std::chrono::seconds instance_get_uuid_frequency_sec{10};
|
|
||||||
|
|
||||||
auto CoordinatorSocketAddress() const -> std::string { return fmt::format("{}:{}", ip_address, port); }
|
auto SocketAddress() const -> std::string { return ip_address + ":" + std::to_string(port); }
|
||||||
auto ReplicationSocketAddress() const -> std::string {
|
|
||||||
return fmt::format("{}:{}", replication_client_info.replication_ip_address,
|
|
||||||
replication_client_info.replication_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReplicationClientInfo {
|
struct ReplicationClientInfo {
|
||||||
std::string instance_name;
|
std::string instance_name;
|
||||||
@@ -83,11 +73,5 @@ struct CoordinatorServerConfig {
|
|||||||
friend bool operator==(CoordinatorServerConfig const &, CoordinatorServerConfig const &) = default;
|
friend bool operator==(CoordinatorServerConfig const &, CoordinatorServerConfig const &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, CoordinatorClientConfig const &config);
|
|
||||||
void from_json(nlohmann::json const &j, CoordinatorClientConfig &config);
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, ReplClientInfo const &config);
|
|
||||||
void from_json(nlohmann::json const &j, ReplClientInfo &config);
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
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,49 +50,5 @@ class RaftAddServerException final : public utils::BasicException {
|
|||||||
SPECIALIZE_GET_EXCEPTION_NAME(RaftAddServerException)
|
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)
|
|
||||||
};
|
|
||||||
|
|
||||||
class InvalidRaftLogActionException final : public utils::BasicException {
|
|
||||||
public:
|
|
||||||
explicit InvalidRaftLogActionException(std::string_view what) noexcept : BasicException(what) {}
|
|
||||||
|
|
||||||
template <class... Args>
|
|
||||||
explicit InvalidRaftLogActionException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
|
||||||
: InvalidRaftLogActionException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
|
||||||
|
|
||||||
SPECIALIZE_GET_EXCEPTION_NAME(InvalidRaftLogActionException)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -33,17 +33,6 @@ class CoordinatorHandlers {
|
|||||||
slk::Builder *res_builder);
|
slk::Builder *res_builder);
|
||||||
static void SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
static void SwapMainUUIDHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
||||||
slk::Builder *res_builder);
|
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);
|
|
||||||
|
|
||||||
static void GetDatabaseHistoriesHandler(replication::ReplicationHandler &replication_handler, slk::Reader *req_reader,
|
|
||||||
slk::Builder *res_builder);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::dbms
|
} // namespace memgraph::dbms
|
||||||
|
|||||||
@@ -13,69 +13,45 @@
|
|||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_server.hpp"
|
#include <flags/replication.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/resource_lock.hpp"
|
|
||||||
#include "utils/rw_lock.hpp"
|
|
||||||
#include "utils/thread_pool.hpp"
|
|
||||||
|
|
||||||
#include <list>
|
#include <libnuraft/nuraft.hxx>
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
struct NewMainRes {
|
using nuraft::logger;
|
||||||
std::string most_up_to_date_instance;
|
using nuraft::ptr;
|
||||||
std::string latest_epoch;
|
using nuraft::raft_launcher;
|
||||||
uint64_t latest_commit_timestamp;
|
using nuraft::raft_server;
|
||||||
};
|
using nuraft::srv_config;
|
||||||
using InstanceNameDbHistories = std::pair<std::string, replication_coordination_glue::DatabaseHistories>;
|
using nuraft::state_machine;
|
||||||
|
using nuraft::state_mgr;
|
||||||
|
|
||||||
class CoordinatorInstance {
|
class CoordinatorInstance {
|
||||||
public:
|
public:
|
||||||
CoordinatorInstance();
|
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 const &config)
|
auto InstanceName() const -> std::string;
|
||||||
-> RegisterInstanceCoordinatorStatus;
|
auto RaftSocketAddress() const -> std::string;
|
||||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string_view instance_name)
|
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||||
-> UnregisterInstanceCoordinatorStatus;
|
auto GetAllCoordinators() const -> std::vector<ptr<srv_config>>;
|
||||||
|
|
||||||
[[nodiscard]] auto SetReplicationInstanceToMain(std::string_view 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_view raft_address) -> void;
|
|
||||||
|
|
||||||
static auto ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> histories) -> NewMainRes;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HealthCheckClientCallback client_succ_cb_, client_fail_cb_;
|
ptr<state_machine> state_machine_;
|
||||||
|
ptr<state_mgr> state_manager_;
|
||||||
|
ptr<raft_server> raft_server_;
|
||||||
|
ptr<logger> logger_;
|
||||||
|
raft_launcher launcher_;
|
||||||
|
|
||||||
auto OnRaftCommitCallback(TRaftLog const &log_entry, RaftLogAction log_action) -> void;
|
// TODO: (andi) I think variables below can be abstracted
|
||||||
|
uint32_t raft_server_id_;
|
||||||
auto FindReplicationInstance(std::string_view replication_instance_name) -> ReplicationInstance &;
|
uint32_t raft_port_;
|
||||||
|
std::string raft_address_;
|
||||||
void MainFailCallback(std::string_view);
|
|
||||||
|
|
||||||
void MainSuccessCallback(std::string_view);
|
|
||||||
|
|
||||||
void ReplicaSuccessCallback(std::string_view);
|
|
||||||
|
|
||||||
void ReplicaFailCallback(std::string_view);
|
|
||||||
|
|
||||||
auto IsMain(std::string_view instance_name) const -> bool;
|
|
||||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
// NOTE: Must be std::list because we rely on pointer stability.
|
|
||||||
// Leader and followers should both have same view on repl_instances_
|
|
||||||
std::list<ReplicationInstance> repl_instances_;
|
|
||||||
mutable utils::ResourceLock coord_instance_lock_{};
|
|
||||||
|
|
||||||
RaftState raft_state_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
#include "coordination/coordinator_config.hpp"
|
||||||
#include "replication_coordination_glue/common.hpp"
|
|
||||||
#include "rpc/messages.hpp"
|
#include "rpc/messages.hpp"
|
||||||
#include "slk/serialization.hpp"
|
#include "slk/serialization.hpp"
|
||||||
|
|
||||||
@@ -83,111 +82,6 @@ struct DemoteMainToReplicaRes {
|
|||||||
|
|
||||||
using DemoteMainToReplicaRpc = rpc::RequestResponse<DemoteMainToReplicaReq, 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_view inst_name) : instance_name(inst_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>;
|
|
||||||
|
|
||||||
struct GetDatabaseHistoriesReq {
|
|
||||||
static const utils::TypeInfo kType;
|
|
||||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
|
||||||
|
|
||||||
static void Load(GetDatabaseHistoriesReq *self, memgraph::slk::Reader *reader);
|
|
||||||
static void Save(const GetDatabaseHistoriesReq &self, memgraph::slk::Builder *builder);
|
|
||||||
|
|
||||||
GetDatabaseHistoriesReq() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GetDatabaseHistoriesRes {
|
|
||||||
static const utils::TypeInfo kType;
|
|
||||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
|
||||||
|
|
||||||
static void Load(GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader);
|
|
||||||
static void Save(const GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder);
|
|
||||||
|
|
||||||
explicit GetDatabaseHistoriesRes(const replication_coordination_glue::DatabaseHistories &database_histories)
|
|
||||||
: database_histories(database_histories) {}
|
|
||||||
GetDatabaseHistoriesRes() = default;
|
|
||||||
|
|
||||||
replication_coordination_glue::DatabaseHistories database_histories;
|
|
||||||
};
|
|
||||||
|
|
||||||
using GetDatabaseHistoriesRpc = rpc::RequestResponse<GetDatabaseHistoriesReq, GetDatabaseHistoriesRes>;
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|
||||||
// SLK serialization declarations
|
// SLK serialization declarations
|
||||||
@@ -205,25 +99,6 @@ void Load(memgraph::coordination::DemoteMainToReplicaRes *self, memgraph::slk::R
|
|||||||
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
void Save(const memgraph::coordination::DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
||||||
void Load(memgraph::coordination::DemoteMainToReplicaReq *self, memgraph::slk::Reader *reader);
|
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);
|
|
||||||
|
|
||||||
// EnableWritingOnMainRpc
|
|
||||||
void Save(memgraph::coordination::EnableWritingOnMainRes const &self, memgraph::slk::Builder *builder);
|
|
||||||
void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::Reader *reader);
|
|
||||||
|
|
||||||
// GetDatabaseHistoriesRpc
|
|
||||||
void Save(const memgraph::coordination::GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder);
|
|
||||||
void Load(memgraph::coordination::GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader);
|
|
||||||
|
|
||||||
} // namespace memgraph::slk
|
} // namespace memgraph::slk
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
#include "coordination/coordinator_config.hpp"
|
||||||
#include "replication_coordination_glue/common.hpp"
|
|
||||||
#include "slk/serialization.hpp"
|
#include "slk/serialization.hpp"
|
||||||
#include "slk/streams.hpp"
|
#include "slk/streams.hpp"
|
||||||
|
|
||||||
@@ -35,18 +34,5 @@ inline void Load(ReplicationClientInfo *obj, Reader *reader) {
|
|||||||
Load(&obj->replication_ip_address, reader);
|
Load(&obj->replication_ip_address, reader);
|
||||||
Load(&obj->replication_port, reader);
|
Load(&obj->replication_port, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Save(const replication_coordination_glue::DatabaseHistory &obj, Builder *builder) {
|
|
||||||
Save(obj.db_uuid, builder);
|
|
||||||
Save(obj.history, builder);
|
|
||||||
Save(obj.name, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Load(replication_coordination_glue::DatabaseHistory *obj, Reader *reader) {
|
|
||||||
Load(&obj->db_uuid, reader);
|
|
||||||
Load(&obj->history, reader);
|
|
||||||
Load(&obj->name, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace memgraph::slk
|
} // namespace memgraph::slk
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_instance.hpp"
|
#include "coordination/coordinator_data.hpp"
|
||||||
#include "coordination/coordinator_server.hpp"
|
#include "coordination/coordinator_server.hpp"
|
||||||
#include "coordination/instance_status.hpp"
|
#include "coordination/instance_status.hpp"
|
||||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||||
@@ -33,26 +33,19 @@ class CoordinatorState {
|
|||||||
CoordinatorState(CoordinatorState &&) noexcept = delete;
|
CoordinatorState(CoordinatorState &&) noexcept = delete;
|
||||||
CoordinatorState &operator=(CoordinatorState &&) noexcept = delete;
|
CoordinatorState &operator=(CoordinatorState &&) noexcept = delete;
|
||||||
|
|
||||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
[[nodiscard]] auto RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||||
-> RegisterInstanceCoordinatorStatus;
|
|
||||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string_view instance_name)
|
|
||||||
-> UnregisterInstanceCoordinatorStatus;
|
|
||||||
|
|
||||||
[[nodiscard]] auto SetReplicationInstanceToMain(std::string_view instance_name) -> SetInstanceToMainCoordinatorStatus;
|
[[nodiscard]] auto SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||||
|
|
||||||
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
||||||
|
|
||||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
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 &;
|
auto GetCoordinatorServer() const -> CoordinatorServer &;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CoordinatorMainReplicaData {
|
std::variant<CoordinatorData, CoordinatorMainReplicaData> data_;
|
||||||
std::unique_ptr<CoordinatorServer> coordinator_server_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::variant<CoordinatorInstance, CoordinatorMainReplicaData> data_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ struct InstanceStatus {
|
|||||||
std::string raft_socket_address;
|
std::string raft_socket_address;
|
||||||
std::string coord_socket_address;
|
std::string coord_socket_address;
|
||||||
std::string cluster_role;
|
std::string cluster_role;
|
||||||
std::string health;
|
bool is_alive;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -1,97 +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 "io/network/endpoint.hpp"
|
|
||||||
#include "nuraft/coordinator_state_machine.hpp"
|
|
||||||
#include "nuraft/coordinator_state_manager.hpp"
|
|
||||||
|
|
||||||
#include <libnuraft/nuraft.hxx>
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
|
||||||
|
|
||||||
class CoordinatorInstance;
|
|
||||||
struct CoordinatorClientConfig;
|
|
||||||
|
|
||||||
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_view raft_address) -> void;
|
|
||||||
auto GetAllCoordinators() const -> std::vector<ptr<srv_config>>;
|
|
||||||
|
|
||||||
auto RequestLeadership() -> bool;
|
|
||||||
auto IsLeader() const -> bool;
|
|
||||||
|
|
||||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
|
||||||
auto MainExists() const -> bool;
|
|
||||||
auto IsMain(std::string_view instance_name) const -> bool;
|
|
||||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
auto AppendRegisterReplicationInstanceLog(CoordinatorClientConfig const &config) -> bool;
|
|
||||||
auto AppendUnregisterReplicationInstanceLog(std::string_view instance_name) -> bool;
|
|
||||||
auto AppendSetInstanceAsMainLog(std::string_view instance_name) -> bool;
|
|
||||||
auto AppendSetInstanceAsReplicaLog(std::string_view instance_name) -> bool;
|
|
||||||
auto AppendUpdateUUIDLog(utils::UUID const &uuid) -> bool;
|
|
||||||
|
|
||||||
auto GetInstances() const -> std::vector<InstanceState>;
|
|
||||||
auto GetUUID() const -> utils::UUID;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// TODO: (andi) I think variables below can be abstracted/clean them.
|
|
||||||
io::network::Endpoint raft_endpoint_;
|
|
||||||
uint32_t raft_server_id_;
|
|
||||||
|
|
||||||
ptr<CoordinatorStateMachine> state_machine_;
|
|
||||||
ptr<CoordinatorStateManager> 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
|
|
||||||
@@ -19,34 +19,18 @@ namespace memgraph::coordination {
|
|||||||
|
|
||||||
enum class RegisterInstanceCoordinatorStatus : uint8_t {
|
enum class RegisterInstanceCoordinatorStatus : uint8_t {
|
||||||
NAME_EXISTS,
|
NAME_EXISTS,
|
||||||
COORD_ENDPOINT_EXISTS,
|
ENDPOINT_EXISTS,
|
||||||
REPL_ENDPOINT_EXISTS,
|
|
||||||
NOT_COORDINATOR,
|
NOT_COORDINATOR,
|
||||||
NOT_LEADER,
|
|
||||||
RPC_FAILED,
|
RPC_FAILED,
|
||||||
RAFT_LOG_ERROR,
|
|
||||||
SUCCESS
|
SUCCESS
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class UnregisterInstanceCoordinatorStatus : uint8_t {
|
|
||||||
NO_INSTANCE_WITH_NAME,
|
|
||||||
IS_MAIN,
|
|
||||||
NOT_COORDINATOR,
|
|
||||||
RPC_FAILED,
|
|
||||||
NOT_LEADER,
|
|
||||||
RAFT_LOG_ERROR,
|
|
||||||
SUCCESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SetInstanceToMainCoordinatorStatus : uint8_t {
|
enum class SetInstanceToMainCoordinatorStatus : uint8_t {
|
||||||
NO_INSTANCE_WITH_NAME,
|
NO_INSTANCE_WITH_NAME,
|
||||||
MAIN_ALREADY_EXISTS,
|
|
||||||
NOT_COORDINATOR,
|
NOT_COORDINATOR,
|
||||||
NOT_LEADER,
|
|
||||||
RAFT_LOG_ERROR,
|
|
||||||
COULD_NOT_PROMOTE_TO_MAIN,
|
|
||||||
SWAP_UUID_FAILED,
|
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
COULD_NOT_PROMOTE_TO_MAIN,
|
||||||
|
SWAP_UUID_FAILED
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -14,27 +14,21 @@
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_client.hpp"
|
#include "coordination/coordinator_client.hpp"
|
||||||
|
#include "coordination/coordinator_cluster_config.hpp"
|
||||||
#include "coordination/coordinator_exceptions.hpp"
|
#include "coordination/coordinator_exceptions.hpp"
|
||||||
#include "replication_coordination_glue/role.hpp"
|
#include "replication_coordination_glue/role.hpp"
|
||||||
|
|
||||||
#include "utils/resource_lock.hpp"
|
|
||||||
#include "utils/result.hpp"
|
|
||||||
#include "utils/uuid.hpp"
|
|
||||||
|
|
||||||
#include <libnuraft/nuraft.hxx>
|
#include <libnuraft/nuraft.hxx>
|
||||||
|
#include "utils/uuid.hpp"
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
class CoordinatorInstance;
|
class CoordinatorData;
|
||||||
class ReplicationInstance;
|
|
||||||
|
|
||||||
using HealthCheckInstanceCallback = void (CoordinatorInstance::*)(std::string_view);
|
|
||||||
|
|
||||||
class ReplicationInstance {
|
class ReplicationInstance {
|
||||||
public:
|
public:
|
||||||
ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config, HealthCheckClientCallback succ_cb,
|
ReplicationInstance(CoordinatorData *data, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||||
HealthCheckClientCallback fail_cb, HealthCheckInstanceCallback succ_instance_cb,
|
HealthCheckCallback fail_cb);
|
||||||
HealthCheckInstanceCallback fail_instance_cb);
|
|
||||||
|
|
||||||
ReplicationInstance(ReplicationInstance const &other) = delete;
|
ReplicationInstance(ReplicationInstance const &other) = delete;
|
||||||
ReplicationInstance &operator=(ReplicationInstance const &other) = delete;
|
ReplicationInstance &operator=(ReplicationInstance const &other) = delete;
|
||||||
@@ -44,53 +38,35 @@ class ReplicationInstance {
|
|||||||
|
|
||||||
auto OnSuccessPing() -> void;
|
auto OnSuccessPing() -> void;
|
||||||
auto OnFailPing() -> bool;
|
auto OnFailPing() -> bool;
|
||||||
auto IsReadyForUUIDPing() -> bool;
|
|
||||||
|
|
||||||
void UpdateReplicaLastResponseUUID();
|
|
||||||
|
|
||||||
auto IsAlive() const -> bool;
|
auto IsAlive() const -> bool;
|
||||||
|
|
||||||
auto InstanceName() const -> std::string;
|
auto InstanceName() const -> std::string;
|
||||||
auto CoordinatorSocketAddress() const -> std::string;
|
auto SocketAddress() const -> std::string;
|
||||||
auto ReplicationSocketAddress() const -> std::string;
|
|
||||||
|
|
||||||
auto PromoteToMain(utils::UUID const &uuid, ReplicationClientsInfo repl_clients_info,
|
auto IsReplica() const -> bool;
|
||||||
HealthCheckInstanceCallback main_succ_cb, HealthCheckInstanceCallback main_fail_cb) -> bool;
|
auto IsMain() const -> bool;
|
||||||
|
|
||||||
auto SendDemoteToReplicaRpc() -> bool;
|
auto PromoteToMain(utils::UUID uuid, ReplicationClientsInfo repl_clients_info, HealthCheckCallback main_succ_cb,
|
||||||
|
HealthCheckCallback main_fail_cb) -> bool;
|
||||||
|
auto DemoteToReplica(HealthCheckCallback replica_succ_cb, HealthCheckCallback replica_fail_cb) -> bool;
|
||||||
|
|
||||||
auto DemoteToReplica(HealthCheckInstanceCallback replica_succ_cb, HealthCheckInstanceCallback replica_fail_cb)
|
|
||||||
-> bool;
|
|
||||||
|
|
||||||
auto StartFrequentCheck() -> void;
|
|
||||||
auto StopFrequentCheck() -> void;
|
|
||||||
auto PauseFrequentCheck() -> void;
|
auto PauseFrequentCheck() -> void;
|
||||||
auto ResumeFrequentCheck() -> void;
|
auto ResumeFrequentCheck() -> void;
|
||||||
|
|
||||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||||
|
|
||||||
auto EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool;
|
auto SendSwapAndUpdateUUID(const utils::UUID &main_uuid) -> bool;
|
||||||
|
|
||||||
auto SendSwapAndUpdateUUID(utils::UUID const &new_main_uuid) -> bool;
|
|
||||||
auto SendUnregisterReplicaRpc(std::string_view instance_name) -> bool;
|
|
||||||
|
|
||||||
auto SendGetInstanceUUID() -> utils::BasicResult<coordination::GetInstanceUUIDError, std::optional<utils::UUID>>;
|
|
||||||
auto GetClient() -> CoordinatorClient &;
|
auto GetClient() -> CoordinatorClient &;
|
||||||
|
|
||||||
auto EnableWritingOnMain() -> bool;
|
void SetNewMainUUID(const std::optional<utils::UUID> &main_uuid = std::nullopt);
|
||||||
|
auto GetMainUUID() -> const std::optional<utils::UUID> &;
|
||||||
auto SetNewMainUUID(utils::UUID const &main_uuid) -> void;
|
|
||||||
auto ResetMainUUID() -> void;
|
|
||||||
auto GetMainUUID() const -> std::optional<utils::UUID> const &;
|
|
||||||
|
|
||||||
auto GetSuccessCallback() -> HealthCheckInstanceCallback &;
|
|
||||||
auto GetFailCallback() -> HealthCheckInstanceCallback &;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CoordinatorClient client_;
|
CoordinatorClient client_;
|
||||||
|
replication_coordination_glue::ReplicationRole replication_role_;
|
||||||
std::chrono::system_clock::time_point last_response_time_{};
|
std::chrono::system_clock::time_point last_response_time_{};
|
||||||
bool is_alive_{false};
|
bool is_alive_{false};
|
||||||
std::chrono::system_clock::time_point last_check_of_uuid_{};
|
|
||||||
|
|
||||||
// for replica this is main uuid of current main
|
// for replica this is main uuid of current main
|
||||||
// for "main" main this same as in CoordinatorData
|
// for "main" main this same as in CoordinatorData
|
||||||
@@ -99,12 +75,8 @@ class ReplicationInstance {
|
|||||||
// so we need to send swap uuid again
|
// so we need to send swap uuid again
|
||||||
std::optional<utils::UUID> main_uuid_;
|
std::optional<utils::UUID> main_uuid_;
|
||||||
|
|
||||||
HealthCheckInstanceCallback succ_cb_;
|
|
||||||
HealthCheckInstanceCallback fail_cb_;
|
|
||||||
|
|
||||||
friend bool operator==(ReplicationInstance const &first, ReplicationInstance const &second) {
|
friend bool operator==(ReplicationInstance const &first, ReplicationInstance const &second) {
|
||||||
return first.client_ == second.client_ && first.last_response_time_ == second.last_response_time_ &&
|
return first.client_ == second.client_ && first.replication_role_ == second.replication_role_;
|
||||||
first.is_alive_ == second.is_alive_ && first.main_uuid_ == second.main_uuid_;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,92 +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 "coordination/coordinator_config.hpp"
|
|
||||||
#include "nuraft/raft_log_action.hpp"
|
|
||||||
#include "replication_coordination_glue/role.hpp"
|
|
||||||
#include "utils/resource_lock.hpp"
|
|
||||||
#include "utils/uuid.hpp"
|
|
||||||
|
|
||||||
#include <libnuraft/nuraft.hxx>
|
|
||||||
#include <range/v3/view.hpp>
|
|
||||||
#include "json/json.hpp"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <numeric>
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
|
||||||
|
|
||||||
using replication_coordination_glue::ReplicationRole;
|
|
||||||
|
|
||||||
struct InstanceState {
|
|
||||||
CoordinatorClientConfig config;
|
|
||||||
ReplicationRole status;
|
|
||||||
|
|
||||||
friend auto operator==(InstanceState const &lhs, InstanceState const &rhs) -> bool {
|
|
||||||
return lhs.config == rhs.config && lhs.status == rhs.status;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void to_json(nlohmann::json &j, InstanceState const &instance_state);
|
|
||||||
void from_json(nlohmann::json const &j, InstanceState &instance_state);
|
|
||||||
|
|
||||||
using TRaftLog = std::variant<CoordinatorClientConfig, std::string, utils::UUID>;
|
|
||||||
|
|
||||||
using nuraft::buffer;
|
|
||||||
using nuraft::buffer_serializer;
|
|
||||||
using nuraft::ptr;
|
|
||||||
|
|
||||||
class CoordinatorClusterState {
|
|
||||||
public:
|
|
||||||
CoordinatorClusterState() = default;
|
|
||||||
explicit CoordinatorClusterState(std::map<std::string, InstanceState, std::less<>> instances);
|
|
||||||
|
|
||||||
CoordinatorClusterState(CoordinatorClusterState const &);
|
|
||||||
CoordinatorClusterState &operator=(CoordinatorClusterState const &);
|
|
||||||
|
|
||||||
CoordinatorClusterState(CoordinatorClusterState &&other) noexcept;
|
|
||||||
CoordinatorClusterState &operator=(CoordinatorClusterState &&other) noexcept;
|
|
||||||
~CoordinatorClusterState() = default;
|
|
||||||
|
|
||||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
|
||||||
|
|
||||||
auto MainExists() const -> bool;
|
|
||||||
|
|
||||||
auto IsMain(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
auto InsertInstance(std::string instance_name, InstanceState instance_state) -> void;
|
|
||||||
|
|
||||||
auto DoAction(TRaftLog log_entry, RaftLogAction log_action) -> void;
|
|
||||||
|
|
||||||
auto Serialize(ptr<buffer> &data) -> void;
|
|
||||||
|
|
||||||
static auto Deserialize(buffer &data) -> CoordinatorClusterState;
|
|
||||||
|
|
||||||
auto GetInstances() const -> std::vector<InstanceState>;
|
|
||||||
|
|
||||||
auto GetUUID() const -> utils::UUID;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<std::string, InstanceState, std::less<>> instances_{};
|
|
||||||
utils::UUID uuid_{};
|
|
||||||
mutable utils::ResourceLock log_lock_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
|
||||||
#endif
|
|
||||||
@@ -46,6 +46,9 @@ class CoordinatorLogStore : public log_store {
|
|||||||
|
|
||||||
ptr<std::vector<ptr<log_entry>>> log_entries(ulong start, ulong end) override;
|
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;
|
ptr<log_entry> entry_at(ulong index) override;
|
||||||
|
|
||||||
ulong term_at(ulong index) override;
|
ulong term_at(ulong index) override;
|
||||||
@@ -58,12 +61,67 @@ class CoordinatorLogStore : public log_store {
|
|||||||
|
|
||||||
bool flush() override;
|
bool flush() override;
|
||||||
|
|
||||||
private:
|
ulong last_durable_index() override;
|
||||||
auto FindOrDefault_(ulong index) const -> ptr<log_entry>;
|
|
||||||
|
|
||||||
|
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_;
|
std::map<ulong, ptr<log_entry>> logs_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock for `logs_`.
|
||||||
|
*/
|
||||||
mutable std::mutex logs_lock_;
|
mutable std::mutex logs_lock_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the first log.
|
||||||
|
*/
|
||||||
std::atomic<ulong> start_idx_;
|
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
|
} // namespace memgraph::coordination
|
||||||
|
|||||||
@@ -13,15 +13,9 @@
|
|||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
|
||||||
#include "nuraft/coordinator_cluster_state.hpp"
|
|
||||||
#include "nuraft/raft_log_action.hpp"
|
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <libnuraft/nuraft.hxx>
|
#include <libnuraft/nuraft.hxx>
|
||||||
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
using nuraft::async_result;
|
using nuraft::async_result;
|
||||||
@@ -42,20 +36,6 @@ class CoordinatorStateMachine : public state_machine {
|
|||||||
CoordinatorStateMachine &operator=(CoordinatorStateMachine &&) = delete;
|
CoordinatorStateMachine &operator=(CoordinatorStateMachine &&) = delete;
|
||||||
~CoordinatorStateMachine() override {}
|
~CoordinatorStateMachine() override {}
|
||||||
|
|
||||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
|
||||||
auto MainExists() const -> bool;
|
|
||||||
auto IsMain(std::string_view instance_name) const -> bool;
|
|
||||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
|
||||||
|
|
||||||
static auto CreateLog(nlohmann::json &&log) -> ptr<buffer>;
|
|
||||||
static auto SerializeRegisterInstance(CoordinatorClientConfig const &config) -> ptr<buffer>;
|
|
||||||
static auto SerializeUnregisterInstance(std::string_view instance_name) -> ptr<buffer>;
|
|
||||||
static auto SerializeSetInstanceAsMain(std::string_view instance_name) -> ptr<buffer>;
|
|
||||||
static auto SerializeSetInstanceAsReplica(std::string_view instance_name) -> ptr<buffer>;
|
|
||||||
static auto SerializeUpdateUUID(utils::UUID const &uuid) -> ptr<buffer>;
|
|
||||||
|
|
||||||
static auto DecodeLog(buffer &data) -> std::pair<TRaftLog, RaftLogAction>;
|
|
||||||
|
|
||||||
auto pre_commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
auto pre_commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
||||||
|
|
||||||
auto commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
auto commit(ulong log_idx, buffer &data) -> ptr<buffer> override;
|
||||||
@@ -80,27 +60,11 @@ class CoordinatorStateMachine : public state_machine {
|
|||||||
|
|
||||||
auto create_snapshot(snapshot &s, async_result<bool>::handler_type &when_done) -> void override;
|
auto create_snapshot(snapshot &s, async_result<bool>::handler_type &when_done) -> void override;
|
||||||
|
|
||||||
auto GetInstances() const -> std::vector<InstanceState>;
|
|
||||||
auto GetUUID() const -> utils::UUID;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SnapshotCtx {
|
|
||||||
SnapshotCtx(ptr<snapshot> &snapshot, CoordinatorClusterState const &cluster_state)
|
|
||||||
: snapshot_(snapshot), cluster_state_(cluster_state) {}
|
|
||||||
|
|
||||||
ptr<snapshot> snapshot_;
|
|
||||||
CoordinatorClusterState cluster_state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto create_snapshot_internal(ptr<snapshot> snapshot) -> void;
|
|
||||||
|
|
||||||
CoordinatorClusterState cluster_state_;
|
|
||||||
std::atomic<uint64_t> last_committed_idx_{0};
|
std::atomic<uint64_t> last_committed_idx_{0};
|
||||||
|
|
||||||
std::map<uint64_t, ptr<SnapshotCtx>> snapshots_;
|
|
||||||
std::mutex snapshots_lock_;
|
|
||||||
|
|
||||||
ptr<snapshot> last_snapshot_;
|
ptr<snapshot> last_snapshot_;
|
||||||
|
|
||||||
std::mutex last_snapshot_lock_;
|
std::mutex last_snapshot_lock_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +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 "coordination/coordinator_exceptions.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "json/json.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
|
||||||
|
|
||||||
enum class RaftLogAction : uint8_t {
|
|
||||||
REGISTER_REPLICATION_INSTANCE,
|
|
||||||
UNREGISTER_REPLICATION_INSTANCE,
|
|
||||||
SET_INSTANCE_AS_MAIN,
|
|
||||||
SET_INSTANCE_AS_REPLICA,
|
|
||||||
UPDATE_UUID
|
|
||||||
};
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(RaftLogAction, {
|
|
||||||
{RaftLogAction::REGISTER_REPLICATION_INSTANCE, "register"},
|
|
||||||
{RaftLogAction::UNREGISTER_REPLICATION_INSTANCE, "unregister"},
|
|
||||||
{RaftLogAction::SET_INSTANCE_AS_MAIN, "promote"},
|
|
||||||
{RaftLogAction::SET_INSTANCE_AS_REPLICA, "demote"},
|
|
||||||
{RaftLogAction::UPDATE_UUID, "update_uuid"},
|
|
||||||
})
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
|
||||||
#endif
|
|
||||||
@@ -1,279 +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 <chrono>
|
|
||||||
|
|
||||||
#include "coordination/coordinator_config.hpp"
|
|
||||||
#include "coordination/coordinator_exceptions.hpp"
|
|
||||||
#include "coordination/raft_state.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_endpoint_(raft_address, raft_port),
|
|
||||||
raft_server_id_(raft_server_id),
|
|
||||||
state_machine_(cs_new<CoordinatorStateMachine>()),
|
|
||||||
state_manager_(cs_new<CoordinatorStateManager>(raft_server_id_, raft_endpoint_.SocketAddress())),
|
|
||||||
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;
|
|
||||||
|
|
||||||
raft_params params;
|
|
||||||
params.heart_beat_interval_ = 100;
|
|
||||||
params.election_timeout_lower_bound_ = 200;
|
|
||||||
params.election_timeout_upper_bound_ = 400;
|
|
||||||
params.reserved_log_items_ = 5;
|
|
||||||
params.snapshot_distance_ = 5;
|
|
||||||
params.client_req_timeout_ = 3000;
|
|
||||||
params.return_method_ = raft_params::blocking;
|
|
||||||
|
|
||||||
// If the leader doesn't receive any response from quorum nodes
|
|
||||||
// in 200ms, it will step down.
|
|
||||||
// This allows us to achieve strong consistency even if network partition
|
|
||||||
// happens between the current leader and followers.
|
|
||||||
// The value must be <= election_timeout_lower_bound_ so that cluster can never
|
|
||||||
// have multiple leaders.
|
|
||||||
params.leadership_expiry_ = 200;
|
|
||||||
|
|
||||||
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_, raft_endpoint_.port, asio_opts, params, init_opts);
|
|
||||||
|
|
||||||
if (!raft_server_) {
|
|
||||||
throw RaftServerStartException("Failed to launch raft server on {}", raft_endpoint_.SocketAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
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_endpoint_.SocketAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::MakeRaftState(BecomeLeaderCb &&become_leader_cb, BecomeFollowerCb &&become_follower_cb) -> RaftState {
|
|
||||||
uint32_t raft_server_id = FLAGS_raft_server_id;
|
|
||||||
uint32_t raft_port = FLAGS_raft_server_port;
|
|
||||||
|
|
||||||
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 fmt::format("coordinator_{}", std::to_string(raft_server_id_));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::RaftSocketAddress() const -> std::string { return raft_endpoint_.SocketAddress(); }
|
|
||||||
|
|
||||||
auto RaftState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address)
|
|
||||||
-> void {
|
|
||||||
auto const endpoint = fmt::format("{}:{}", raft_address, raft_port);
|
|
||||||
srv_config const srv_config_to_add(static_cast<int>(raft_server_id), endpoint);
|
|
||||||
|
|
||||||
auto cmd_result = raft_server_->add_srv(srv_config_to_add);
|
|
||||||
|
|
||||||
if (cmd_result->get_result_code() == nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::info("Request to add server {} to the cluster accepted", endpoint);
|
|
||||||
} else {
|
|
||||||
throw RaftAddServerException("Failed to accept request to add server {} to the cluster with error code {}",
|
|
||||||
endpoint, cmd_result->get_result_code());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Waiting for server to join
|
|
||||||
constexpr int max_tries{10};
|
|
||||||
auto maybe_stop = utils::ResettableCounter<max_tries>();
|
|
||||||
constexpr int waiting_period{200};
|
|
||||||
bool added{false};
|
|
||||||
while (!maybe_stop()) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(waiting_period));
|
|
||||||
const auto server_config = raft_server_->get_srv_config(static_cast<nuraft::int32>(raft_server_id));
|
|
||||||
if (server_config) {
|
|
||||||
spdlog::trace("Server with id {} added to cluster", raft_server_id);
|
|
||||||
added = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!added) {
|
|
||||||
throw RaftAddServerException("Failed to add server {} to the cluster in {}ms", endpoint,
|
|
||||||
max_tries * waiting_period);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::AppendRegisterReplicationInstanceLog(CoordinatorClientConfig const &config) -> bool {
|
|
||||||
auto new_log = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
|
||||||
auto const res = raft_server_->append_entries({new_log});
|
|
||||||
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Request for registering instance {} accepted", config.instance_name);
|
|
||||||
|
|
||||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::error("Failed to register instance {} with error code {}", config.instance_name, res->get_result_code());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::AppendUnregisterReplicationInstanceLog(std::string_view instance_name) -> bool {
|
|
||||||
auto new_log = CoordinatorStateMachine::SerializeUnregisterInstance(instance_name);
|
|
||||||
auto const res = raft_server_->append_entries({new_log});
|
|
||||||
if (!res->get_accepted()) {
|
|
||||||
spdlog::error(
|
|
||||||
"Failed to accept request for unregistering instance {}. Most likely the reason is that the instance is not "
|
|
||||||
"the leader.",
|
|
||||||
instance_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Request for unregistering instance {} accepted", instance_name);
|
|
||||||
|
|
||||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::error("Failed to unregister instance {} with error code {}", instance_name, res->get_result_code());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::AppendSetInstanceAsMainLog(std::string_view instance_name) -> bool {
|
|
||||||
auto new_log = CoordinatorStateMachine::SerializeSetInstanceAsMain(instance_name);
|
|
||||||
auto const res = raft_server_->append_entries({new_log});
|
|
||||||
if (!res->get_accepted()) {
|
|
||||||
spdlog::error(
|
|
||||||
"Failed to accept request for promoting instance {}. Most likely the reason is that the instance is not "
|
|
||||||
"the leader.",
|
|
||||||
instance_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Request for promoting instance {} accepted", instance_name);
|
|
||||||
|
|
||||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::error("Failed to promote instance {} with error code {}", instance_name, res->get_result_code());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::AppendSetInstanceAsReplicaLog(std::string_view instance_name) -> bool {
|
|
||||||
auto new_log = CoordinatorStateMachine::SerializeSetInstanceAsReplica(instance_name);
|
|
||||||
auto const res = raft_server_->append_entries({new_log});
|
|
||||||
if (!res->get_accepted()) {
|
|
||||||
spdlog::error(
|
|
||||||
"Failed to accept request for demoting instance {}. Most likely the reason is that the instance is not "
|
|
||||||
"the leader.",
|
|
||||||
instance_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
spdlog::info("Request for demoting instance {} accepted", instance_name);
|
|
||||||
|
|
||||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::error("Failed to promote instance {} with error code {}", instance_name, res->get_result_code());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::AppendUpdateUUIDLog(utils::UUID const &uuid) -> bool {
|
|
||||||
auto new_log = CoordinatorStateMachine::SerializeUpdateUUID(uuid);
|
|
||||||
auto const res = raft_server_->append_entries({new_log});
|
|
||||||
if (!res->get_accepted()) {
|
|
||||||
spdlog::error(
|
|
||||||
"Failed to accept request for updating UUID. Most likely the reason is that the instance is not "
|
|
||||||
"the leader.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
spdlog::info("Request for updating UUID accepted");
|
|
||||||
|
|
||||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
|
||||||
spdlog::error("Failed to update UUID with error code {}", res->get_result_code());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
|
||||||
return state_machine_->FindCurrentMainInstanceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::MainExists() const -> bool { return state_machine_->MainExists(); }
|
|
||||||
|
|
||||||
auto RaftState::IsMain(std::string_view instance_name) const -> bool { return state_machine_->IsMain(instance_name); }
|
|
||||||
|
|
||||||
auto RaftState::IsReplica(std::string_view instance_name) const -> bool {
|
|
||||||
return state_machine_->IsReplica(instance_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto RaftState::GetInstances() const -> std::vector<InstanceState> { return state_machine_->GetInstances(); }
|
|
||||||
|
|
||||||
auto RaftState::GetUUID() const -> utils::UUID { return state_machine_->GetUUID(); }
|
|
||||||
|
|
||||||
} // namespace memgraph::coordination
|
|
||||||
#endif
|
|
||||||
@@ -13,20 +13,20 @@
|
|||||||
|
|
||||||
#include "coordination/replication_instance.hpp"
|
#include "coordination/replication_instance.hpp"
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "replication_coordination_glue/handler.hpp"
|
#include "replication_coordination_glue/handler.hpp"
|
||||||
#include "utils/result.hpp"
|
|
||||||
|
|
||||||
namespace memgraph::coordination {
|
namespace memgraph::coordination {
|
||||||
|
|
||||||
ReplicationInstance::ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config,
|
ReplicationInstance::ReplicationInstance(CoordinatorData *data, CoordinatorClientConfig config,
|
||||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb,
|
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||||
HealthCheckInstanceCallback succ_instance_cb,
|
: client_(data, std::move(config), std::move(succ_cb), std::move(fail_cb)),
|
||||||
HealthCheckInstanceCallback fail_instance_cb)
|
replication_role_(replication_coordination_glue::ReplicationRole::REPLICA),
|
||||||
: client_(peer, std::move(config), std::move(succ_cb), std::move(fail_cb)),
|
is_alive_(true) {
|
||||||
succ_cb_(succ_instance_cb),
|
if (!client_.DemoteToReplica()) {
|
||||||
fail_cb_(fail_instance_cb) {}
|
throw CoordinatorRegisterInstanceException("Failed to demote instance {} to replica", client_.InstanceName());
|
||||||
|
}
|
||||||
|
client_.StartFrequentCheck();
|
||||||
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::OnSuccessPing() -> void {
|
auto ReplicationInstance::OnSuccessPing() -> void {
|
||||||
last_response_time_ = std::chrono::system_clock::now();
|
last_response_time_ = std::chrono::system_clock::now();
|
||||||
@@ -34,51 +34,47 @@ auto ReplicationInstance::OnSuccessPing() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::OnFailPing() -> bool {
|
auto ReplicationInstance::OnFailPing() -> bool {
|
||||||
auto elapsed_time = std::chrono::system_clock::now() - last_response_time_;
|
is_alive_ =
|
||||||
is_alive_ = elapsed_time < client_.InstanceDownTimeoutSec();
|
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_;
|
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::InstanceName() const -> std::string { return client_.InstanceName(); }
|
||||||
auto ReplicationInstance::CoordinatorSocketAddress() const -> std::string { return client_.CoordinatorSocketAddress(); }
|
auto ReplicationInstance::SocketAddress() const -> std::string { return client_.SocketAddress(); }
|
||||||
auto ReplicationInstance::ReplicationSocketAddress() const -> std::string { return client_.ReplicationSocketAddress(); }
|
|
||||||
auto ReplicationInstance::IsAlive() const -> bool { return is_alive_; }
|
auto ReplicationInstance::IsAlive() const -> bool { return is_alive_; }
|
||||||
|
|
||||||
auto ReplicationInstance::PromoteToMain(utils::UUID const &new_uuid, ReplicationClientsInfo repl_clients_info,
|
auto ReplicationInstance::IsReplica() const -> bool {
|
||||||
HealthCheckInstanceCallback main_succ_cb,
|
return replication_role_ == replication_coordination_glue::ReplicationRole::REPLICA;
|
||||||
HealthCheckInstanceCallback main_fail_cb) -> bool {
|
}
|
||||||
if (!client_.SendPromoteReplicaToMainRpc(new_uuid, std::move(repl_clients_info))) {
|
auto ReplicationInstance::IsMain() const -> bool {
|
||||||
|
return replication_role_ == replication_coordination_glue::ReplicationRole::MAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ReplicationInstance::PromoteToMain(utils::UUID uuid, ReplicationClientsInfo repl_clients_info,
|
||||||
|
HealthCheckCallback main_succ_cb, HealthCheckCallback main_fail_cb) -> bool {
|
||||||
|
if (!client_.SendPromoteReplicaToMainRpc(uuid, std::move(repl_clients_info))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
main_uuid_ = new_uuid;
|
replication_role_ = replication_coordination_glue::ReplicationRole::MAIN;
|
||||||
succ_cb_ = main_succ_cb;
|
client_.SetCallbacks(std::move(main_succ_cb), std::move(main_fail_cb));
|
||||||
fail_cb_ = main_fail_cb;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::SendDemoteToReplicaRpc() -> bool { return client_.DemoteToReplica(); }
|
auto ReplicationInstance::DemoteToReplica(HealthCheckCallback replica_succ_cb, HealthCheckCallback replica_fail_cb)
|
||||||
|
-> bool {
|
||||||
auto ReplicationInstance::DemoteToReplica(HealthCheckInstanceCallback replica_succ_cb,
|
|
||||||
HealthCheckInstanceCallback replica_fail_cb) -> bool {
|
|
||||||
if (!client_.DemoteToReplica()) {
|
if (!client_.DemoteToReplica()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
succ_cb_ = replica_succ_cb;
|
replication_role_ = replication_coordination_glue::ReplicationRole::REPLICA;
|
||||||
fail_cb_ = replica_fail_cb;
|
client_.SetCallbacks(std::move(replica_succ_cb), std::move(replica_fail_cb));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::StartFrequentCheck() -> void { client_.StartFrequentCheck(); }
|
|
||||||
auto ReplicationInstance::StopFrequentCheck() -> void { client_.StopFrequentCheck(); }
|
|
||||||
auto ReplicationInstance::PauseFrequentCheck() -> void { client_.PauseFrequentCheck(); }
|
auto ReplicationInstance::PauseFrequentCheck() -> void { client_.PauseFrequentCheck(); }
|
||||||
auto ReplicationInstance::ResumeFrequentCheck() -> void { client_.ResumeFrequentCheck(); }
|
auto ReplicationInstance::ResumeFrequentCheck() -> void { client_.ResumeFrequentCheck(); }
|
||||||
|
|
||||||
@@ -86,52 +82,17 @@ auto ReplicationInstance::ReplicationClientInfo() const -> CoordinatorClientConf
|
|||||||
return client_.ReplicationClientInfo();
|
return client_.ReplicationClientInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::GetSuccessCallback() -> HealthCheckInstanceCallback & { return succ_cb_; }
|
|
||||||
auto ReplicationInstance::GetFailCallback() -> HealthCheckInstanceCallback & { return fail_cb_; }
|
|
||||||
|
|
||||||
auto ReplicationInstance::GetClient() -> CoordinatorClient & { return client_; }
|
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::SendSwapAndUpdateUUID(const utils::UUID &main_uuid) -> bool {
|
||||||
auto ReplicationInstance::GetMainUUID() const -> std::optional<utils::UUID> const & { return main_uuid_; }
|
if (!replication_coordination_glue::SendSwapMainUUIDRpc(client_.RpcClient(), main_uuid)) {
|
||||||
|
|
||||||
auto ReplicationInstance::EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool {
|
|
||||||
if (!IsReadyForUUIDPing()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
auto res = SendGetInstanceUUID();
|
|
||||||
if (res.HasError()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UpdateReplicaLastResponseUUID();
|
SetNewMainUUID(main_uuid_);
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
|
||||||
if (res.GetValue().has_value() && res.GetValue().value() == curr_main_uuid) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SendSwapAndUpdateUUID(curr_main_uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ReplicationInstance::SendSwapAndUpdateUUID(utils::UUID const &new_main_uuid) -> bool {
|
|
||||||
if (!replication_coordination_glue::SendSwapMainUUIDRpc(client_.RpcClient(), new_main_uuid)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SetNewMainUUID(new_main_uuid);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ReplicationInstance::SendUnregisterReplicaRpc(std::string_view 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
|
} // namespace memgraph::coordination
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,4 +16,10 @@ namespace memgraph::dbms {
|
|||||||
constexpr std::string_view kDefaultDB = "memgraph"; //!< Name of the default database
|
constexpr std::string_view kDefaultDB = "memgraph"; //!< Name of the default database
|
||||||
constexpr std::string_view kMultiTenantDir = "databases"; //!< Name of the multi-tenant directory
|
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
|
} // namespace memgraph::dbms
|
||||||
|
|||||||
@@ -20,28 +20,23 @@ namespace memgraph::dbms {
|
|||||||
CoordinatorHandler::CoordinatorHandler(coordination::CoordinatorState &coordinator_state)
|
CoordinatorHandler::CoordinatorHandler(coordination::CoordinatorState &coordinator_state)
|
||||||
: coordinator_state_(coordinator_state) {}
|
: coordinator_state_(coordinator_state) {}
|
||||||
|
|
||||||
auto CoordinatorHandler::RegisterReplicationInstance(coordination::CoordinatorClientConfig const &config)
|
auto CoordinatorHandler::RegisterInstance(memgraph::coordination::CoordinatorClientConfig config)
|
||||||
-> coordination::RegisterInstanceCoordinatorStatus {
|
-> coordination::RegisterInstanceCoordinatorStatus {
|
||||||
return coordinator_state_.RegisterReplicationInstance(config);
|
return coordinator_state_.RegisterInstance(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorHandler::UnregisterReplicationInstance(std::string_view instance_name)
|
auto CoordinatorHandler::SetInstanceToMain(std::string instance_name)
|
||||||
-> coordination::UnregisterInstanceCoordinatorStatus {
|
|
||||||
return coordinator_state_.UnregisterReplicationInstance(instance_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CoordinatorHandler::SetReplicationInstanceToMain(std::string_view instance_name)
|
|
||||||
-> coordination::SetInstanceToMainCoordinatorStatus {
|
-> coordination::SetInstanceToMainCoordinatorStatus {
|
||||||
return coordinator_state_.SetReplicationInstanceToMain(instance_name);
|
return coordinator_state_.SetInstanceToMain(std::move(instance_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorHandler::ShowInstances() const -> std::vector<coordination::InstanceStatus> {
|
auto CoordinatorHandler::ShowInstances() const -> std::vector<coordination::InstanceStatus> {
|
||||||
return coordinator_state_.ShowInstances();
|
return coordinator_state_.ShowInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CoordinatorHandler::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
auto CoordinatorHandler::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address)
|
||||||
std::string_view raft_address) -> void {
|
-> void {
|
||||||
coordinator_state_.AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
coordinator_state_.AddCoordinatorInstance(raft_server_id, raft_port, std::move(raft_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memgraph::dbms
|
} // namespace memgraph::dbms
|
||||||
|
|||||||
@@ -28,19 +28,14 @@ class CoordinatorHandler {
|
|||||||
public:
|
public:
|
||||||
explicit CoordinatorHandler(coordination::CoordinatorState &coordinator_state);
|
explicit CoordinatorHandler(coordination::CoordinatorState &coordinator_state);
|
||||||
|
|
||||||
// TODO: (andi) When moving coordinator state on same instances, rename from RegisterReplicationInstance to
|
auto RegisterInstance(coordination::CoordinatorClientConfig config)
|
||||||
// RegisterInstance
|
|
||||||
auto RegisterReplicationInstance(coordination::CoordinatorClientConfig const &config)
|
|
||||||
-> coordination::RegisterInstanceCoordinatorStatus;
|
-> coordination::RegisterInstanceCoordinatorStatus;
|
||||||
|
|
||||||
auto UnregisterReplicationInstance(std::string_view instance_name)
|
auto SetInstanceToMain(std::string instance_name) -> coordination::SetInstanceToMainCoordinatorStatus;
|
||||||
-> coordination::UnregisterInstanceCoordinatorStatus;
|
|
||||||
|
|
||||||
auto SetReplicationInstanceToMain(std::string_view instance_name) -> coordination::SetInstanceToMainCoordinatorStatus;
|
|
||||||
|
|
||||||
auto ShowInstances() const -> std::vector<coordination::InstanceStatus>;
|
auto ShowInstances() const -> std::vector<coordination::InstanceStatus>;
|
||||||
|
|
||||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string raft_address) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
coordination::CoordinatorState &coordinator_state_;
|
coordination::CoordinatorState &coordinator_state_;
|
||||||
|
|||||||
@@ -110,9 +110,9 @@ class Database {
|
|||||||
* @param force_directory Use the configured directory, do not try to decipher the multi-db version
|
* @param force_directory Use the configured directory, do not try to decipher the multi-db version
|
||||||
* @return DatabaseInfo
|
* @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;
|
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.triggers = trigger_store_.GetTriggerInfo().size();
|
||||||
info.streams = streams_.GetStreamInfo().size();
|
info.streams = streams_.GetStreamInfo().size();
|
||||||
return info;
|
return info;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2024 Memgraph Ltd.
|
// Copyright 2023 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "dbms/constants.hpp"
|
#include "dbms/constants.hpp"
|
||||||
#include "dbms/global.hpp"
|
#include "dbms/global.hpp"
|
||||||
#include "flags/experimental.hpp"
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "system/include/system/system.hpp"
|
#include "system/include/system/system.hpp"
|
||||||
#include "utils/exceptions.hpp"
|
#include "utils/exceptions.hpp"
|
||||||
@@ -159,9 +158,9 @@ struct Durability {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DbmsHandler::DbmsHandler(storage::Config config, replication::ReplicationState &repl_state, auth::SynchedAuth &auth,
|
DbmsHandler::DbmsHandler(storage::Config config, memgraph::system::System &system,
|
||||||
bool recovery_on_startup)
|
replication::ReplicationState &repl_state, auth::SynchedAuth &auth, bool recovery_on_startup)
|
||||||
: default_config_{std::move(config)}, auth_{auth}, repl_state_{repl_state} {
|
: default_config_{std::move(config)}, auth_{auth}, repl_state_{repl_state}, system_{&system} {
|
||||||
// TODO: Decouple storage config from dbms config
|
// TODO: Decouple storage config from dbms config
|
||||||
// TODO: Save individual db configs inside the kvstore and restore from there
|
// TODO: Save individual db configs inside the kvstore and restore from there
|
||||||
|
|
||||||
@@ -185,16 +184,6 @@ DbmsHandler::DbmsHandler(storage::Config config, replication::ReplicationState &
|
|||||||
auto directories = std::set{std::string{kDefaultDB}};
|
auto directories = std::set{std::string{kDefaultDB}};
|
||||||
|
|
||||||
// Recover previous databases
|
// Recover previous databases
|
||||||
if (flags::AreExperimentsEnabled(flags::Experiments::SYSTEM_REPLICATION) && !recovery_on_startup) {
|
|
||||||
// This will result in dropping databases on SystemRecoveryHandler
|
|
||||||
// for MT case, and for single DB case we might not even set replication as commit timestamp is checked
|
|
||||||
spdlog::warn(
|
|
||||||
"Data recovery on startup not set, this will result in dropping database in case of multi-tenancy enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Problem is if user doesn't set this up "database" name won't be recovered
|
|
||||||
// but if storage-recover-on-startup is true storage will be recovered which is an issue
|
|
||||||
spdlog::info("Data recovery on startup set to {}", recovery_on_startup);
|
|
||||||
if (recovery_on_startup) {
|
if (recovery_on_startup) {
|
||||||
auto it = durability_->begin(std::string(kDBPrefix));
|
auto it = durability_->begin(std::string(kDBPrefix));
|
||||||
auto end = durability_->end(std::string(kDBPrefix));
|
auto end = durability_->end(std::string(kDBPrefix));
|
||||||
@@ -420,10 +409,9 @@ void DbmsHandler::UpdateDurability(const storage::Config &config, std::optional<
|
|||||||
if (!durability_) return;
|
if (!durability_) return;
|
||||||
// Save database in a list of active databases
|
// Save database in a list of active databases
|
||||||
const auto &key = Durability::GenKey(config.salient.name);
|
const auto &key = Durability::GenKey(config.salient.name);
|
||||||
if (rel_dir == std::nullopt) {
|
if (rel_dir == std::nullopt)
|
||||||
rel_dir =
|
rel_dir =
|
||||||
std::filesystem::relative(config.durability.storage_directory, default_config_.durability.storage_directory);
|
std::filesystem::relative(config.durability.storage_directory, default_config_.durability.storage_directory);
|
||||||
}
|
|
||||||
const auto &val = Durability::GenVal(config.salient.uuid, *rel_dir);
|
const auto &val = Durability::GenVal(config.salient.uuid, *rel_dir);
|
||||||
durability_->Put(key, val);
|
durability_->Put(key, val);
|
||||||
}
|
}
|
||||||
@@ -431,10 +419,7 @@ void DbmsHandler::UpdateDurability(const storage::Config &config, std::optional<
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void DbmsHandler::RecoverStorageReplication(DatabaseAccess db_acc, replication::RoleMainData &role_main_data) {
|
void DbmsHandler::RecoverStorageReplication(DatabaseAccess db_acc, replication::RoleMainData &role_main_data) {
|
||||||
using enum memgraph::flags::Experiments;
|
if (allow_mt_repl || db_acc->name() == dbms::kDefaultDB) {
|
||||||
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) {
|
|
||||||
// Handle global replication state
|
// Handle global replication state
|
||||||
spdlog::info("Replication configuration will be stored and will be automatically restored in case of a crash.");
|
spdlog::info("Replication configuration will be stored and will be automatically restored in case of a crash.");
|
||||||
// RECOVER REPLICA CONNECTIONS
|
// RECOVER REPLICA CONNECTIONS
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user