Initial commit

This commit is contained in:
dev
2025-02-27 21:53:53 +08:00
commit 815e55e4c0
1291 changed files with 185445 additions and 0 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[build]
rustflags = ["-Clinker=clang++-14", "-Clink-arg=-fuse-ld=lld"]

29
.clang-format Normal file
View File

@@ -0,0 +1,29 @@
BasedOnStyle: Google
Language: Cpp
Standard: c++20
AlignAfterOpenBracket: Align
AlignEscapedNewlines: Left
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
IndentWidth: 2
PointerAlignment: Right
DerivePointerAlignment: false
ColumnLimit: 120
PackConstructorInitializers: Never
SpaceAfterTemplateKeyword: true
IncludeCategories:
- Regex: 'Flat_generated\.h("|>)'
Priority: 1000
SortPriority: 1000
- Regex: '"fbs/macros/'
Priority: 1001
SortPriority: 1001

117
.clang-tidy Normal file
View File

@@ -0,0 +1,117 @@
Checks: >
-*,
bugprone-undelegated-constructor,
bugprone-argument-comment,
bugprone-bad-signal-to-kill-thread,
bugprone-bool-pointer-implicit-conversion,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-forward-declaration-namespace,
bugprone-fold-init-type,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-artithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-reserved-identifier,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-string-compare,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
# bugprone-macro-parentheses,
# bugprone-narrowing-conversions,
# bugprone-exception-escape,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-algorithm,
performance-inefficient-vector-operation,
performance-move-constructor-init,
performance-no-automatic-move,
performance-trivially-destructible,
performance-unnecessary-copy-initialization,
performance-move-const-arg,
modernize-avoid-bind,
modernize-loop-convert,
modernize-make-shared,
modernize-make-unique,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-random-shuffle,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-nullptr,
modernize-use-using,
modernize-use-override,
modernize-use-equals-default,
modernize-use-equals-delete,
misc-throw-by-value-catch-by-reference,
misc-misplaced-const,
misc-unconventional-assign-operator,
misc-redundant-expression,
misc-static-assert,
misc-unconventional-assign-operator,
misc-uniqueptr-reset-release,
misc-unused-alias-decls,
misc-unused-parameters,
misc-unused-using-decls,
readability-avoid-const-params-in-decls
readability-const-return-type,
readability-container-size-empty,
readability-delete-null-pointer,
readability-deleted-default,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-function-ptr-dereference,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-redundant-string-init,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-uniqueptr-delete-release,
readability-simplify-subscript-expr,
readability-simplify-boolean-expr,
readability-inconsistent-declaration-parameter-name,
# readability-qualified-auto,
cert-flp30-c,
cert-mem57-cpp,
cert-oop58-cpp,
google-build-explicit-make-pair,
google-runtime-operator,
hicpp-exception-baseclass,
cppcoreguidelines-virtual-class-destructor,
WarningsAsErrors: ""
CheckOptions:
- key: performance-move-const-arg.CheckTriviallyCopyableMove
value: false

3
.clangd Normal file
View File

@@ -0,0 +1,3 @@
CompileFlags:
Add: -fcoroutines-ts
Remove: [-fcoroutines, -DNO_INTELLISENSE]

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
clang
build
deploy/*/Dockerfile

36
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Build
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: self-hosted # or `ubuntu-22.04`
steps:
- uses: actions/checkout@v4
- name: Configure sccache-cache
run: |
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.4
- name: Prepare
run: |
sudo apt install -y cmake libuv1-dev liblz4-dev liblzma-dev libdouble-conversion-dev libprocps-dev libdwarf-dev libunwind-dev
sudo apt install -y libaio-dev libgflags-dev libgoogle-glog-dev libgtest-dev libgmock-dev clang-format-14 clang-14 clang-tidy-14 lld-14
sudo apt install -y libgoogle-perftools-dev google-perftools libssl-dev ccache gcc-12 g++-12 libboost-all-dev meson rustc cargo
wget https://github.com/apple/foundationdb/releases/download/7.1.61/foundationdb-clients_7.1.61-1_amd64.deb && sudo dpkg -i foundationdb-clients_7.1.61-1_amd64.deb
git clone https://github.com/libfuse/libfuse.git libfuse -b fuse-3.16.2 --depth 1 && mkdir libfuse/build && cd libfuse/build && meson setup .. && ninja && sudo ninja install && cd ../.. && rm -rf libfuse
git submodule update --init --recursive
./patches/apply.sh
- name: Build
run: |
cargo build --release
cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build -j 32

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
/.cache
/.vscode
/build
/debug
/release
/clang
/clangdbg
/packages
*.o
*.a
*.so
*.log
compile_commands.json
*_generated.h
*~
__pycache__
/target

42
.gitmodules vendored Normal file
View File

@@ -0,0 +1,42 @@
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git
[submodule "third_party/folly"]
path = third_party/folly
url = https://github.com/facebook/folly.git
[submodule "third_party/leveldb"]
path = third_party/leveldb
url = https://github.com/google/leveldb.git
[submodule "third_party/rocksdb"]
path = third_party/rocksdb
url = https://github.com/facebook/rocksdb.git
[submodule "third_party/scnlib"]
path = third_party/scnlib
url = https://github.com/eliaskosunen/scnlib.git
[submodule "third_party/pybind11"]
path = third_party/pybind11
url = https://github.com/pybind/pybind11.git
[submodule "third_party/clickhouse-cpp"]
path = third_party/clickhouse-cpp
url = https://github.com/ClickHouse/clickhouse-cpp.git
[submodule "third_party/fmt"]
path = third_party/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "third_party/toml11"]
path = third_party/toml11
url = https://github.com/ToruNiina/toml11.git
[submodule "third_party/jemalloc"]
path = third_party/jemalloc
url = https://github.com/jemalloc/jemalloc.git
[submodule "third_party/mimalloc"]
path = third_party/mimalloc
url = https://github.com/microsoft/mimalloc.git
[submodule "third_party/zstd"]
path = third_party/zstd
url = https://github.com/facebook/zstd.git
[submodule "third_party/liburing"]
path = third_party/liburing
url = https://github.com/axboe/liburing.git
[submodule "third_party/gtest-parallel"]
path = third_party/gtest-parallel
url = https://github.com/google/gtest-parallel.git

172
CMakeLists.txt Normal file
View File

@@ -0,0 +1,172 @@
cmake_minimum_required(VERSION 3.12)
project(3FS VERSION 0.1.5 LANGUAGES C CXX)
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Debug;Release;MinSizeRel" CACHE STRING "" FORCE)
if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "None")
set (CMAKE_BUILD_TYPE "RelWithDebInfo")
message (STATUS "CMAKE_BUILD_TYPE is not set, set to default = ${CMAKE_BUILD_TYPE}")
endif ()
message (STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
string (TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
if(CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG" )
option(ENABLE_ASSERTIONS "Enable assertions" ON)
else()
option(ENABLE_ASSERTIONS "Enable assertions" OFF)
endif()
message (STATUS "ENABLE_ASSERTIONS: ${ENABLE_ASSERTIONS}")
if(ENABLE_ASSERTIONS)
add_definitions(-D_DEBUG)
# On non-Debug builds cmake automatically defines NDEBUG, so we explicitly undefine it:
if(NOT CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG")
# NOTE: use `add_compile_options` rather than `add_definitions` since
# `add_definitions` does not support generator expressions.
add_compile_options($<$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>:-UNDEBUG>)
endif()
endif()
option(OVERRIDE_CXX_NEW_DELETE "Override C++ new/delete operator" OFF)
option(SAVE_ALLOCATE_SIZE "Use more memory to save allocate size" OFF)
option(ENABLE_FUSE_APPLICATION "" ON)
if (DEFINED SANITIZER AND SANITIZER)
set(OVERRIDE_CXX_NEW_DELETE OFF)
endif()
message (STATUS "OVERRIDE_CXX_NEW_DELETE: ${OVERRIDE_CXX_NEW_DELETE}")
if (OVERRIDE_CXX_NEW_DELETE)
add_definitions(-DOVERRIDE_CXX_NEW_DELETE)
if (SAVE_ALLOCATE_SIZE)
add_definitions(-DSAVE_ALLOCATE_SIZE)
endif()
endif()
message (STATUS "SAVE_ALLOCATE_SIZE: ${SAVE_ALLOCATE_SIZE}")
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED OFF)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic")
add_link_options(-fuse-ld=lld)
# Do not build with libc++ (LLVM's implementation of the C++ standard library) in fdb
set(USE_LIBCXX OFF)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
endif()
# Remove project root from the __FILE__ macro variable
add_compile_options(-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.)
add_compile_options(-msse4.2 -mavx2)
add_compile_definitions(ROCKSDB_NAMESPACE=rocksdb_internal)
include(cmake/Sanitizers.cmake)
include(cmake/CompileFlags.cmake)
# folly can't work normally under -Werror
store_compile_flags()
add_subdirectory("third_party/fmt" EXCLUDE_FROM_ALL)
restore_compile_flags()
set(ZSTD_BUILD_STATIC ON)
add_subdirectory("third_party/zstd/build/cmake" EXCLUDE_FROM_ALL)
set(zstd_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/third_party/zstd/lib")
set(ZSTD_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/third_party/zstd/lib")
set(zstd_FOUND ON)
set(ZSTD_FOUND ON)
set(zstd_LIBRARIES "${PROJECT_BINARY_DIR}/third_party/zstd/build/cmake/lib/libzstd.a")
set(ZSTD_LIBRARY "${PROJECT_BINARY_DIR}/third_party/zstd/build/cmake/lib/libzstd.a")
restore_compile_flags()
add_subdirectory("third_party/googletest" EXCLUDE_FROM_ALL)
restore_compile_flags()
set(FOLLY_NO_EXCEPTION_TRACER ON)
add_subdirectory("third_party/folly" EXCLUDE_FROM_ALL)
restore_compile_flags()
set(LEVELDB_BUILD_TESTS OFF CACHE BOOL "Disable LevelDB tests")
set(LEVELDB_BUILD_BENCHMARKS OFF CACHE BOOL "Disable LevelDB benchmarks")
set(LEVELDB_INSTALL OFF CACHE BOOL "Disable LevelDB install")
add_subdirectory("third_party/leveldb" EXCLUDE_FROM_ALL)
restore_compile_flags()
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(WITH_LZ4 ON)
set(WITH_ZSTD ON)
set(USE_RTTI ON)
set(WITH_TESTS OFF)
set(WITH_BENCHMARK_TOOLS OFF)
set(WITH_TOOLS OFF)
set(WITH_ALL_TESTS OFF)
add_subdirectory("third_party/rocksdb" EXCLUDE_FROM_ALL)
restore_compile_flags()
set(SCN_TESTS OFF)
set(SCN_EXAMPLES OFF)
set(SCN_BENCHMARKS OFF)
set(SCN_DOCS OFF)
set(SCN_INSTALL OFF)
set(SCN_PEDANTIC OFF)
add_subdirectory("third_party/scnlib" EXCLUDE_FROM_ALL)
restore_compile_flags()
add_subdirectory("third_party/pybind11" EXCLUDE_FROM_ALL)
restore_compile_flags()
add_subdirectory("third_party/toml11" EXCLUDE_FROM_ALL)
restore_compile_flags()
set (MI_OVERRIDE OFF)
add_subdirectory("third_party/mimalloc" EXCLUDE_FROM_ALL)
restore_compile_flags()
add_subdirectory("third_party/clickhouse-cpp" EXCLUDE_FROM_ALL)
TARGET_INCLUDE_DIRECTORIES(clickhouse-cpp-lib
PUBLIC ${PROJECT_SOURCE_DIR}/third_party/clickhouse-cpp
)
TARGET_INCLUDE_DIRECTORIES(clickhouse-cpp-lib-static
PUBLIC ${PROJECT_SOURCE_DIR}/third_party/clickhouse-cpp
)
TARGET_INCLUDE_DIRECTORIES (absl-lib
PUBLIC ${PROJECT_SOURCE_DIR}/third_party/clickhouse-cpp/contrib
)
restore_compile_flags()
add_subdirectory("third_party/liburing-cmake" EXCLUDE_FROM_ALL)
restore_compile_flags()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wpedantic")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}")
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 ${DEBUG_INFO_FLAGS} ${CMAKE_CXX_FLAGS_ADD}")
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${DEBUG_INFO_FLAGS} -fno-inline ${CMAKE_CXX_FLAGS_ADD}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS} ${CMAKE_C_FLAGS_ADD}")
set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O3 ${DEBUG_INFO_FLAGS} ${CMAKE_C_FLAGS_ADD}")
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 ${DEBUG_INFO_FLAGS} -fno-inline ${CMAKE_C_FLAGS_ADD}")
set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${COMPILER_FLAGS} ${CMAKE_ASM_FLAGS_ADD}")
set (CMAKE_ASM_FLAGS_RELWITHDEBINFO "${CMAKE_ASM_FLAGS_RELWITHDEBINFO} -O3 ${DEBUG_INFO_FLAGS} ${CMAKE_ASM_FLAGS_ADD}")
set (CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 ${DEBUG_INFO_FLAGS} -fno-inline ${CMAKE_ASM_FLAGS_ADD}")
set(Boost_USE_STATIC_LIBS ON)
set(FDB_VERSION 7.1.5-ibe)
find_package(Threads REQUIRED)
find_package(Boost REQUIRED COMPONENTS filesystem system program_options)
find_library(LIBUV_LIBRARY NAMES libuv1)
enable_testing()
include(cmake/CodeCoverage.cmake)
include(cmake/CLangFormat.cmake)
include(cmake/CLangTidy.cmake)
include(cmake/Target.cmake)
include(cmake/DumpConfig.cmake)
include(cmake/Jemalloc.cmake)
include(cmake/ApacheArrow.cmake)
include(cmake/AddCrate.cmake)
configure_file(cmake/CTestCustom.cmake ${CMAKE_BINARY_DIR} @ONLY)
add_subdirectory(src)
add_subdirectory(tests)
add_subdirectory(benchmarks)

1968
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[workspace]
members = [
"src/client/trash_cleaner",
"src/storage/chunk_engine"
]
resolver = "2"
[workspace.package]
authors = ["dev <noreply@deepseek.com>"]
edition = "2021"
license = "MIT"
[profile.release-cmake]
debug = true
inherits = "release"
lto = true

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 DeepSeek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

101
README.md Normal file
View File

@@ -0,0 +1,101 @@
# Fire-Flyer File System
[![Build](https://github.com/deepseek-ai/3fs/actions/workflows/build.yml/badge.svg)](https://github.com/deepseek-ai/3fs/actions/workflows/build.yml)
[![License](https://img.shields.io/badge/LICENSE-MIT-blue.svg)](LICENSE)
The Fire-Flyer File System (3FS) is a high-performance distributed file system designed to address the challenges of AI training and inference workloads. It leverages modern SSDs and RDMA networks to provide a shared storage layer that simplifies development of distributed applications. Key features and benefits of 3FS include:
- Performance and Usability
- **Disaggregated Architecture** Combines the throughput of thousands of SSDs and the network bandwidth of hundreds of storage nodes, enabling applications to access storage resource in a locality-oblivious manner.
- **Strong Consistency** Implements Chain Replication with Apportioned Queries (CRAQ) for strong consistency, making application code simple and easy to reason about.
- **File Interfaces** Develops stateless metadata services backed by a transactional key-value store (e.g., FoundationDB). The file interface is well known and used everywhere. There is no need to learn a new storage API.
- Diverse Workloads
- **Data Preparation** Organizes outputs of data analytics pipelines into hierarchical directory structures and manages large volume of intermediate outputs efficiently.
- **Dataloaders** Eliminates the need for prefetching or shuffling datasets by enabling random access to training samples across compute nodes.
- **Checkpointing** Supports high-throughput parallel checkpointing for large-scale training.
- **KVCache for Inference** Provides a cost-effective alternative to DRAM-based caching, offering high throughput and significantly larger capacity.
## Documentation
* [Design Notes](docs/design_notes.md)
* [Setup Guide](deploy/README.md)
* [USRBIO API Reference](src/lib/api/UsrbIo.md)
* [P Specifications](./specs/README.md)
## Performance
### 1. Peak throughput
The following figure demonstrates the throughput of read stress test on a large 3FS cluster. This cluster consists of 180 storage nodes, each equipped with 2×200Gbps InfiniBand NICs and sixteen 14TiB NVMe SSDs. Approximately 500+ client nodes were used for the read stress test, with each client node configured with 1x200Gbps InfiniBand NIC. The final aggregate read throughput reached approximately 6.6 TiB/s with background traffic from training jobs.
![Large block read throughput under stress test on a 180-node cluster](docs/images/peak_throughput.jpg)
### 2. GraySort
We evaluated [smallpond](https://github.com/deepseek-ai/smallpond) using the GraySort benchmark, which measures sort performance on large-scale datasets. Our implementation adopts a two-phase approach: (1) partitioning data via shuffle using the prefix bits of keys, and (2) in-partition sorting. Both phases read/write data from/to 3FS.
The test cluster comprised 25 storage nodes (2 NUMA domains/node, 1 storage service/NUMA, 2×400Gbps NICs/node) and 50 compute nodes (2 NUMA domains, 192 physical cores, 2.2 TiB RAM, and 1×200 Gbps NIC/node). Sorting 110.5 TiB of data across 8,192 partitions completed in 30 minutes and 14 seconds, achieving an average throughput of *3.66 TiB/min*.
![](docs/images/gray_sort_server.png)
![](docs/images/gray_sort_client.png)
### 3. KVCache
KVCache is a technique used to optimize the LLM inference process. It avoids redundant computations by caching the key and value vectors of previous tokens in the decoder layers.
The top figure demonstrates the read throughput of all KVCache clients, highlighting both peak and average values, with peak throughput reaching up to 40 GiB/s. The bottom figure presents the IOPS of remove ops from garbage collection (GC) during the same time period.
![KVCache Read Throughput](./docs/images/kvcache_read_throughput.png)
![KVCache GC IOPS](./docs/images/kvcache_gc_iops.png)
## Check out source code
Clone 3FS repository from github:
git clone https://github.com/deepseek-ai/3fs
When `deepseek-ai/3fs` has been cloned to local file system, run the
following commands to check out the submodules:
```bash
cd 3fs
git submodule update --init --recursive
./patches/apply.sh
```
## Install dependencies
Install dependencies:
```bash
# for Ubuntu 20.04.
apt install cmake libuv1-dev liblz4-dev liblzma-dev libdouble-conversion-dev libprocps-dev libdwarf-dev libunwind-dev \
libaio-dev libgflags-dev libgoogle-glog-dev libgtest-dev libgmock-dev clang-format-14 clang-14 clang-tidy-14 lld-14 \
libgoogle-perftools-dev google-perftools libssl-dev ccache libclang-rt-14-dev gcc-10 g++-10 libboost1.71-all-dev
# for Ubuntu 22.04.
apt install cmake libuv1-dev liblz4-dev liblzma-dev libdouble-conversion-dev libprocps-dev libdwarf-dev libunwind-dev \
libaio-dev libgflags-dev libgoogle-glog-dev libgtest-dev libgmock-dev clang-format-14 clang-14 clang-tidy-14 lld-14 \
libgoogle-perftools-dev google-perftools libssl-dev ccache gcc-12 g++-12 libboost-all-dev
```
Install other build prerequisites:
- [`libfuse`](https://github.com/libfuse/libfuse/releases/tag/fuse-3.16.1) 3.16.1 or newer version
- [FoundationDB](https://apple.github.io/foundationdb/getting-started-linux.html) 7.1 or newer version
- [Rust](https://www.rust-lang.org/tools/install) toolchain
## Build 3FS
Build 3FS in `build` folder:
cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build -j 32
## Run a test cluster
Follow instructions in [setup guide](deploy/README.md) to run a test cluster.
## Report Issues
Please visit https://github.com/deepseek-ai/3fs/issues to report issues.

View File

@@ -0,0 +1 @@
add_subdirectory(storage_bench)

View File

@@ -0,0 +1 @@
target_add_bin(storage_bench "StorageBench.cc" test-fabric-lib storage-client storage memory-common follybenchmark gmock fdb mgmtd)

View File

@@ -0,0 +1,291 @@
#include "StorageBench.h"
#include <folly/init/Init.h>
#include "common/monitor/Monitor.h"
#include "memory/common/OverrideCppNewDelete.h"
DEFINE_bool(benchmarkNetwork, false, "Run in network benchmark mode");
DEFINE_bool(benchmarkStorage, false, "Run in storage benchmark mode");
DEFINE_bool(ignoreIOError, false, "Ignore all IO errors");
DEFINE_bool(injectRandomServerError, false, "Inject random server errors");
DEFINE_bool(injectRandomClientError, false, "Inject random client errors");
DEFINE_bool(retryPermanentError, false, "Retry requests with permanent errors");
DEFINE_bool(verifyReadData, false, "Check if the read data are correct");
DEFINE_bool(verifyReadChecksum, false, "Verify the checksum of read IOs");
DEFINE_bool(verifyWriteChecksum, true, "Verify the checksum of write IOs");
DEFINE_bool(randomShuffleChunkIds, false, "Random shuffle generated chunk IDs");
DEFINE_bool(generateTestData, true, "Generate test data for read test");
DEFINE_bool(sparseChunkIds, false, "Generate sparse chunk IDs");
DEFINE_bool(truncateChunks, false, "Truncate chunks");
DEFINE_bool(cleanupChunks, false, "Clean up (remove) chunks after benchmark");
DEFINE_bool(cleanupChunksBeforeBench, false, "Clean up (remove) chunks before benchmark");
DEFINE_bool(serverMode, false, "Run in server mode");
DEFINE_bool(clientMode, false, "Run in client mode");
DEFINE_bool(clusterMode, false, "Run in cluster mode (get routing info from mgmtd)");
DEFINE_bool(printMetrics, false, "Enable printing metrics in logs");
DEFINE_bool(reportMetrics, false, "Enable reporting metrics to ClickHouse");
DEFINE_uint32(metaStoreType, 0, "Metadata store type (0=LevelDB, 1=RocksDB, 2=MemDB)");
DEFINE_uint32(chunkSizeKB, 512, "Chunk size (KB)");
DEFINE_uint32(chainTableId, 0, "Chain table id");
DEFINE_uint32(chainTableVersion, 0, "Chain table version");
DEFINE_string(chainIds, "", "List of chain ids");
DEFINE_string(storageNodeIds, "", "List of storage node ids");
DEFINE_uint32(numChains, 1, "Number of chains");
DEFINE_uint32(numReplicas, 1, "Number of replicas");
DEFINE_uint32(numStorageNodes, 1, "Number of storage nodes");
DEFINE_uint32(numChunks, 1, "Number of chunks");
DEFINE_uint32(readSize, 4096, "Read IO size");
DEFINE_uint32(writeSize, 131072, "Write IO size");
DEFINE_uint32(memoryAlignment, 1, "Alignment size of each IO buffer");
DEFINE_uint32(readOffAlignment, 0, "Alignment size of each read IO offset");
DEFINE_uint32(batchSize, 1, "Read/write batch size");
DEFINE_uint32(readBatchSize, 0, "Read batch size");
DEFINE_uint32(writeBatchSize, 0, "Write batch size");
DEFINE_uint32(removeBatchSize, 0, "Remove batch size");
DEFINE_uint32(numReadSecs, 0, "Read test duration");
DEFINE_uint32(numWriteSecs, 0, "Write test duration");
DEFINE_uint32(numCoroutines, 1, "Number of coroutines");
DEFINE_uint32(numTestThreads, 1, "Number of test threads");
DEFINE_uint32(randSeed, 0, "Random seed for chunk id generation");
DEFINE_uint32(chunkIdPrefix, 0xFFFF, "The most significant 2 bytes of chunk ids");
DEFINE_uint32(serviceLevel, 0, "Service level");
DEFINE_uint32(listenPort, 0, "Listen port");
DEFINE_uint32(clientTimeoutMS, 30000, "Client timeout (milliseconds)");
DEFINE_string(dataPaths, folly::fs::temp_directory_path().string(), "Comma or space separated list of paths");
DEFINE_string(clientConfig, "", "Path of client config");
DEFINE_string(serverConfig, "", "Path of server config");
DEFINE_string(statsFilePath, "./perfstats.csv", "Path of performance stats file");
DEFINE_string(ibvDevices, "mlx5_0,mlx5_1", "Comma or space separated list of ibv devices");
DEFINE_string(ibnetZones, "", "Comma or space separated list of IB network zones");
DEFINE_string(clusterId, "stage", "Cluster id used to connect to mgmtd");
DEFINE_string(mgmtdEndpoints,
"",
"Comma or space separated list of mgmtd endpoints, "
"e.g. 'RDMA://10.1.1.1:1234,RDMA://10.1.1.2:1234'");
DEFINE_string(storageEndpoints,
"",
"Comma or space separated list of storage ids and endpoints, "
"e.g. '1@RDMA://10.1.1.1:1234,2@RDMA://10.1.1.2:1234'");
DEFINE_string(monitorEndpoint, "", "Monitor endpoint");
DEFINE_uint32(defaultPKeyIndex, 1, "IB default pkey index");
namespace hf3fs::storage::benchmark {
using namespace hf3fs::storage::client;
std::vector<uint32_t> stringToIntVec(const std::string &str) {
std::vector<uint32_t> vec;
std::vector<std::string> substrs;
boost::split(substrs, str, boost::is_any_of(", "));
for (auto s : substrs) {
boost::trim(s);
if (s.empty()) continue;
uint32_t n = std::stoul(s);
vec.push_back(n);
}
return vec;
}
bool runBenchmarks() {
std::vector<std::string> dataPathStrs;
boost::split(dataPathStrs, FLAGS_dataPaths, boost::is_any_of(", "));
std::vector<hf3fs::Path> dataPaths;
dataPaths.reserve(dataPathStrs.size());
for (auto str : dataPathStrs) {
boost::trim(str);
if (str.empty()) continue;
dataPaths.emplace_back(str);
}
std::vector<std::string> endpointRawStrs;
boost::split(endpointRawStrs, FLAGS_storageEndpoints, boost::is_any_of(", "));
std::map<NodeId, net::Address> storageEndpoints;
for (auto str : endpointRawStrs) {
boost::trim(str);
if (str.empty()) continue;
std::vector<std::string> nodeEndpointStrs;
boost::split(nodeEndpointStrs, str, boost::is_any_of("@"));
if (nodeEndpointStrs.size() != 2) {
XLOGF(ERR, "Wrong node endpoint string: {}", str);
return false;
}
auto nodeIdStr = nodeEndpointStrs[0];
auto endpointStr = nodeEndpointStrs[1];
NodeId nodeId{std::stoul(nodeIdStr)};
auto endpoint = net::Address::fromString(endpointStr);
storageEndpoints[nodeId] = endpoint;
XLOGF(WARN, "Add storage endpoint: {} @ {}", nodeId, endpoint);
}
if (FLAGS_clientMode && storageEndpoints.empty()) {
XLOGF(ERR, "No storage endpoint specified for client mode");
return false;
}
if (FLAGS_readSize > FLAGS_chunkSizeKB * 1024) {
XLOGF(ERR, "Read size {} is greater than chunk size {}", FLAGS_readSize, FLAGS_chunkSizeKB * 1024);
return false;
}
auto metaStoreType = static_cast<kv::KVStore::Type>(FLAGS_metaStoreType);
test::SystemSetupConfig setupConfig = {
FLAGS_chunkSizeKB * 1024 /*chunkSize*/,
FLAGS_numChains /*numChains*/,
FLAGS_numReplicas /*numReplicas*/,
FLAGS_numStorageNodes /*numStorageNodes*/,
dataPaths /*dataPaths*/,
FLAGS_clientConfig,
FLAGS_serverConfig,
storageEndpoints,
FLAGS_serviceLevel,
FLAGS_listenPort,
StorageClient::ImplementationType::RPC /*clientImplType*/,
metaStoreType,
true /*useFakeMgmtdClient*/,
!FLAGS_clientMode /*startStorageServer*/,
false,
};
std::vector<std::string> ibvDevices;
boost::split(ibvDevices, FLAGS_ibvDevices, boost::is_any_of(", "));
std::vector<std::string> ibnetZones;
boost::split(ibnetZones, FLAGS_ibnetZones, boost::is_any_of(", "));
endpointRawStrs.clear();
boost::split(endpointRawStrs, FLAGS_mgmtdEndpoints, boost::is_any_of(", "));
std::vector<net::Address> mgmtdEndpoints;
for (auto str : endpointRawStrs) {
boost::trim(str);
if (str.empty()) continue;
auto endpoint = net::Address::fromString(str);
mgmtdEndpoints.push_back(endpoint);
XLOGF(WARN, "Add mgmtd endpoint: {}", endpoint);
}
StorageBench::Options benchOptions{FLAGS_numChunks,
FLAGS_readSize,
FLAGS_writeSize,
FLAGS_batchSize,
FLAGS_numReadSecs,
FLAGS_numWriteSecs,
FLAGS_clientTimeoutMS,
FLAGS_numCoroutines,
FLAGS_numTestThreads,
FLAGS_randSeed,
(uint16_t)FLAGS_chunkIdPrefix,
FLAGS_benchmarkNetwork,
FLAGS_benchmarkStorage,
FLAGS_ignoreIOError,
FLAGS_injectRandomServerError,
FLAGS_injectRandomClientError,
FLAGS_retryPermanentError,
FLAGS_verifyReadData,
FLAGS_verifyReadChecksum,
FLAGS_verifyWriteChecksum,
FLAGS_randomShuffleChunkIds,
FLAGS_generateTestData,
FLAGS_sparseChunkIds,
FLAGS_statsFilePath,
ibvDevices,
ibnetZones,
mgmtdEndpoints,
FLAGS_clusterId,
FLAGS_chainTableId,
FLAGS_chainTableVersion,
stringToIntVec(FLAGS_chainIds),
stringToIntVec(FLAGS_storageNodeIds),
FLAGS_memoryAlignment,
FLAGS_readOffAlignment,
FLAGS_defaultPKeyIndex,
FLAGS_readBatchSize,
FLAGS_writeBatchSize,
FLAGS_removeBatchSize};
StorageBench bench(setupConfig, benchOptions);
if (FLAGS_clusterMode) {
if (!bench.connect()) {
XLOGF(WARN, "Failed to connect to cluster");
return false;
}
} else {
if (!bench.setup()) {
XLOGF(WARN, "Failed to set up benchmark");
return false;
}
}
bench.generateChunkIds();
if (FLAGS_cleanupChunksBeforeBench) {
bench.cleanup();
}
bool runOK = true;
if (FLAGS_serverMode) {
XLOGF(WARN, "Waiting...");
while (true) {
::sleep(1);
}
} else {
runOK = bench.run();
}
if (FLAGS_truncateChunks) {
bench.truncate();
}
if (FLAGS_cleanupChunks) {
bench.cleanup();
}
bench.teardown();
return runOK;
}
} // namespace hf3fs::storage::benchmark
int main(int argc, char **argv) {
folly::init(&argc, &argv, true);
hf3fs::monitor::Monitor::Config monitorConfig;
if (FLAGS_printMetrics || FLAGS_reportMetrics) {
if (FLAGS_printMetrics) {
monitorConfig.reporters(0).set_type("log");
} else if (FLAGS_reportMetrics) {
monitorConfig.reporters(0).set_type("monitor_collector");
monitorConfig.reporters(0).monitor_collector().set_remote_ip(FLAGS_monitorEndpoint);
monitorConfig.set_reporters_length(1);
}
auto monitorResult = hf3fs::monitor::Monitor::start(monitorConfig);
XLOGF_IF(FATAL, !monitorResult, "Failed to start monitor: {}", monitorResult.error());
}
bool ok = hf3fs::storage::benchmark::runBenchmarks();
hf3fs::monitor::Monitor::stop();
hf3fs::memory::shutdown();
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -0,0 +1,895 @@
#pragma once
#include <boost/algorithm/string.hpp>
#include <boost/core/ignore_unused.hpp>
#include <common/utils/UtcTime.h>
#include <folly/experimental/coro/Collect.h>
#include <folly/futures/Barrier.h>
#include <folly/stats/TDigest.h>
#include <numeric>
#include <optional>
#include <random>
#include <vector>
#include "common/logging/LogInit.h"
#include "common/net/ib/IBDevice.h"
#include "common/utils/Duration.h"
#include "common/utils/SysResource.h"
#include "tests/lib/UnitTestFabric.h"
namespace hf3fs::storage::benchmark {
using namespace hf3fs::storage::client;
class StorageBench : public test::UnitTestFabric {
public:
struct Options {
const size_t numChunks;
const size_t readSize;
const size_t writeSize;
const size_t batchSize;
const uint64_t numReadSecs;
const uint64_t numWriteSecs;
const uint64_t clientTimeoutMS;
const size_t numCoroutines;
const size_t numTestThreads;
const uint32_t randSeed = 0;
const uint16_t chunkIdPrefix = 0xFFFF;
const bool benchmarkNetwork = false;
const bool benchmarkStorage = false;
const bool ignoreIOError = false;
const bool injectRandomServerError = false;
const bool injectRandomClientError = false;
const bool retryPermanentError = false;
const bool verifyReadData = false;
const bool verifyReadChecksum = false;
const bool verifyWriteChecksum = true;
const bool randomShuffleChunkIds = true;
const bool generateTestData = true;
const bool sparseChunkIds = true;
const std::string statsFilePath = "./perfstats.csv";
const std::vector<std::string> ibvDevices = {};
const std::vector<std::string> ibnetZones = {};
const std::vector<net::Address> mgmtdEndpoints = {};
const std::string clusterId = kClusterId;
const uint32_t chainTableId = 0;
const uint32_t chainTableVersion = 0;
const std::vector<uint32_t> chainIds = {};
const std::vector<uint32_t> storageNodeIds = {};
const size_t memoryAlignment = 1;
const size_t readOffAlignment = 0;
const size_t defaultPKeyIndex = 1;
size_t readBatchSize = 0;
size_t writeBatchSize = 0;
size_t removeBatchSize = 0;
};
private:
static constexpr uint32_t kTDigestMaxSize = 1000;
struct ChunkInfo {
ChainId chainId;
ChunkId chunkId;
size_t size;
};
Options benchOptions_;
std::vector<folly::TDigest> writeLatencyDigests_;
std::vector<folly::TDigest> readLatencyDigests_;
folly::CPUThreadPoolExecutor testExecutor_;
std::atomic_uint64_t numWriteBytes_;
std::atomic_uint64_t numReadBytes_;
folly::Random::DefaultGenerator randGen_;
std::vector<std::vector<ChunkInfo>> chunkInfos_;
std::vector<size_t> numCreatedChunks_;
size_t totalNumChunks_;
double totalChunkGiB_;
public:
StorageBench(const test::SystemSetupConfig &setupConfig, const Options &options)
: UnitTestFabric(setupConfig),
benchOptions_(options),
writeLatencyDigests_(benchOptions_.numCoroutines, folly::TDigest(kTDigestMaxSize)),
readLatencyDigests_(benchOptions_.numCoroutines, folly::TDigest(kTDigestMaxSize)),
testExecutor_(benchOptions_.numTestThreads),
numWriteBytes_(0),
numReadBytes_(0),
randGen_(folly::Random::create()),
chunkInfos_(benchOptions_.numCoroutines),
numCreatedChunks_(benchOptions_.numCoroutines) {
if (benchOptions_.readBatchSize == 0) benchOptions_.readBatchSize = benchOptions_.batchSize;
if (benchOptions_.writeBatchSize == 0) benchOptions_.writeBatchSize = benchOptions_.batchSize;
if (benchOptions_.removeBatchSize == 0) benchOptions_.removeBatchSize = benchOptions_.batchSize;
}
void generateChunkIds() {
static_assert(sizeof(benchOptions_.chunkIdPrefix) == 2);
uint64_t chunkIdPrefix64 = ((uint64_t)benchOptions_.chunkIdPrefix) << (UINT64_WIDTH - UINT16_WIDTH);
std::sort(chainIds_.begin(), chainIds_.end());
static thread_local std::mt19937 generator;
randGen_.seed(benchOptions_.randSeed);
XLOGF(WARN,
"Generating {} chunk ids with prefix {:08X} and random seed {}...",
totalNumChunks_,
chunkIdPrefix64,
benchOptions_.randSeed);
for (auto &chunkInfos : chunkInfos_) {
uint64_t instancePrefix = chunkIdPrefix64 | folly::Random::rand64(randGen_);
XLOGF(DBG3, "Random chunk id prefix {:08X}", instancePrefix);
chunkInfos.reserve(chainIds_.size() * benchOptions_.numChunks);
for (auto chainId : chainIds_) {
for (size_t chunkIndex = 0; chunkIndex < benchOptions_.numChunks; chunkIndex++) {
if (benchOptions_.sparseChunkIds) {
uint64_t chunkIdHigh = chunkIdPrefix64 | (folly::Random::rand64(randGen_) & 0x000000FFFFFFFFFF);
uint64_t chunkIdLow = (folly::Random::rand64(randGen_) << UINT32_WIDTH) + chunkIndex;
chunkInfos.push_back({chainId, ChunkId(chunkIdHigh, chunkIdLow), 0});
} else {
chunkInfos.push_back({chainId, ChunkId(instancePrefix, chunkIndex), 0});
}
}
}
if (benchOptions_.randomShuffleChunkIds) std::shuffle(chunkInfos.begin(), chunkInfos.end(), generator);
}
}
bool connect() {
XLOGF(INFO, "Start to connect...");
if (!setupIBSock()) {
return false;
}
mgmtdClientConfig_.set_mgmtd_server_addresses(benchOptions_.mgmtdEndpoints);
mgmtdClientConfig_.set_enable_auto_refresh(true);
mgmtdClientConfig_.set_enable_auto_heartbeat(false);
mgmtdClientConfig_.set_enable_auto_extend_client_session(true);
mgmtdClientConfig_.set_auto_refresh_interval(3_s);
mgmtdClientConfig_.set_auto_heartbeat_interval(3_s);
mgmtdClientConfig_.set_auto_extend_client_session_interval(3_s);
mgmtdClientConfig_.set_accept_incomplete_routing_info_during_mgmtd_bootstrapping(false);
if (!client_.start()) {
XLOGF(ERR, "Failed to start net client for mgmtd client");
return false;
}
XLOGF(INFO, "Creating mgmtd client...");
auto stubFactory = std::make_unique<hf3fs::stubs::RealStubFactory<hf3fs::mgmtd::MgmtdServiceStub>>(
stubs::ClientContextCreator{[this](net::Address addr) { return client_.serdeCtx(addr); }});
auto mgmtdClient = std::make_unique<hf3fs::client::MgmtdClientForClient>(benchOptions_.clusterId,
std::move(stubFactory),
mgmtdClientConfig_);
auto physicalHostnameRes = SysResource::hostname(/*physicalMachineName=*/true);
if (!physicalHostnameRes) {
XLOGF(ERR, "getHostname(true) failed: {}", physicalHostnameRes.error());
return false;
}
auto containerHostnameRes = SysResource::hostname(/*physicalMachineName=*/false);
if (!containerHostnameRes) {
XLOGF(ERR, "getHostname(false) failed: {}", containerHostnameRes.error());
return false;
}
mgmtdClient->setClientSessionPayload({clientId_.uuid.toHexString(),
flat::NodeType::CLIENT,
flat::ClientSessionData::create(
/*universalId=*/*physicalHostnameRes,
/*description=*/fmt::format("StorageBench: {}", *containerHostnameRes),
/*serviceGroups=*/std::vector<flat::ServiceGroupInfo>{},
flat::ReleaseVersion::fromVersionInfo()),
flat::UserInfo{}});
folly::coro::blockingWait(mgmtdClient->start(&client_.tpg().bgThreadPool().randomPick()));
mgmtdForClient_ = std::move(mgmtdClient);
// get routing info
for (size_t retry = 0; retry < 15; retry++) {
auto routingInfo = mgmtdForClient_->getRoutingInfo();
if (routingInfo == nullptr || routingInfo->raw()->chains.empty()) {
XLOGF(WARN, "Empty routing info, #{} retry...", retry + 1);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
} else {
for (const auto &[tableId, tableVersions] : routingInfo->raw()->chainTables) {
if (tableId == benchOptions_.chainTableId) {
if (tableVersions.empty()) {
XLOGF(WARN, "No version found for chain table with id {}", tableId);
return false;
}
XLOGF(INFO, "Found {} version(s) of chain table {}", tableVersions.size(), benchOptions_.chainTableId);
flat::ChainTable chainTable;
if (benchOptions_.chainTableVersion > 0) {
flat::ChainTableVersion tableVersion(benchOptions_.chainTableVersion);
auto tableIter = tableVersions.find(tableVersion);
if (tableIter == tableVersions.end()) {
XLOGF(WARN, "Version {} not found in chain table with id {}", tableVersion, tableId);
return false;
}
chainTable = tableIter->second;
XLOGF(INFO,
"Found version {} of chain table {}: {}",
benchOptions_.chainTableVersion,
benchOptions_.chainTableId,
chainTable.chainTableVersion);
} else {
const auto iter = --tableVersions.cend();
const auto &latestTable = iter->second;
chainTable = latestTable;
XLOGF(INFO,
"Found latest version of chain table {}: {}",
benchOptions_.chainTableId,
chainTable.chainTableVersion);
}
XLOGF(WARN,
"Selected chain table: {}@{} [{}] {} chains",
chainTable.chainTableId,
chainTable.chainTableVersion,
chainTable.desc,
chainTable.chains.size());
if (!benchOptions_.storageNodeIds.empty()) {
for (const auto &chainId : chainTable.chains) {
const auto chainInfo = routingInfo->raw()->getChain(chainId);
for (const auto &target : chainInfo->targets) {
const auto targetInfo = routingInfo->raw()->getTarget(target.targetId);
auto nodeIter = std::find(benchOptions_.storageNodeIds.begin(),
benchOptions_.storageNodeIds.end(),
*targetInfo->nodeId);
if (nodeIter != benchOptions_.storageNodeIds.end()) {
chainIds_.push_back(chainId);
break;
}
}
}
} else if (!benchOptions_.chainIds.empty()) {
for (const auto &chainId : chainTable.chains) {
auto chainIter = std::find(benchOptions_.chainIds.begin(), benchOptions_.chainIds.end(), chainId);
if (chainIter != benchOptions_.chainIds.end()) {
chainIds_.push_back(chainId);
}
}
} else {
chainIds_ = chainTable.chains;
}
break;
}
}
if (!chainIds_.empty()) break;
}
}
if (chainIds_.empty()) {
XLOGF(ERR, "Failed to get chain table with id {}", benchOptions_.chainTableId);
return false;
} else {
XLOGF(WARN, "Selected {} replication chains for benchmark", chainIds_.size());
}
// create storage client
if (setupConfig_.client_config().empty()) {
XLOGF(ERR, "Storage client config not specified");
return false;
}
auto configRes = clientConfig_.atomicallyUpdate(setupConfig_.client_config(), false /*isHotUpdate*/);
if (!configRes) {
XLOGF(ERR, "Cannot load client config from {}, error: {}", setupConfig_.client_config(), configRes.error());
return false;
}
totalNumChunks_ = chainIds_.size() * benchOptions_.numCoroutines * benchOptions_.numChunks;
totalChunkGiB_ = (double)totalNumChunks_ * setupConfig_.chunk_size() / 1_GB;
clientConfig_.retry().set_max_retry_time(Duration(std::chrono::milliseconds(benchOptions_.clientTimeoutMS)));
clientConfig_.net_client().io_worker().ibsocket().set_sl(setupConfig_.service_level());
XLOGF(INFO, "Creating storage client...");
storageClient_ = client::StorageClient::create(clientId_, clientConfig_, *mgmtdForClient_);
return true;
}
bool setupIBSock() {
XLOGF(WARN, "Setting up IB socket...");
std::vector<net::IBConfig::Subnet> subnets;
for (const auto &ibnetZoneStr : benchOptions_.ibnetZones) {
std::vector<std::string> ibnetZoneSubnet;
boost::split(ibnetZoneSubnet, ibnetZoneStr, boost::is_any_of(":"));
if (ibnetZoneSubnet.size() != 2) {
XLOGF(CRITICAL, "Invalid IB zone subnet: {}", ibnetZoneStr);
return false;
}
auto zone = boost::trim_copy(ibnetZoneSubnet[0]);
auto subnet = boost::trim_copy(ibnetZoneSubnet[1]);
if (zone.empty() || subnet.empty()) {
XLOGF(CRITICAL, "Invalid IB zone subnet: {}", ibnetZoneStr);
return false;
}
subnets.emplace_back();
subnets.back().set_network_zones({zone});
subnets.back().set_subnet(*net::IBConfig::Network::from(subnet));
XLOGF(WARN, "Add IB network zone: {} -- {}", zone, subnet);
}
net::IBConfig ibConfig;
ibConfig.set_subnets(subnets);
ibConfig.set_allow_unknown_zone(false);
ibConfig.set_default_network_zone("$HF3FS_NETWORK_ZONE");
ibConfig.set_device_filter(benchOptions_.ibvDevices);
ibConfig.set_default_pkey_index(benchOptions_.defaultPKeyIndex);
auto ibResult = net::IBManager::start(ibConfig);
if (ibResult.hasError()) {
XLOGF(CRITICAL, "Cannot initialize IB device: {}", ibResult.error());
return false;
}
return true;
}
bool setup() {
XLOGF(WARN, "Setting up benchmark...");
if (!setupIBSock()) {
return false;
}
bool ok = setUpStorageSystem();
totalNumChunks_ = chainIds_.size() * benchOptions_.numCoroutines * benchOptions_.numChunks;
totalChunkGiB_ = (double)totalNumChunks_ * setupConfig_.chunk_size() / 1_GB;
clientConfig_.retry().set_max_retry_time(Duration(std::chrono::milliseconds(benchOptions_.clientTimeoutMS)));
return ok;
}
void teardown() {
tearDownStorageSystem();
net::IBManager::stop();
}
void printThroughput(hf3fs::SteadyClock::duration elapsedMicro, double totalGiB) {
auto elapsedMilli = std::chrono::duration_cast<std::chrono::milliseconds>(elapsedMicro);
double throughput = totalGiB / (elapsedMilli.count() / 1000.0);
XLOGF(WARN, "Average throughput: {:.3f}GiB/s, total {:.3f} GiB", throughput, totalGiB);
}
void printLatencyDigest(const folly::TDigest &digest) {
XLOGF(WARN, "latency summary ({} samples)", digest.count());
XLOGF(WARN, "min: {:10.1f}us", digest.min());
XLOGF(WARN, "max: {:10.1f}us", digest.max());
XLOGF(WARN, "avg: {:10.1f}us", digest.mean());
for (double p : {0.1, 0.2, 0.5, 0.9, 0.95, 0.99}) {
XLOGF(WARN, "{}%: {:10.1f}us", p * 100.0, digest.estimateQuantile(p));
}
}
void dumpPerfStats(const std::string &testName,
const folly::TDigest &digest,
hf3fs::SteadyClock::duration elapsedTime,
double totalGiB,
bool readIO) {
if (benchOptions_.statsFilePath.empty()) return;
boost::filesystem::path outFilePath(benchOptions_.statsFilePath);
if (!boost::filesystem::exists(outFilePath) || boost::filesystem::is_empty(outFilePath)) {
XLOGF(INFO, "Create a file for perfermance stats at {}", outFilePath);
boost::filesystem::save_string_file(
outFilePath,
"test name,#storages,#chains,#replicas,concurrency,batch size,"
"io size (bytes),effective batch size (batch size / #replicas),elapsed time (us),"
"QPS,IOPS,bandwidth (MB/s),latency samples,min latency (us),max latency (us),avg latency (us),"
"latency P50 (us),latency P75 (us),latency P90 (us),latency P95 (us),latency P99 (us)\n");
}
auto elapsedMicro = std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime);
double bandwidthMBps = totalGiB * 1024.0 / (elapsedMicro.count() / 1'000'000.0);
size_t ioSize = readIO ? benchOptions_.readSize : benchOptions_.writeSize;
size_t batchSize = readIO ? benchOptions_.readBatchSize : benchOptions_.writeBatchSize;
double iops = bandwidthMBps * 1024.0 * 1024.0 / ioSize;
double qps = bandwidthMBps * 1024.0 * 1024.0 / (batchSize * ioSize);
boost::filesystem::ofstream fout(outFilePath, std::ios_base::app);
fout << fmt::format("{},{},{},{},{},{},{},{:.1f},{},{:.1f},{:.1f},{:.3f},{},{:.1f},{:.1f},{:.1f}",
testName,
setupConfig_.num_storage_nodes(),
setupConfig_.num_chains(),
setupConfig_.num_replicas(),
benchOptions_.numCoroutines,
batchSize,
ioSize,
double(batchSize) / setupConfig_.num_storage_nodes(),
elapsedMicro.count(),
qps,
iops,
bandwidthMBps,
digest.count(),
digest.min(),
digest.max(),
digest.mean());
for (double p : {0.5, 0.75, 0.9, 0.95, 0.99}) {
fout << fmt::format(",{:.1f}", digest.estimateQuantile(p));
}
fout << "\n";
fout.close();
}
CoTask<uint32_t> batchWrite(uint32_t instanceId, size_t writeBatchSize, size_t writeSize, uint32_t numWriteSecs) {
// create an aligned memory block
size_t memoryBlockSize = ALIGN_UPPER(setupConfig_.chunk_size(), benchOptions_.memoryAlignment);
auto memoryBlock = (uint8_t *)folly::aligned_malloc(memoryBlockSize, sysconf(_SC_PAGESIZE));
auto deleter = [](uint8_t *ptr) { folly::aligned_free(ptr); };
std::unique_ptr<uint8_t, decltype(deleter)> memoryBlockPtr(memoryBlock, deleter);
std::memset(memoryBlock, 0xFF, memoryBlockSize);
if (benchOptions_.verifyReadData) {
for (size_t byteIndex = 0; byteIndex < memoryBlockSize; byteIndex++) {
memoryBlock[byteIndex] = byteIndex;
}
}
// register a block of memory
auto regRes = storageClient_->registerIOBuffer(memoryBlock, memoryBlockSize);
if (regRes.hasError()) {
co_return regRes.error().code();
}
// create write IOs
auto ioBuffer = std::move(*regRes);
WriteOptions options;
options.set_enableChecksum(benchOptions_.verifyWriteChecksum);
options.debug().set_bypass_disk_io(benchOptions_.benchmarkNetwork);
options.debug().set_bypass_rdma_xmit(benchOptions_.benchmarkStorage);
options.debug().set_inject_random_server_error(benchOptions_.injectRandomServerError);
options.debug().set_inject_random_client_error(benchOptions_.injectRandomClientError);
options.retry().set_retry_permanent_error(benchOptions_.retryPermanentError);
std::vector<double> elapsedMicroSecs;
uint64_t numWriteBytes = 0;
std::vector<WriteIO> writeIOs;
writeIOs.reserve(writeBatchSize);
auto benchStart = hf3fs::SteadyClock::now();
std::vector<ChunkInfo> &chunkInfos = chunkInfos_[instanceId];
size_t &numCreatedChunks = numCreatedChunks_[instanceId];
size_t seqChunkIndex = 0;
while (true) {
if (numWriteSecs) {
auto accumElapsedSecs =
std::chrono::duration_cast<std::chrono::seconds>(hf3fs::SteadyClock::now() - benchStart);
if (accumElapsedSecs >= std::chrono::seconds(numWriteSecs)) break;
} else {
if (numCreatedChunks >= chunkInfos.size()) break;
}
writeIOs.clear();
for (size_t writeIndex = 0; writeIndex < writeBatchSize; writeIndex++) {
auto &[chainId, chunkId, chunkSize] = chunkInfos[seqChunkIndex++ % chunkInfos.size()];
size_t writeOffset = 0;
size_t writeLength = 0;
if (chunkSize < setupConfig_.chunk_size()) {
writeOffset = chunkSize;
writeLength = std::min(writeSize, setupConfig_.chunk_size() - writeOffset);
chunkSize += writeLength;
numCreatedChunks += chunkSize == setupConfig_.chunk_size();
} else {
writeOffset = folly::Random::rand32(0, setupConfig_.chunk_size() - writeSize);
writeLength = writeSize;
}
auto writeIO = storageClient_->createWriteIO(chainId,
chunkId,
writeOffset,
writeLength,
setupConfig_.chunk_size(),
&memoryBlock[writeOffset],
&ioBuffer);
writeIOs.push_back(std::move(writeIO));
numWriteBytes += writeLength;
}
auto rpcStart = hf3fs::SteadyClock::now();
co_await storageClient_->batchWrite(writeIOs, flat::UserInfo(), options);
auto elapsedMicro = std::chrono::duration_cast<std::chrono::microseconds>(hf3fs::SteadyClock::now() - rpcStart);
elapsedMicroSecs.push_back(elapsedMicro.count());
if (!benchOptions_.ignoreIOError) {
for (const auto &writeIO : writeIOs) {
if (writeIO.result.lengthInfo.hasError()) {
XLOGF(ERR, "Error in write result: {}", writeIO.result);
co_return writeIO.result.lengthInfo.error().code();
}
if (writeIO.length != *writeIO.result.lengthInfo) {
XLOGF(ERR, "Unexpected write length: {} != {}", *writeIO.result.lengthInfo, writeIO.length);
co_return StorageClientCode::kRemoteIOError;
}
}
}
}
folly::TDigest digest;
writeLatencyDigests_[instanceId] = digest.merge(elapsedMicroSecs);
numWriteBytes_ += numWriteBytes;
co_return StatusCode::kOK;
}
CoTask<uint32_t> batchRead(uint32_t instanceId) {
// create an aligned memory block
size_t alignedBufSize = ALIGN_UPPER(std::max(size_t(1), benchOptions_.readSize), benchOptions_.memoryAlignment);
size_t memoryBlockSize = alignedBufSize * benchOptions_.readBatchSize;
auto memoryBlock = (uint8_t *)folly::aligned_malloc(memoryBlockSize, sysconf(_SC_PAGESIZE));
auto deleter = [](uint8_t *ptr) { folly::aligned_free(ptr); };
std::unique_ptr<uint8_t, decltype(deleter)> memoryBlockPtr(memoryBlock, deleter);
std::memset(memoryBlock, 0, memoryBlockSize);
// register a block of memory
auto regRes = storageClient_->registerIOBuffer(memoryBlock, memoryBlockSize);
if (regRes.hasError()) {
co_return regRes.error().code();
}
std::vector<uint8_t> expectedChunkData(setupConfig_.chunk_size());
if (benchOptions_.verifyReadData) {
for (size_t byteIndex = 0; byteIndex < expectedChunkData.size(); byteIndex++) {
expectedChunkData[byteIndex] = byteIndex;
}
}
// create read IOs
auto ioBuffer = std::move(*regRes);
ReadOptions options;
options.set_enableChecksum(benchOptions_.verifyReadChecksum);
options.debug().set_bypass_disk_io(benchOptions_.benchmarkNetwork);
options.debug().set_bypass_rdma_xmit(benchOptions_.benchmarkStorage);
options.debug().set_inject_random_server_error(benchOptions_.injectRandomServerError);
options.debug().set_inject_random_client_error(benchOptions_.injectRandomClientError);
options.retry().set_retry_permanent_error(benchOptions_.retryPermanentError);
std::vector<double> elapsedMicroSecs;
uint64_t numReadBytes = 0;
size_t offsetAlignment =
benchOptions_.readOffAlignment ? benchOptions_.readOffAlignment : std::max(size_t(1), benchOptions_.readSize);
std::vector<client::ReadIO> readIOs;
readIOs.reserve(benchOptions_.readBatchSize);
auto benchStart = hf3fs::SteadyClock::now();
std::vector<ChunkInfo> &chunkInfos = chunkInfos_[instanceId];
while (true) {
auto accumElapsedSecs = std::chrono::duration_cast<std::chrono::seconds>(hf3fs::SteadyClock::now() - benchStart);
if (accumElapsedSecs >= std::chrono::seconds(benchOptions_.numReadSecs)) break;
readIOs.clear();
for (size_t readIndex = 0; readIndex < benchOptions_.readBatchSize; readIndex++) {
uint64_t randChunkIndex = folly::Random::rand64(0, chunkInfos.size());
const auto &[chainId, chunkId, chunkSize] = chunkInfos[randChunkIndex];
uint32_t offset = folly::Random::rand32(0, setupConfig_.chunk_size() - benchOptions_.readSize);
uint32_t alignedOffset = ALIGN_LOWER(offset, offsetAlignment);
auto readIO = storageClient_->createReadIO(chainId,
chunkId,
alignedOffset /*offset*/,
benchOptions_.readSize /*length*/,
&memoryBlock[readIndex * alignedBufSize],
&ioBuffer);
readIOs.push_back(std::move(readIO));
numReadBytes += benchOptions_.readSize;
}
auto rpcStart = hf3fs::SteadyClock::now();
co_await storageClient_->batchRead(readIOs, flat::UserInfo(), options);
auto elapsedMicro = std::chrono::duration_cast<std::chrono::microseconds>(hf3fs::SteadyClock::now() - rpcStart);
elapsedMicroSecs.push_back(elapsedMicro.count());
if (!benchOptions_.ignoreIOError) {
for (const auto &readIO : readIOs) {
if (readIO.result.lengthInfo.hasError()) {
XLOGF(ERR, "Error in read result: {}", readIO.result);
co_return readIO.result.lengthInfo.error().code();
}
if (readIO.length != *readIO.result.lengthInfo) {
XLOGF(ERR, "Unexpected read length: {} != {}", *readIO.result.lengthInfo, readIO.length);
co_return StorageClientCode::kRemoteIOError;
}
}
}
if (benchOptions_.verifyReadData) {
for (const auto &readIO : readIOs) {
auto diffPos = std::mismatch(&readIO.data[0], &readIO.data[readIO.length], &expectedChunkData[readIO.offset]);
uint32_t byteIndex = diffPos.first - &readIO.data[0];
if (byteIndex < readIO.length) {
XLOGF(ERR,
"Wrong data at bytes index {} and chunk offset {}: data {:#x} != expected {:#x}",
byteIndex,
readIO.offset + byteIndex,
*diffPos.first,
*diffPos.second);
co_return StorageClientCode::kFoundBug;
}
}
}
}
folly::TDigest digest;
readLatencyDigests_[instanceId] = digest.merge(elapsedMicroSecs);
numReadBytes_ += numReadBytes;
co_return StatusCode::kOK;
}
uint32_t generateChunks() {
XLOGF(WARN, "Generating {} test chunks ({:.3f} GiB)...", totalNumChunks_, totalChunkGiB_);
auto testStart = hf3fs::SteadyClock::now();
std::vector<folly::SemiFuture<uint32_t>> writeTasks;
numWriteBytes_ = 0;
size_t writeBatchSize =
std::max(benchOptions_.writeBatchSize,
clientConfig_.traffic_control().write().max_concurrent_requests() / benchOptions_.numCoroutines);
for (size_t instanceId = 0; instanceId < benchOptions_.numCoroutines; instanceId++) {
writeTasks.push_back(batchWrite(instanceId, writeBatchSize, setupConfig_.chunk_size(), 0 /*numWriteSecs*/)
.scheduleOn(folly::Executor::getKeepAliveToken(testExecutor_))
.start());
}
auto results = folly::coro::blockingWait(folly::coro::collectAllRange(std::move(writeTasks)));
for (auto res : results) {
if (res != StatusCode::kOK) {
XLOGF(WARN, "Test task failed with status code: {}", res);
return res;
}
}
auto elapsedTime = hf3fs::SteadyClock::now() - testStart;
double totalGiB = (double)numWriteBytes_ / 1_GB;
printThroughput(elapsedTime, totalGiB);
auto mergedDigest = folly::TDigest::merge(writeLatencyDigests_);
printLatencyDigest(mergedDigest);
return StatusCode::kOK;
}
uint32_t runWriteBench() {
XLOGF(WARN,
"Running write benchmark ({} secs, {} chunks, {:.3f} GiB)...",
benchOptions_.numWriteSecs,
totalNumChunks_,
totalChunkGiB_);
auto testStart = hf3fs::SteadyClock::now();
std::vector<folly::SemiFuture<uint32_t>> writeTasks;
numWriteBytes_ = 0;
for (size_t instanceId = 0; instanceId < benchOptions_.numCoroutines; instanceId++) {
writeTasks.push_back(
batchWrite(instanceId, benchOptions_.writeBatchSize, benchOptions_.writeSize, benchOptions_.numWriteSecs)
.scheduleOn(folly::Executor::getKeepAliveToken(testExecutor_))
.start());
}
auto results = folly::coro::blockingWait(folly::coro::collectAllRange(std::move(writeTasks)));
for (auto res : results) {
if (res != StatusCode::kOK) {
XLOGF(WARN, "Test task failed with status code: {}", res);
return res;
}
}
auto elapsedTime = hf3fs::SteadyClock::now() - testStart;
double totalGiB = (double)numWriteBytes_ / 1_GB;
printThroughput(elapsedTime, totalGiB);
auto mergedDigest = folly::TDigest::merge(writeLatencyDigests_);
printLatencyDigest(mergedDigest);
dumpPerfStats("batch write", mergedDigest, elapsedTime, totalGiB, false /*readIO*/);
return StatusCode::kOK;
}
uint32_t runReadBench() {
XLOGF(WARN, "Running read benchmark ({} secs)...", benchOptions_.numReadSecs);
auto testStart = hf3fs::SteadyClock::now();
std::vector<folly::SemiFuture<uint32_t>> readTasks;
numReadBytes_ = 0;
for (size_t instanceId = 0; instanceId < benchOptions_.numCoroutines; instanceId++) {
readTasks.push_back(batchRead(instanceId).scheduleOn(folly::Executor::getKeepAliveToken(testExecutor_)).start());
}
auto results = folly::coro::blockingWait(folly::coro::collectAllRange(std::move(readTasks)));
for (auto res : results) {
if (res != StatusCode::kOK) {
XLOGF(WARN, "Test task failed with status code: {}", res);
return res;
}
}
auto elapsedTime = hf3fs::SteadyClock::now() - testStart;
double totalGiB = (double)numReadBytes_ / 1_GB;
printThroughput(elapsedTime, totalGiB);
auto mergedDigest = folly::TDigest::merge(readLatencyDigests_);
printLatencyDigest(mergedDigest);
dumpPerfStats("batch read", mergedDigest, elapsedTime, totalGiB, false /*readIO*/);
return StatusCode::kOK;
}
uint32_t cleanup() {
XLOGF(WARN, "Clean up chunks...");
std::vector<folly::SemiFuture<uint32_t>> removeTasks;
for (size_t instanceId = 0; instanceId < benchOptions_.numCoroutines; instanceId++) {
auto batchRemove = [this](size_t instanceId) -> folly::coro::Task<uint32_t> {
std::vector<client::RemoveChunksOp> removeOps;
size_t totalNumChunksRemoved = 0;
for (const auto &[chainId, chunkId, chunkSize] : chunkInfos_[instanceId]) {
removeOps.push_back(storageClient_->createRemoveOp(chainId, chunkId, ChunkId(chunkId, 1)));
if (removeOps.size() >= benchOptions_.removeBatchSize) {
WriteOptions options;
options.debug().set_inject_random_server_error(benchOptions_.injectRandomServerError);
options.debug().set_inject_random_client_error(benchOptions_.injectRandomClientError);
options.retry().set_retry_permanent_error(benchOptions_.retryPermanentError);
co_await storageClient_->removeChunks(removeOps, flat::UserInfo(), options);
for (const auto &removeOp : removeOps) {
if (removeOp.result.statusCode.hasError()) {
XLOGF(WARN, "Remove operation failed with error: {}", removeOp.result.statusCode.error());
co_return removeOp.result.statusCode.error().code();
}
XLOGF_IF(DBG5,
removeOp.result.numChunksRemoved != 1,
"{} chunks removed in range {}",
removeOp.result.numChunksRemoved,
removeOp.chunkRange());
totalNumChunksRemoved += removeOp.result.numChunksRemoved;
}
removeOps.clear();
}
}
XLOGF(WARN, "{} chunks removed by instance #{}", totalNumChunksRemoved, instanceId);
co_return StatusCode::kOK;
};
removeTasks.push_back(
batchRemove(instanceId).scheduleOn(folly::Executor::getKeepAliveToken(testExecutor_)).start());
}
auto results = folly::coro::blockingWait(folly::coro::collectAllRange(std::move(removeTasks)));
for (auto res : results) {
if (res != StatusCode::kOK) {
XLOGF(WARN, "Test task failed with status code: {}", res);
return res;
}
}
return StatusCode::kOK;
};
uint32_t truncate() {
XLOGF(WARN, "Truncate chunks...");
std::vector<folly::SemiFuture<uint32_t>> truncateTasks;
for (size_t instanceId = 0; instanceId < benchOptions_.numCoroutines; instanceId++) {
auto batchTruncate = [this](size_t instanceId) -> folly::coro::Task<uint32_t> {
std::vector<client::TruncateChunkOp> truncateOps;
for (const auto &[chainId, chunkId, chunkSize] : chunkInfos_[instanceId]) {
truncateOps.push_back(storageClient_->createTruncateOp(chainId, chunkId, 0, setupConfig_.chunk_size()));
if (truncateOps.size() >= benchOptions_.writeBatchSize) {
WriteOptions options;
options.debug().set_inject_random_server_error(benchOptions_.injectRandomServerError);
options.debug().set_inject_random_client_error(benchOptions_.injectRandomClientError);
options.retry().set_retry_permanent_error(benchOptions_.retryPermanentError);
co_await storageClient_->truncateChunks(truncateOps, flat::UserInfo(), options);
for (const auto &truncateOp : truncateOps) {
if (truncateOp.result.lengthInfo.hasError()) {
XLOGF(WARN, "Truncate operation failed with error: {}", truncateOp.result.lengthInfo.error());
co_return truncateOp.result.lengthInfo.error().code();
}
}
truncateOps.clear();
}
}
co_return StatusCode::kOK;
};
truncateTasks.push_back(
batchTruncate(instanceId).scheduleOn(folly::Executor::getKeepAliveToken(testExecutor_)).start());
}
auto results = folly::coro::blockingWait(folly::coro::collectAllRange(std::move(truncateTasks)));
for (auto res : results) {
if (res != StatusCode::kOK) {
XLOGF(WARN, "Test task failed with status code: {}", res);
return res;
}
}
return StatusCode::kOK;
};
bool run() {
if (benchOptions_.numWriteSecs > 0)
if (runWriteBench() != StatusCode::kOK) return false;
if (benchOptions_.generateTestData)
if (generateChunks() != StatusCode::kOK) return false;
if (benchOptions_.numReadSecs > 0)
if (runReadBench() != StatusCode::kOK) return false;
return true;
}
uint64_t getWriteBytes() { return numWriteBytes_; }
uint64_t getReadBytes() { return numReadBytes_; }
};
} // namespace hf3fs::storage::benchmark

33
cmake/AddCrate.cmake Normal file
View File

@@ -0,0 +1,33 @@
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CARGO_CMD cargo build)
set(TARGET_DIR "debug")
else ()
set(CARGO_CMD cargo build --release)
set(TARGET_DIR "release")
endif ()
add_custom_target(
cargo_build_all ALL
COMMAND ${CARGO_CMD}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
)
macro(add_crate NAME)
set(LIBRARY "${PROJECT_SOURCE_DIR}/target/${TARGET_DIR}/lib${NAME}.a")
set(SOURCES
"${PROJECT_SOURCE_DIR}/target/cxxbridge/${NAME}/src/cxx.rs.h"
"${PROJECT_SOURCE_DIR}/target/cxxbridge/${NAME}/src/cxx.rs.cc"
)
add_custom_command(
OUTPUT ${SOURCES} ${LIBRARY}
COMMAND ${CARGO_CMD}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${NAME}"
)
add_library(${NAME} STATIC ${SOURCES} ${LIBRARY})
target_link_libraries(${NAME} pthread dl ${LIBRARY})
target_include_directories(${NAME} PUBLIC "${PROJECT_SOURCE_DIR}/target/cxxbridge")
target_compile_options(${NAME} PUBLIC -Wno-dollar-in-identifier-extension)
add_dependencies(${NAME} cargo_build_all)
endmacro()

54
cmake/ApacheArrow.cmake Normal file
View File

@@ -0,0 +1,54 @@
add_library(apache_arrow_static INTERFACE)
add_library(arrow_static STATIC IMPORTED)
add_library(parquet_static STATIC IMPORTED)
add_library(arrow_dependencies STATIC IMPORTED)
set(PREFIX "${CMAKE_CURRENT_BINARY_DIR}")
set(ARROW_RELEASE_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/src/apache-arrow-cpp/cpp/build/release")
# https://cmake.org/cmake/help/latest/policy/CMP0097.html
# Starting with CMake 3.16, explicitly setting GIT_SUBMODULES to an empty string
# means no submodules will be initialized or updated.
cmake_policy(SET CMP0097 NEW)
include(ExternalProject)
ExternalProject_Add(
apache-arrow-cpp
PREFIX ${PREFIX}
GIT_REPOSITORY https://github.com/apache/arrow.git
GIT_TAG b7d2f7ffca66c868bd2fce5b3749c6caa002a7f0
GIT_SHALLOW ON
GIT_PROGRESS ON
GIT_SUBMODULES ""
SOURCE_SUBDIR "cpp"
BUILD_IN_SOURCE ON
INSTALL_DIR ${PREFIX}
CONFIGURE_COMMAND bash -x -c "\
( cd thirdparty && [[ -f export.sh ]] || ./download_dependencies.sh | tee export.sh ) && \
source thirdparty/export.sh && cmake -S . -B . \
-DCMAKE_BUILD_TYPE=Release \
-DARROW_USE_CCACHE=OFF \
-DARROW_USE_SCCACHE=OFF \
-DARROW_DEPENDENCY_SOURCE=BUNDLED \
-DARROW_BUILD_STATIC=ON \
-DARROW_JEMALLOC=ON \
-DARROW_SIMD_LEVEL=DEFAULT \
-DARROW_BUILD_EXAMPLES=OFF \
-DARROW_PARQUET=ON -DARROW_CSV=ON \
-DARROW_WITH_ZSTD=ON -DARROW_WITH_LZ4=ON -DARROW_WITH_ZLIB=ON"
BUILD_COMMAND bash -x -c "source thirdparty/export.sh && cmake --build . -j"
INSTALL_COMMAND cmake --install . --prefix "${PREFIX}"
BUILD_BYPRODUCTS
"${ARROW_RELEASE_BUILD_DIR}/libarrow.a"
"${ARROW_RELEASE_BUILD_DIR}/libparquet.a"
"${ARROW_RELEASE_BUILD_DIR}/libarrow_bundled_dependencies.a"
)
add_dependencies(arrow_static apache-arrow-cpp)
add_dependencies(parquet_static apache-arrow-cpp)
add_dependencies(arrow_dependencies apache-arrow-cpp)
set_target_properties(arrow_static PROPERTIES IMPORTED_LOCATION "${ARROW_RELEASE_BUILD_DIR}/libarrow.a")
set_target_properties(parquet_static PROPERTIES IMPORTED_LOCATION "${ARROW_RELEASE_BUILD_DIR}/libparquet.a")
set_target_properties(arrow_dependencies PROPERTIES IMPORTED_LOCATION "${ARROW_RELEASE_BUILD_DIR}/libarrow_bundled_dependencies.a")
target_include_directories(apache_arrow_static SYSTEM INTERFACE "${PREFIX}/include")
target_link_libraries(apache_arrow_static INTERFACE parquet_static arrow_static arrow_dependencies)

24
cmake/CLangFormat.cmake Normal file
View File

@@ -0,0 +1,24 @@
set(CLANG_FORMAT "/usr/bin/clang-format-14")
if(EXISTS ${CLANG_FORMAT})
message(STATUS "Found clang-format at ${CLANG_FORMAT}")
set(SOURCE_DIRS
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/tests
${CMAKE_SOURCE_DIR}/benchmarks
)
# For now, it just hard codes the source files list to globs. That works
# fine until we have another directory in `src/`. We should ideally gather
# this from SOURCE_FILES list. But, should filter the thirs_party sources.
# Taking a quick route for now. We should deal with it sometime down the line.
add_custom_target(format
COMMENT "Running clang-format"
COMMAND find ${SOURCE_DIRS} -name '*.cc' -o -name '*.cpp' -o -name '*.h' | grep -v "_generated.h" | xargs ${CLANG_FORMAT} -i)
add_custom_target(check-format
COMMENT "Running clang-format"
COMMAND find ${SOURCE_DIRS} -name '*.cc' -o -name '*.cpp' -o -name '*.h' | grep -v "_generated.h" | xargs ${CLANG_FORMAT} --Werror --dry-run)
else()
message(FATAL_ERROR "clang-format-14 not found")
endif()

52
cmake/CLangTidy.cmake Normal file
View File

@@ -0,0 +1,52 @@
# clang-tidy generates too many warnings, so just disable it by default.
option(ENABLE_CLANG_TIDY "Run clang-tidy during build" OFF)
find_program(CLANG_TIDY NAMES clang-tidy-14)
if(CLANG_TIDY)
if(CMake_SOURCE_DIR STREQUAL CMake_BINARY_DIR)
message(FATAL_ERROR "CMake_RUN_CLANG_TIDY requires an out-of-source build!")
endif()
if(NOT CMAKE_EXPORT_COMPILE_COMMANDS)
message(WARNING "CMAKE_EXPORT_COMPILE_COMMANDS=OFF, clang-tidy may not works!!!")
endif()
set(HEADER_FILTER "${CMAKE_SOURCE_DIR}/\\(src\\|tests\\|benchmarks\\|demos\\)")
if(ENABLE_CLANG_TIDY)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY} --header-filter ${HEADER_FILTER})
# Create a preprocessor definition that depends on .clang-tidy content so
# the compile command will change when .clang-tidy changes. This ensures
# that a subsequent build re-runs clang-tidy on all sources even if they
# do not otherwise need to be recompiled. Nothing actually uses this
# definition. We add it to targets on which we run clang-tidy just to
# get the build dependency on the .clang-tidy file.
file(SHA1 ${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy clang_tidy_sha1)
set(CLANG_TIDY_DEFINITIONS "CLANG_TIDY_SHA1=${clang_tidy_sha1}")
unset(clang_tidy_sha1)
configure_file(.clang-tidy .clang-tidy COPYONLY)
endif()
set(SOURCE_DIRS
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/tests
${CMAKE_SOURCE_DIR}/demos
${CMAKE_SOURCE_DIR}/benchmarks
)
# For now, it just hard codes the source files list to globs. That works
# fine until we have another directory in `src/`. We should ideally gather
# this from SOURCE_FILES list. But, should filter the thirs_party sources.
# Taking a quick route for now. We should deal with it sometime down the line.
add_custom_target(clang-tidy
COMMENT "Running clang-tidy"
COMMAND run-clang-tidy-14 -header-filter ${HEADER_FILTER} `find ${SOURCE_DIRS} -name "*.cc" -o -name "*.cpp" -not -name "*.actor.cpp" ` -quiet)
add_custom_target(clang-tidy-fix
COMMENT "Running clang-tidy -fix"
COMMAND run-clang-tidy-14 -header-filter ${HEADER_FILTER} `find ${SOURCE_DIRS} -name "*.cc" -o -name "*.cpp" -not -name "*.actor.cpp" ` -fix -quiet)
else()
message(WARNING "clang-tidy-14 not found!!!")
endif()

12
cmake/CTestCustom.cmake Normal file
View File

@@ -0,0 +1,12 @@
file (STRINGS "@CMAKE_BINARY_DIR@/CTestTestfile.cmake" LINES)
# overwrite the file....
file(WRITE "@CMAKE_BINARY_DIR@/CTestTestfile.cmake" "")
# loop through the lines,
foreach(LINE IN LISTS LINES)
# remove unwanted parts
string(REGEX REPLACE ".*third_party.*" "" STRIPPED "${LINE}")
# and write the (changed) line ...
file(APPEND "@CMAKE_BINARY_DIR@/CTestTestfile.cmake" "${STRIPPED}\n")
endforeach()

27
cmake/CodeCoverage.cmake Normal file
View File

@@ -0,0 +1,27 @@
option(ENABLE_CODE_COVERAGE "Enable code coverage" OFF)
if(ENABLE_CODE_COVERAGE)
if(CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG" )
message(STATUS "Enable code coverage with debug mode.")
else()
message(WARNING "Code coverage with no debug mode!!!")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage")
link_libraries(gcov)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang")
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-instr-generate -fcoverage-mapping")
else()
message(FATAL_ERROR "Unknown compiler iid ${CMAKE_CXX_COMPILER_ID}")
endif()
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endif()

21
cmake/CompileFlags.cmake Normal file
View File

@@ -0,0 +1,21 @@
macro(store_compile_flags)
set(ORIGINAL_C_FLAGS "${CMAKE_C_FLAGS}")
set(ORIGINAL_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
set(ORIGINAL_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
set(ORIGINAL_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
set(ORIGINAL_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(ORIGINAL_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
set(ORIGINAL_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
set(ORIGINAL_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
endmacro()
macro(restore_compile_flags)
set(CMAKE_C_FLAGS "${ORIGINAL_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${ORIGINAL_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "${ORIGINAL_C_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${ORIGINAL_C_FLAGS_RELWITHDEBINFO}")
set(CMAKE_CXX_FLAGS "${ORIGINAL_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${ORIGINAL_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_RELEASE "${ORIGINAL_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${ORIGINAL_CXX_FLAGS_RELWITHDEBINFO}")
endmacro()

15
cmake/DumpConfig.cmake Normal file
View File

@@ -0,0 +1,15 @@
if (ENABLE_FUSE_APPLICATION)
add_custom_target(dump-config
COMMENT "Running dump config"
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' -o -name 'admin_cli' -o -name 'hf3fs_fuse_main' | xargs -I {} bash -c '{} --dump_default_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`.toml'
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' -o -name 'hf3fs_fuse_main' | xargs -I {} bash -c '{} --dump_default_app_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`_app.toml'
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' -o -name 'hf3fs_fuse_main' | xargs -I {} bash -c '{} --dump_default_launcher_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`_launcher.toml')
add_dependencies(dump-config meta_main mgmtd_main storage_main admin_cli hf3fs_fuse_main)
else()
add_custom_target(dump-config
COMMENT "Running dump config"
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' -o -name 'admin_cli' | xargs -I {} bash -c '{} --dump_default_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`.toml'
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' | xargs -I {} bash -c '{} --dump_default_app_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`_app.toml'
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name 'meta_main' -o -name 'mgmtd_main' -o -name 'storage_main' | xargs -I {} bash -c '{} --dump_default_launcher_cfg > ${CMAKE_SOURCE_DIR}/configs/`basename {}`_launcher.toml')
add_dependencies(dump-config meta_main mgmtd_main storage_main admin_cli)
endif()

127
cmake/GitVersion.cmake Normal file
View File

@@ -0,0 +1,127 @@
find_package(Git REQUIRED)
if (NOT DEFINED PRE_CONFIGURE_DIR)
set(PRE_CONFIGURE_DIR ${PROJECT_SOURCE_DIR}/src/common/utils)
endif ()
if (NOT DEFINED POST_BUILD_DIR)
set(POST_BUILD_DIR ${PROJECT_BINARY_DIR})
endif ()
set(PRE_CONFIGURE_FILE ${PRE_CONFIGURE_DIR}/VersionInfo.cc.in)
set(POST_CONFIGURE_FILE ${POST_BUILD_DIR}/src/common/utils/VersionInfo.cc)
function(CheckGitWrite git_hash)
file(WRITE ${POST_BUILD_DIR}/git-state.txt ${git_hash})
endfunction()
function(CheckGitRead git_hash)
if (EXISTS ${POST_BUILD_DIR}/git-state.txt)
file(STRINGS ${POST_BUILD_DIR}/git-state.txt CONTENT)
LIST(GET CONTENT 0 var)
set(${git_hash} ${var} PARENT_SCOPE)
endif ()
endfunction()
function(CheckGitVersion)
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short=8 HEAD
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_COMMIT_HASH_FULL
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --format=%at --date=local
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_TIMESTAMP
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND date -d @${BUILD_TIMESTAMP} +%Y%m%d
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_DATE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND bash -c "${GIT_EXECUTABLE} describe --tags --long | sed -E 's/(.*)-([0-9]+)-(\\w+)/\\2/g'"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_TAG_SEQ_NUM
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT DEFINED BUILD_TAG OR BUILD_TAG STREQUAL "")
set(BUILD_TAG "250228")
endif()
if(NOT DEFINED BUILD_TAG_SEQ_NUM OR BUILD_TAG_SEQ_NUM STREQUAL "")
set(BUILD_TAG_SEQ_NUM "1")
endif()
message(STATUS "Git Commit hash: ${BUILD_COMMIT_HASH_SHORT} ${BUILD_COMMIT_HASH_FULL}")
message(STATUS "Git Commit Date & Timestamp: ${BUILD_DATE} ${BUILD_TIMESTAMP}")
message(STATUS "Git Commit Tag & Seq Num: ${BUILD_TAG} ${BUILD_TAG_SEQ_NUM}")
set(BUILD_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(BUILD_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(BUILD_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(BUILD_VERSION "${PROJECT_VERSION}")
CheckGitRead(GIT_HASH_CACHE)
if (NOT DEFINED GIT_HASH_CACHE)
set(GIT_HASH_CACHE "INVALID")
endif ()
if (NOT DEFINED BUILD_ON_RELEASE_BRANCH)
set(BUILD_ON_RELEASE_BRANCH "false")
endif ()
if (NOT DEFINED BUILD_PIPELINE_ID)
set(BUILD_PIPELINE_ID "999999")
endif()
# Only update the git_version.cpp if the hash has changed. This will
# prevent us from rebuilding the project more than we need to.
if (NOT ${BUILD_COMMIT_HASH_FULL} STREQUAL ${GIT_HASH_CACHE} OR NOT EXISTS ${POST_CONFIGURE_FILE})
# Set che GIT_HASH_CACHE variable the next build won't have
# to regenerate the source file.
CheckGitWrite("${BUILD_COMMIT_HASH_FULL}")
configure_file(${PRE_CONFIGURE_FILE} ${POST_CONFIGURE_FILE} @ONLY)
endif ()
endfunction()
function(CheckGitSetup project_src_dir)
add_custom_target(AlwaysCheckGit COMMAND ${CMAKE_COMMAND}
-DRUN_CHECK_GIT_VERSION=1
-DPRE_CONFIGURE_DIR=${PRE_CONFIGURE_DIR}
-DPOST_BUILD_DIR=${POST_BUILD_DIR}
-DGIT_HASH_CACHE=${GIT_HASH_CACHE}
-DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
-DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR}
-DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH}
-DPROJECT_VERSION=${PROJECT_VERSION}
-DPROJECT_SOURCE_DIR=${project_src_dir}
-P ${PROJECT_SOURCE_DIR}/cmake/GitVersion.cmake
DEPENDS ${PRE_CONFIGURE_FILE}
BYPRODUCTS ${POST_CONFIGURE_FILE}
)
add_library(version-info STATIC ${POST_CONFIGURE_FILE})
target_include_directories(version-info PUBLIC ${PROJECT_SOURCE_DIR}/src)
add_dependencies(version-info AlwaysCheckGit)
CheckGitVersion()
endfunction()
if (RUN_CHECK_GIT_VERSION)
CheckGitVersion()
endif ()

20
cmake/Jemalloc.cmake Normal file
View File

@@ -0,0 +1,20 @@
add_library(jemalloc INTERFACE)
add_library(hf3fs_jemalloc_shared SHARED IMPORTED)
include(ExternalProject)
set(JEMALLOC_DIR "${CMAKE_BINARY_DIR}/third_party/jemalloc")
ExternalProject_add(Hf3fsJemalloc_project
SOURCE_DIR "${PROJECT_SOURCE_DIR}/third_party/jemalloc"
BUILD_BYPRODUCTS "${JEMALLOC_DIR}/include/jemalloc/jemalloc.h"
"${JEMALLOC_DIR}/lib/libjemalloc.so.2"
CONFIGURE_COMMAND ./autogen.sh && ./configure --prefix=${JEMALLOC_DIR} --disable-cxx --enable-prof --disable-initial-exec-tls
BUILD_IN_SOURCE ON
BUILD_COMMAND make -j 6
INSTALL_DIR "${JEMALLOC_DIR}"
INSTALL_COMMAND make install)
add_dependencies(hf3fs_jemalloc_shared Hf3fsJemalloc_project)
set_target_properties(hf3fs_jemalloc_shared PROPERTIES IMPORTED_LOCATION "${JEMALLOC_DIR}/lib/libjemalloc.so.2")
target_include_directories(hf3fs_jemalloc_shared INTERFACE "${JEMALLOC_DIR}/include")
target_link_libraries(jemalloc INTERFACE hf3fs_jemalloc_shared)

242
cmake/Sanitizers.cmake Normal file
View File

@@ -0,0 +1,242 @@
#
# Copyright (C) 2018-2022 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
include(CheckCXXSourceCompiles)
set(SANITIZER "" CACHE STRING "Sanitizer: ASAN, MSAN, UBSAN, TSAN")
if(SANITIZER STREQUAL "ASAN")
message(STATUS "Build with Address Sanitizer.")
set(USE_SANITIZER "Address")
set(USE_ASAN ON)
elseif(SANITIZER STREQUAL "MSAN")
message(STATUS "Build with Memory Sanitizer.")
set(USE_SANITIZER "Memory")
set(USE_MSAN ON)
elseif(SANITIZER STREQUAL "UBSAN")
message(STATUS "Build with Undefined behavior Sanitizer.")
message(WARNING "With clang++ v14.0.6 undefined behavior sanitizer doesn't work with coroutine!!!")
set(USE_SANITIZER "Undefined")
set(USE_UBSAN ON)
elseif(SANITIZER STREQUAL "TSAN")
message(STATUS "Build with Thread Sanitizer.")
set(USE_SANITIZER "Thread")
set(USE_TSAN ON)
else()
message(STATUS "Sanitizer not enabled. ${SANITIZER}")
set(USE_SANITIZER "")
endif()
# set(USE_SANITIZER "" CACHE STRING
# "Compile with a sanitizer. Options are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined', CFI"
# )
function(append value)
foreach(variable ${ARGN})
set(${variable}
"${${variable}} ${value}"
PARENT_SCOPE)
endforeach(variable)
endfunction()
function(append_quoteless value)
foreach(variable ${ARGN})
set(${variable}
${${variable}} ${value}
PARENT_SCOPE)
endforeach(variable)
endfunction()
function(test_san_flags return_var flags)
set(QUIET_BACKUP ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET TRUE)
unset(${return_var} CACHE)
set(FLAGS_BACKUP ${CMAKE_REQUIRED_FLAGS})
set(CMAKE_REQUIRED_FLAGS "${flags}")
check_cxx_source_compiles("int main() { return 0; }" ${return_var})
set(CMAKE_REQUIRED_FLAGS "${FLAGS_BACKUP}")
set(CMAKE_REQUIRED_QUIET "${QUIET_BACKUP}")
endfunction()
if(USE_SANITIZER)
append("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
unset(SANITIZER_SELECTED_FLAGS)
if(UNIX)
if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
append("-O1" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
endif()
if(USE_SANITIZER MATCHES "([Aa]ddress)")
# Optional: -fno-optimize-sibling-calls -fsanitize-address-use-after-scope
message(STATUS "Testing with Address sanitizer")
set(SANITIZER_ADDR_FLAG "-fsanitize=address")
test_san_flags(SANITIZER_ADDR_AVAILABLE ${SANITIZER_ADDR_FLAG})
if(SANITIZER_ADDR_AVAILABLE)
message(STATUS " Building with Address sanitizer")
append("${SANITIZER_ADDR_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_ASAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR
"Address sanitizer not available for ${CMAKE_CXX_COMPILER}")
endif()
endif()
if(USE_SANITIZER MATCHES "([Mm]emory([Ww]ith[Oo]rigins)?)")
# Optional: -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2
set(SANITIZER_MEM_FLAG "-fsanitize=memory")
if(USE_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)")
message(STATUS "Testing with MemoryWithOrigins sanitizer")
append("-fsanitize-memory-track-origins" SANITIZER_MEM_FLAG)
else()
message(STATUS "Testing with Memory sanitizer")
endif()
test_san_flags(SANITIZER_MEM_AVAILABLE ${SANITIZER_MEM_FLAG})
if(SANITIZER_MEM_AVAILABLE)
if(USE_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)")
message(STATUS " Building with MemoryWithOrigins sanitizer")
else()
message(STATUS " Building with Memory sanitizer")
endif()
append("${SANITIZER_MEM_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_MSAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR
"Memory [With Origins] sanitizer not available for ${CMAKE_CXX_COMPILER}"
)
endif()
endif()
if(USE_SANITIZER MATCHES "([Uu]ndefined)")
message(STATUS "Testing with Undefined Behaviour sanitizer")
set(SANITIZER_UB_FLAG "-fsanitize=undefined")
if(EXISTS "${BLACKLIST_FILE}")
append("-fsanitize-blacklist=${BLACKLIST_FILE}" SANITIZER_UB_FLAG)
endif()
test_san_flags(SANITIZER_UB_AVAILABLE ${SANITIZER_UB_FLAG})
if(SANITIZER_UB_AVAILABLE)
message(STATUS " Building with Undefined Behaviour sanitizer")
append("${SANITIZER_UB_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_UBSAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR
"Undefined Behaviour sanitizer not available for ${CMAKE_CXX_COMPILER}"
)
endif()
endif()
if(USE_SANITIZER MATCHES "([Tt]hread)")
message(STATUS "Testing with Thread sanitizer")
set(SANITIZER_THREAD_FLAG "-fsanitize=thread -fsanitize-ignorelist=${CMAKE_SOURCE_DIR}/tsan_ignore.txt")
test_san_flags(SANITIZER_THREAD_AVAILABLE ${SANITIZER_THREAD_FLAG})
if(SANITIZER_THREAD_AVAILABLE)
message(STATUS " Building with Thread sanitizer")
append("${SANITIZER_THREAD_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_TSAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR "Thread sanitizer not available for ${CMAKE_CXX_COMPILER}"
)
endif()
endif()
if(USE_SANITIZER MATCHES "([Ll]eak)")
message(STATUS "Testing with Leak sanitizer")
set(SANITIZER_LEAK_FLAG "-fsanitize=leak")
test_san_flags(SANITIZER_LEAK_AVAILABLE ${SANITIZER_LEAK_FLAG})
if(SANITIZER_LEAK_AVAILABLE)
message(STATUS " Building with Leak sanitizer")
append("${SANITIZER_LEAK_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_LSAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR "Thread sanitizer not available for ${CMAKE_CXX_COMPILER}"
)
endif()
endif()
if(USE_SANITIZER MATCHES "([Cc][Ff][Ii])")
message(STATUS "Testing with Control Flow Integrity(CFI) sanitizer")
set(SANITIZER_CFI_FLAG "-fsanitize=cfi")
test_san_flags(SANITIZER_CFI_AVAILABLE ${SANITIZER_CFI_FLAG})
if(SANITIZER_CFI_AVAILABLE)
message(STATUS " Building with Control Flow Integrity(CFI) sanitizer")
append("${SANITIZER_LEAK_FLAG}" SANITIZER_SELECTED_FLAGS)
if(AFL)
append_quoteless(AFL_USE_CFISAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR
"Control Flow Integrity(CFI) sanitizer not available for ${CMAKE_CXX_COMPILER}"
)
endif()
endif()
message(STATUS "Sanitizer flags: ${SANITIZER_SELECTED_FLAGS}")
test_san_flags(SANITIZER_SELECTED_COMPATIBLE ${SANITIZER_SELECTED_FLAGS})
if(SANITIZER_SELECTED_COMPATIBLE)
message(STATUS " Building with ${SANITIZER_SELECTED_FLAGS}")
append("${SANITIZER_SELECTED_FLAGS}" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
else()
message(
FATAL_ERROR
" Sanitizer flags ${SANITIZER_SELECTED_FLAGS} are not compatible.")
endif()
elseif(MSVC)
if(USE_SANITIZER MATCHES "([Aa]ddress)")
message(STATUS "Building with Address sanitizer")
append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(AFL)
append_quoteless(AFL_USE_ASAN=1 CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER_LAUNCHER)
endif()
else()
message(
FATAL_ERROR
"This sanitizer not yet supported in the MSVC environment: ${USE_SANITIZER}"
)
endif()
else()
message(FATAL_ERROR "USE_SANITIZER is not supported on this platform.")
endif()
endif()

109
cmake/Target.cmake Normal file
View File

@@ -0,0 +1,109 @@
# Check if IPO is supported
include(CheckIPOSupported)
check_ipo_supported(RESULT HAVE_IPO)
# Enable IPO in non-debug build
macro(target_enable_ipo NAME)
if(NOT CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG" AND HAVE_IPO)
set_property(TARGET ${NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
message (STATUS "Enabled IPO for target: ${NAME}")
endif()
endmacro()
macro(target_add_lib NAME)
file(GLOB_RECURSE FILES CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" "*.h")
add_library(${NAME} STATIC ${FILES} ${FBS_FILES})
target_include_directories(${NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/src
${PROJECT_BINARY_DIR}
)
target_link_libraries(${NAME} ${ARGN} "")
endmacro()
macro(target_add_shared_lib NAME)
file(GLOB_RECURSE FILES CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" "*.h")
add_library(${NAME} SHARED ${FILES} ${FBS_FILES})
target_include_directories(${NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/src
${PROJECT_BINARY_DIR}
)
target_link_libraries(${NAME} ${ARGN} "")
target_enable_ipo(${NAME})
endmacro()
macro(target_add_bin NAME MAIN_FILE)
add_executable(${NAME} ${MAIN_FILE})
target_link_libraries(${NAME} ${ARGN} "")
target_include_directories(${NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/src/lib/api
${PROJECT_BINARY_DIR}/src
${PROJECT_BINARY_DIR}
)
set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
target_enable_ipo(${NAME})
endmacro()
macro(target_add_test NAME)
file(GLOB_RECURSE FILES CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc")
add_executable(${NAME} ${FILES})
target_link_libraries(${NAME} gmock test_main ${ARGN} "")
target_include_directories(${NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/src
${PROJECT_BINARY_DIR}
)
add_test(NAME ${NAME} COMMAND ${NAME})
set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")
endmacro()
macro(target_add_fbs NAME PATH)
cmake_parse_arguments(FBS "SERVICE" "" "DEPS" ${ARGN})
message("Fbs " ${NAME} " FBS_SERVICE " ${FBS_SERVICE} " ARGN " ${ARGN})
set(FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS
--scoped-enums
--gen-object-api
--gen-mutable
--gen-compare
--cpp-std=c++17
--python
--hf3fs
--keep-prefix
)
get_filename_component(NAME_WE ${PATH} NAME_WE)
get_filename_component(DIR ${PATH} DIRECTORY)
build_flatbuffers(${PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/${DIR};${CMAKE_SOURCE_DIR}/src"
"${NAME_WE}-generated"
""
"${CMAKE_CURRENT_BINARY_DIR}/${DIR}"
""
""
)
add_library(${NAME} INTERFACE)
target_link_libraries(${NAME} INTERFACE common)
target_include_directories(${NAME}
INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/src
${PROJECT_BINARY_DIR}
)
add_dependencies(${NAME} "${NAME_WE}-generated" ${FBS_DEPS})
endmacro()

338
configs/admin_cli.toml Normal file
View File

@@ -0,0 +1,338 @@
break_multi_line_command_on_failure = false
cluster_id = ''
log = 'DBG:normal; normal=file:path=/var/log/3fs/cli.log,async=true,sync_level=ERR'
num_timeout_ms = 1000
profile = false
verbose = false
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[fdb]
casual_read_risky = false
clusterFile = ''
default_backoff = 0
enableMultipleClient = false
externalClientDir = ''
externalClientPath = ''
multipleClientThreadNum = 4
readonly = false
trace_file = ''
trace_format = 'json'
[ib_devices]
allow_no_usable_devices = false
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_roce_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
prefer_ibdevice = true
skip_inactive_ports = true
skip_unusable_device = true
subnets = []
[meta_client]
check_server_interval = '5s'
dynamic_stripe = false
max_concurrent_requests = 128
network_type = 'RDMA'
remove_chunks_batch_size = 32
remove_chunks_max_iters = 1024
selection_mode = 'RandomFollow'
[meta_client.background_closer]
prune_session_batch_count = 128
prune_session_batch_interval = '10s'
retry_first_wait = '100ms'
retry_max_wait = '10s'
task_scan = '50ms'
[meta_client.background_closer.coroutine_pool]
coroutines_num = 8
enable_work_stealing = false
queue_size = 128
[meta_client.retry_default]
max_failures_before_failover = 1
retry_fast = '1s'
retry_init_wait = '500ms'
retry_max_wait = '5s'
retry_send = 1
retry_total_time = '1min'
rpc_timeout = '5s'
[mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '1s'
enable_auto_extend_client_session = false
enable_auto_heartbeat = false
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100
[monitor]
collect_period = '1s'
num_collectors = 1
reporters = []
[storage_client]
check_overlapping_read_buffers = true
check_overlapping_write_buffers = false
chunk_checksum_type = 'CRC32C'
create_net_client_for_updates = false
implementation_type = 'RPC'
max_inline_read_bytes = '0'
max_inline_write_bytes = '0'
max_read_io_bytes = '0'
[storage_client.net_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[storage_client.net_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[storage_client.net_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[storage_client.net_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[storage_client.net_client.io_worker.transport_pool]
max_connections = 1
[storage_client.net_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[storage_client.net_client.rdma_control]
max_concurrent_transmission = 64
[storage_client.net_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[storage_client.net_client_for_updates]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[storage_client.net_client_for_updates.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[storage_client.net_client_for_updates.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[storage_client.net_client_for_updates.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[storage_client.net_client_for_updates.io_worker.transport_pool]
max_connections = 1
[storage_client.net_client_for_updates.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[storage_client.net_client_for_updates.rdma_control]
max_concurrent_transmission = 64
[storage_client.net_client_for_updates.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[storage_client.retry]
init_wait_time = '10s'
max_failures_before_failover = 1
max_retry_time = '1min'
max_wait_time = '30s'
[storage_client.traffic_control.query]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage_client.traffic_control.read]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage_client.traffic_control.remove]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage_client.traffic_control.truncate]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage_client.traffic_control.write]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[user_info]
gid = -1
gids = []
token = ''
uid = -1

View File

@@ -0,0 +1,557 @@
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.handlers]]
async = true
file_path = ''
max_file_size = '10MB'
max_files = 100
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = ''
max_file_size = '10MB'
max_files = 100
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = ''
max_file_size = '10MB'
max_files = 100
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[common.memory]
prof_active = false
prof_prefix = ''
[common.monitor]
reporters = []
[server.agent]
allow_read_holes = true
auth_timeout = '5min'
background_ibreg = true
list_entry_limit = 100
max_concurrent_iovallocs = 0
mock_storage_dir = ''
mount_name = ''
read_only_mode = false
truncate_if_write_after_eof = false
[server.agent.inode_cache]
capacity = 4194304
entry_lifetime = '5min'
[server.agent.limit_per_process]
fd = 1048576
shm = 1048576
[server.agent.periodic_sync]
interval = '10min'
on = false
[server.agent.proc_watch]
interval = '1min'
on = true
[server.agent.storage_io.read]
enableChecksum = false
[server.agent.storage_io.read.debug]
bypass_disk_io = false
bypass_rdma_xmit = false
inject_random_client_error = false
inject_random_server_error = false
max_num_of_injection_points = 100
[server.agent.storage_io.read.retry]
init_wait_time = '0ns'
max_retry_time = '0ns'
max_wait_time = '0ns'
retry_permanent_error = false
[server.agent.storage_io.read.targetSelection]
mode = 'Default'
targetIndex = 0
trafficZone = ''
[server.agent.storage_io.write]
enableChecksum = true
[server.agent.storage_io.write.debug]
bypass_disk_io = false
bypass_rdma_xmit = false
inject_random_client_error = false
inject_random_server_error = false
max_num_of_injection_points = 100
[server.agent.storage_io.write.retry]
init_wait_time = '0ns'
max_retry_time = '0ns'
max_wait_time = '0ns'
retry_permanent_error = false
[server.agent.storage_io.write.targetSelection]
mode = 'Default'
targetIndex = 0
trafficZone = ''
[server.agent.storage_ops]
enable_read = true
enable_write = true
[server.background_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.background_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.background_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.background_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.background_client.io_worker.transport_pool]
max_connections = 1
[server.background_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.background_client.rdma_control]
max_concurrent_transmission = 64
[server.background_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.independent_thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 64
num_proc_threads = 64
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'LOCAL'
services = [ 'ClientAgentSerde' ]
use_independent_thread_pool = false
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
filter_list = []
listen_port = 0
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'TCP'
services = [ 'Core' ]
use_independent_thread_pool = true
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
filter_list = []
listen_port = 9000
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.meta]
check_server_interval = '5s'
dynamic_stripe = false
max_concurrent_requests = 128
network_type = 'RDMA'
remove_chunks_batch_size = 32
remove_chunks_max_iters = 1024
selection_mode = 'RandomFollow'
[server.meta.background_closer]
prune_session_batch_count = 128
prune_session_batch_interval = '10s'
retry_first_wait = '100ms'
retry_max_wait = '10s'
task_scan = '50ms'
[server.meta.background_closer.coroutine_pool]
coroutines_num = 8
enable_work_stealing = false
queue_size = 128
[server.meta.retry_default]
max_failures_before_failover = 1
retry_init_wait = '500ms'
retry_max_wait = '5s'
retry_send = 1
retry_total_time = '1min'
rpc_timeout = '2s'
[server.meta.retry_truncate]
max_failures_before_failover = 1
retry_init_wait = '2s'
retry_max_wait = '5s'
retry_send = 1
retry_total_time = '1min'
rpc_timeout = '15s'
[server.mgmtd]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = true
enable_auto_heartbeat = false
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100
[server.storage]
check_overlapping_read_buffers = true
check_overlapping_write_buffers = false
chunk_checksum_type = 'CRC32C'
create_net_client_for_updates = false
implementation_type = 'RPC'
max_inline_read_bytes = '0'
max_inline_write_bytes = '0'
max_read_io_bytes = '0'
[server.storage.net_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.storage.net_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.storage.net_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.storage.net_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.storage.net_client.io_worker.transport_pool]
max_connections = 1
[server.storage.net_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.storage.net_client.rdma_control]
max_concurrent_transmission = 64
[server.storage.net_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.storage.net_client_for_updates]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.storage.net_client_for_updates.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.storage.net_client_for_updates.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.storage.net_client_for_updates.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.storage.net_client_for_updates.io_worker.transport_pool]
max_connections = 1
[server.storage.net_client_for_updates.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.storage.net_client_for_updates.rdma_control]
max_concurrent_transmission = 64
[server.storage.net_client_for_updates.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.storage.retry]
init_wait_time = '10s'
max_failures_before_failover = 1
max_retry_time = '1min'
max_wait_time = '30s'
[server.storage.traffic_control.query]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage.traffic_control.read]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage.traffic_control.remove]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage.traffic_control.truncate]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage.traffic_control.write]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,88 @@
allow_dev_version = true
cluster_id = ''
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[ib_devices]
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
skip_inactive_ports = true
subnets = []
[mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = true
enable_auto_heartbeat = false
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100

View File

@@ -0,0 +1,450 @@
attr_timeout = 30.0
batch_io_coros = 128
check_rmrf = true
chunk_size_limit = '0'
dryrun_bench_mode = false
enable_interrupt = false
enable_priority = false
enable_read_cache = true
enable_writeback_cache = false
entry_timeout = 30.0
fdatasync_update_length = false
flush_on_stat = true
fsync_length_hint = false
io_job_deq_timeout = '1ms'
io_jobq_size = 1024
iov_limit = '1MB'
max_background = 32
max_idle_threads = 10
max_jobs_per_ioring = 32
max_readahead = '16MB'
max_threads = 256
max_uid = '1M'
memset_before_read = false
negative_timeout = 5.0
notify_inval_threads = 32
rdma_buf_pool_size = 1024
readonly = false
submit_wait_jitter = '1ms'
symlink_timeout = 5.0
sync_on_stat = true
time_granularity = '1s'
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/hf3fs_fuse_main.log'
max_file_size = '100MB'
max_files = 10
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/hf3fs_fuse_main-err.log'
max_file_size = '100MB'
max_files = 10
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/hf3fs_fuse_main-fatal.log'
max_file_size = '100MB'
max_files = 10
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[common.memory]
prof_active = false
prof_prefix = ''
[common.monitor]
collect_period = '1s'
num_collectors = 1
[[common.monitor.reporters]]
type = 'monitor_collector'
[common.monitor.reporters.monitor_collector]
remote_ip = ''
[io_bufs]
max_buf_size = '1MB'
max_readahead = '256KB'
write_buf_size = '1MB'
[io_jobq_sizes]
hi = 32
lo = 4096
[io_worker_coros]
hi = 8
lo = 8
[meta]
check_server_interval = '5s'
dynamic_stripe = true
max_concurrent_requests = 128
network_type = 'RDMA'
remove_chunks_batch_size = 32
remove_chunks_max_iters = 1024
selection_mode = 'RandomFollow'
[meta.background_closer]
prune_session_batch_count = 128
prune_session_batch_interval = '10s'
retry_first_wait = '100ms'
retry_max_wait = '10s'
task_scan = '50ms'
[meta.background_closer.coroutine_pool]
coroutines_num = 8
enable_work_stealing = false
queue_size = 128
[meta.retry_default]
max_failures_before_failover = 1
retry_fast = '1s'
retry_init_wait = '500ms'
retry_max_wait = '5s'
retry_send = 1
retry_total_time = '1min'
rpc_timeout = '5s'
[mgmtd]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = true
enable_auto_heartbeat = false
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100
[periodic_sync]
enable = true
flush_write_buf = true
interval = '30s'
limit = 1000
[periodic_sync.worker]
coroutines_num = 4
enable_work_stealing = false
queue_size = 1024
[storage]
check_overlapping_read_buffers = true
check_overlapping_write_buffers = false
chunk_checksum_type = 'CRC32C'
create_net_client_for_updates = false
implementation_type = 'RPC'
max_inline_read_bytes = '0'
max_inline_write_bytes = '0'
max_read_io_bytes = '0'
[storage.net_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[storage.net_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[storage.net_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[storage.net_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[storage.net_client.io_worker.transport_pool]
max_connections = 1
[storage.net_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[storage.net_client.rdma_control]
max_concurrent_transmission = 64
[storage.net_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[storage.net_client_for_updates]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[storage.net_client_for_updates.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[storage.net_client_for_updates.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[storage.net_client_for_updates.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[storage.net_client_for_updates.io_worker.transport_pool]
max_connections = 1
[storage.net_client_for_updates.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[storage.net_client_for_updates.rdma_control]
max_concurrent_transmission = 64
[storage.net_client_for_updates.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[storage.retry]
init_wait_time = '10s'
max_failures_before_failover = 1
max_retry_time = '1min'
max_wait_time = '30s'
[storage.traffic_control.query]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage.traffic_control.read]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage.traffic_control.remove]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage.traffic_control.truncate]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage.traffic_control.write]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[storage_io.read]
allowReadUncommitted = false
enableChecksum = false
[storage_io.read.debug]
bypass_disk_io = false
bypass_rdma_xmit = false
inject_random_client_error = false
inject_random_server_error = false
max_num_of_injection_points = 100
[storage_io.read.retry]
init_wait_time = '0ns'
max_retry_time = '0ns'
max_wait_time = '0ns'
retry_permanent_error = false
[storage_io.read.targetSelection]
mode = 'Default'
targetIndex = 0
trafficZone = ''
[storage_io.write]
enableChecksum = true
[storage_io.write.debug]
bypass_disk_io = false
bypass_rdma_xmit = false
inject_random_client_error = false
inject_random_server_error = false
max_num_of_injection_points = 100
[storage_io.write.retry]
init_wait_time = '0ns'
max_retry_time = '0ns'
max_wait_time = '0ns'
retry_permanent_error = false
[storage_io.write.targetSelection]
mode = 'Default'
targetIndex = 0
trafficZone = ''

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,95 @@
allow_other = true
cluster_id = ''
mountpoint = ''
token_file = ''
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[ib_devices]
allow_no_usable_devices = false
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_roce_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
prefer_ibdevice = true
skip_inactive_ports = true
skip_unusable_device = true
subnets = []
[mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = true
enable_auto_heartbeat = false
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100

633
configs/meta_main.toml Normal file
View File

@@ -0,0 +1,633 @@
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.categories]]
categories = [ 'eventlog' ]
handlers = [ 'event' ]
inherit = false
level = 'INFO'
propagate = 'ERR'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/hf3fs_meta_main.log'
max_file_size = '100MB'
max_files = 10
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/hf3fs_meta_main-err.log'
max_file_size = '100MB'
max_files = 10
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/hf3fs_meta_main-fatal.log'
max_file_size = '100MB'
max_files = 10
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/hf3fs_meta_main-event.log'
max_file_size = '100MB'
max_files = 10
name = 'event'
rotate = true
rotate_on_open = false
start_level = 'INFO'
stream_type = 'STDERR'
writer_type = 'EVENT'
[common.memory]
prof_active = false
prof_prefix = ''
[common.monitor]
collect_period = '1s'
num_collectors = 1
[[common.monitor.reporters]]
type = 'monitor_collector'
[common.monitor.reporters.monitor_collector]
remote_ip = ''
[server]
use_memkv = false
[server.background_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.background_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.background_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.background_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.background_client.io_worker.transport_pool]
max_connections = 1
[server.background_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.background_client.rdma_control]
max_concurrent_transmission = 64
[server.background_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.independent_thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'RDMA'
services = [ 'MetaSerde' ]
use_independent_thread_pool = false
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 8001
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'TCP'
services = [ 'Core' ]
use_independent_thread_pool = true
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 9001
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.fdb]
casual_read_risky = false
clusterFile = ''
default_backoff = 0
enableMultipleClient = false
externalClientDir = ''
externalClientPath = ''
multipleClientThreadNum = 4
readonly = false
trace_file = ''
trace_format = 'json'
[server.kv_engine]
use_memkv = false
[server.kv_engine.fdb]
casual_read_risky = false
clusterFile = ''
default_backoff = 0
enableMultipleClient = false
externalClientDir = ''
externalClientPath = ''
multipleClientThreadNum = 4
readonly = false
trace_file = ''
trace_format = 'json'
[server.meta]
acl_cache_time = '15s'
allow_directly_move_to_trash = false
allow_owner_change_immutable = false
allow_stat_deleted_inodes = true
authenticate = false
batch_stat_by_path_concurrent = 4
batch_stat_concurrent = 8
check_file_hole = false
dynamic_stripe = false
dynamic_stripe_growth = 2
dynamic_stripe_initial = 16
enable_new_chunk_engine = false
grv_cache = false
idempotent_record_clean = '1min'
idempotent_record_expire = '30min'
idempotent_remove = true
idempotent_rename = false
iflags_chain_allocation = false
iflags_chunk_engine = true
ignore_length_hint = false
inodeId_abort_on_duplicate = false
inodeId_check_unique = true
list_default_limit = 128
max_batch_operations = 4096
max_directory_depth = 64
max_remove_chunks_per_request = 32
max_symlink_count = 10
max_symlink_depth = 4
operation_timeout = '5s'
otrunc_replace_file = true
otrunc_replace_file_threshold = '1GB'
readonly = false
recursive_remove_check_owner = true
recursive_remove_perm_check = 1024
statfs_cache_time = '1min'
statfs_space_imbalance_threshold = 5
statfs_update_interval = '5s'
sync_on_prune_session = false
time_granularity = '1s'
[server.meta.background_hole_checker]
coroutines_num = 16
enable_work_stealing = false
queue_size = 4096
[server.meta.distributor]
timeout = '30s'
update_interval = '1s'
[server.meta.event_trace_log]
dump_interval = '30s'
enabled = true
max_num_writers = 1
max_row_group_length = 100000
trace_file_dir = '.'
[server.meta.forward]
addr_type = 'RDMA'
debug = true
timeout = '10s'
[server.meta.gc]
check_session = true
distributed_gc = true
enable = true
gc_delay_free_space_threshold = 5
gc_directory_concurrent = 4
gc_directory_delay = '0ns'
gc_directory_entry_batch = 32
gc_directory_entry_concurrent = 4
gc_file_concurrent = 32
gc_file_delay = '5min'
large_file_chunks = 128
recursive_perm_check = true
remove_chunks_batch_size = 32
retry_delay = '10min'
scan_batch = 4096
scan_interval = '200ms'
small_file_chunks = 32
txn_low_priority = false
[server.meta.gc.retry_remove_chunks]
init_wait_time = '10s'
max_retry_time = '30s'
max_wait_time = '10s'
retry_permanent_error = false
[server.meta.gc.workers]
coroutines_num = 8
enable_work_stealing = false
queue_size = 1024
[server.meta.retry_remove_chunks]
init_wait_time = '10s'
max_retry_time = '30s'
max_wait_time = '10s'
retry_permanent_error = false
[server.meta.retry_transaction]
max_backoff = '1s'
max_retry_count = 10
[server.meta.session_manager]
enable = true
scan_batch = 1024
scan_interval = '5min'
session_timeout = '5min'
sync_on_prune_session = false
[server.meta.session_manager.close_workers]
coroutines_num = 32
enable_work_stealing = false
queue_size = 1024
[server.meta.session_manager.scan_workers]
coroutines_num = 8
enable_work_stealing = false
queue_size = 128
[server.meta.user_cache]
buckets = 127
exist_ttl = '5min'
inexist_ttl = '10s'
[server.mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = false
enable_auto_heartbeat = true
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100
[server.storage_client]
check_overlapping_read_buffers = true
check_overlapping_write_buffers = false
chunk_checksum_type = 'CRC32C'
create_net_client_for_updates = false
implementation_type = 'RPC'
max_inline_read_bytes = '0'
max_inline_write_bytes = '0'
max_read_io_bytes = '0'
[server.storage_client.net_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.storage_client.net_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.storage_client.net_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.storage_client.net_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.storage_client.net_client.io_worker.transport_pool]
max_connections = 1
[server.storage_client.net_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.storage_client.net_client.rdma_control]
max_concurrent_transmission = 64
[server.storage_client.net_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.storage_client.net_client_for_updates]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.storage_client.net_client_for_updates.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.storage_client.net_client_for_updates.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.storage_client.net_client_for_updates.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.storage_client.net_client_for_updates.io_worker.transport_pool]
max_connections = 1
[server.storage_client.net_client_for_updates.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.storage_client.net_client_for_updates.rdma_control]
max_concurrent_transmission = 64
[server.storage_client.net_client_for_updates.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.storage_client.retry]
init_wait_time = '2s'
max_failures_before_failover = 1
max_retry_time = '5s'
max_wait_time = '5s'
[server.storage_client.traffic_control.query]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage_client.traffic_control.read]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage_client.traffic_control.remove]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage_client.traffic_control.truncate]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true
[server.storage_client.traffic_control.write]
max_batch_bytes = '4MB'
max_batch_size = 128
max_concurrent_requests = 32
max_concurrent_requests_per_server = 8
process_batches_in_parallel = true
random_shuffle_requests = true

View File

@@ -0,0 +1,2 @@
allow_empty_node_id = true
node_id = 0

View File

@@ -0,0 +1,93 @@
allow_dev_version = true
cluster_id = ''
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[ib_devices]
allow_no_usable_devices = false
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_roce_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
prefer_ibdevice = true
skip_inactive_ports = true
skip_unusable_device = true
subnets = []
[mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = false
enable_auto_heartbeat = true
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100

232
configs/mgmtd_main.toml Normal file
View File

@@ -0,0 +1,232 @@
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/mgmtd_main.log'
max_file_size = '100MB'
max_files = 10
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/mgmtd_main-err.log'
max_file_size = '100MB'
max_files = 10
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/mgmtd_main-fatal.log'
max_file_size = '100MB'
max_files = 10
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[common.memory]
prof_active = false
prof_prefix = ''
[common.monitor]
collect_period = '1s'
num_collectors = 1
[[common.monitor.reporters]]
type = 'monitor_collector'
[common.monitor.reporters.monitor_collector]
remote_ip = ""
[server.base.independent_thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'RDMA'
services = [ 'Mgmtd' ]
use_independent_thread_pool = false
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 8000
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'TCP'
services = [ 'Core' ]
use_independent_thread_pool = true
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 9000
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.service]
allow_heartbeat_from_unregistered = true
authenticate = false
bootstrapping_length = '2min'
bump_routing_info_version_interval = '5s'
check_status_interval = '10s'
client_session_timeout = '20min'
enable_routinginfo_cache = true
extend_lease_check_release_version = true
extend_lease_interval = '10s'
heartbeat_fail_interval = '1min'
heartbeat_ignore_stale_targets = true
heartbeat_ignore_unknown_targets = false
heartbeat_timestamp_valid_window = '30s'
lease_length = '1min'
new_chain_bootstrap_interval = '2min'
only_accept_client_uuid = false
retry_times_on_txn_errors = -1
send_heartbeat = true
send_heartbeat_interval = '10s'
suspicious_lease_interval = '20s'
target_info_load_interval = '1s'
target_info_persist_batch = 1000
target_info_persist_interval = '1s'
try_adjust_target_order_as_preferred = false
update_chains_interval = '1s'
update_metrics_interval = '1s'
validate_lease_on_write = true
[server.service.retry_transaction]
max_backoff = '1s'
max_retry_count = 10
[server.service.user_cache]
buckets = 127
exist_ttl = '5min'
inexist_ttl = '10s'

View File

@@ -0,0 +1,2 @@
allow_empty_node_id = true
node_id = 0

View File

@@ -0,0 +1,44 @@
allow_dev_version = true
cluster_id = ''
use_memkv = false
[fdb]
casual_read_risky = false
clusterFile = ''
default_backoff = 0
enableMultipleClient = false
externalClientDir = ''
externalClientPath = ''
multipleClientThreadNum = 4
readonly = false
trace_file = ''
trace_format = 'json'
[ib_devices]
allow_no_usable_devices = false
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_roce_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
prefer_ibdevice = true
skip_inactive_ports = true
skip_unusable_device = true
subnets = []
[kv_engine]
use_memkv = false
[kv_engine.fdb]
casual_read_risky = false
clusterFile = ''
default_backoff = 0
enableMultipleClient = false
externalClientDir = ''
externalClientPath = ''
multipleClientThreadNum = 4
readonly = false
trace_file = ''
trace_format = 'json'

View File

@@ -0,0 +1,141 @@
[common]
cluster_id = ''
[common.ib_devices]
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
device_filter = []
subnets = []
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/monitor_collector_main.log'
max_file_size = '100MB'
max_files = 10
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/monitor_collector_main-err.log'
max_file_size = '100MB'
max_files = 10
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/monitor_collector_main-fatal.log'
max_file_size = '100MB'
max_files = 10
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[server.base.independent_thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[server.base.groups]]
#default_timeout = '1s'
#drop_connections_interval = '1h'
network_type = 'TCP'
services = [ 'MonitorCollector' ]
use_independent_thread_pool = false
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drop_connections = 0
event_ack_batch = 128
#gid_index = 0
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
pkey_index = 0
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
traffic_class = 0
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
filter_list = []
listen_port = 10000
listen_queue_depth = 4096
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
[server.monitor_collector]
batch_commit_size = 4096
conn_threads = 32
queue_capacity = 204800
[server.monitor_collector.reporter]
type = 'clickhouse'
[server.monitor_collector.reporter.clickhouse]
db = ''
host = ''
passwd = ''
port = ''
user = ''

509
configs/storage_main.toml Normal file
View File

@@ -0,0 +1,509 @@
[[common.log.categories]]
categories = [ '.' ]
handlers = [ 'normal', 'err', 'fatal' ]
inherit = true
level = 'INFO'
propagate = 'NONE'
[[common.log.handlers]]
async = true
file_path = '/var/log/3fs/storage_main.log'
max_file_size = '100MB'
max_files = 10
name = 'normal'
rotate = true
rotate_on_open = false
start_level = 'NONE'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/storage_main-err.log'
max_file_size = '100MB'
max_files = 10
name = 'err'
rotate = true
rotate_on_open = false
start_level = 'ERR'
stream_type = 'STDERR'
writer_type = 'FILE'
[[common.log.handlers]]
async = false
file_path = '/var/log/3fs/storage_main-fatal.log'
max_file_size = '100MB'
max_files = 10
name = 'fatal'
rotate = true
rotate_on_open = false
start_level = 'FATAL'
stream_type = 'STDERR'
writer_type = 'STREAM'
[common.memory]
prof_active = false
prof_prefix = ''
[common.monitor]
collect_period = '1s'
num_collectors = 1
[[common.monitor.reporters]]
type = 'monitor_collector'
[common.monitor.reporters.monitor_collector]
remote_ip = ""
[server]
speed_up_quit = true
use_coroutines_pool_read = true
use_coroutines_pool_update = true
[server.aio_read_worker]
enable_io_uring = true
inflight_control_offset = 128
ioengine = 'libaio'
max_events = 512
min_complete = 128
num_threads = 32
queue_size = 4096
wait_all_inflight = false
[server.allocate_worker]
max_remain_groups = 8
max_remain_ultra_groups = 4
max_reserved_chunks = '1GB'
min_remain_groups = 4
min_remain_ultra_groups = 0
[server.base.independent_thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.base.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 32
num_proc_threads = 32
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'RDMA'
services = [ 'StorageSerde' ]
use_independent_thread_pool = false
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 8000
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[[server.base.groups]]
check_connections_interval = '1min'
connection_expiration_time = '1day'
network_type = 'TCP'
services = [ 'Core' ]
use_independent_thread_pool = true
[server.base.groups.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.base.groups.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.base.groups.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.base.groups.io_worker.transport_pool]
max_connections = 1
[server.base.groups.listener]
domain_socket_index = 1
filter_list = []
listen_port = 9000
listen_queue_depth = 4096
rdma_accept_timeout = '15s'
rdma_listen_ethernet = true
reuse_port = false
[server.base.groups.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.buffer_pool]
big_rdmabuf_count = 64
big_rdmabuf_size = '64MB'
rdmabuf_count = 1024
rdmabuf_size = '4MB'
[server.check_worker]
disk_low_space_threshold = 0.95999999999999996
disk_reject_create_chunk_threshold = 0.97999999999999998
emergency_recycling_ratio = 0.94999999999999996
update_target_size_interval = '10s'
[server.client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.client.io_worker.transport_pool]
max_connections = 1
[server.client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.client.rdma_control]
max_concurrent_transmission = 64
[server.client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.coroutines_pool_default]
coroutines_num = 64
queue_size = 1024
threads_num = 8
[server.coroutines_pool_read]
coroutines_num = 64
queue_size = 1024
threads_num = 8
[server.coroutines_pool_sync]
coroutines_num = 64
queue_size = 1024
threads_num = 8
[server.coroutines_pool_update]
coroutines_num = 64
queue_size = 1024
threads_num = 8
[server.dump_worker]
dump_interval = '1day'
dump_root_path = ''
high_cpu_usage_threshold = 100
[server.forward_client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[server.forward_client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[server.forward_client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[server.forward_client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[server.forward_client.io_worker.transport_pool]
max_connections = 1
[server.forward_client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[server.forward_client.rdma_control]
max_concurrent_transmission = 64
[server.forward_client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[server.mgmtd]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = false
enable_auto_heartbeat = true
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100
[server.reliable_forwarding]
max_inline_forward_bytes = '0'
retry_first_wait = '100ms'
retry_max_wait = '1s'
retry_total_time = '1min'
[server.reliable_update]
clean_up_expired_clients = false
expired_clients_timeout = '1h'
[server.storage]
apply_transmission_before_getting_semaphore = true
batch_read_ignore_chain_version = false
batch_read_job_split_size = 1024
max_concurrent_rdma_reads = 256
max_concurrent_rdma_writes = 256
max_num_results_per_query = 100
post_buffer_per_bytes = '64KB'
rdma_transmission_req_timeout = '0ns'
read_only = false
[server.storage.event_trace_log]
dump_interval = '30s'
enabled = true
max_num_writers = 1
max_row_group_length = 100000
trace_file_dir = '.'
[server.storage.write_worker]
bg_num_threads = 8
num_threads = 32
queue_size = 4096
[server.sync_meta_kv_worker]
sync_meta_kv_interval = '1min'
[server.sync_worker]
batch_size = 16
full_sync_chains = []
full_sync_level = 'NONE'
num_channels = 1024
num_threads = 16
sync_start_timeout = '10s'
[server.sync_worker.batch_concurrency_limiter]
max_concurrency = 64
[server.sync_worker.pool]
coroutines_num = 64
enable_work_stealing = false
queue_size = 1024
[server.targets]
allow_disk_without_uuid = false
collect_all_fds = true
create_engine_path = true
space_info_cache_timeout = '5s'
target_num_per_path = 0
target_paths = []
[server.targets.storage_target]
force_persist = true
kv_path = ''
migrate_kv_store = false
mutex_num = 257
point_query_strategy = 'NONE'
[server.targets.storage_target.file_store]
preopen_chunk_size_list = []
[server.targets.storage_target.kv_store]
create_if_missing = false
integrate_leveldb_log = false
leveldb_block_cache_size = '8GB'
leveldb_iterator_fill_cache = true
leveldb_shared_block_cache = true
leveldb_sst_file_size = '16MB'
leveldb_write_buffer_size = '16MB'
rocksdb_avoid_flush_during_recovery = false
rocksdb_avoid_flush_during_shutdown = false
rocksdb_avoid_unnecessary_blocking_io = false
rocksdb_block_cache_size = '8GB'
rocksdb_block_size = '4KB'
rocksdb_bloom_filter_bits_per_key = 10
rocksdb_compression = 'kNoCompression'
rocksdb_enable_bloom_filter = true
rocksdb_enable_pipelined_write = false
rocksdb_enable_prefix_transform = true
rocksdb_keep_log_file_num = 10
rocksdb_level0_file_num_compaction_trigger = 4
rocksdb_lowest_used_cache_tier = 'kNonVolatileBlockTier'
rocksdb_max_manifest_file_size = '64MB'
rocksdb_num_levels = 7
rocksdb_prepopulate_block_cache = 'kDisable'
rocksdb_readahead_size = '2MB'
rocksdb_shared_block_cache = true
rocksdb_stats_dump_period = '2min'
rocksdb_target_file_size_base = '64MB'
rocksdb_target_file_size_multiplier = 1
rocksdb_threads_num = 8
rocksdb_unordered_write = false
rocksdb_wal_recovery_mode = 'kTolerateCorruptedTailRecords'
rocksdb_write_buffer_size = '16MB'
sync_when_write = true
type = 'LevelDB'
[server.targets.storage_target.meta_store]
allocate_size = '256MB'
punch_hole_batch_size = 16
recycle_batch_size = 256
removed_chunk_expiration_time = '3day'
removed_chunk_force_recycled_time = '1h'

View File

@@ -0,0 +1,2 @@
allow_empty_node_id = true
node_id = 0

View File

@@ -0,0 +1,93 @@
allow_dev_version = true
cluster_id = ''
[client]
default_compression_level = 0
default_compression_threshold = '128KB'
default_log_long_running_threshold = '0ns'
default_report_metrics = false
default_send_retry_times = 1
default_timeout = '1s'
enable_rdma_control = false
force_use_tcp = false
[client.io_worker]
num_event_loop = 1
rdma_connect_timeout = '5s'
read_write_rdma_in_event_thread = false
read_write_tcp_in_event_thread = false
tcp_connect_timeout = '1s'
wait_to_retry_send = '100ms'
[client.io_worker.connect_concurrency_limiter]
max_concurrency = 4
[client.io_worker.ibsocket]
buf_ack_batch = 8
buf_signal_batch = 8
buf_size = 16384
drain_timeout = '5s'
drop_connections = 0
event_ack_batch = 128
max_rd_atomic = 16
max_rdma_wr = 128
max_rdma_wr_per_post = 32
max_sge = 16
min_rnr_timer = 1
record_bytes_per_peer = false
record_latency_per_peer = false
retry_cnt = 7
rnr_retry = 0
send_buf_cnt = 32
sl = 0
start_psn = 0
timeout = 14
[client.io_worker.transport_pool]
max_connections = 1
[client.processor]
enable_coroutines_pool = true
max_coroutines_num = 256
max_processing_requests_num = 4096
response_compression_level = 1
response_compression_threshold = '128KB'
[client.rdma_control]
max_concurrent_transmission = 64
[client.thread_pool]
bg_thread_pool_stratetry = 'SHARED_QUEUE'
collect_stats = false
enable_work_stealing = false
io_thread_pool_stratetry = 'SHARED_QUEUE'
num_bg_threads = 2
num_connect_threads = 2
num_io_threads = 2
num_proc_threads = 2
proc_thread_pool_stratetry = 'SHARED_QUEUE'
[ib_devices]
allow_no_usable_devices = false
allow_unknown_zone = true
default_network_zone = 'UNKNOWN'
default_pkey_index = 0
default_roce_pkey_index = 0
default_traffic_class = 0
device_filter = []
fork_safe = true
prefer_ibdevice = true
skip_inactive_ports = true
skip_unusable_device = true
subnets = []
[mgmtd_client]
accept_incomplete_routing_info_during_mgmtd_bootstrapping = true
auto_extend_client_session_interval = '10s'
auto_heartbeat_interval = '10s'
auto_refresh_interval = '10s'
enable_auto_extend_client_session = false
enable_auto_heartbeat = true
enable_auto_refresh = true
mgmtd_server_addresses = []
work_queue_size = 100

373
deploy/README.md Normal file
View File

@@ -0,0 +1,373 @@
# 3FS Setup Guide
This section provides a manual deployment guide for setting up a six-node cluster with the cluster ID `stage`.
## Installation prerequisites
### Hardware specifications
| Node | OS | IP | Memory | SSD | RDMA |
|----------|---------------|--------------|--------|------------|-------|
| meta | Ubuntu 22.04 | 192.168.1.1 | 128GB | - | RoCE |
| storage1 | Ubuntu 22.04 | 192.168.1.2 | 512GB | 14TB × 16 | RoCE |
| storage2 | Ubuntu 22.04 | 192.168.1.3 | 512GB | 14TB × 16 | RoCE |
| storage3 | Ubuntu 22.04 | 192.168.1.4 | 512GB | 14TB × 16 | RoCE |
| storage4 | Ubuntu 22.04 | 192.168.1.5 | 512GB | 14TB × 16 | RoCE |
| storage5 | Ubuntu 22.04 | 192.168.1.6 | 512GB | 14TB × 16 | RoCE |
> **RDMA Configuration**
> 1. Assign IP addresses to RDMA NICs. Multiple RDMA NICs (InfiniBand or RoCE) are supported on each node.
> 2. Check RDMA connectivity between nodes using `ib_write_bw`.
### Third-party dependencies
In production environment, it is recommended to install FoundationDB and ClickHouse on dedicated nodes.
| Service | Node |
|------------|-------------------------|
| [ClickHouse](https://clickhouse.com/docs/install) | meta |
| [FoundationDB](https://apple.github.io/foundationdb/administration.html) | meta |
> **FoundationDB**
> 1. Ensure that the version of FoundationDB client matches the server version, or copy the corresponding version of `libfdb_c.so` to maintain compatibility.
> 2. Find the `fdb.cluster` file and `libfdb_c.so` at `/etc/foundationdb/fdb.cluster`, `/usr/lib/libfdb_c.so` on nodes with FoundationDB installed.
---
## Step 0: Build 3FS
Follow the [instructions](../README.md#build-3fs) to build 3FS. Binaries can be found in `build/bin`.
### Services and clients
The following steps show how to install 3FS services in `/opt/3fs/bin` and the config files in `/opt/3fs/etc`.
| Service | Binary | Config files | NodeID | Node |
|------------|-------------------------|-----------------------------------------------------------------------------|--------|---------------|
| monitor | monitor_collector_main | [monitor_collector_main.toml](../configs/monitor_collector_main.toml) | - | meta |
| admin_cli | admin_cli | [admin_cli.toml](../configs/admin_cli.toml)<br>fdb.cluster | - | meta<br>storage1<br>storage2<br>storage3<br>storage4<br>storage5 |
| mgmtd | mgmtd_main | [mgmtd_main_launcher.toml](../configs/mgmtd_main_launcher.toml)<br>[mgmtd_main.toml](../configs/mgmtd_main.toml)<br>[mgmtd_main_app.toml](../configs/mgmtd_main_app.toml)<br>fdb.cluster | 1 | meta |
| meta | meta_main | [meta_main_launcher.toml](../configs/meta_main_launcher.toml)<br>[meta_main.toml](../configs/meta_main.toml)<br>[meta_main_app.toml](../configs/meta_main_app.toml)<br>fdb.cluster | 100 | meta |
| storage | storage_main | [storage_main_launcher.toml](../configs/storage_main_launcher.toml)<br>[storage_main.toml](../configs/storage_main.toml)<br>[storage_main_app.toml](../configs/storage_main_app.toml) | 10001~10005 | storage1<br>storage2<br>storage3<br>storage4<br>storage5 |
| client | hf3fs_fuse_main | [hf3fs_fuse_main_launcher.toml](../configs/hf3fs_fuse_main_launcher.toml)<br>[hf3fs_fuse_main.toml](../configs/hf3fs_fuse_main.toml) | - | meta |
---
## Step 1: Create ClickHouse tables for metrics
Import the SQL file into ClickHouse:
```bash
clickhouse-client -n < ~/3fs/deploy/sql/3fs-monitor.sql
```
---
## Step 2: Monitor service
Install `monitor_collector` service on the **meta** node.
1. Copy `monitor_collector_main` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`, and create log directory `/var/log/3fs`.
```bash
mkdir -p /opt/3fs/{bin,etc}
mkdir -p /var/log/3fs
cp ~/3fs/build/bin/monitor_collector_main /opt/3fs/bin
cp ~/3fs/configs/monitor_collector_main.toml /opt/3fs/etc
```
2. Update [`monitor_collector_main.toml`](../configs/monitor_collector_main.toml) to add a ClickHouse connection:
```toml
[server.monitor_collector.reporter]
type = 'clickhouse'
[server.monitor_collector.reporter.clickhouse]
db = '3fs'
host = '<CH_HOST>'
passwd = '<CH_PASSWD>'
port = '<CH_PORT>'
user = '<CH_USER>'
```
3. Start monitor service:
```bash
cp ~/3fs/deploy/systemd/monitor_collector_main.service /usr/lib/systemd/system
systemctl start monitor_collector_main
```
Note that
> - Multiple instances of monitor services can be deployed behind a virtual IP address to share the traffic.
> - Other services communicate with the monitor service over a TCP connection.
---
## Step 3: Admin client
Install `admin_cli` on **all** nodes.
1. Copy `admin_cli` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`.
```bash
mkdir -p /opt/3fs/{bin,etc}
rsync -avz meta:~/3fs/build/bin/admin_cli /opt/3fs/bin
rsync -avz meta:~/3fs/configs/admin_cli.toml /opt/3fs/etc
rsync -avz meta:/etc/foundationdb/fdb.cluster /opt/3fs/etc
```
2. Update [`admin_cli.toml`](../configs/admin_cli.toml) to set `cluster_id` and `clusterFile`:
```toml
cluster_id = "stage"
[fdb]
clusterFile = '/opt/3fs/etc/fdb.cluster'
```
The full help documentation for `admin_cli` can be displayed by running the following command:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml help
```
---
## Step 4: Mgmtd service
Install `mgmtd` service on **meta** node.
1. Copy `mgmtd_main` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`.
```bash
cp ~/3fs/build/bin/mgmtd_main /opt/3fs/bin
cp ~/3fs/configs/{mgmtd_main.toml,mgmtd_main_launcher.toml,mgmtd_main_app.toml} /opt/3fs/etc
```
2. Update config files:
- Set mgmtd `node_id = 1` in [`mgmtd_main_app.toml`](../configs/mgmtd_main_app.toml).
- Edit [`mgmtd_main_launcher.toml`](../configs/mgmtd_main_launcher.toml) to set the `cluster_id` and `clusterFile`:
```toml
cluster_id = "stage"
[fdb]
clusterFile = '/opt/3fs/etc/fdb.cluster'
```
- Set monitor address in [`mgmtd_main.toml`](../configs/mgmtd_main.toml):
```toml
[common.monitor.reporters.monitor_collector]
remote_ip = "192.168.1.1:10000"
```
3. Initialize the cluster:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml "init-cluster --mgmtd /opt/3fs/etc/mgmtd_main.toml 1 1048576 16"
```
The parameters of `admin_cli`:
> - `1` the chain table ID
> - `1048576` the chunk size in bytes
> - `16` the file strip size
Run `help init-cluster` for full documentation.
4. Start mgmtd service:
```bash
cp ~/3fs/deploy/systemd/mgmtd_main.service /usr/lib/systemd/system
systemctl start mgmtd_main
```
5. Run `list-nodes` command to check if the cluster has been successfully initialized:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "list-nodes"
```
If multiple instances of `mgmtd` services deployed, one of the `mgmtd` services is elected as the primary; others are secondaries. Automatic failover occurs when the primary fails.
---
## Step 5: Meta service
Install `meta` service on **meta** node.
1. Copy `meta_main` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`.
```bash
cp ~/3fs/build/bin/meta_main /opt/3fs/bin
cp ~/3fs/configs/{meta_main_launcher.toml,meta_main.toml,meta_main_app.toml} /opt/3fs/etc
```
2. Update config files:
- Set meta `node_id = 100` in [`meta_main_app.toml`](../configs/meta_main_app.toml).
- Set `cluster_id`, `clusterFile` and mgmtd address in [`meta_main_launcher.toml`](../configs/meta_main_launcher.toml):
```toml
cluster_id = "stage"
[mgmtd_client]
mgmtd_server_addresses = ["RDMA://192.168.1.1:8000"]
```
- Set mgmtd and monitor addresses in [`meta_main.toml`](../configs/meta_main.toml).
```toml
[server.mgmtd_client]
mgmtd_server_addresses = ["RDMA://192.168.1.1:8000"]
[common.monitor.reporters.monitor_collector]
remote_ip = "192.168.1.1:10000"
[server.fdb]
clusterFile = '/opt/3fs/etc/fdb.cluster'
```
3. Config file of meta service is managed by mgmtd service. Use `admin_cli` to upload the config file to mgmtd:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "set-config --type META --file /opt/3fs/etc/meta_main.toml"
```
4. Start meta service:
```bash
cp ~/3fs/deploy/systemd/meta_main.service /usr/lib/systemd/system
systemctl start meta_main
```
5. Run `list-nodes` command to check if meta service has joined the cluster:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "list-nodes"
```
If multiple instances of `meta` services deployed, meta requests will be evenly distributed to all instances.
---
## Step 6: Storage service
Install `storage` service on **storage** node.
1. Format the attached 16 SSDs as XFS and mount at `/storage/data{1..16}`, then create data directories `/storage/data{1..16}/3fs` and log directory `/var/log/3fs`.
```bash
mkdir -p /storage/data{1..16}
mkdir -p /var/log/3fs
for i in {1..16};do mkfs.xfs -L data${i} /dev/nvme${i}n1;mount -o noatime,nodiratime -L data${i} /storage/data${i};done
mkdir -p /storage/data{1..16}/3fs
```
2. Increase the max number of asynchronous aio requests:
```bash
sysctl -w fs.aio-max-nr=67108864
```
3. Copy `storage_main` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`.
```bash
rsync -avz meta:~/3fs/build/bin/storage_main /opt/3fs/bin
rsync -avz meta:~/3fs/configs/{storage_main_launcher.toml,storage_main.toml,storage_main_app.toml} /opt/3fs/etc
```
4. Update config files:
- Set `node_id` in [`storage_main_app.toml`](../configs/storage_main_app.toml). Each storage service is assigned a unique id between `10001` and `10005`.
- Set `cluster_id` and mgmtd address in [`storage_main_launcher.toml`](../configs/storage_main_launcher.toml).
```toml
cluster_id = "stage"
[mgmtd_client]
mgmtd_server_addresses = ["RDMA://192.168.1.1:8000"]
```
- Add target paths in [`storage_main.toml`](../configs/storage_main.toml):
```toml
[server.mgmtd]
mgmtd_server_address = ["RDMA://192.168.1.1:8000"]
[common.monitor.reporters.monitor_collector]
remote_ip = "192.168.1.1:10000"
[server.targets]
target_paths = ["/storage/data1/3fs","/storage/data2/3fs","/storage/data3/3fs","/storage/data4/3fs","/storage/data5/3fs","/storage/data6/3fs","/storage/data7/3fs","/storage/data8/3fs","/storage/data9/3fs","/storage/data10/3fs","/storage/data11/3fs","/storage/data12/3fs","/storage/data13/3fs","/storage/data14/3fs","/storage/data15/3fs","/storage/data16/3fs",]
```
5. Config file of storage service is managed by mgmtd service. Use `admin_cli` to upload the config file to mgmtd:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "set-config --type STORAGE --file /opt/3fs/etc/storage_main.toml"
```
6. Start storage service:
```bash
rsync -avz meta:~/3fs/deploy/systemd/storage_main.service /usr/lib/systemd/system
systemctl start storage_main
```
7. Run `list-nodes` command to check if storage service has joined the cluster:
```
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "list-nodes"
```
---
## Step 7: Create admin user, storage targets and chain table
1. Create an admin user:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "user-add --root --admin 0 root"
```
Save the admin token to `/opt/3fs/etc/token.txt`.
2. Generate `admin_cli` commands to create storage targets on 5 storage nodes (16 SSD per node, 6 targets per SSD).
- Follow instructions at [here](data_placement/README.md) to install Python packages.
```bash
python ~/3fs/deploy/data_placement/src/model/data_placement.py \
-ql -relax -type CR --num_nodes 5 --replication_factor 3 --min_targets_per_disk 6
python ~/3fs/deploy/data_placement/src/setup/gen_chain_table.py \
--chain_table_type CR --node_id_begin 10001 --node_id_end 10005 \
--num_disks_per_node 16 --num_targets_per_disk 6 \
--target_id_prefix 1 --chain_id_prefix 9 \
--incidence_matrix_path output/DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1/incidence_matrix.pickle
```
The following 3 files will be generated in `output` directory: `create_target_cmd.txt`, `generated_chains.csv`, and `generated_chain_table.csv`.
3. Create storage targets:
```bash
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' --config.user_info.token $(<"/opt/3fs/etc/token.txt") < output/create_target_cmd.txt
```
4. Upload chains to mgmtd service:
```bash
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' --config.user_info.token $(<"/opt/3fs/etc/token.txt") "upload-chains output/generated_chains.csv"
```
5. Upload chain table to mgmtd service:
```bash
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' --config.user_info.token $(<"/opt/3fs/etc/token.txt") "upload-chain-table --desc stage 1 output/generated_chain_table.csv"
```
6. List chains and chain tables to check if they have been correctly uploaded:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "list-chains"
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "list-chain-tables"
```
---
## Step 8: FUSE client
For simplicity FUSE client is deployed on the **meta** node in this guide. However, we strongly advise against deploying clients on service nodes in production environment.
1. Copy `hf3fs_fuse_main` to `/opt/3fs/bin` and config files to `/opt/3fs/etc`.
```bash
cp ~/3fs/build/bin/hf3fs_fuse_main /opt/3fs/bin
cp ~/3fs/configs/{hf3fs_fuse_main_launcher.toml,hf3fs_fuse_main.toml,hf3fs_fuse_main_app.toml} /opt/3fs/etc
```
2. Create the mount point:
```bash
mkdir -p /3fs/stage
```
3. Set cluster ID, mountpoint, token file and mgmtd address in [`hf3fs_fuse_main_launcher.toml`](../configs/hf3fs_fuse_main_launcher.toml)
```toml
cluster_id = "stage"
mountpoint = '/3fs/stage'
token_file = '/opt/3fs/etc/token.txt'
[mgmtd_client]
mgmtd_server_addresses = ["RDMA://192.168.1.1:8000"]
```
4. Set mgmtd and monitor address in [`hf3fs_fuse_main.toml`](../configs/hf3fs_fuse_main.toml).
```toml
[mgmtd]
mgmtd_server_addresses = ["RDMA://192.168.1.1:8000"]
[common.monitor.reporters.monitor_collector]
remote_ip = "192.168.1.1:10000"
```
5. Config file of FUSE client is also managed by mgmtd service. Use `admin_cli` to upload the config file to mgmtd:
```bash
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml --config.mgmtd_client.mgmtd_server_addresses '["RDMA://192.168.1.1:8000"]' "set-config --type FUSE --file /opt/3fs/etc/hf3fs_fuse_main.toml"
```
6. Start FUSE client:
```bash
cp ~/3fs/deploy/systemd/hf3fs_fuse_main.service /usr/lib/systemd/system
systemctl start hf3fs_fuse_main
```
7. Check if 3FS has been mounted at `/3fs/stage`:
```bash
mount | grep '/3fs/stage'
```
## FAQ
<details>
<summary>How to troubleshoot <code>admin_cli init-cluster</code> error?</summary>
If mgmtd fails to start after running `init-cluster`, the most likely cause is an error in `mgmtd_main.toml`. Any changes to this file require clearing all FoundationDB data and re-running `init-cluster`
</details>
---
<details>
<summary>How to build a single-node cluster?</summary>
A minimum of two storage services is required for data replication. If set `--num-nodes=1`, the `gen_chain_table.py` script will fail. In a test environment, this limitation can be bypassed by deploying multiple storage services on a single machine.
</details>
---
<details>
<summary>How to update config files?</summary>
All config files are managed by mgmtd. If any `*_main.toml` is updated, such as `storage_main.toml`, the modified file should be uploaded using `admin_cli set-config`.
</details>
---
<details>
<summary>How to troubleshoot common deployment issues?</summary>
When encountering any error during deployment,
- Check the log messages in `stdout/stderr` using `journalctl`, especially during service startup.
- Check log files stored in `/var/log/3fs/` on service and client nodes.
- Ensure that the directory `/var/log/3fs/` exists before starting any service.
</details>

17
deploy/data_placement/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
__pycache__
.ipynb_checkpoints
.tmp/
dist/
build/
output/
*.egg-info/
test/scratch/
test/runtime/
*.log
*.pyc
*.xml
.tmp/
.idea
.coverage
.vscode/
.hypothesis/

View File

@@ -0,0 +1,60 @@
# How to generate chain tables
Suppose we are going to setup a small 3FS cluster:
- 3 replicas for each chunk
- 5 storage nodes: `10001 ... 10005`
- 16 SSDs attached to each node
- 6 storage targets on each SSD
First generate a solution of the data placement problem.
```bash
$ python src/model/data_placement.py -ql -relax -type CR --num_nodes 5 --replication_factor 3 --min_targets_per_disk 6 --init_timelimit 600
...
2025-02-24 14:25:13.623 | SUCCESS | __main__:solve:165 - optimal solution:
- Status: ok
Termination condition: optimal
Termination message: TerminationCondition.optimal
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 1,2: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 1,3: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 1,4: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 1,5: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 2,1: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 2,3: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 2,4: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 2,5: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 3,1: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 3,2: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 3,4: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 3,5: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 4,1: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 4,2: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 4,3: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 4,5: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 5,1: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 5,2: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 5,3: 1.5
2025-02-24 14:25:13.624 | DEBUG | __main__:check_solution:322 - 5,4: 1.5
2025-02-24 14:25:13.624 | INFO | __main__:check_solution:331 - min_peer_traffic=1.5 max_peer_traffic=1.5
2025-02-24 14:25:13.624 | INFO | __main__:check_solution:332 - total_traffic=30.0 max_total_traffic=30
2025-02-24 14:25:14.147 | SUCCESS | __main__:run:147 - saved solution to: output/DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1
```
Note that some combinations of `--num_nodes` and `--replication_factor` may have no solution.
Then generate commands to create/remove storage targets.
```bash
$ python src/setup/gen_chain_table.py --chain_table_type CR --node_id_begin 10001 --node_id_end 10005 --num_disks_per_node 16 --num_targets_per_disk 6 --incidence_matrix_path output/DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1/incidence_matrix.pickle
$ ls -1 output/
DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1
appsi_highs.log
create_target_cmd.txt
generated_chain_table.csv
generated_chains.csv
remove_target_cmd.txt
```

View File

@@ -0,0 +1,12 @@
psutil
pandas
plotly
loguru
highspy==1.8.0
pyomo==6.8.0
coverage~=7.4.4
pytest==8.2.1
pytest-cov==5.0.0
pytest-forked==1.6.0
pytest-xdist==3.6.1
pytest-timeout==2.3.1

View File

View File

@@ -0,0 +1,549 @@
import math
import pickle
import random
import time
import psutil
import os.path
import pandas as pd
import pyomo.environ as po
import plotly.express as px
from typing import Dict, Generator, Literal, Tuple
from loguru import logger
from pyomo.opt import SolverStatus, TerminationCondition
class InfeasibleModel(Exception):
pass
class SolverTimeout(Exception):
pass
class SolverError(Exception):
pass
class InvalidSolution(Exception):
pass
class DataPlacementModel(object):
def __init__(self, chain_table_type: Literal["EC", "CR"], num_nodes, group_size, num_groups=None, num_targets_per_disk=None, min_targets_per_disk=1, bibd_only=False, qlinearize=False, relax_lb=1, relax_ub=0):
if num_targets_per_disk is None:
num_nodes, num_groups, num_targets_per_disk, group_size = DataPlacementModel.find_params(num_nodes, group_size, min_r=min_targets_per_disk, bibd_only=bibd_only)
self.chain_table_type = chain_table_type
self.num_nodes = num_nodes
self.group_size = group_size
self.num_targets_per_disk = num_targets_per_disk
self.num_groups = num_groups or self.num_targets_total // self.group_size
self.bibd_only = bibd_only
self.qlinearize = qlinearize
self.relax_lb = relax_lb
self.relax_ub = relax_ub
def __repr__(self):
v, b, r, k, λ = self.v, self.b, self.r, self.k, self.λ
lb, ub = self.relax_lb, self.relax_ub
return f"{self.__class__.__name__}-{v=},{b=},{r=},{k=},{λ=},{lb=},{ub=}"
__str__ = __repr__
@property
def path_name(self):
return str(self).translate(str.maketrans(' ,:=', '---_'))
@property
def v(self):
return self.num_nodes
@property
def b(self):
return self.num_groups
@property
def r(self):
return self.num_targets_per_disk
@property
def k(self):
return self.group_size
@property
def λ(self):
return self.max_recovery_traffic_on_peer
@property
def num_targets_used(self):
return self.num_groups * self.group_size
@property
def num_targets_total(self):
return self.num_nodes * self.num_targets_per_disk
@property
def all_targets_used(self):
return self.num_targets_used == self.num_targets_total
@property
def balanced_peer_traffic(self):
return self.all_targets_used and self.sum_recovery_traffic_per_failure % (self.num_nodes-1) == 0
@property
def recovery_traffic_factor(self):
return (self.group_size - 1) if self.chain_table_type == "EC" else 1
@property
def sum_recovery_traffic_per_failure(self):
return self.num_targets_per_disk * self.recovery_traffic_factor
@property
def max_recovery_traffic_on_peer(self):
return math.ceil(self.sum_recovery_traffic_per_failure / (self.num_nodes-1))
@property
def balanced_incomplete_block_design(self):
return self.bibd_only and self.balanced_peer_traffic and self.relax_ub == 0
@staticmethod
def find_params(v, k, min_r=1, max_r=100, bibd_only=False):
if bibd_only: min_r = max(min_r, k)
for r in range(min_r, max_r):
if v * r % k == 0 and r * (k - 1) >= v - 1:
b = v * r // k
if not bibd_only or r * (k - 1) % (v - 1) == 0:
return v, b, r, k
raise ValueError(f"cannot find valid params: {v=}, {k=}")
def run(self, pyomo_solver=None, threads=psutil.cpu_count(logical=False), init_timelimit=1800, max_timelimit=3600*2, auto_relax=False, output_root="output", verbose=False, add_elapsed_time=None):
init_relax_lb = self.relax_lb
init_relax_ub = self.relax_ub
timelimit = 0
num_loops = self.max_recovery_traffic_on_peer*2
os.makedirs(output_root, exist_ok=True)
for loop in range(num_loops):
try:
logger.info(f"solving model with {pyomo_solver} #{loop}: {self}")
if add_elapsed_time is not None:
add_elapsed_time()
timelimit = min(timelimit + init_timelimit, max_timelimit)
instance = self.solve(pyomo_solver, threads, timelimit, output_root, verbose)
if add_elapsed_time is not None:
add_elapsed_time(f"solve model time (lb={self.relax_lb}, ub={self.relax_ub})")
except (InfeasibleModel, SolverTimeout) as ex:
logger.error(f"cannot find solution for current params: {ex}")
if auto_relax:
self.relax_lb = init_relax_lb + (loop+1) // 2
self.relax_ub = init_relax_ub + (loop+2) // 2
continue
elif loop + 1 < num_loops:
logger.critical(f"failed to find solution after {num_loops} attempts")
raise ex
else:
raise ex
else:
output_path = os.path.join(output_root, self.path_name)
os.makedirs(output_path, exist_ok=True)
self.save_solution(instance, output_path)
self.visualize_solution(instance, output_path)
logger.success(f"saved solution to: {output_path}")
return instance
logger.catch(reraise=True, message="failed to solve model")
def solve(self, pyomo_solver=None, threads=psutil.cpu_count(logical=False), timelimit=3600, output_path="output", verbose=False):
if "highs" in pyomo_solver:
self.qlinearize = True
instance = self.build_model()
if verbose: instance.pprint()
try:
results = self.solve_model(instance, pyomo_solver, threads, timelimit, output_path)
except RuntimeError as ex:
raise SolverError("unknown runtime error") from ex
if (results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal):
logger.success(f"optimal solution: {str(results.solver)}")
if pyomo_solver is not None: instance.solutions.load_from(results)
elif results.solver.termination_condition == TerminationCondition.infeasible:
raise InfeasibleModel(f"infeasible: {str(results.solver)}")
elif results.solver.termination_condition in (TerminationCondition.maxTimeLimit, TerminationCondition.maxIterations):
raise SolverTimeout(f"timeout: {str(results.solver)}")
else:
raise SolverError(f"error: {str(results.solver)}")
if verbose: self.print_solution(instance)
try:
self.check_solution(instance)
except AssertionError as ex:
raise InvalidSolution from ex
return instance
def build_model(self):
logger.info(f"{self.num_nodes=} {self.num_targets_per_disk=} {self.group_size=} {self.num_groups=} {self.qlinearize=} {self.relax_lb=} {self.relax_ub=}")
# v >= k
assert self.num_nodes >= self.group_size, f"{self.num_nodes=} < {self.group_size=}"
# Fisher's inequality
if self.balanced_incomplete_block_design:
# b >= v
assert self.num_groups >= self.num_nodes, f"{self.num_groups=} < {self.num_nodes=}"
# r >= k
assert self.num_targets_per_disk >= self.group_size, f"{self.num_targets_per_disk=} < {self.group_size=}"
logger.info(f"{self.sum_recovery_traffic_per_failure=} {self.max_recovery_traffic_on_peer=}")
if self.sum_recovery_traffic_per_failure < self.num_nodes - 1:
logger.warning(f"some disks do not share recovery traffic: {self.sum_recovery_traffic_per_failure=} < {self.num_nodes=} - 1")
logger.info(f"{self.all_targets_used=} {self.balanced_peer_traffic=}")
logger.info(f"{self.num_targets_used=} {self.num_targets_total=}")
if self.num_targets_used < self.num_targets_total:
logger.warning(f"some disks have unused targets: {self.num_targets_used=} < {self.num_targets_total=}")
else:
assert self.num_targets_used == self.num_targets_total, f"{self.num_targets_used=} > {self.num_targets_total=}"
model = po.ConcreteModel()
# index sets
model.disks = po.RangeSet(1, self.num_nodes)
model.target_idxs = po.RangeSet(1, self.num_targets_per_disk)
model.targets = model.disks * model.target_idxs
model.groups = po.RangeSet(1, self.num_groups)
def disk_pairs_init(model):
for disk in model.disks:
for peer in model.disks:
if peer > disk:
yield (disk, peer)
model.disk_pairs = po.Set(dimen=2, initialize=disk_pairs_init)
# variables
model.disk_used_by_group = po.Var(model.disks, model.groups, domain=po.Binary)
if self.qlinearize:
model.disk_in_same_group = po.Var(model.disk_pairs, model.groups, domain=po.Binary)
# constraints
def calc_disk_in_same_group(model, disk, peer, group):
return model.disk_used_by_group[disk,group] * model.disk_used_by_group[peer,group]
def define_disk_in_same_group_lower_bound(model, disk, peer, group):
return model.disk_used_by_group[disk,group] + model.disk_used_by_group[peer,group] <= model.disk_in_same_group[disk,peer,group] + 1
def define_disk_in_same_group_upper_bound1(model, disk, peer, group):
return model.disk_in_same_group[disk,peer,group] <= model.disk_used_by_group[disk,group]
def define_disk_in_same_group_upper_bound2(model, disk, peer, group):
return model.disk_in_same_group[disk,peer,group] <= model.disk_used_by_group[peer,group]
if self.qlinearize:
model.define_disk_in_same_group_lower_bound_eqn = po.Constraint(model.disk_pairs, model.groups, rule=define_disk_in_same_group_lower_bound)
model.define_disk_in_same_group_upper_bound1_eqn = po.Constraint(model.disk_pairs, model.groups, rule=define_disk_in_same_group_upper_bound1)
model.define_disk_in_same_group_upper_bound2_eqn = po.Constraint(model.disk_pairs, model.groups, rule=define_disk_in_same_group_upper_bound2)
def each_disk_has_limited_capcity(model, disk):
if self.all_targets_used:
return po.quicksum(model.disk_used_by_group[disk,group] for group in model.groups) == self.num_targets_per_disk
else:
return po.quicksum(model.disk_used_by_group[disk,group] for group in model.groups) <= self.num_targets_per_disk
model.each_disk_has_limited_capcity_eqn = po.Constraint(model.disks, rule=each_disk_has_limited_capcity)
def enough_disks_assigned_to_each_group(model, group):
return po.quicksum(model.disk_used_by_group[disk,group] for disk in model.disks) == self.group_size
model.enough_disks_assigned_to_each_group_eqn = po.Constraint(model.groups, rule=enough_disks_assigned_to_each_group)
def calc_peer_recovery_traffic(model, disk, peer):
if self.qlinearize:
return po.quicksum(model.disk_in_same_group[disk,peer,group] for group in model.groups)
else:
return po.quicksum(calc_disk_in_same_group(model, disk, peer, group) for group in model.groups)
def peer_recovery_traffic_upper_bound(model, disk, peer):
if self.balanced_incomplete_block_design:
return calc_peer_recovery_traffic(model, disk, peer) == self.max_recovery_traffic_on_peer
else:
return calc_peer_recovery_traffic(model, disk, peer) <= self.max_recovery_traffic_on_peer + self.relax_ub
model.peer_recovery_traffic_upper_bound_eqn = po.Constraint(model.disk_pairs, rule=peer_recovery_traffic_upper_bound)
def peer_recovery_traffic_lower_bound(model, disk, peer):
return calc_peer_recovery_traffic(model, disk, peer) >= max(0, self.max_recovery_traffic_on_peer - self.relax_lb)
if self.balanced_incomplete_block_design:
logger.info(f"lower bound not needed for balanced incomplete block design (BIBD)")
elif self.all_targets_used:
logger.info(f"lower bound imposed on peer traffic: {self.relax_lb=} {self.qlinearize=} {self.all_targets_used=}")
model.peer_recovery_traffic_lower_bound_eqn = po.Constraint(model.disk_pairs, rule=peer_recovery_traffic_lower_bound)
else:
logger.info(f"lower bound not imposed on peer traffic: {self.relax_lb=} {self.qlinearize=} {self.all_targets_used=}")
def total_recovery_traffic(model):
return po.summation(model.disk_in_same_group) * 2
# model.obj = po.Objective(rule=total_recovery_traffic, sense=po.minimize)
model.obj = po.Objective(expr=1) # dummy objective
return model
def solve_model(self, instance, pyomo_solver, threads, timelimit, output_path):
if pyomo_solver is not None:
solver = po.SolverFactory(pyomo_solver)
return solver.solve(instance, options={"threads": str(threads), "log_file": os.path.join(output_path, f"{pyomo_solver}.log")}, load_solutions=False, timelimit=timelimit, tee=True)
else:
raise ValueError(f"no solver specified")
def get_peer_traffic(self, instance) -> Dict[Tuple[int,int], int]:
peer_traffic_map = {}
for disk in instance.disks:
for peer in instance.disks:
if disk == peer: continue
peer_traffic_map[(disk, peer)] = sum(
po.value(instance.disk_used_by_group[disk,group]) *
po.value(instance.disk_used_by_group[peer,group])
for group in instance.groups) * self.recovery_traffic_factor / (self.group_size - 1)
return peer_traffic_map
def get_incidence_matrix(self, instance) -> Dict[Tuple[int, int], bool]:
incidence_matrix = {}
for disk in instance.disks:
for group in instance.groups:
val = instance.disk_used_by_group[disk,group]
if math.isclose(po.value(val), 1):
incidence_matrix[(disk,group)] = True
if self.all_targets_used:
assert len(incidence_matrix) % self.num_nodes == 0, f"{len(incidence_matrix)=} % {self.num_nodes=}"
assert len(incidence_matrix) % self.num_groups == 0, f"{len(incidence_matrix)=} % {self.num_groups=}"
return incidence_matrix
def check_solution(self, instance):
has_peer_traffic_lower_bound = False
for c in instance.component_objects(po.Constraint):
if "peer_recovery_traffic_lower_bound_eqn" in str(c):
has_peer_traffic_lower_bound = True
peer_traffic_map = self.get_peer_traffic(instance)
for (disk, peer), peer_traffic in peer_traffic_map.items():
logger.debug(f"{disk},{peer}: {peer_traffic:.1f}")
assert peer_traffic <= self.max_recovery_traffic_on_peer + self.relax_ub + 1e-5, f"{peer_traffic=} > {self.max_recovery_traffic_on_peer=} + {self.relax_ub}"
if has_peer_traffic_lower_bound:
assert peer_traffic >= max(0, self.max_recovery_traffic_on_peer - self.relax_lb) - 1e-5, f"{peer_traffic=} < {self.max_recovery_traffic_on_peer=} - {self.relax_lb}"
min_peer_traffic = min(peer_traffic_map.values())
max_peer_traffic = max(peer_traffic_map.values())
total_traffic = sum(peer_traffic_map.values())
max_total_traffic = self.num_nodes * self.sum_recovery_traffic_per_failure
logger.info(f"{min_peer_traffic=:.1f} {max_peer_traffic=:.1f}")
logger.info(f"{total_traffic=} {max_total_traffic=}")
peer_traffic_diff = max_peer_traffic - min_peer_traffic
if has_peer_traffic_lower_bound:
assert peer_traffic_diff <= self.relax_ub + self.relax_lb + 1e-5, f"{peer_traffic_diff=}"
if self.balanced_incomplete_block_design:
assert math.isclose(peer_traffic_diff, 0.0, abs_tol=1e-9), f"{peer_traffic_diff=}"
assert total_traffic <= max_total_traffic + 1e-5
return total_traffic, min_peer_traffic, max_peer_traffic
def print_solution(self, instance):
for disk in instance.disks:
for group in instance.groups:
val = instance.disk_used_by_group[disk,group]
if math.isclose(po.value(val), 1):
logger.info(f"{val}: {po.value(val)}")
def save_solution(self, instance, output_path: str="output"):
incidence_matrix = self.get_incidence_matrix(instance)
with open(os.path.join(output_path, "incidence_matrix.pickle"), "wb") as fout:
pickle.dump(incidence_matrix, fout)
peer_traffic_map = self.get_peer_traffic(instance)
with open(os.path.join(output_path, "peer_traffic_map.pickle"), "wb") as fout:
pickle.dump(peer_traffic_map, fout)
def visualize_solution(self, instance, output_path: str="output", write_html=True):
incidence_matrix = self.get_incidence_matrix(instance)
disks, groups = zip(*incidence_matrix.keys())
incidence_df = pd.DataFrame(zip(disks, groups), columns=["disk", "group"])
peer_traffic_map = self.get_peer_traffic(instance)
min_peer_traffic = min(peer_traffic_map.values())
max_peer_traffic = max(peer_traffic_map.values())
fig = px.scatter(
incidence_df,
x="disk",
y="group",
title=f"{self}, min/max peer traffic: {min_peer_traffic:.1f}/{max_peer_traffic:.1f}")
fig.update_layout(
xaxis_title="Nodes",
yaxis_title="Groups",
xaxis = dict(
tickmode = 'array',
tickvals = list(range(1, self.num_nodes+1)),
),
yaxis = dict(
tickmode = 'array',
tickvals = list(range(1, self.num_groups+1)),
),
)
if write_html:
fig.write_html(os.path.join(output_path, f"data_placement.html"), include_plotlyjs=True)
return fig
class RebalanceTrafficModel(DataPlacementModel):
def __init__(self, existing_incidence_matrix, chain_table_type: Literal["EC", "CR"], num_nodes, group_size, num_groups=None, num_targets_per_disk=None, min_targets_per_disk=1, bibd_only=False, qlinearize=False, relax_lb=1, relax_ub=0):
self.existing_incidence_matrix = existing_incidence_matrix
self.existing_disks, self.existing_groups = zip(*existing_incidence_matrix.keys())
num_existing_targets_per_disk = math.ceil(self.total_existing_targets / self.num_existing_disk)
min_targets_per_disk = max(min_targets_per_disk, num_existing_targets_per_disk)
if num_targets_per_disk is None:
num_nodes, num_groups, num_targets_per_disk, group_size = DataPlacementModel.find_params(num_nodes, group_size, min_r=min_targets_per_disk, bibd_only=bibd_only)
else:
assert num_targets_per_disk >= min_targets_per_disk
super().__init__(chain_table_type, num_nodes, group_size, num_groups, num_targets_per_disk, min_targets_per_disk, bibd_only, qlinearize, relax_lb, relax_ub)
@property
def num_existing_disk(self):
return max(self.existing_disks)
@property
def num_existing_groups(self):
return max(self.existing_groups)
@property
def total_existing_targets(self):
return len(self.existing_disks)
@property
def existing_group_size(self):
assert self.total_existing_targets % self.num_existing_groups == 0, f"{self.total_existing_targets=} % {self.num_existing_groups=}"
return self.total_existing_targets // self.num_existing_groups
def build_model(self):
max_existing_targets_per_disk = math.ceil(self.total_existing_targets / self.num_nodes)
logger.info(f"{self.num_existing_disk=} {self.num_existing_groups=} {self.total_existing_targets=} {max_existing_targets_per_disk=}")
assert self.num_nodes >= self.num_existing_disk, f"{self.num_nodes=} < {self.num_existing_disk=}"
assert self.num_groups >= self.num_existing_groups, f"{self.num_groups=} < {self.num_existing_groups=}"
assert self.group_size == self.existing_group_size, f"{self.group_size=} != {self.existing_group_size=}"
assert self.num_targets_per_disk >= max_existing_targets_per_disk, f"{self.num_targets_per_disk=} >= {max_existing_targets_per_disk=}"
model = super().build_model()
def existing_targets_evenly_distributed_to_disks(model, disk):
return po.quicksum(model.disk_used_by_group[disk,group] for group in model.groups if group <= self.num_existing_groups) <= max_existing_targets_per_disk
model.existing_targets_evenly_distributed_to_disks_eqn = po.Constraint(model.disks, rule=existing_targets_evenly_distributed_to_disks)
def num_existing_targets_not_moved(model):
return po.quicksum(model.disk_used_by_group[disk,group] for disk in model.disks for group in model.groups if (disk,group) in self.existing_incidence_matrix)
def total_rebalance_traffic(model):
return self.total_existing_targets - num_existing_targets_not_moved(model)
model.obj = po.Objective(expr=total_rebalance_traffic, sense=po.minimize)
return model
def visualize_solution(self, instance, output_path = "output", write_html=True):
incidence_matrix = self.get_incidence_matrix(instance)
disks, groups = zip(*incidence_matrix.keys())
incidence_df = pd.DataFrame(zip(disks, groups, [g > self.num_existing_groups for g in groups]), columns=["disk", "group", "new"])
peer_traffic_map = self.get_peer_traffic(instance)
min_peer_traffic = min(peer_traffic_map.values())
max_peer_traffic = max(peer_traffic_map.values())
fig = px.scatter(
incidence_df,
x="disk",
y="group",
color="new",
title=f"{self}, min/max peer traffic: {min_peer_traffic:.1f}/{max_peer_traffic:.1f}, rebalance traffic: {po.value(instance.obj.expr)}")
fig.update_layout(
xaxis_title="Nodes",
yaxis_title="Groups",
xaxis = dict(
tickmode = 'array',
tickvals = list(range(1, self.num_nodes+1)),
),
yaxis = dict(
tickmode = 'array',
tickvals = list(range(1, self.num_groups+1)),
),
)
if write_html:
fig.write_html(os.path.join(output_path, f"{self.path_name}.html"), include_plotlyjs=True)
return fig
def main():
import psutil
import argparse
parser = argparse.ArgumentParser(prog="model.py", description="3FS data placement")
parser.add_argument("-pyomo", "--pyomo_solver", default="appsi_highs", choices=["appsi_highs", "cbc", "scip"], help="Solver used by Pyomo")
parser.add_argument("-type", "--chain_table_type", type=str, required=True, choices=["CR", "EC"], help="CR - Chain Replication; EC - Erasure Coding")
parser.add_argument("-j", "--solver_threads", type=int, default=psutil.cpu_count(logical=False)//2, help="Number of solver threads")
parser.add_argument("-v", "--num_nodes", type=int, required=True, help="Number of storage nodes")
parser.add_argument("-r", "--num_targets_per_disk", type=int, default=None, help="Number of storage targets on each disk")
parser.add_argument("-min_r", "--min_targets_per_disk", type=int, default=1, help="Min number of storage targets on each disk")
parser.add_argument("-k", "--replication_factor", "--group_size", dest="group_size", type=int, default=3, help="Replication factor or erasure coding group size")
parser.add_argument("-b", "--num_groups", type=int, default=None, help="Number of chains or EC groups")
parser.add_argument("-ql", "--qlinearize", action="store_true", help="Enable linearization of quadratic equations")
parser.add_argument("-lb", "--relax_lb", type=int, default=1, help="Relax the lower bound of peer recovery traffic")
parser.add_argument("-ub", "--relax_ub", type=int, default=0, help="Relax the upper bound of peer recovery traffic")
parser.add_argument("-relax", "--auto_relax", action="store_true", help="Auto relax the lower/upper bound of peer recovery traffic when timeout")
parser.add_argument("-bibd", "--bibd_only", action="store_true", help="Only create balanced incomplete block design (BIBD)")
parser.add_argument("-t", "--init_timelimit", type=int, default=1800, help="Initial timeout for solver")
parser.add_argument("-T", "--max_timelimit", type=int, default=3600*2, help="Max timeout for solver")
parser.add_argument("-o", "--output_path", default="output", help="Path of output files")
parser.add_argument("-m", "--existing_incidence_matrix", default=None, help="Existing incidence matrix for rebalance traffic model")
parser.add_argument("-V", "--verbose", action="store_true", help="Show verbose output")
args = parser.parse_args()
if args.existing_incidence_matrix is None:
DataPlacementModel(
args.chain_table_type,
args.num_nodes,
args.group_size,
args.num_groups,
args.num_targets_per_disk,
args.min_targets_per_disk,
args.bibd_only,
args.qlinearize,
args.relax_lb,
args.relax_ub,
).run(
args.pyomo_solver,
args.solver_threads,
args.init_timelimit,
args.max_timelimit,
args.auto_relax,
args.output_path,
args.verbose)
else:
with open(args.existing_incidence_matrix, "rb") as fin:
existing_incidence_matrix = pickle.load(fin)
RebalanceTrafficModel(
existing_incidence_matrix,
args.chain_table_type,
args.num_nodes,
args.group_size,
args.num_groups,
args.num_targets_per_disk,
args.min_targets_per_disk,
args.bibd_only,
args.qlinearize,
args.relax_lb,
args.relax_ub,
).run(
args.pyomo_solver,
args.solver_threads,
args.init_timelimit,
args.max_timelimit,
args.auto_relax,
args.output_path,
args.verbose)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,108 @@
# local test
# pytest test/test_plan.py -v -x
# production setup
import functools
import socket
import sys
import os.path
import itertools
import pandas as pd
import pyarrow as arrow
from typing import List, Literal
from loguru import logger
from smallpond.common import pytest_running
from smallpond.logical.dataset import ArrowTableDataSet
from smallpond.logical.node import Context, ConsolidateNode, DataSetPartitionNode, DataSourceNode, ArrowComputeNode, LogicalPlan, SqlEngineNode
from smallpond.execution.driver import Driver
from smallpond.execution.task import RuntimeContext, ArrowComputeTask
def solve_model(runtime_task: ArrowComputeTask,
chain_table_type, num_nodes, group_size, min_targets_per_disk,
init_timelimit, max_timelimit,
pyomo_solver="appsi_highs"):
import logging
pyomo_logger = logging.getLogger('pyomo')
pyomo_logger.setLevel(logging.WARNING)
try:
from src.model.data_placement import DataPlacementModel
except:
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from src.model.data_placement import DataPlacementModel
model = DataPlacementModel(chain_table_type, num_nodes, group_size, min_targets_per_disk=min_targets_per_disk, bibd_only=False, qlinearize=True, relax_lb=1, relax_ub=0)
runtime_task.add_elapsed_time("build model time")
instance = model.run(
pyomo_solver=pyomo_solver,
threads=runtime_task.cpu_limit,
init_timelimit=init_timelimit,
max_timelimit=max_timelimit,
auto_relax=True,
output_root=runtime_task.runtime_output_abspath,
add_elapsed_time=runtime_task.add_elapsed_time)
return model, instance
def solve_loop(runtime_ctx: RuntimeContext, input_tables: List[arrow.Table],
init_timelimit, max_timelimit,
pyomo_solver="appsi_highs") -> arrow.Table:
runtime_task = runtime_ctx.task
model_params, = input_tables
output_table = None
schema = arrow.schema([
arrow.field("chain_table_type", arrow.string()),
arrow.field("num_nodes", arrow.uint32()),
arrow.field("group_size", arrow.uint32()),
arrow.field("disks", arrow.list_(arrow.uint32())),
arrow.field("groups", arrow.list_(arrow.uint32())),
])
for chain_table_type, num_nodes, group_size, min_targets_per_disk in zip(*model_params.to_pydict().values()):
model, instance = solve_model(runtime_task, chain_table_type, num_nodes, group_size, min_targets_per_disk, init_timelimit, max_timelimit, pyomo_solver)
incidence_matrix = model.get_incidence_matrix(instance)
disks, groups = zip(*incidence_matrix.keys())
sol_table = arrow.Table.from_arrays([[chain_table_type], [num_nodes], [group_size], [disks], [groups]], schema=schema)
output_table = sol_table if output_table is None else arrow.concat_tables((output_table, sol_table))
return output_table
def search_data_placement_plans(
chain_table_type: Literal["EC", "CR"],
num_nodes: List[int], group_size: List[int], min_targets_per_disk=1,
init_timelimit=1800, max_timelimit=3600*3,
solver_threads: int=64,
pyomo_solver="appsi_highs"):
params = pd.DataFrame([(chain_table_type, v, k, min_targets_per_disk)
for v, k in itertools.product(num_nodes, group_size) if v >= k],
columns=["chain_table_type", "num_nodes", "group_size", "min_targets_per_disk"])
logger.warning(f"params: {params}")