Merge branch 'release-6.3' of github.com:apple/foundationdb into bugfixes/4022
This commit is contained in:
commit
bf50a083df
|
|
@ -95,3 +95,6 @@ flow/coveragetool/obj
|
|||
.DS_Store
|
||||
temp/
|
||||
/versions.target
|
||||
/compile_commands.json
|
||||
/.ccls-cache
|
||||
.clangd/
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
# limitations under the License.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(foundationdb
|
||||
VERSION 6.3.10
|
||||
VERSION 6.3.11
|
||||
DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions."
|
||||
HOMEPAGE_URL "http://www.foundationdb.org/"
|
||||
LANGUAGES C CXX ASM)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ void fdb_flow_test() {
|
|||
g_network->run();
|
||||
}
|
||||
|
||||
// FDB object used by bindings
|
||||
namespace FDB {
|
||||
class DatabaseImpl : public Database, NonCopyable {
|
||||
public:
|
||||
|
|
|
|||
101
build/Dockerfile
101
build/Dockerfile
|
|
@ -1,17 +1,28 @@
|
|||
ARG IMAGE_TAG=0.1.24
|
||||
FROM centos:6
|
||||
|
||||
# Clean yum cache, disable default Base repo and enable Vault
|
||||
RUN yum clean all &&\
|
||||
sed -i -e 's/gpgcheck=1/enabled=0/g' /etc/yum.repos.d/CentOS-Base.repo &&\
|
||||
sed -i -e 's/enabled=0/enabled=1/g' /etc/yum.repos.d/CentOS-Vault.repo &&\
|
||||
sed -i -n '/6.1/q;p' /etc/yum.repos.d/CentOS-Vault.repo &&\
|
||||
sed -i -e "s/6\.0/$(cut -d\ -f3 /etc/redhat-release)/g" /etc/yum.repos.d/CentOS-Vault.repo &&\
|
||||
yum install -y yum-utils &&\
|
||||
yum-config-manager --enable rhel-server-rhscl-7-rpms &&\
|
||||
yum -y install centos-release-scl-rh epel-release \
|
||||
http://opensource.wandisco.com/centos/6/git/x86_64/wandisco-git-release-6-1.noarch.rpm &&\
|
||||
sed -i -e 's/#baseurl=/baseurl=/g' -e 's/mirror.centos.org/vault.centos.org/g' \
|
||||
-e 's/mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo &&\
|
||||
yum clean all
|
||||
|
||||
# Install dependencies for developer tools, bindings,\
|
||||
# documentation, actorcompiler, and packaging tools\
|
||||
RUN yum install -y yum-utils &&\
|
||||
yum-config-manager --enable rhel-server-rhscl-7-rpms &&\
|
||||
yum -y install centos-release-scl epel-release \
|
||||
http://opensource.wandisco.com/centos/6/git/x86_64/wandisco-git-release-6-1.noarch.rpm &&\
|
||||
yum -y install devtoolset-8-8.1-1.el6 java-1.8.0-openjdk-devel \
|
||||
devtoolset-8-gcc-8.3.1 devtoolset-8-gcc-c++-8.3.1 \
|
||||
devtoolset-8-libubsan-devel devtoolset-8-libasan-devel devtoolset-8-valgrind-devel \
|
||||
rh-python36-python-devel rh-ruby24 golang python27 rpm-build \
|
||||
mono-core debbuild python-pip dos2unix valgrind-devel ccache \
|
||||
distcc wget git lz4 lz4-devel lz4-static &&\
|
||||
RUN yum -y install devtoolset-8-8.1-1.el6 java-1.8.0-openjdk-devel \
|
||||
devtoolset-8-gcc-8.3.1 devtoolset-8-gcc-c++-8.3.1 \
|
||||
devtoolset-8-libubsan-devel devtoolset-8-libasan-devel devtoolset-8-valgrind-devel \
|
||||
rh-python36-python-devel rh-ruby24 golang python27 rpm-build \
|
||||
mono-core debbuild python-pip dos2unix valgrind-devel ccache \
|
||||
distcc wget libxslt git lz4 lz4-devel lz4-static &&\
|
||||
pip install boto3==1.1.1
|
||||
|
||||
USER root
|
||||
|
|
@ -19,17 +30,35 @@ USER root
|
|||
RUN adduser --comment '' fdb && chown fdb /opt
|
||||
|
||||
# wget of bintray without forcing UTF-8 encoding results in 403 Forbidden
|
||||
# Old versions of FDB need boost 1.67
|
||||
RUN cd /opt/ &&\
|
||||
curl -L https://dl.bintray.com/boostorg/release/1.67.0/source/boost_1_67_0.tar.bz2 -o boost_1_67_0.tar.bz2 &&\
|
||||
echo "2684c972994ee57fc5632e03bf044746f6eb45d4920c343937a465fd67a5adba boost_1_67_0.tar.bz2" > boost-sha-67.txt &&\
|
||||
sha256sum -c boost-sha-67.txt &&\
|
||||
tar -xjf boost_1_67_0.tar.bz2 &&\
|
||||
rm -rf boost_1_67_0.tar.bz2 boost-sha-67.txt boost_1_67_0/libs &&\
|
||||
rm -rf boost_1_67_0.tar.bz2 boost-sha-67.txt boost_1_67_0/libs
|
||||
|
||||
# install Boost 1.72
|
||||
# wget of bintray without forcing UTF-8 encoding results in 403 Forbidden
|
||||
RUN cd /tmp/ &&\
|
||||
curl -L https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.bz2 -o boost_1_72_0.tar.bz2 &&\
|
||||
echo "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722 boost_1_72_0.tar.bz2" > boost-sha-72.txt &&\
|
||||
sha256sum -c boost-sha-72.txt &&\
|
||||
tar -xjf boost_1_72_0.tar.bz2 &&\
|
||||
rm -rf boost_1_72_0.tar.bz2 boost-sha-72.txt boost_1_72_0/libs
|
||||
cd boost_1_72_0 &&\
|
||||
scl enable devtoolset-8 -- ./bootstrap.sh --with-libraries=context &&\
|
||||
scl enable devtoolset-8 -- ./b2 link=static cxxflags=-std=c++14 --prefix=/opt/boost_1_72_0 install &&\
|
||||
rm -rf boost_1_72_0.tar.bz2 boost-sha-72.txt boost_1_72_0
|
||||
|
||||
# jemalloc (needed for FDB after 6.3)
|
||||
RUN cd /tmp/ &&\
|
||||
curl -L https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2 -o jemalloc-5.2.1.tar.bz2 &&\
|
||||
echo "34330e5ce276099e2e8950d9335db5a875689a4c6a56751ef3b1d8c537f887f6 jemalloc-5.2.1.tar.bz2" > jemalloc-sha.txt &&\
|
||||
sha256sum -c jemalloc-sha.txt &&\
|
||||
tar --no-same-owner --no-same-permissions -xjf jemalloc-5.2.1.tar.bz2 &&\
|
||||
cd jemalloc-5.2.1 &&\
|
||||
scl enable devtoolset-8 -- ./configure --enable-static --disable-cxx &&\
|
||||
scl enable devtoolset-8 -- make install
|
||||
|
||||
# install cmake
|
||||
RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.13.4/cmake-3.13.4-Linux-x86_64.tar.gz -o /tmp/cmake.tar.gz &&\
|
||||
|
|
@ -41,6 +70,8 @@ RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.13.4/cmake-3.1
|
|||
|
||||
# install Ninja
|
||||
RUN cd /tmp && curl -L https://github.com/ninja-build/ninja/archive/v1.9.0.zip -o ninja.zip &&\
|
||||
echo "8e2e654a418373f10c22e4cc9bdbe9baeca8527ace8d572e0b421e9d9b85b7ef ninja.zip" > /tmp/ninja-sha.txt &&\
|
||||
sha256sum -c /tmp/ninja-sha.txt &&\
|
||||
unzip ninja.zip && cd ninja-1.9.0 && scl enable devtoolset-8 -- ./configure.py --bootstrap && cp ninja /usr/bin &&\
|
||||
cd .. && rm -rf ninja-1.9.0 ninja.zip
|
||||
|
||||
|
|
@ -53,17 +84,59 @@ RUN cd /tmp && curl -L https://www.openssl.org/source/openssl-1.1.1h.tar.gz -o o
|
|||
ln -sv /usr/local/lib64/lib*.so.1.1 /usr/lib64/ &&\
|
||||
cd /tmp/ && rm -rf /tmp/openssl-1.1.1h /tmp/openssl.tar.gz
|
||||
|
||||
# Install toml11
|
||||
RUN cd /tmp && curl -L https://github.com/ToruNiina/toml11/archive/v3.4.0.tar.gz > toml.tar.gz &&\
|
||||
echo "bc6d733efd9216af8c119d8ac64a805578c79cc82b813e4d1d880ca128bd154d toml.tar.gz" > toml-sha256.txt &&\
|
||||
sha256sum -c toml-sha256.txt &&\
|
||||
tar xf toml.tar.gz && rm -rf build && mkdir build && cd build && scl enable devtoolset-8 -- cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -Dtoml11_BUILD_TEST=OFF ../toml11-3.4.0 &&\
|
||||
scl enable devtoolset-8 -- cmake --build . --target install && cd / && rm -rf tmp/build && rm -rf tmp/toml11-3.4.0
|
||||
|
||||
RUN cd /opt/ && curl -L https://github.com/facebook/rocksdb/archive/v6.10.1.tar.gz -o rocksdb.tar.gz &&\
|
||||
echo "d573d2f15cdda883714f7e0bc87b814a8d4a53a82edde558f08f940e905541ee rocksdb.tar.gz" > rocksdb-sha.txt &&\
|
||||
sha256sum -c rocksdb-sha.txt && tar xf rocksdb.tar.gz && rm -rf rocksdb.tar.gz rocksdb-sha.txt
|
||||
|
||||
RUN cd /opt/ && curl -L https://github.com/manticoresoftware/manticoresearch/raw/master/misc/junit/ctest2junit.xsl -o ctest2junit.xsl
|
||||
|
||||
# Setting this environment variable switches from OpenSSL to BoringSSL
|
||||
ENV OPENSSL_ROOT_DIR=/opt/boringssl
|
||||
|
||||
# install BoringSSL: TODO: They don't seem to have releases(?) I picked today's master SHA.
|
||||
RUN cd /opt &&\
|
||||
git clone https://boringssl.googlesource.com/boringssl &&\
|
||||
cd boringssl &&\
|
||||
git checkout e796cc65025982ed1fb9ef41b3f74e8115092816 &&\
|
||||
mkdir build
|
||||
|
||||
# ninja doesn't respect CXXFLAGS, and the boringssl CMakeLists doesn't expose an option to define __STDC_FORMAT_MACROS
|
||||
# also, enable -fPIC.
|
||||
# this is moderately uglier than creating a patchfile, but easier to maintain.
|
||||
RUN cd /opt/boringssl &&\
|
||||
for f in crypto/fipsmodule/rand/fork_detect_test.cc \
|
||||
include/openssl/bn.h \
|
||||
ssl/test/bssl_shim.cc ; do \
|
||||
perl -p -i -e 's/#include <inttypes.h>/#define __STDC_FORMAT_MACROS 1\n#include <inttypes.h>/g;' $f ; \
|
||||
done &&\
|
||||
perl -p -i -e 's/-Werror/-Werror -fPIC/' CMakeLists.txt &&\
|
||||
git diff
|
||||
|
||||
RUN cd /opt/boringssl/build &&\
|
||||
scl enable devtoolset-8 rh-python36 rh-ruby24 -- cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. &&\
|
||||
scl enable devtoolset-8 rh-python36 rh-ruby24 -- ninja &&\
|
||||
./ssl/ssl_test &&\
|
||||
mkdir -p ../lib && cp crypto/libcrypto.a ssl/libssl.a ../lib
|
||||
|
||||
# Localize time zone
|
||||
ARG TIMEZONEINFO=America/Los_Angeles
|
||||
RUN rm -f /etc/localtime && ln -s /usr/share/zoneinfo/${TIMEZONEINFO} /etc/localtime
|
||||
|
||||
LABEL version=0.1.19
|
||||
ENV DOCKER_IMAGEVER=0.1.19
|
||||
LABEL version=${IMAGE_TAG}
|
||||
ENV DOCKER_IMAGEVER=${IMAGE_TAG}
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0
|
||||
ENV CC=/opt/rh/devtoolset-8/root/usr/bin/gcc
|
||||
ENV CXX=/opt/rh/devtoolset-8/root/usr/bin/g++
|
||||
|
||||
ENV CCACHE_NOHASHDIR=true
|
||||
ENV CCACHE_UMASK=0000
|
||||
ENV CCACHE_SLOPPINESS="file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches"
|
||||
|
||||
CMD scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
FROM foundationdb/foundationdb-build:0.1.19
|
||||
ARG IMAGE_TAG=0.1.24
|
||||
ARG IMAGE_VERSION=0.11.15
|
||||
FROM foundationdb/foundationdb-build:${IMAGE_TAG}
|
||||
|
||||
USER root
|
||||
|
||||
|
|
@ -50,8 +52,8 @@ RUN cp -iv /usr/local/bin/clang++ /usr/local/bin/clang++.deref &&\
|
|||
ldconfig &&\
|
||||
rm -rf /mnt/artifacts
|
||||
|
||||
LABEL version=0.11.10
|
||||
ENV DOCKER_IMAGEVER=0.11.10
|
||||
LABEL version=${IMAGE_VERSION}
|
||||
ENV DOCKER_IMAGEVER=${IMAGE_VERSION}
|
||||
|
||||
ENV CLANGCC=/usr/local/bin/clang.de8a65ef
|
||||
ENV CLANGCXX=/usr/local/bin/clang++.de8a65ef
|
||||
|
|
@ -63,8 +65,5 @@ ENV CC=/usr/local/bin/clang.de8a65ef
|
|||
ENV CXX=/usr/local/bin/clang++.de8a65ef
|
||||
ENV USE_LD=LLD
|
||||
ENV USE_LIBCXX=1
|
||||
ENV CCACHE_NOHASHDIR=true
|
||||
ENV CCACHE_UMASK=0000
|
||||
ENV CCACHE_SLOPPINESS="file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches"
|
||||
|
||||
CMD scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ version: "3"
|
|||
|
||||
services:
|
||||
common: &common
|
||||
image: foundationdb/foundationdb-build:0.1.19
|
||||
image: foundationdb/foundationdb-build:0.1.24
|
||||
|
||||
build-setup: &build-setup
|
||||
<<: *common
|
||||
|
|
@ -60,7 +60,7 @@ services:
|
|||
|
||||
snapshot-cmake: &snapshot-cmake
|
||||
<<: *build-setup
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=0 /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" "packages" "strip_targets" && cpack'
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=0 -DUSE_WERROR=ON /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" "packages" "strip_targets" && cpack'
|
||||
|
||||
prb-cmake:
|
||||
<<: *snapshot-cmake
|
||||
|
|
@ -68,7 +68,7 @@ services:
|
|||
|
||||
snapshot-bindings-cmake: &snapshot-bindings-cmake
|
||||
<<: *build-setup
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=0 /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" "bindings/all"'
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=0 -DUSE_WERROR=ON /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" "bindings/all"'
|
||||
|
||||
prb-bindings-cmake:
|
||||
<<: *snapshot-bindings-cmake
|
||||
|
|
@ -84,7 +84,7 @@ services:
|
|||
|
||||
snapshot-ctest: &snapshot-ctest
|
||||
<<: *build-setup
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=1 /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" && ctest -L fast -j "$${MAKEJOBS}" --output-on-failure'
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=1 -DUSE_WERROR=ON /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" && ctest -L fast -j "$${MAKEJOBS}" --output-on-failure'
|
||||
|
||||
prb-ctest:
|
||||
<<: *snapshot-ctest
|
||||
|
|
@ -92,7 +92,7 @@ services:
|
|||
|
||||
snapshot-correctness: &snapshot-correctness
|
||||
<<: *build-setup
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=1 /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" && ctest -j "$${MAKEJOBS}" --output-on-failure'
|
||||
command: scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash -c 'mkdir -p "$${BUILD_DIR}" && cd "$${BUILD_DIR}" && cmake -G "Ninja" -DFDB_RELEASE=1 -DUSE_WERROR=ON /__this_is_some_very_long_name_dir_needed_to_fix_a_bug_with_debug_rpms__/foundationdb && ninja -v -j "$${MAKEJOBS}" && ctest -j "$${MAKEJOBS}" --output-on-failure'
|
||||
|
||||
prb-correctness:
|
||||
<<: *snapshot-correctness
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ endif()
|
|||
# - OUT_DIR the directory where files will be staged
|
||||
# - CONTEXT the type of correctness package being built (e.g. 'valgrind correctness')
|
||||
function(stage_correctness_package)
|
||||
set(oneValueArgs OUT_DIR CONTEXT)
|
||||
set(oneValueArgs OUT_DIR CONTEXT OUT_FILES)
|
||||
cmake_parse_arguments(STAGE "" "${oneValueArgs}" "" "${ARGN}")
|
||||
file(MAKE_DIRECTORY ${STAGE_OUT_DIR}/bin)
|
||||
string(LENGTH "${CMAKE_SOURCE_DIR}/tests/" base_length)
|
||||
|
|
@ -200,6 +200,10 @@ function(stage_correctness_package)
|
|||
endforeach()
|
||||
endforeach()
|
||||
list(APPEND package_files ${STAGE_OUT_DIR}/bin/fdbserver
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbserver.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbclient.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbrpc.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.flow.xml
|
||||
${STAGE_OUT_DIR}/bin/TestHarness.exe
|
||||
${STAGE_OUT_DIR}/bin/TraceLogHelper.dll
|
||||
${STAGE_OUT_DIR}/CMakeCache.txt
|
||||
|
|
@ -208,17 +212,27 @@ function(stage_correctness_package)
|
|||
OUTPUT ${package_files}
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/CMakeCache.txt
|
||||
${CMAKE_BINARY_DIR}/packages/bin/fdbserver
|
||||
${CMAKE_BINARY_DIR}/bin/coverage.fdbserver.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbclient.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbrpc.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.flow.xml
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/CMakeCache.txt ${STAGE_OUT_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/packages/bin/fdbserver
|
||||
${CMAKE_BINARY_DIR}/bin/coverage.fdbserver.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbclient.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbrpc.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.flow.xml
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll
|
||||
${STAGE_OUT_DIR}/bin
|
||||
COMMENT "Copying files for ${STAGE_CONTEXT} package"
|
||||
)
|
||||
list(APPEND package_files ${test_files} ${external_files})
|
||||
set(package_files ${package_files} PARENT_SCOPE)
|
||||
if(STAGE_OUT_FILES)
|
||||
set(${STAGE_OUT_FILES} ${package_files} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(create_correctness_package)
|
||||
|
|
@ -226,7 +240,7 @@ function(create_correctness_package)
|
|||
return()
|
||||
endif()
|
||||
set(out_dir "${CMAKE_BINARY_DIR}/correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "correctness" OUT_FILES package_files)
|
||||
set(tar_file ${CMAKE_BINARY_DIR}/packages/correctness-${CMAKE_PROJECT_VERSION}.tar.gz)
|
||||
add_custom_command(
|
||||
OUTPUT ${tar_file}
|
||||
|
|
@ -253,7 +267,7 @@ function(create_valgrind_correctness_package)
|
|||
endif()
|
||||
if(USE_VALGRIND)
|
||||
set(out_dir "${CMAKE_BINARY_DIR}/valgrind_correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "valgrind correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "valgrind correctness" OUT_FILES package_files)
|
||||
set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${CMAKE_PROJECT_VERSION}.tar.gz)
|
||||
add_custom_command(
|
||||
OUTPUT ${tar_file}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ if (NOT OPEN_FOR_IDE)
|
|||
endif()
|
||||
if(WIN32)
|
||||
add_definitions(-DUSE_USEFIBERS)
|
||||
add_definitions(-DBOOST_USE_WINDOWS_H)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
else()
|
||||
add_definitions(-DUSE_UCONTEXT)
|
||||
endif()
|
||||
|
|
@ -107,7 +109,7 @@ if(WIN32)
|
|||
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
add_compile_options(/W0 /EHsc /bigobj $<$<CONFIG:Release>:/Zi> /MP /FC /Gm-)
|
||||
add_compile_definitions(_WIN32_WINNT=${WINDOWS_TARGET} WINVER=${WINDOWS_TARGET} NTDDI_VERSION=0x05020000 BOOST_ALL_NO_LIB)
|
||||
add_compile_definitions(_WIN32_WINNT=${WINDOWS_TARGET} WINVER=${WINDOWS_TARGET} NTDDI_VERSION=0x05020000 BOOST_ALL_NO_LIB NOMINMAX)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||
else()
|
||||
|
|
@ -259,7 +261,6 @@ else()
|
|||
-Wno-unused-function
|
||||
-Wno-unused-local-typedef
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-self-assign
|
||||
)
|
||||
if (USE_CCACHE)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ endif()
|
|||
################################################################################
|
||||
# SSL
|
||||
################################################################################
|
||||
|
||||
include(CheckSymbolExists)
|
||||
|
||||
set(DISABLE_TLS OFF CACHE BOOL "Don't try to find OpenSSL and always build without TLS support")
|
||||
|
|
@ -21,13 +22,13 @@ else()
|
|||
find_package(OpenSSL)
|
||||
if(OPENSSL_FOUND)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
|
||||
set(WITH_TLS ON)
|
||||
add_compile_options(-DHAVE_OPENSSL)
|
||||
check_symbol_exists("OPENSSL_INIT_NO_ATEXIT" "openssl/crypto.h" OPENSSL_HAS_NO_ATEXIT)
|
||||
if(OPENSSL_HAS_NO_ATEXIT)
|
||||
set(WITH_TLS ON)
|
||||
add_compile_options(-DHAVE_OPENSSL)
|
||||
add_compile_options(-DHAVE_OPENSSL_INIT_NO_AT_EXIT)
|
||||
else()
|
||||
message(WARNING "An OpenSSL version was found, but it doesn't support OPENSSL_INIT_NO_ATEXIT - Will compile without TLS Support")
|
||||
set(WITH_TLS OFF)
|
||||
message(STATUS "Found OpenSSL without OPENSSL_INIT_NO_ATEXIT: assuming BoringSSL")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenSSL was not found - Will compile without TLS Support")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ set(SRCS
|
|||
Properties/AssemblyInfo.cs)
|
||||
|
||||
set(TEST_HARNESS_REFERENCES
|
||||
"-r:System,System.Core,System.Xml.Linq,System.Data.DataSetExtensions,Microsoft.CSharp,System.Data,System.Xml,${TraceLogHelperDll}")
|
||||
"-r:System,System.Core,System.Xml.Linq,System.Data.DataSetExtensions,Microsoft.CSharp,System.Data,System.Xml,System.Runtime.Serialization,${TraceLogHelperDll}")
|
||||
|
||||
set(out_file ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -51,7 +51,7 @@ namespace Magnesium
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to parse {0}", root), e);
|
||||
throw new Exception(string.Format("Failed to parse JSON {0}", root), e);
|
||||
}
|
||||
if (ev != null) yield return ev;
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ namespace Magnesium
|
|||
DDetails = xEvent.Elements()
|
||||
.Where(a=>a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "ID" && a.Name != "Severity" && (!rolledEvent || a.Name != "OriginalTime"))
|
||||
.ToDictionary(a=>string.Intern(a.Name.LocalName), a=>(object)a.Value),
|
||||
original = keepOriginalElement ? xEvent : null,
|
||||
original = keepOriginalElement ? xEvent : null
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace Magnesium
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to parse {0}", xev), e);
|
||||
throw new Exception(string.Format("Failed to parse XML {0}", xev), e);
|
||||
}
|
||||
if (ev != null) yield return ev;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,6 +125,17 @@ Because it can have only a single network thread, a client process may become li
|
|||
|
||||
If you suspect that a client process's workload may be saturating the network thread, this can be confirmed by checking whether the network thread is running with high CPU usage. In the :ref:`client trace logs <client-trace-logging>`, the ``ProcessMetrics`` trace event has a field for ``MainThreadCPUSeconds`` that indicates the number of seconds out of ``Elapsed`` that the network thread was busy. You can also attempt to identify a busy thread from any tool that reports the CPU activity of threads in your process.
|
||||
|
||||
.. note:: FoundationDB 6.3 introduced :ref:`multi-threaded client <multi-threaded-client>`, and can alternatively be used to scale clients.
|
||||
|
||||
.. _multi-threaded-client:
|
||||
|
||||
Multi-threaded Client
|
||||
=====================
|
||||
|
||||
FoundationDB client library can start multiple worker threads for each version of client that is loaded. Every single cluster will be serviced by one of the client threads. If the client is connected to only one cluster, exactly one thread would be active and the rest will remain idle. Hence, using this feature is useful when the client is actively using more than one cluster.
|
||||
|
||||
Clients can be configured to use worker-threads by setting the ``FDBNetworkOptions::CLIENT_THREADS_PER_VERSION`` option.
|
||||
|
||||
.. _client-trace-logging:
|
||||
|
||||
Client trace logging
|
||||
|
|
|
|||
|
|
@ -469,6 +469,11 @@ Prints a list of currently active transaction tag throttles.
|
|||
|
||||
``LIMIT`` - The number of throttles to print. Defaults to 100.
|
||||
|
||||
triggerddteaminfolog
|
||||
--------------------
|
||||
|
||||
The ``triggerddteaminfolog`` command would trigger the data distributor to log very detailed teams information into trace event logs.
|
||||
|
||||
unlock
|
||||
------
|
||||
|
||||
|
|
|
|||
|
|
@ -10,38 +10,38 @@ macOS
|
|||
|
||||
The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server.
|
||||
|
||||
* `FoundationDB-6.3.9.pkg <https://www.foundationdb.org/downloads/6.3.9/macOS/installers/FoundationDB-6.3.9.pkg>`_
|
||||
* `FoundationDB-6.3.10.pkg <https://www.foundationdb.org/downloads/6.3.10/macOS/installers/FoundationDB-6.3.10.pkg>`_
|
||||
|
||||
Ubuntu
|
||||
------
|
||||
|
||||
The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x.
|
||||
|
||||
* `foundationdb-clients-6.3.9-1_amd64.deb <https://www.foundationdb.org/downloads/6.3.9/ubuntu/installers/foundationdb-clients_6.3.9-1_amd64.deb>`_
|
||||
* `foundationdb-server-6.3.9-1_amd64.deb <https://www.foundationdb.org/downloads/6.3.9/ubuntu/installers/foundationdb-server_6.3.9-1_amd64.deb>`_ (depends on the clients package)
|
||||
* `foundationdb-clients-6.3.10-1_amd64.deb <https://www.foundationdb.org/downloads/6.3.10/ubuntu/installers/foundationdb-clients_6.3.10-1_amd64.deb>`_
|
||||
* `foundationdb-server-6.3.10-1_amd64.deb <https://www.foundationdb.org/downloads/6.3.10/ubuntu/installers/foundationdb-server_6.3.10-1_amd64.deb>`_ (depends on the clients package)
|
||||
|
||||
RHEL/CentOS EL6
|
||||
---------------
|
||||
|
||||
The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x.
|
||||
|
||||
* `foundationdb-clients-6.3.9-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.9/rhel6/installers/foundationdb-clients-6.3.9-1.el6.x86_64.rpm>`_
|
||||
* `foundationdb-server-6.3.9-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.9/rhel6/installers/foundationdb-server-6.3.9-1.el6.x86_64.rpm>`_ (depends on the clients package)
|
||||
* `foundationdb-clients-6.3.10-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.10/rhel6/installers/foundationdb-clients-6.3.10-1.el6.x86_64.rpm>`_
|
||||
* `foundationdb-server-6.3.10-1.el6.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.10/rhel6/installers/foundationdb-server-6.3.10-1.el6.x86_64.rpm>`_ (depends on the clients package)
|
||||
|
||||
RHEL/CentOS EL7
|
||||
---------------
|
||||
|
||||
The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x.
|
||||
|
||||
* `foundationdb-clients-6.3.9-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.9/rhel7/installers/foundationdb-clients-6.3.9-1.el7.x86_64.rpm>`_
|
||||
* `foundationdb-server-6.3.9-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.9/rhel7/installers/foundationdb-server-6.3.9-1.el7.x86_64.rpm>`_ (depends on the clients package)
|
||||
* `foundationdb-clients-6.3.10-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.10/rhel7/installers/foundationdb-clients-6.3.10-1.el7.x86_64.rpm>`_
|
||||
* `foundationdb-server-6.3.10-1.el7.x86_64.rpm <https://www.foundationdb.org/downloads/6.3.10/rhel7/installers/foundationdb-server-6.3.10-1.el7.x86_64.rpm>`_ (depends on the clients package)
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server.
|
||||
|
||||
* `foundationdb-6.3.9-x64.msi <https://www.foundationdb.org/downloads/6.3.9/windows/installers/foundationdb-6.3.9-x64.msi>`_
|
||||
* `foundationdb-6.3.10-x64.msi <https://www.foundationdb.org/downloads/6.3.10/windows/installers/foundationdb-6.3.10-x64.msi>`_
|
||||
|
||||
API Language Bindings
|
||||
=====================
|
||||
|
|
@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part
|
|||
|
||||
If you need to use the FoundationDB Python API from other Python installations or paths, use the Python package manager ``pip`` (``pip install foundationdb``) or download the Python package:
|
||||
|
||||
* `foundationdb-6.3.9.tar.gz <https://www.foundationdb.org/downloads/6.3.9/bindings/python/foundationdb-6.3.9.tar.gz>`_
|
||||
* `foundationdb-6.3.10.tar.gz <https://www.foundationdb.org/downloads/6.3.10/bindings/python/foundationdb-6.3.10.tar.gz>`_
|
||||
|
||||
Ruby 1.9.3/2.0.0+
|
||||
-----------------
|
||||
|
||||
* `fdb-6.3.9.gem <https://www.foundationdb.org/downloads/6.3.9/bindings/ruby/fdb-6.3.9.gem>`_
|
||||
* `fdb-6.3.10.gem <https://www.foundationdb.org/downloads/6.3.10/bindings/ruby/fdb-6.3.10.gem>`_
|
||||
|
||||
Java 8+
|
||||
-------
|
||||
|
||||
* `fdb-java-6.3.9.jar <https://www.foundationdb.org/downloads/6.3.9/bindings/java/fdb-java-6.3.9.jar>`_
|
||||
* `fdb-java-6.3.9-javadoc.jar <https://www.foundationdb.org/downloads/6.3.9/bindings/java/fdb-java-6.3.9-javadoc.jar>`_
|
||||
* `fdb-java-6.3.10.jar <https://www.foundationdb.org/downloads/6.3.10/bindings/java/fdb-java-6.3.10.jar>`_
|
||||
* `fdb-java-6.3.10-javadoc.jar <https://www.foundationdb.org/downloads/6.3.10/bindings/java/fdb-java-6.3.10-javadoc.jar>`_
|
||||
|
||||
Go 1.11+
|
||||
--------
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 449 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 467 KiB |
|
|
@ -93,6 +93,11 @@
|
|||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"low_priority_queries":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"bytes_queried":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
|
|
@ -479,6 +484,11 @@
|
|||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"low_priority_reads":{ // measures number of incoming low priority read requests
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"location_requests":{ // measures number of outgoing key server location responses
|
||||
"hz":0.0,
|
||||
|
|
@ -536,6 +546,11 @@
|
|||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"rejected_for_queued_too_long":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"committed":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,469 @@
|
|||
##############################
|
||||
FDB Read and Write Path
|
||||
##############################
|
||||
|
||||
| Author: Meng Xu
|
||||
| Reviewer: Evan Tschannen, Jingyu Zhou
|
||||
| Audience: FDB developers, SREs and expert users.
|
||||
|
||||
This document explains how FDB works at high level in database terms without mentioning FDB internal concepts.
|
||||
|
||||
We first discuss the read path and the write path separately for a single transaction.
|
||||
We then describe how the read path and write path work together for a read and write transaction.
|
||||
In the last section, we illustrate how multiple outstanding write transactions are processed and *ordered* in FDB.
|
||||
The processing order of multiple transactions is important because it affects the parallelism of transaction processing and the write throughput.
|
||||
|
||||
The content is based on FDB 6.2 and is true for FDB 6.3. A new timestamp proxy role is introduced in post FDB 6.3,
|
||||
which affects the read path. We will discuss the timestamp proxy role in the future version of this document.
|
||||
|
||||
.. image:: /images/FDB_read_path.png
|
||||
|
||||
Components
|
||||
=================
|
||||
|
||||
FDB is built on top of several key components.
|
||||
The terms below are common database or distributed system terms, instead of FDB specific terms.
|
||||
|
||||
**Timestamp generator.** It serves logical time, which defines happen-before relation:
|
||||
An event at t1 happens before another event at t2, if t1 < t2.
|
||||
The logic time is used to order events in FDB distributed systems and it is used by concurrency control to decide if two transactions have conflicts.
|
||||
The logical time is the timestamp for a transaction.
|
||||
|
||||
* A read-only transaction has only one timestamp which is assigned when the transaction is created;
|
||||
* A read-write transaction has one timestamp at the transaction’s creation time and one timestamp at its commit time.
|
||||
|
||||
|
||||
**Concurrency Control.** It decides if two transactions can be executed concurrently without violating Strict Serializable Isolation (SSI) property.
|
||||
It uses the Optimistic Concurrency Control (OCC) mechanism described in [SSI] to achieve that.
|
||||
|
||||
**Client.** It is a library, an FDB application uses, to access the database.
|
||||
It exposes the transaction concept to applications.
|
||||
Client in FDB is a *fat* client that does multiple complex operations:
|
||||
(1) It calculates read and write conflict ranges for transactions;
|
||||
(2) it batches a transaction's operations and send them all together at commit for better throughput;
|
||||
(3) it automatically retries failed transactions.
|
||||
|
||||
**Proxies.** It is a subsystem that acts like reverse proxies to serve clients’ requests. Its main purposes is:
|
||||
|
||||
* Serve for read request by (1) serving the logical time to client; and (2) providing which storage server has data for a key;
|
||||
* Process write transactions on behalf of clients and return the results;
|
||||
|
||||
Each proxy has the system’s metadata, called transaction state store (txnStateStore). The metadata decides:
|
||||
(1) which key should go to which storage servers in the storage system;
|
||||
(2) which key should go to which processes in the durable queuing system;
|
||||
(3) is the database locked; etc.
|
||||
|
||||
The metadata on all proxies are consistent at any given timestamp.
|
||||
To achieve that, when a proxy has a metadata mutation that changes the metadata at the timestamp V1,
|
||||
the mutation is propagated to all proxies (through the concurrency control component), and
|
||||
its effect is applied on all proxies before any proxy can process transactions after the timestamp V1.
|
||||
|
||||
**Durable queuing system.** It is a queuing system for write traffic.
|
||||
Its producers are proxies that send transaction mutation data for durability purpose.
|
||||
Its consumers are storage systems that index data and serve read request.
|
||||
The queuing system is partitioned for the key-space.
|
||||
A shard (i.e., key-range) is mapped to *k* log processes in the queuing system, where *k* is the replication factor.
|
||||
The mapping between shard and storage servers decides the mapping between shard and log processes.
|
||||
|
||||
**Storage system.** It is a collection of storage servers (SS), each of which is a sqlite database running on a single thread.
|
||||
It indexes data and serves read requests.
|
||||
Each SS has an in-memory p-tree data structure that stores the past 5-second mutations and an on-disk sqlite data.
|
||||
The in-memory data structure can serve multiple versions of key-values in the past 5 seconds.
|
||||
Due to memory limit, the in-memory data cannot hold more than 5 seconds’ multi-version key-values,
|
||||
which is the root cause why FDB’s transactions cannot be longer than 5 seconds.
|
||||
The on-disk sqlite data has only the most-recent key-value.
|
||||
|
||||
**Zookeeper like system.** The system solves two main problems:
|
||||
|
||||
* Store the configuration of the transaction system, which includes information such as generations of queuing systems and their processes.
|
||||
The system used to be zookeeper. FDB later replaced it with its own implementation.
|
||||
|
||||
* Service discovery. Processes in the zookeeper-like system serve as well-known endpoints for clients to connect to the cluster.
|
||||
These well-known endpoint returns the list of proxies to clients.
|
||||
|
||||
|
||||
|
||||
Read path of a transaction
|
||||
==================================
|
||||
|
||||
Fig. 1 above shows a high-level view of the read path. An application uses FDB client library to read data.
|
||||
It creates a transaction and calls its read() function. The read() operation will lead to several steps.
|
||||
|
||||
* **Step 1 (Timestamp request)**: The read operation needs a timestamp.
|
||||
The client initiates the timestamp request through an RPC to proxy. The request will trigger Step 2 and Step 3;
|
||||
|
||||
* To improve throughput and reduce load on the server side, each client dynamically batches the timestamp requests.
|
||||
A client keeps adding requests to the current batch until
|
||||
*when* the number of requests in a batch exceeds a configurable threshold or
|
||||
*when* the batching times out at a dynamically computed threshold.
|
||||
Each batch sends only one timestamp request to proxy and all requests in the same batch share the same timestamp.
|
||||
|
||||
* **Step 2 (Get latest commit version)**: When the timestamp request arrives at a proxy,
|
||||
the proxy wants to get the largest commit version as the return value.
|
||||
So it contacts the rest of (n-1) proxies for their latest commit versions and
|
||||
uses the largest one as the return value for Step 1.
|
||||
|
||||
* O(n^2) communication cost: Because each proxy needs to contact the rest of (n-1) proxies to serve clients’ timestamp request,
|
||||
the communication cost is n*(n-1), where n is the number of proxies;
|
||||
|
||||
* Batching: To reduce communication cost, each proxy batches clients’ timestamp requests for a configurable time period (say 1ms) and
|
||||
return the same timestamp for requests in the same batch.
|
||||
|
||||
* **Step 3 (Confirm proxy’s liveness)**: To prevent proxies that are no longer a part of the system (such as due to network partition) from serving requests,
|
||||
each proxy contacts the queuing system for each timestamp request to confirm it is still a valid proxy
|
||||
(i.e., not replaced by a newer generation proxy process).
|
||||
This is based on the FDB property that at most one active queuing system is available at any given time.
|
||||
|
||||
* Why do we need this step? This is to achieve consensus (i.e., external consistency).
|
||||
Compared to serializable isolation, Strict Serializable Isolation (SSI) requires external consistency.
|
||||
It means the timestamp received by clients cannot decrease. If we do not have step and network partition happens,
|
||||
a set of old proxies that are disconnected from the rest of systems can still serve timestamp requests to clients.
|
||||
These timestamps can be smaller than the new generation of proxies, which breaks the external consistency in SSI.
|
||||
|
||||
* O(n * m) communication cost: To confirm a proxy’s liveness, the proxy has to contact all members in the queuing system to
|
||||
ensure the queuing system is still active. This causes *m* network communication, where *m* is the number of processes in the queuing system.
|
||||
A system with n proxies will have O(n * m) network communications at the step 3. In our deployment, n is typically equal to m;
|
||||
|
||||
* Do FDB production clusters have this overhead? No. Our production clusters disable the external consistency by
|
||||
configuring the knob ALWAYS_CAUSAL_READ_RISKY.
|
||||
|
||||
* **Step 4 (Locality request)**: The client gets which storage servers have its requested keys by sending another RPC to proxy.
|
||||
This step returns a set of *k* storage server interfaces, where k is the replication factor;
|
||||
|
||||
* Client cache mechanism: The key location will be cached in client.
|
||||
Future requests will use the cache to directly read from storage servers,
|
||||
which saves a trip to proxy. If location is stale, read will return error and client will retry and refresh the cache.
|
||||
|
||||
* **Step 5 (Get data request)**: The client uses the location information from step 4 to directly query keys from corresponding storage servers.
|
||||
* Direct read from client’s memory: If a key’s value exists in the client’s memory, the client reads it directly from its local memory.
|
||||
This happens when a client updates a key’s value and later reads it.
|
||||
This optimization reduces the amount of unnecessary requests to storage servers.
|
||||
|
||||
* Load balance: Each data exists on k storage servers, where k is the replication factor.
|
||||
To balance the load across the k replicas, client has a load balancing algorithm to balance the number of requests to each replica.
|
||||
|
||||
* Transaction succeed: If the storage server has the data at the read timestamp, the client will receive the data and return succeed.
|
||||
|
||||
* Transaction too old error: If the read request’s timestamp is older than 5 seconds,
|
||||
storage server may have already flushed the data from its in-memory multi-version data structure to its on-disk single-version data structure.
|
||||
This means storage server does not have the data older than 5 seconds. So client will receive transaction too old error.
|
||||
The client will retry with a new timestamp.
|
||||
One scenario that can lead to the error is when it takes too long for a client to send the read request after it gets the timestamp.
|
||||
|
||||
* Future transaction error: Each storage server pulls data in increasing order of data’s timestamp from the queuing system.
|
||||
Let’s define a storage server’s timestamp as the largest timestamp of data the storage server has.
|
||||
If the read request’s timestamp is larger than the storage server’s timestamp,
|
||||
the storage server will reply future-transaction-error to the client.
|
||||
The client will retry. One scenario that can lead to the error is when the connection between the SS and the queuing system is slow.
|
||||
|
||||
* Wrong shard error: If keys in the request or result depend on data outside this storage server OR
|
||||
if a large selector offset prevents all data from being read in one range read.
|
||||
Client will invalidate its locality cache for the key and retry the read request at the failed key.
|
||||
|
||||
Implementation of FDB read path
|
||||
------------------------------------------
|
||||
|
||||
* **Step 1 (Timestamp request)**:
|
||||
* Each read request tries to get a timestamp if its transaction has not got one:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L2104
|
||||
* Client batches the get-timestamp requests:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L3172
|
||||
* Dynamic batching algorithm:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L3101-L3104
|
||||
|
||||
* **Step 2 (Get latest commit version)**: Contacting (n-1) proxies for commit version:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L1196
|
||||
|
||||
* **Step 3 (Confirm proxy’s liveness)**:
|
||||
* We typically set our clusters’ knob ALWAYS_CAUSAL_READ_RISKY to 1 to skip this step
|
||||
* Proxy confirm queuing system is alive:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L1199
|
||||
* How is confirmEpochLive(..) implemented for the above item:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/TagPartitionedLogSystem.actor.cpp#L1216-L1225
|
||||
|
||||
* **Step 4 (Locality request)**:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L1312-L1313
|
||||
|
||||
* **Step 5 (Get data request)**:
|
||||
* Logics of handling get value request:
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L1306-L1396
|
||||
* Load balance algorithm: The loadBalance() at
|
||||
https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L1342-L1344
|
||||
|
||||
|
||||
|
||||
Write path of a transaction
|
||||
================================
|
||||
|
||||
Suppose a client has a write-only transaction. Fig. 2 below shows the write path in a non-HA cluster.
|
||||
We will discuss how a transaction with both read and write works in the next section.
|
||||
|
||||
.. image:: /images/FDB_write_path.png
|
||||
|
||||
To simplify the explanation, the steps below do not include transaction batching on proxy,
|
||||
which is a typical database technique to increase transaction throughput.
|
||||
|
||||
* **Step 1 (Client buffers write mutations):** Client buffers all writes in a transaction until commit is called on the transaction.
|
||||
In the rest of document, a write is also named as a mutation.
|
||||
|
||||
* Client is a fat client that preprocess transactions:
|
||||
(a) For atomic operations, if client knows the key value, it will convert atomic operations to set operations;
|
||||
(b) For version stamp atomic operations, client adds extra bytes to key or value for the version stamp;
|
||||
(c) If a key has multiple operations, client coalesces them to one operation whenever possible.
|
||||
|
||||
* How client buffers mutations:
|
||||
https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2345-L2361
|
||||
|
||||
* **Step 2 (Client commits the transaction):** When a client calls commit(), it performs several operations:
|
||||
|
||||
* **Step 2-1**: Add extra conflict ranges that are added by user but cannot be calculated from mutations.
|
||||
|
||||
* **Step 2-2**: Get a timestamp as the transaction’s start time. The timestamp does not need causal consistency because the transaction has no read.
|
||||
* This request goes to one of proxies. The proxy will contact all other (n-1) proxies to get the most recent commit version as it does in read path.
|
||||
The proxy does not need to contact log systems to confirm its activeness because it does not need causal consistency.
|
||||
|
||||
* **Step 2-3**: Sends the transaction’s information to a proxy. Load balancer in client decides which proxy will be used to handle a transaction.
|
||||
A transaction’s information includes:
|
||||
|
||||
* All of its mutations;
|
||||
* Read and write conflict range;
|
||||
* Transaction options that control a transaction’s behavior. For example, should the transaction write when the DB is locked?
|
||||
Shall the transaction uses the first proxy in the proxy list to commit?
|
||||
|
||||
* Implementation:
|
||||
* Transaction commit function: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2895-L2899
|
||||
* Major work of commit in client side is done at here: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2784-L2868
|
||||
* Step 2-1: Add extra conflict ranges: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2826-L2828
|
||||
* Step 2-2: getReadVersion at commit which does not need external consistency because we do not have read in the transaction: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2822-L2823
|
||||
* Step 2-3: Send transaction to a proxy via RPC: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbclient/NativeAPI.actor.cpp#L2691-L2700
|
||||
|
||||
* When a proxy receives clients’ transactions, it commits the transaction on behalf of clients with Step 3 - 9.
|
||||
|
||||
* **Step 3 (Proxy gets commit timestamp)**: The proxy gets the timestamp of the transaction’s commit time from the time oracle through an RPC call.
|
||||
|
||||
* To improve transaction throughput and reduce network communication overhead,
|
||||
each proxy dynamically batch transactions and process transactions in batches.
|
||||
A proxy keeps batching transactions until the batch time exceeds a configurable timeout value or
|
||||
until the number of transactions exceed a configurable value or
|
||||
until the total bytes of the batch exceeds a dynamically calculated desired size.
|
||||
|
||||
* The network overhead is 1 network communication per batch of commit transactions;
|
||||
|
||||
* How is the dynamically calculated batch size calculated: https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L1770-L1774
|
||||
* How commit transactions are batched: https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L416-L486
|
||||
* How each transaction batch is handled: https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L523-L1174
|
||||
* Where does proxy sends commit timestamp request to the timestamp generator: https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbserver/MasterProxyServer.actor.cpp#L586-L587
|
||||
|
||||
* **Step 4 (Proxy builds transactions’ conflict ranges)**: Because the concurrency control component may have multiple processes,
|
||||
each of which is responsible for resolving conflicts in a key range,
|
||||
the proxy needs to build one transaction-conflict-resolution request for each concurrency control process:
|
||||
For each transaction, the proxy splits its read and write conflict ranges based on concurrency control process’ responsible ranges.
|
||||
The proxy will create k conflict resolution requests for each transaction, where k is the number of processes in the concurrency control component.
|
||||
|
||||
* Implementation: https://github.com/apple/foundationdb/blob/4086e3a2750b776cc8bfb0f0e463fe00ac905595/fdbserver/MasterProxyServer.actor.cpp#L607-L618
|
||||
|
||||
* **Step 5 (Proxy sends conflict resolution requests to concurrency control)**:
|
||||
Each concurrency control process is responsible for checking conflicts in a key range.
|
||||
Each process checks if the transaction has conflicts with other transactions in its key-range.
|
||||
Each process returns the conflict checking result back to the proxy.
|
||||
|
||||
* What is conflict range?
|
||||
* A transaction’s write conflict range includes any key and key-ranges that are modified in the transactions.
|
||||
* A transaction’s read conflict range includes any key and key-ranges that are read in the transaction.
|
||||
* Client can also use transaction options to add explicit read-conflict-range or write-conflict-range.
|
||||
Example: https://github.com/apple/foundationdb/blob/4b0fba6ea89b51b82df7868ca24b81f6997db4e4/fdbclient/NativeAPI.actor.cpp#L2634-L2635
|
||||
|
||||
* **Piggy-back metadata change**. If the transaction changes database’s metadata, such as locking the database,
|
||||
the change is considered as a special mutation and also checked for conflicts by the concurrency control component.
|
||||
The primary difference between metadata mutation and normal mutations is that the metadata change must be propagated to all proxies
|
||||
so that all proxies have a consistent view of database’s metadata.
|
||||
This is achieved by piggy-backing metadata change in the reply from resolver to proxies.
|
||||
|
||||
* Implementation
|
||||
* Create conflict resolution requests for a batch of transactions: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L607-L618
|
||||
* Metadata mutations are sent from proxy to concurrency control processes: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L366-L369
|
||||
|
||||
* **Step 6 (Resolve conflicts among concurrent transactions)**:
|
||||
Each concurrency control process checks conflicts among transactions based on the theory in [1].
|
||||
In a nutshell, it checks for read-write conflicts. Suppose two transactions operates on the same key.
|
||||
If a write transaction’s time overlaps between another read-write transaction’s start time and commit time,
|
||||
only one transaction can commit: the one that arrives first at all concurrency control processes will commit.
|
||||
|
||||
* Implementation
|
||||
* Proxy sends conflict checking request: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L626-L629
|
||||
* Concurrency control process handles the request: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/Resolver.actor.cpp#L320-L322
|
||||
|
||||
* **Step 7 (Proxy’s post resolution processing)**:
|
||||
Once the proxy receives conflict-resolution replies from all concurrency control processes, it performs three steps
|
||||
|
||||
* **Step 7-1 (Apply metadata effect caused by other proxies)**: As mentioned above, when a proxy changes database’s metadata,
|
||||
the metadata mutations will be propagated via the concurrency control component to other proxies.
|
||||
So the proxy needs to first compute and apply these metadata mutations onto the proxy’s local states.
|
||||
Otherwise, the proxy will operate in a different view of database’s metadata.
|
||||
|
||||
* For example, if one proxy locks the database in a committed transaction at time t1, all other proxies should have seen the lock immediately after t1. Since another proxy may have transactions in flight already at t1, the proxy must first apply the “lock“ effect before it can process its in-flight transactions.
|
||||
* How metadata effect is applied in implementation: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L678-L719
|
||||
|
||||
* **Step 7-2 (Determine which transactions are committed)**: Proxy combines results from all concurrency control processes.
|
||||
Only if all concurrency control processes say a transaction is committed, will the transaction be considered as committed by the proxy.
|
||||
|
||||
* Implementation: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L721-L757
|
||||
|
||||
* **Step 7-3 (Apply metadata effect caused by this proxy)**: For each committed transaction,
|
||||
this proxy applies its metadata mutations to the proxy’s local state.
|
||||
|
||||
* Note: These metadata mutations are also sent to concurrency control processes and propagated to other proxies at Step 5.
|
||||
This step is to apply metadata effect on its own proxy’s states.
|
||||
* Implementation: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L763-L777
|
||||
|
||||
* **Step 7-4 (Assign mutations to storage servers and serialize them)**:
|
||||
In order to let the rest of system (the queuing system and storage system) know which process a mutation should be routed to,
|
||||
the proxy needs to add tags to mutations.
|
||||
The proxy serializes mutations with the same tag into the same message and sends the serialized message to the queuing system.
|
||||
|
||||
* Implementation of adding tags and serializing mutations into messages: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L800-L910
|
||||
* The lines that add tags to a mutation and serialize it: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L846-L847
|
||||
|
||||
* **Step 7-5 (Duplicate and serialize mutations to backup system keyspace)**:
|
||||
When backup or disaster recovery (DR) is enabled, each proxy captures mutation streams into a dedicated system keyspace.
|
||||
Mutations in a transaction batch are serialized as a single mutation in a dedicated system keyspace.
|
||||
|
||||
* How mutations are duplicated for backup and DR: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L912-L986
|
||||
* Note: FDB will have a new backup system that avoids duplicating mutations to the system keyspace.
|
||||
Its design is similar to database’s Change Data Capture (CDC) design. The new backup system is not production-ready yet.
|
||||
|
||||
* **Step 8 (Make mutation messages durable in the queuing system)**:
|
||||
Proxy sends serialized mutation messages to the queuing system.
|
||||
The queuing system will append the mutation to an append-only file, fsync it, and send the respnose back.
|
||||
Each message has a tag, which decides which process in the queuing system the message should be sent to.
|
||||
The queuing system returns to the proxy the minimum known committed version, which is the smallest commit version among all proxies.
|
||||
The minimum known commit version is used when the system recovers from fault.
|
||||
|
||||
* Sending messages to the queuing system is abstracted into a push() operation: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L1045
|
||||
* The minimum known committed version is called minKnownCommittedVersion. It is updated for each commit: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L1067
|
||||
|
||||
* **Step 9 (Reply to client)**: Proxy replies the transaction’s result to client.
|
||||
If the transaction fails (say due to transaction conflicts), proxy sends the error message to the client.
|
||||
|
||||
* Reply to clients based on different transaction’s results: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/MasterProxyServer.actor.cpp#L1117-L1138
|
||||
|
||||
* **Step 10 (Storage systems pull data from queuing system)**:
|
||||
Storage system asynchronously pulls data from queuing system and indexes data for read path.
|
||||
|
||||
* Each SS has a primary process (called primary tLog) in the queuing system to pull data from the SS’s data from the queuing system.
|
||||
Each SS only gets in-ordered streams of mutations that are owned by the SS.
|
||||
|
||||
* In failure scenario when a SS cannot reach the primary tLog, the SS will pull data from different tLogs that have part of the SS’s data.
|
||||
The SS will then merge the stream of data from different tLogs.
|
||||
|
||||
* Each SS does not make its pulled data durable to disk until the data becomes
|
||||
at least 5 seconds older than the most recent data the SS has pulled.
|
||||
This allows each SS to roll back at least 5 seconds of mutations.
|
||||
|
||||
* Why do we need roll back feature for SS? This comes from an optimization used in FDB.
|
||||
To make a mutation available in a SS as soon as possible,
|
||||
a SS may fetch a mutation from the queuing system that has not been fully replicated.
|
||||
The mutation’s transaction may be aborted in rare situations, such as
|
||||
when FDB has to recover from faults and decides to throw away the last few non-fully-durable transactions.
|
||||
SSes must throw away data in the aborted transactions.
|
||||
|
||||
* Why does SS not make data durable until 5 seconds later?
|
||||
This is because today’s SS does not support rolling back data that has already been made durable on disk.
|
||||
To support roll back, SS keeps data that might be rolled back in memory.
|
||||
When roll-back is needed, SS just throws away the in-memory data. This simplifies the SS implementation.
|
||||
|
||||
|
||||
* Each storage process pulls data from the queuing system: https://github.com/apple/foundationdb/blob/07e354c499158630d760283aa845440cbeaaa1ca/fdbserver/storageserver.actor.cpp#L3593-L3599
|
||||
|
||||
|
||||
|
||||
Read write path of a transaction
|
||||
====================================
|
||||
|
||||
This section uses an example transaction to describe how a transaction with both read and write operation works in FDB.
|
||||
|
||||
Suppose application creates the following transaction, where *Future<int>* is an object that holds an asynchronous call and
|
||||
becomes ready when the async call returns, and *wait()* is a synchronous point when the code waits for futures to be ready.
|
||||
The following code reads key k1 and k2 from database, increases k1’s value by 1 and write back k1’s new value into database.
|
||||
|
||||
**Example Transaction** ::
|
||||
|
||||
Line1: Transaction tr;
|
||||
Line2: Future<int> fv1 = tr.get(k1);
|
||||
Line3: Future<int> fv2 = tr.get(k2);
|
||||
Line4: v1 = wait(fv1);
|
||||
Line5: v2 = wait(fv2);
|
||||
Line6: tr.set(v1+v2);
|
||||
Line7: tr.commit();
|
||||
|
||||
The transaction starts with the read path:
|
||||
|
||||
* When tr.get() is called, FDB client issues a timestamp request to proxies *if* the transaction has not set its start timestamp.
|
||||
The logic is the Step 1 in the read path;
|
||||
|
||||
* Batching timestamp requests. When another tr.get() is called, it will try to get a timestamp as well. If we let every get request to follow the Step 1 in the read path, the performance overhead (especially network communication) will be a lot. In addition, this is not necessary because a transaction has only one start timestamp. To solve this problem, client chooses to batch timestamp requests from the same transaction and only issues one timestamp request when the transaction size reaches a preconfigured threshold or when the transaction duration reaches the batching timeout threshold.
|
||||
* Timestamp requests are batched: https://github.com/apple/foundationdb/blob/4086e3a2750b776cc8bfb0f0e463fe00ac905595/fdbclient/NativeAPI.actor.cpp#L3185
|
||||
* Thresholds for client to send the timestamp request: https://github.com/apple/foundationdb/blob/4086e3a2750b776cc8bfb0f0e463fe00ac905595/fdbclient/NativeAPI.actor.cpp#L3095-L3098
|
||||
|
||||
* Each read request, i.e., tr.get operation in the example, will follow the read path to get data from storage servers, except that they will share the same timestamp;
|
||||
* These read requests are sent to FDB cluster in parallel.
|
||||
The ordering of which read request will be ready first depends on requests’ network path and storage servers’ load.
|
||||
* In the example, tr.get(k2) may return result earlier than tr.get(k1).
|
||||
|
||||
* Client will likely block at the synchronization point at Line 4, until the value is returned from the cluster.
|
||||
* To maximize clients’ performance, a client can issue multiple transactions concurrently.
|
||||
When one transaction is blocked at the synchronization point,
|
||||
the client can switch to work on the other transactions concurrently.
|
||||
|
||||
* Client may or may not block at the synchronization point at Line 5.
|
||||
If tr.get(k2) returns earlier than tr.get(k1), the future fv2 is already ready when the client arrives at Line 5.
|
||||
|
||||
* At Line 6, client starts the write path. Because the transaction already has its start timestamp,
|
||||
client does not need to request for the transaction’s start time any more and can skip the Step 2-2 in the write path.
|
||||
|
||||
* At Line 7, client commits the transaction, which will trigger the operations from Step 2 in the write path.
|
||||
|
||||
|
||||
A transaction can get more complex than the example above.
|
||||
|
||||
* A transaction can have more writes operations between Line 6 and Line 7.
|
||||
Those writes will be buffered in client’s memory, which is the Step 1 in the write path.
|
||||
Only when the client calls commit(), will the rest of steps in the write path will be triggered;
|
||||
|
||||
* A transaction can have reads operations between Line 6 and Line 7 as well.
|
||||
|
||||
* A transaction may return commit_unknown_result, which indicate the transaction may or may not succeed.
|
||||
If application simply retries the transaction, the transaction may get executed twice.
|
||||
To solve this problem, the application can adds a transaction id to the transaction and
|
||||
check if the transaction id exists on the commit_unknown_result error.
|
||||
|
||||
|
||||
|
||||
Concurrency and ordering of multiple write transactions
|
||||
=======================================================================
|
||||
|
||||
FDB orders concurrent transactions in increasing order of the transactions’ commit timestamp.
|
||||
The ordering is enforced in the timestamp generator, the concurrency control component and the durable queuing system.
|
||||
|
||||
* When timestamp generator serves the commit timestamp request from a proxy,
|
||||
the reply includes not only the commit timestamp but also the latest commit timestamp the generator has sent out.
|
||||
For example, the timestamp generator just gave out the commit timestamp 50.
|
||||
When the next request arrives, the generator’s timestamp is 100 and the generator replies (50, 100).
|
||||
When the second request arrives and the generator’s timestamp is 200, the generator replies (100, 200).
|
||||
|
||||
* When a proxy sends conflict resolution requests to concurrency control processes or durable requests to the queuing system,
|
||||
each request includes both the current transaction’s commit timestamp and the previous transaction’s commit timestamp.
|
||||
|
||||
* Each concurrency control process and each process in the queuing system always process requests in the strict order of the request’s commit version.
|
||||
The semantics is do not process a request whose commit timestamp is V2 until the request at its previous commit timestamp V1 has been processed.
|
||||
|
||||
|
||||
We use the following example and draw its swimlane diagram to illustrate how two write transactions are ordered in FDB.
|
||||
The diagram with notes can be viewed at `here <https://lucid.app/lucidchart/6336dbe3-cff4-4c46-995a-4ca3d9260696/view?page=0_0#?folder_id=home&browser=icon>`_.
|
||||
|
||||
.. image:: /images/FDB_multiple_txn_swimlane_diagram.png
|
||||
|
||||
Reference
|
||||
============
|
||||
|
||||
[SSI] Serializable Snapshot Isolation in PostgreSQL. https://arxiv.org/pdf/1208.4179.pdf
|
||||
|
|
@ -2,12 +2,36 @@
|
|||
Release Notes
|
||||
#############
|
||||
|
||||
6.2.31
|
||||
======
|
||||
* Fix a rare invalid memory access on data distributor when snapshotting large clusters. This is a follow up to `PR #4076 <https://github.com/apple/foundationdb/pull/4076>`_. `(PR #4317) <https://github.com/apple/foundationdb/pull/4317>`_
|
||||
|
||||
6.2.30
|
||||
======
|
||||
* A storage server which has fallen behind will deprioritize reads in order to catch up. This change causes some saturating workloads to experience high read latencies instead of high GRV latencies. `(PR #4218) <https://github.com/apple/foundationdb/pull/4218>`_
|
||||
* Added ``low_priority_queries`` to the ``processes.roles`` section of status to record the number of deprioritized reads on each storage server. `(PR #4218) <https://github.com/apple/foundationdb/pull/4218>`_
|
||||
* Added ``low_priority_reads`` to the ``workload.operations`` section of status to record the total number of deprioritized reads. `(PR #4218) <https://github.com/apple/foundationdb/pull/4218>`_
|
||||
* Backup to locally mounted filesystems now appends to files in large block writes, 1MB each by default. `(PR #4199) <https://github.com/apple/foundationdb/pull/4199>`_
|
||||
* Changed the default SSL implementation from OpenSSL to BoringSSL `(PR #4153) <https://github.com/apple/foundationdb/pull/4153>`_
|
||||
* SQLite now supports configurable disk write rate limiting. `(PR #4259) <https://github.com/apple/foundationdb/pull/4259>`_
|
||||
* If a disk operation takes more than two minutes, the system will treat the disk as failed. `(PR #4243) <https://github.com/apple/foundationdb/pull/4243>`_
|
||||
|
||||
6.2.29
|
||||
======
|
||||
* Fix invalid memory access on data distributor when snapshotting large clusters. `(PR #4076) <https://github.com/apple/foundationdb/pull/4076>`_
|
||||
* Add human-readable DateTime to trace events `(PR #4087) <https://github.com/apple/foundationdb/pull/4087>`_
|
||||
* Proxy rejects transaction batch that exceeds MVCC window `(PR #4113) <https://github.com/apple/foundationdb/pull/4113>`_
|
||||
* Add a command in fdbcli to manually trigger the detailed teams information loggings in data distribution. `(PR #4060) <https://github.com/apple/foundationdb/pull/4060>`_
|
||||
* Add documentation on read and write Path. `(PR #4099) <https://github.com/apple/foundationdb/pull/4099>`_
|
||||
* Add a histogram to expose commit batching window on Proxies. `(PR #4166) <https://github.com/apple/foundationdb/pull/4166>`_
|
||||
* Fix double counting of range reads in TransactionMetrics. `(PR #4130) <https://github.com/apple/foundationdb/pull/4130>`_
|
||||
* Add a trace event that can be used as an indicator of the load on the proxy. `(PR #4166) <https://github.com/apple/foundationdb/pull/4166>`_
|
||||
|
||||
6.2.28
|
||||
======
|
||||
* Log detailed team collection information when median available space ratio of all teams is too low. `(PR #3912) <https://github.com/apple/foundationdb/pull/3912>`_
|
||||
* Bug fix, blob client did not support authentication key sizes over 64 bytes. `(PR #3964) <https://github.com/apple/foundationdb/pull/3964>`_
|
||||
|
||||
|
||||
6.2.27
|
||||
======
|
||||
* For clusters with a large number of shards, avoid slow tasks in the data distributor by adding yields to the shard map destruction. `(PR #3834) <https://github.com/apple/foundationdb/pull/3834>`_
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ Release Notes
|
|||
|
||||
6.3.10
|
||||
======
|
||||
* Make fault tolerance metric calculation in HA clusters consistent with 6.2 branch. `(PR #4175) <https://github.com/apple/foundationdb/pull/4175>`_
|
||||
* Bug fix, stack overflow in redwood storage engine. `(PR #4161) <https://github.com/apple/foundationdb/pull/4161>`_
|
||||
* Bug fix, getting certain special keys fail. `(PR #4128) <https://github.com/apple/foundationdb/pull/4128>`_
|
||||
* Prevent slow task on TLog by yielding while processing ignored pop requests. `(PR #4112) <https://github.com/apple/foundationdb/pull/4112>`_
|
||||
* Support reading xxhash3 sqlite checksums. `(PR #4104) <https://github.com/apple/foundationdb/pull/4104>`_
|
||||
* Fix a race between submit and abort backup. `(PR #3935) <https://github.com/apple/foundationdb/pull/3935>`_
|
||||
|
||||
Packaging
|
||||
---------
|
||||
|
|
@ -84,6 +90,7 @@ Status
|
|||
* Removed fields ``worst_version_lag_storage_server`` and ``limiting_version_lag_storage_server`` from the ``cluster.qos`` section. The ``worst_data_lag_storage_server`` and ``limiting_data_lag_storage_server`` objects can be used instead. `(PR #3196) <https://github.com/apple/foundationdb/pull/3196>`_
|
||||
* If a process is unable to flush trace logs to disk, the problem will now be reported via the output of ``status`` command inside ``fdbcli``. `(PR #2605) <https://github.com/apple/foundationdb/pull/2605>`_ `(PR #2820) <https://github.com/apple/foundationdb/pull/2820>`_
|
||||
* When a configuration key is changed, it will always be included in ``status json`` output, even the value is reverted back to the default value. [6.3.5] `(PR #3610) <https://github.com/apple/foundationdb/pull/3610>`_
|
||||
* Added transactions.rejected_for_queued_too_long for bookkeeping the number of transactions rejected by commit proxy because its queuing time exceeds MVCC window. `(PR #4353) <https://github.com/apple/foundationdb/pull/4353>`_
|
||||
|
||||
Bindings
|
||||
--------
|
||||
|
|
@ -134,7 +141,7 @@ Fixes from previous versions
|
|||
* The 6.3.3 patch release includes all fixes from the patch release 6.2.23. :doc:`(6.2 Release Notes) </release-notes/release-notes-620>`
|
||||
* The 6.3.5 patch release includes all fixes from the patch releases 6.2.24 and 6.2.25. :doc:`(6.2 Release Notes) </release-notes/release-notes-620>`
|
||||
* The 6.3.9 patch release includes all fixes from the patch releases 6.2.26. :doc:`(6.2 Release Notes) </release-notes/release-notes-620>`
|
||||
* The 6.3.10 patch release includes all fixes from the patch releases 6.2.27. :doc:`(6.2 Release Notes) </release-notes/release-notes-620>`
|
||||
* The 6.3.10 patch release includes all fixes from the patch releases 6.2.27-6.2.29 :doc:`(6.2 Release Notes) </release-notes/release-notes-620>`
|
||||
|
||||
Fixes only impacting 6.3.0+
|
||||
---------------------------
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ These documents explain the engineering design of FoundationDB, with detailed in
|
|||
|
||||
* :doc:`kv-architecture` provides a description of every major role a process in FoundationDB can fulfill.
|
||||
|
||||
* :doc:`read-write-path` describes how FDB read and write path works.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:titlesonly:
|
||||
|
|
@ -45,3 +47,4 @@ These documents explain the engineering design of FoundationDB, with detailed in
|
|||
flow
|
||||
testing
|
||||
kv-architecture
|
||||
read-write-path
|
||||
|
|
|
|||
|
|
@ -590,6 +590,9 @@ void initHelp() {
|
|||
CommandHelp("unlock <UID>", "unlock the database with the provided lockUID",
|
||||
"Unlocks the database with the provided lockUID. This is a potentially dangerous operation, so the "
|
||||
"user will be asked to enter a passphrase to confirm their intent.");
|
||||
helpMap["triggerddteaminfolog"] =
|
||||
CommandHelp("triggerddteaminfolog", "trigger the data distributor teams logging",
|
||||
"Trigger the data distributor to log detailed information about its teams.");
|
||||
|
||||
hiddenCommands.insert("expensive_data_check");
|
||||
hiddenCommands.insert("datadistribution");
|
||||
|
|
@ -1741,6 +1744,23 @@ int printStatusFromJSON( std::string const& jsonFileName ) {
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> triggerDDTeamInfoLog(Database db) {
|
||||
state ReadYourWritesTransaction tr(db);
|
||||
loop {
|
||||
try {
|
||||
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
||||
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
||||
std::string v = deterministicRandom()->randomUniqueID().toString();
|
||||
tr.set(triggerDDTeamInfoPrintKey, v);
|
||||
wait(tr.commit());
|
||||
printf("Triggered team info logging in data distribution.\n");
|
||||
return Void();
|
||||
} catch (Error& e) {
|
||||
wait(tr.onError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> timeWarning( double when, const char* msg ) {
|
||||
wait( delay(when) );
|
||||
fputs( msg, stderr );
|
||||
|
|
@ -3161,6 +3181,11 @@ ACTOR Future<int> cli(CLIOptions opt, LineNoise* plinenoise) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "triggerddteaminfolog")) {
|
||||
wait(triggerDDTeamInfoLog(db));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokencmp(tokens[0], "configure")) {
|
||||
bool err = wait(configure(db, tokens, db->getConnectionFile(), &linenoise, warn));
|
||||
if (err) is_error = true;
|
||||
|
|
|
|||
|
|
@ -377,6 +377,9 @@ public:
|
|||
Reference<FutureBucket> futureBucket;
|
||||
};
|
||||
|
||||
template<> inline Tuple Codec<FileBackupAgent::ERestoreState>::pack(FileBackupAgent::ERestoreState const &val) { return Tuple().append(val); }
|
||||
template<> inline FileBackupAgent::ERestoreState Codec<FileBackupAgent::ERestoreState>::unpack(Tuple const &val) { return (FileBackupAgent::ERestoreState)val.getInt(0); }
|
||||
|
||||
class DatabaseBackupAgent : public BackupAgentBase {
|
||||
public:
|
||||
DatabaseBackupAgent();
|
||||
|
|
|
|||
|
|
@ -1716,15 +1716,39 @@ public:
|
|||
|
||||
class BackupFile : public IBackupFile, ReferenceCounted<BackupFile> {
|
||||
public:
|
||||
BackupFile(std::string fileName, Reference<IAsyncFile> file, std::string finalFullPath) : IBackupFile(fileName), m_file(file), m_finalFullPath(finalFullPath) {}
|
||||
BackupFile(std::string fileName, Reference<IAsyncFile> file, std::string finalFullPath)
|
||||
: IBackupFile(fileName), m_file(file), m_finalFullPath(finalFullPath), m_writeOffset(0)
|
||||
{
|
||||
m_buffer.reserve(m_buffer.arena(), CLIENT_KNOBS->BACKUP_LOCAL_FILE_WRITE_BLOCK);
|
||||
}
|
||||
|
||||
Future<Void> append(const void *data, int len) {
|
||||
Future<Void> r = m_file->write(data, len, m_offset);
|
||||
m_offset += len;
|
||||
m_buffer.append(m_buffer.arena(), (const uint8_t *)data, len);
|
||||
|
||||
if(m_buffer.size() >= CLIENT_KNOBS->BACKUP_LOCAL_FILE_WRITE_BLOCK) {
|
||||
return flush(CLIENT_KNOBS->BACKUP_LOCAL_FILE_WRITE_BLOCK);
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
Future<Void> flush(int size) {
|
||||
ASSERT(size <= m_buffer.size());
|
||||
|
||||
// Keep a reference to the old buffer
|
||||
Standalone<VectorRef<uint8_t>> old = m_buffer;
|
||||
// Make a new buffer, initialized with the excess bytes over the block size from the old buffer
|
||||
m_buffer = Standalone<VectorRef<uint8_t>>(old.slice(size, old.size()));
|
||||
|
||||
// Write the old buffer to the underlying file and update the write offset
|
||||
Future<Void> r = holdWhile(old, m_file->write(old.begin(), size, m_writeOffset));
|
||||
m_writeOffset += size;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> finish_impl(Reference<BackupFile> f) {
|
||||
wait(f->flush(f->m_buffer.size()));
|
||||
wait(f->m_file->truncate(f->size())); // Some IAsyncFile implementations extend in whole block sizes.
|
||||
wait(f->m_file->sync());
|
||||
std::string name = f->m_file->getFilename();
|
||||
|
|
@ -1733,6 +1757,10 @@ public:
|
|||
return Void();
|
||||
}
|
||||
|
||||
int64_t size() const {
|
||||
return m_buffer.size() + m_writeOffset;
|
||||
}
|
||||
|
||||
Future<Void> finish() {
|
||||
return finish_impl(Reference<BackupFile>::addRef(this));
|
||||
}
|
||||
|
|
@ -1742,6 +1770,8 @@ public:
|
|||
|
||||
private:
|
||||
Reference<IAsyncFile> m_file;
|
||||
Standalone<VectorRef<uint8_t>> m_buffer;
|
||||
int64_t m_writeOffset;
|
||||
std::string m_finalFullPath;
|
||||
};
|
||||
|
||||
|
|
@ -1874,7 +1904,7 @@ public:
|
|||
|
||||
class BackupFile : public IBackupFile, ReferenceCounted<BackupFile> {
|
||||
public:
|
||||
BackupFile(std::string fileName, Reference<IAsyncFile> file) : IBackupFile(fileName), m_file(file) {}
|
||||
BackupFile(std::string fileName, Reference<IAsyncFile> file) : IBackupFile(fileName), m_file(file), m_offset(0) {}
|
||||
|
||||
Future<Void> append(const void *data, int len) {
|
||||
Future<Void> r = m_file->write(data, len, m_offset);
|
||||
|
|
@ -1887,11 +1917,16 @@ public:
|
|||
return map(m_file->sync(), [=](Void _) { self->m_file.clear(); return Void(); });
|
||||
}
|
||||
|
||||
int64_t size() const {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void addref() final { return ReferenceCounted<BackupFile>::addref(); }
|
||||
void delref() final { return ReferenceCounted<BackupFile>::delref(); }
|
||||
|
||||
private:
|
||||
Reference<IAsyncFile> m_file;
|
||||
int64_t m_offset;
|
||||
};
|
||||
|
||||
Future<Reference<IBackupFile>> writeFile(std::string path) final {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Future<Version> timeKeeperVersionFromDatetime(std::string const &datetime, Datab
|
|||
// TODO: Move the log file and range file format encoding/decoding stuff to this file and behind interfaces.
|
||||
class IBackupFile {
|
||||
public:
|
||||
IBackupFile(std::string fileName) : m_fileName(fileName), m_offset(0) {}
|
||||
IBackupFile(std::string fileName) : m_fileName(fileName) {}
|
||||
virtual ~IBackupFile() {}
|
||||
// Backup files are append-only and cannot have more than 1 append outstanding at once.
|
||||
virtual Future<Void> append(const void *data, int len) = 0;
|
||||
|
|
@ -48,16 +48,13 @@ public:
|
|||
inline std::string getFileName() const {
|
||||
return m_fileName;
|
||||
}
|
||||
inline int64_t size() const {
|
||||
return m_offset;
|
||||
}
|
||||
virtual int64_t size() const = 0;
|
||||
virtual void addref() = 0;
|
||||
virtual void delref() = 0;
|
||||
|
||||
Future<Void> appendStringRefWithLen(Standalone<StringRef> s);
|
||||
protected:
|
||||
std::string m_fileName;
|
||||
int64_t m_offset;
|
||||
};
|
||||
|
||||
// Structures for various backup components
|
||||
|
|
|
|||
|
|
@ -51,9 +51,19 @@ struct RegionInfo {
|
|||
int32_t priority;
|
||||
|
||||
Reference<IReplicationPolicy> satelliteTLogPolicy;
|
||||
|
||||
// Number of tLogs that should be recruited in satellite datacenters.
|
||||
int32_t satelliteDesiredTLogCount;
|
||||
|
||||
// Total number of copies made for each mutation across all satellite tLogs in all DCs.
|
||||
int32_t satelliteTLogReplicationFactor;
|
||||
|
||||
// Number of tLog replies we can ignore when waiting for quorum. Hence, effective quorum is
|
||||
// satelliteDesiredTLogCount - satelliteTLogWriteAntiQuorum. Locality of individual tLogs is not taken
|
||||
// into account.
|
||||
int32_t satelliteTLogWriteAntiQuorum;
|
||||
|
||||
// Number of satellite datacenters for current region, as set by `satellite_redundancy_mode`.
|
||||
int32_t satelliteTLogUsableDcs;
|
||||
|
||||
Reference<IReplicationPolicy> satelliteTLogPolicyFallback;
|
||||
|
|
@ -63,27 +73,32 @@ struct RegionInfo {
|
|||
|
||||
std::vector<SatelliteInfo> satellites;
|
||||
|
||||
RegionInfo() : priority(0), satelliteDesiredTLogCount(-1), satelliteTLogReplicationFactor(0), satelliteTLogWriteAntiQuorum(0), satelliteTLogUsableDcs(1),
|
||||
satelliteTLogReplicationFactorFallback(0), satelliteTLogWriteAntiQuorumFallback(0), satelliteTLogUsableDcsFallback(0) {}
|
||||
RegionInfo()
|
||||
: priority(0), satelliteDesiredTLogCount(-1), satelliteTLogReplicationFactor(0), satelliteTLogWriteAntiQuorum(0),
|
||||
satelliteTLogUsableDcs(1), satelliteTLogReplicationFactorFallback(0), satelliteTLogWriteAntiQuorumFallback(0),
|
||||
satelliteTLogUsableDcsFallback(0) {}
|
||||
|
||||
struct sort_by_priority {
|
||||
bool operator ()(RegionInfo const&a, RegionInfo const& b) const { return a.priority > b.priority; }
|
||||
bool operator()(RegionInfo const& a, RegionInfo const& b) const { return a.priority > b.priority; }
|
||||
};
|
||||
|
||||
template <class Ar>
|
||||
void serialize(Ar& ar) {
|
||||
serializer(ar, dcId, priority, satelliteTLogPolicy, satelliteDesiredTLogCount, satelliteTLogReplicationFactor, satelliteTLogWriteAntiQuorum, satelliteTLogUsableDcs,
|
||||
satelliteTLogPolicyFallback, satelliteTLogReplicationFactorFallback, satelliteTLogWriteAntiQuorumFallback, satelliteTLogUsableDcsFallback, satellites);
|
||||
serializer(ar, dcId, priority, satelliteTLogPolicy, satelliteDesiredTLogCount, satelliteTLogReplicationFactor,
|
||||
satelliteTLogWriteAntiQuorum, satelliteTLogUsableDcs, satelliteTLogPolicyFallback,
|
||||
satelliteTLogReplicationFactorFallback, satelliteTLogWriteAntiQuorumFallback,
|
||||
satelliteTLogUsableDcsFallback, satellites);
|
||||
}
|
||||
};
|
||||
|
||||
struct DatabaseConfiguration {
|
||||
DatabaseConfiguration();
|
||||
|
||||
void applyMutation( MutationRef mutation );
|
||||
bool set( KeyRef key, ValueRef value ); // Returns true if a configuration option that requires recovery to take effect is changed
|
||||
bool clear( KeyRangeRef keys );
|
||||
Optional<ValueRef> get( KeyRef key ) const;
|
||||
void applyMutation(MutationRef mutation);
|
||||
bool set(KeyRef key,
|
||||
ValueRef value); // Returns true if a configuration option that requires recovery to take effect is changed
|
||||
bool clear(KeyRangeRef keys);
|
||||
Optional<ValueRef> get(KeyRef key) const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
|
|
@ -92,63 +107,75 @@ struct DatabaseConfiguration {
|
|||
std::string toString() const;
|
||||
StatusObject toJSON(bool noPolicies = false) const;
|
||||
StatusArray getRegionJSON() const;
|
||||
|
||||
RegionInfo getRegion( Optional<Key> dcId ) const {
|
||||
if(!dcId.present()) {
|
||||
|
||||
RegionInfo getRegion(Optional<Key> dcId) const {
|
||||
if (!dcId.present()) {
|
||||
return RegionInfo();
|
||||
}
|
||||
for(auto& r : regions) {
|
||||
if(r.dcId == dcId.get()) {
|
||||
for (auto& r : regions) {
|
||||
if (r.dcId == dcId.get()) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return RegionInfo();
|
||||
}
|
||||
|
||||
int expectedLogSets( Optional<Key> dcId ) const {
|
||||
int expectedLogSets(Optional<Key> dcId) const {
|
||||
int result = 1;
|
||||
if(dcId.present() && getRegion(dcId.get()).satelliteTLogReplicationFactor > 0 && usableRegions > 1) {
|
||||
if (dcId.present() && getRegion(dcId.get()).satelliteTLogReplicationFactor > 0 && usableRegions > 1) {
|
||||
result++;
|
||||
}
|
||||
|
||||
if(usableRegions > 1) {
|
||||
|
||||
if (usableRegions > 1) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Counts the number of DCs required including remote and satellites for current database configuraiton.
|
||||
int32_t minDatacentersRequired() const {
|
||||
int minRequired = 0;
|
||||
for(auto& r : regions) {
|
||||
for (auto& r : regions) {
|
||||
minRequired += 1 + r.satellites.size();
|
||||
}
|
||||
return minRequired;
|
||||
}
|
||||
|
||||
int32_t minZonesRequiredPerDatacenter() const {
|
||||
int minRequired = std::max( remoteTLogReplicationFactor, std::max(tLogReplicationFactor, storageTeamSize) );
|
||||
for(auto& r : regions) {
|
||||
minRequired = std::max( minRequired, r.satelliteTLogReplicationFactor/std::max(1, r.satelliteTLogUsableDcs) );
|
||||
int minRequired = std::max(remoteTLogReplicationFactor, std::max(tLogReplicationFactor, storageTeamSize));
|
||||
for (auto& r : regions) {
|
||||
minRequired =
|
||||
std::max(minRequired, r.satelliteTLogReplicationFactor / std::max(1, r.satelliteTLogUsableDcs));
|
||||
}
|
||||
return minRequired;
|
||||
}
|
||||
|
||||
//Killing an entire datacenter counts as killing one zone in modes that support it
|
||||
// Retuns the maximum number of discrete failures a cluster can tolerate.
|
||||
// In HA mode, `fullyReplicatedRegions` is set to false initially when data is being
|
||||
// replicated to remote, and will be true later. `forAvailablity` is set to true
|
||||
// if we want to account the number for machines that can recruit new tLogs/SS after failures.
|
||||
// Killing an entire datacenter counts as killing one zone in modes that support it
|
||||
int32_t maxZoneFailuresTolerated(int fullyReplicatedRegions, bool forAvailability) const {
|
||||
int worstSatellite = regions.size() ? std::numeric_limits<int>::max() : 0;
|
||||
int regionsWithNonNegativePriority = 0;
|
||||
for(auto& r : regions) {
|
||||
if(r.priority >= 0) {
|
||||
for (auto& r : regions) {
|
||||
if (r.priority >= 0) {
|
||||
regionsWithNonNegativePriority++;
|
||||
}
|
||||
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactor - r.satelliteTLogWriteAntiQuorum);
|
||||
if(r.satelliteTLogUsableDcsFallback > 0) {
|
||||
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactorFallback - r.satelliteTLogWriteAntiQuorumFallback);
|
||||
worstSatellite =
|
||||
std::min(worstSatellite, r.satelliteTLogReplicationFactor - r.satelliteTLogWriteAntiQuorum);
|
||||
if (r.satelliteTLogUsableDcsFallback > 0) {
|
||||
worstSatellite = std::min(worstSatellite, r.satelliteTLogReplicationFactorFallback -
|
||||
r.satelliteTLogWriteAntiQuorumFallback);
|
||||
}
|
||||
}
|
||||
if(usableRegions > 1 && fullyReplicatedRegions > 1 && worstSatellite > 0 && (!forAvailability || regionsWithNonNegativePriority > 1)) {
|
||||
return 1 + std::min(std::max(tLogReplicationFactor - 1 - tLogWriteAntiQuorum, worstSatellite - 1), storageTeamSize - 1);
|
||||
} else if(worstSatellite > 0) {
|
||||
return std::min(tLogReplicationFactor + worstSatellite - 2 - tLogWriteAntiQuorum, storageTeamSize - 1);
|
||||
if (usableRegions > 1 && fullyReplicatedRegions > 1 && worstSatellite > 0 &&
|
||||
(!forAvailability || regionsWithNonNegativePriority > 1)) {
|
||||
return 1 + std::min(std::max(tLogReplicationFactor - 1 - tLogWriteAntiQuorum, worstSatellite - 1),
|
||||
storageTeamSize - 1);
|
||||
} else if (worstSatellite > 0) {
|
||||
// Primary and Satellite tLogs are synchronously replicated, hence we can lose all but 1.
|
||||
return std::min(tLogReplicationFactor + worstSatellite - 1 - tLogWriteAntiQuorum, storageTeamSize - 1);
|
||||
}
|
||||
return std::min(tLogReplicationFactor - 1 - tLogWriteAntiQuorum, storageTeamSize - 1);
|
||||
}
|
||||
|
|
@ -185,27 +212,47 @@ struct DatabaseConfiguration {
|
|||
// Backup Workers
|
||||
bool backupWorkerEnabled;
|
||||
|
||||
//Data centers
|
||||
int32_t usableRegions;
|
||||
// Data centers
|
||||
int32_t usableRegions; // Number of regions which have a replica of the database.
|
||||
int32_t repopulateRegionAntiQuorum;
|
||||
std::vector<RegionInfo> regions;
|
||||
|
||||
// Excluded servers (no state should be here)
|
||||
bool isExcludedServer( NetworkAddressList ) const;
|
||||
bool isExcludedServer(NetworkAddressList) const;
|
||||
std::set<AddressExclusion> getExcludedServers() const;
|
||||
|
||||
int32_t getDesiredProxies() const { if(masterProxyCount == -1) return autoMasterProxyCount; return masterProxyCount; }
|
||||
int32_t getDesiredResolvers() const { if(resolverCount == -1) return autoResolverCount; return resolverCount; }
|
||||
int32_t getDesiredLogs() const { if(desiredTLogCount == -1) return autoDesiredTLogCount; return desiredTLogCount; }
|
||||
int32_t getDesiredRemoteLogs() const { if(remoteDesiredTLogCount == -1) return getDesiredLogs(); return remoteDesiredTLogCount; }
|
||||
int32_t getDesiredSatelliteLogs( Optional<Key> dcId ) const {
|
||||
auto desired = getRegion(dcId).satelliteDesiredTLogCount;
|
||||
if(desired == -1) return autoDesiredTLogCount; return desired;
|
||||
int32_t getDesiredProxies() const {
|
||||
if (masterProxyCount == -1) return autoMasterProxyCount;
|
||||
return masterProxyCount;
|
||||
}
|
||||
int32_t getRemoteTLogReplicationFactor() const { if(remoteTLogReplicationFactor == 0) return tLogReplicationFactor; return remoteTLogReplicationFactor; }
|
||||
Reference<IReplicationPolicy> getRemoteTLogPolicy() const { if(remoteTLogReplicationFactor == 0) return tLogPolicy; return remoteTLogPolicy; }
|
||||
int32_t getDesiredResolvers() const {
|
||||
if (resolverCount == -1) return autoResolverCount;
|
||||
return resolverCount;
|
||||
}
|
||||
int32_t getDesiredLogs() const {
|
||||
if (desiredTLogCount == -1) return autoDesiredTLogCount;
|
||||
return desiredTLogCount;
|
||||
}
|
||||
int32_t getDesiredRemoteLogs() const {
|
||||
if (remoteDesiredTLogCount == -1) return getDesiredLogs();
|
||||
return remoteDesiredTLogCount;
|
||||
}
|
||||
int32_t getDesiredSatelliteLogs(Optional<Key> dcId) const {
|
||||
|
||||
bool operator == ( DatabaseConfiguration const& rhs ) const {
|
||||
auto desired = getRegion(dcId).satelliteDesiredTLogCount;
|
||||
if (desired == -1) return autoDesiredTLogCount;
|
||||
return desired;
|
||||
}
|
||||
int32_t getRemoteTLogReplicationFactor() const {
|
||||
if (remoteTLogReplicationFactor == 0) return tLogReplicationFactor;
|
||||
return remoteTLogReplicationFactor;
|
||||
}
|
||||
Reference<IReplicationPolicy> getRemoteTLogPolicy() const {
|
||||
if (remoteTLogReplicationFactor == 0) return tLogPolicy;
|
||||
return remoteTLogPolicy;
|
||||
}
|
||||
|
||||
bool operator==(DatabaseConfiguration const& rhs) const {
|
||||
const_cast<DatabaseConfiguration*>(this)->makeConfigurationImmutable();
|
||||
const_cast<DatabaseConfiguration*>(&rhs)->makeConfigurationImmutable();
|
||||
return rawConfiguration == rhs.rawConfiguration;
|
||||
|
|
@ -216,8 +263,7 @@ struct DatabaseConfiguration {
|
|||
if (!ar.isDeserializing) makeConfigurationImmutable();
|
||||
serializer(ar, rawConfiguration);
|
||||
if (ar.isDeserializing) {
|
||||
for(auto c=rawConfiguration.begin(); c!=rawConfiguration.end(); ++c)
|
||||
setInternal(c->key, c->value);
|
||||
for (auto c = rawConfiguration.begin(); c != rawConfiguration.end(); ++c) setInternal(c->key, c->value);
|
||||
setDefaultReplicationPolicy();
|
||||
}
|
||||
}
|
||||
|
|
@ -225,13 +271,13 @@ struct DatabaseConfiguration {
|
|||
void fromKeyValues(Standalone<VectorRef<KeyValueRef>> rawConfig);
|
||||
|
||||
private:
|
||||
Optional< std::map<std::string, std::string> > mutableConfiguration; // If present, rawConfiguration is not valid
|
||||
Standalone<VectorRef<KeyValueRef>> rawConfiguration; // sorted by key
|
||||
Optional<std::map<std::string, std::string>> mutableConfiguration; // If present, rawConfiguration is not valid
|
||||
Standalone<VectorRef<KeyValueRef>> rawConfiguration; // sorted by key
|
||||
|
||||
void makeConfigurationMutable();
|
||||
void makeConfigurationImmutable();
|
||||
|
||||
bool setInternal( KeyRef key, ValueRef value );
|
||||
bool setInternal(KeyRef key, ValueRef value);
|
||||
void resetInternal();
|
||||
void setDefaultReplicationPolicy();
|
||||
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ public:
|
|||
Counter transactionsCommitCompleted;
|
||||
Counter transactionKeyServerLocationRequests;
|
||||
Counter transactionKeyServerLocationRequestsCompleted;
|
||||
Counter transactionStatusRequests;
|
||||
Counter transactionsTooOld;
|
||||
Counter transactionsFutureVersions;
|
||||
Counter transactionsNotCommitted;
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ typedef StringRef ValueRef;
|
|||
typedef int64_t Generation;
|
||||
|
||||
enum {
|
||||
tagLocalitySpecial = -1,
|
||||
tagLocalitySpecial = -1, // tag with this locality means it is invalidTag (id=0), txsTag (id=1), or cacheTag (id=2)
|
||||
tagLocalityLogRouter = -2,
|
||||
tagLocalityRemoteLog = -3,
|
||||
tagLocalityRemoteLog = -3, // tag created by log router for remote tLogs
|
||||
tagLocalityUpgraded = -4,
|
||||
tagLocalitySatellite = -5,
|
||||
tagLocalityLogRouterMapped = -6, // used by log router to pop from TLogs
|
||||
tagLocalityLogRouterMapped = -6, // The pseudo tag used by log routers to pop the real LogRouter tag (i.e., -2)
|
||||
tagLocalityTxs = -7,
|
||||
tagLocalityBackup = -8, // used by backup role to pop from TLogs
|
||||
tagLocalityInvalid = -99
|
||||
}; //The TLog and LogRouter require these number to be as compact as possible
|
||||
}; // The TLog and LogRouter require these number to be as compact as possible
|
||||
|
||||
inline bool isPseudoLocality(int8_t locality) {
|
||||
return locality == tagLocalityLogRouterMapped || locality == tagLocalityBackup;
|
||||
|
|
@ -54,6 +54,11 @@ inline bool isPseudoLocality(int8_t locality) {
|
|||
|
||||
#pragma pack(push, 1)
|
||||
struct Tag {
|
||||
// if locality > 0,
|
||||
// locality decides which DC id the tLog is in;
|
||||
// id decides which SS owns the tag; id <-> SS mapping is in the system keyspace: serverTagKeys.
|
||||
// if locality < 0, locality decides the type of tLog set: satellite, LR, or remote tLog, etc.
|
||||
// id decides which tLog in the tLog type will be used.
|
||||
int8_t locality;
|
||||
uint16_t id;
|
||||
|
||||
|
|
@ -180,6 +185,10 @@ std::string describe( Reference<T> const& item ) {
|
|||
return item->toString();
|
||||
}
|
||||
|
||||
static std::string describe(UID const& item) {
|
||||
return item.shortString();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string describe( T const& item ) {
|
||||
return item.toString();
|
||||
|
|
|
|||
|
|
@ -99,9 +99,6 @@ StringRef FileBackupAgent::restoreStateText(ERestoreState id) {
|
|||
}
|
||||
}
|
||||
|
||||
template<> Tuple Codec<ERestoreState>::pack(ERestoreState const &val) { return Tuple().append(val); }
|
||||
template<> ERestoreState Codec<ERestoreState>::unpack(Tuple const &val) { return (ERestoreState)val.getInt(0); }
|
||||
|
||||
ACTOR Future<std::vector<KeyBackedTag>> TagUidMap::getAll_impl(TagUidMap *tagsMap, Reference<ReadYourWritesTransaction> tr, bool snapshot) {
|
||||
state Key prefix = tagsMap->prefix; // Copying it here as tagsMap lifetime is not tied to this actor
|
||||
TagMap::PairsType tagPairs = wait(tagsMap->getRange(tr, std::string(), {}, 1e6, snapshot));
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ void ClientKnobs::initialize(bool randomize) {
|
|||
init( TASKBUCKET_MAX_TASK_KEYS, 1000 ); if( randomize && BUGGIFY ) TASKBUCKET_MAX_TASK_KEYS = 20;
|
||||
|
||||
//Backup
|
||||
init( BACKUP_LOCAL_FILE_WRITE_BLOCK, 1024*1024 ); if( randomize && BUGGIFY ) BACKUP_LOCAL_FILE_WRITE_BLOCK = 100;
|
||||
init( BACKUP_CONCURRENT_DELETES, 100 );
|
||||
init( BACKUP_SIMULATED_LIMIT_BYTES, 1e6 ); if( randomize && BUGGIFY ) BACKUP_SIMULATED_LIMIT_BYTES = 1000;
|
||||
init( BACKUP_GET_RANGE_LIMIT_BYTES, 1e6 );
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ public:
|
|||
int TASKBUCKET_MAX_TASK_KEYS;
|
||||
|
||||
// Backup
|
||||
int BACKUP_LOCAL_FILE_WRITE_BLOCK;
|
||||
int BACKUP_CONCURRENT_DELETES;
|
||||
int BACKUP_SIMULATED_LIMIT_BYTES;
|
||||
int BACKUP_GET_RANGE_LIMIT_BYTES;
|
||||
|
|
|
|||
|
|
@ -989,11 +989,12 @@ ACTOR Future<CoordinatorsResult::Type> changeQuorum( Database cx, Reference<IQuo
|
|||
|
||||
if(g_network->isSimulated()) {
|
||||
for(int i = 0; i < (desiredCoordinators.size()/2)+1; i++) {
|
||||
auto addresses = g_simulator.getProcessByAddress(desiredCoordinators[i])->addresses;
|
||||
auto process = g_simulator.getProcessByAddress(desiredCoordinators[i]);
|
||||
ASSERT(process->isReliable() || process->rebooting);
|
||||
|
||||
g_simulator.protectedAddresses.insert(addresses.address);
|
||||
if(addresses.secondaryAddress.present()) {
|
||||
g_simulator.protectedAddresses.insert(addresses.secondaryAddress.get());
|
||||
g_simulator.protectedAddresses.insert(process->addresses.address);
|
||||
if (process->addresses.secondaryAddress.present()) {
|
||||
g_simulator.protectedAddresses.insert(process->addresses.secondaryAddress.get());
|
||||
}
|
||||
TraceEvent("ProtectCoordinator").detail("Address", desiredCoordinators[i]).backtrace();
|
||||
}
|
||||
|
|
@ -1208,8 +1209,7 @@ struct AutoQuorumChange : IQuorumChange {
|
|||
continue;
|
||||
}
|
||||
// Exclude faulty node due to machine assassination
|
||||
if (g_network->isSimulated() && g_simulator.protectedAddresses.count(worker->address) &&
|
||||
!g_simulator.getProcessByAddress(worker->address)->isReliable()) {
|
||||
if (g_network->isSimulated() && !g_simulator.getProcessByAddress(worker->address)->isReliable()) {
|
||||
TraceEvent("AutoSelectCoordinators").detail("SkipUnreliableWorker", worker->address.toString());
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1607,6 +1607,7 @@ ACTOR Future<std::set<NetworkAddress>> checkForExcludingServers(Database cx, vec
|
|||
|
||||
wait( delayJittered( 1.0 ) ); // SOMEDAY: watches!
|
||||
} catch (Error& e) {
|
||||
TraceEvent("CheckForExcludingServersError").error(e);
|
||||
wait( tr.onError(e) );
|
||||
}
|
||||
}
|
||||
|
|
@ -2019,6 +2020,12 @@ TEST_CASE("/ManagementAPI/AutoQuorumChange/checkLocality") {
|
|||
data.locality.set(LiteralStringRef("machineid"), StringRef(machineId));
|
||||
data.address.ip = IPAddress(i);
|
||||
|
||||
if (g_network->isSimulated()) {
|
||||
g_simulator.newProcess("TestCoordinator", data.address.ip, data.address.port, false, 1, data.locality,
|
||||
ProcessClass(ProcessClass::CoordinatorClass, ProcessClass::CommandLineSource), "",
|
||||
"");
|
||||
}
|
||||
|
||||
workers.push_back(data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "fdbclient/MultiVersionTransaction.h"
|
||||
#include "fdbclient/MultiVersionAssignmentVars.h"
|
||||
#include "fdbclient/ThreadSafeTransaction.h"
|
||||
|
|
@ -275,7 +277,8 @@ void loadClientFunction(T *fp, void *lib, std::string libPath, const char *funct
|
|||
}
|
||||
}
|
||||
|
||||
DLApi::DLApi(std::string fdbCPath) : api(new FdbCApi()), fdbCPath(fdbCPath), networkSetup(false) {}
|
||||
DLApi::DLApi(std::string fdbCPath, bool unlinkOnLoad)
|
||||
: api(new FdbCApi()), fdbCPath(fdbCPath), unlinkOnLoad(unlinkOnLoad), networkSetup(false) {}
|
||||
|
||||
void DLApi::init() {
|
||||
if(isLibraryLoaded(fdbCPath.c_str())) {
|
||||
|
|
@ -287,6 +290,13 @@ void DLApi::init() {
|
|||
TraceEvent(SevError, "ErrorLoadingExternalClientLibrary").detail("LibraryPath", fdbCPath);
|
||||
throw platform_error();
|
||||
}
|
||||
if (unlinkOnLoad) {
|
||||
int err = unlink(fdbCPath.c_str());
|
||||
if (err) {
|
||||
TraceEvent(SevError, "ErrorUnlinkingTempClientLibraryFile").GetLastError().detail("LibraryPath", fdbCPath);
|
||||
throw platform_error();
|
||||
}
|
||||
}
|
||||
|
||||
loadClientFunction(&api->selectApiVersion, lib, fdbCPath, "fdb_select_api_version_impl");
|
||||
loadClientFunction(&api->getClientVersion, lib, fdbCPath, "fdb_get_client_version", headerVersion >= 410);
|
||||
|
|
@ -685,7 +695,9 @@ void MultiVersionTransaction::reset() {
|
|||
}
|
||||
|
||||
// MultiVersionDatabase
|
||||
MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi *api, std::string clusterFilePath, Reference<IDatabase> db, bool openConnectors) : dbState(new DatabaseState()) {
|
||||
MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api, int threadIdx, std::string clusterFilePath,
|
||||
Reference<IDatabase> db, bool openConnectors)
|
||||
: dbState(new DatabaseState()), threadIdx(threadIdx) {
|
||||
dbState->db = db;
|
||||
dbState->dbVar->set(db);
|
||||
|
||||
|
|
@ -701,7 +713,7 @@ MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi *api, std::string clu
|
|||
dbState->currentClientIndex = -1;
|
||||
}
|
||||
|
||||
api->runOnExternalClients([this, clusterFilePath](Reference<ClientInfo> client) {
|
||||
api->runOnExternalClients(threadIdx, [this, clusterFilePath](Reference<ClientInfo> client) {
|
||||
dbState->addConnection(client, clusterFilePath);
|
||||
});
|
||||
|
||||
|
|
@ -714,7 +726,8 @@ MultiVersionDatabase::~MultiVersionDatabase() {
|
|||
}
|
||||
|
||||
Reference<IDatabase> MultiVersionDatabase::debugCreateFromExistingDatabase(Reference<IDatabase> db) {
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(MultiVersionApi::api, "", db, false));
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(
|
||||
MultiVersionApi::api, 0, "", db, false));
|
||||
}
|
||||
|
||||
Reference<ITransaction> MultiVersionDatabase::createTransaction() {
|
||||
|
|
@ -841,7 +854,7 @@ void MultiVersionDatabase::DatabaseState::stateChanged() {
|
|||
}
|
||||
|
||||
if(newIndex == -1) {
|
||||
ASSERT(currentClientIndex == 0); // This can only happen for the local client, which we set as the current connection before we know it's connected
|
||||
ASSERT_EQ(currentClientIndex, 0); // This can only happen for the local client, which we set as the current connection before we know it's connected
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -904,30 +917,39 @@ void MultiVersionDatabase::DatabaseState::cancelConnections() {
|
|||
// MultiVersionApi
|
||||
|
||||
bool MultiVersionApi::apiVersionAtLeast(int minVersion) {
|
||||
ASSERT(MultiVersionApi::api->apiVersion != 0);
|
||||
ASSERT_NE(MultiVersionApi::api->apiVersion, 0);
|
||||
return MultiVersionApi::api->apiVersion >= minVersion || MultiVersionApi::api->apiVersion < 0;
|
||||
}
|
||||
|
||||
void MultiVersionApi::runOnExternalClientsAllThreads(std::function<void(Reference<ClientInfo>)> func,
|
||||
bool runOnFailedClients) {
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
runOnExternalClients(i, func, runOnFailedClients);
|
||||
}
|
||||
}
|
||||
|
||||
// runOnFailedClients should be used cautiously. Some failed clients may not have successfully loaded all symbols.
|
||||
void MultiVersionApi::runOnExternalClients(std::function<void(Reference<ClientInfo>)> func, bool runOnFailedClients) {
|
||||
void MultiVersionApi::runOnExternalClients(int threadIdx, std::function<void(Reference<ClientInfo>)> func,
|
||||
bool runOnFailedClients) {
|
||||
bool newFailure = false;
|
||||
|
||||
auto c = externalClients.begin();
|
||||
while(c != externalClients.end()) {
|
||||
auto client = c->second[threadIdx];
|
||||
try {
|
||||
if(!c->second->failed || runOnFailedClients) { // TODO: Should we ignore some failures?
|
||||
func(c->second);
|
||||
if (!client->failed || runOnFailedClients) { // TODO: Should we ignore some failures?
|
||||
func(client);
|
||||
}
|
||||
}
|
||||
catch(Error &e) {
|
||||
if(e.code() == error_code_external_client_already_loaded) {
|
||||
TraceEvent(SevInfo, "ExternalClientAlreadyLoaded").error(e).detail("LibPath", c->second->libPath);
|
||||
TraceEvent(SevInfo, "ExternalClientAlreadyLoaded").error(e).detail("LibPath", c->first);
|
||||
c = externalClients.erase(c);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
TraceEvent(SevWarnAlways, "ExternalClientFailure").error(e).detail("LibPath", c->second->libPath);
|
||||
c->second->failed = true;
|
||||
TraceEvent(SevWarnAlways, "ExternalClientFailure").error(e).detail("LibPath", c->first);
|
||||
client->failed = true;
|
||||
newFailure = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -989,23 +1011,25 @@ void MultiVersionApi::setCallbacksOnExternalThreads() {
|
|||
|
||||
callbackOnMainThread = false;
|
||||
}
|
||||
|
||||
void MultiVersionApi::addExternalLibrary(std::string path) {
|
||||
std::string filename = basename(path);
|
||||
|
||||
if(filename.empty() || !fileExists(path)) {
|
||||
if (filename.empty() || !fileExists(path)) {
|
||||
TraceEvent("ExternalClientNotFound").detail("LibraryPath", filename);
|
||||
throw file_not_found();
|
||||
}
|
||||
|
||||
MutexHolder holder(lock);
|
||||
if(networkStartSetup) {
|
||||
if (networkStartSetup) {
|
||||
throw invalid_option(); // SOMEDAY: it might be good to allow clients to be added after the network is setup
|
||||
}
|
||||
|
||||
if(externalClients.count(filename) == 0) {
|
||||
// external libraries always run on their own thread; ensure we allocate at least one thread to run this library.
|
||||
threadCount = std::max(threadCount, 1);
|
||||
|
||||
if (externalClientDescriptions.count(filename) == 0) {
|
||||
TraceEvent("AddingExternalClient").detail("LibraryPath", filename);
|
||||
externalClients[filename] = Reference<ClientInfo>(new ClientInfo(new DLApi(path), path));
|
||||
externalClientDescriptions.emplace(std::make_pair(filename, ClientDesc(path, true)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1014,25 +1038,92 @@ void MultiVersionApi::addExternalLibraryDirectory(std::string path) {
|
|||
std::vector<std::string> files = platform::listFiles(path, DYNAMIC_LIB_EXT);
|
||||
|
||||
MutexHolder holder(lock);
|
||||
if(networkStartSetup) {
|
||||
throw invalid_option(); // SOMEDAY: it might be good to allow clients to be added after the network is setup. For directories, we can monitor them for the addition of new files.
|
||||
if (networkStartSetup) {
|
||||
throw invalid_option(); // SOMEDAY: it might be good to allow clients to be added after the network is setup
|
||||
}
|
||||
|
||||
// external libraries always run on their own thread; ensure we allocate at least one thread to run this library.
|
||||
threadCount = std::max(threadCount, 1);
|
||||
|
||||
for(auto filename : files) {
|
||||
std::string lib = abspath(joinPath(path, filename));
|
||||
if(externalClients.count(filename) == 0) {
|
||||
if (externalClientDescriptions.count(filename) == 0) {
|
||||
TraceEvent("AddingExternalClient").detail("LibraryPath", filename);
|
||||
externalClients[filename] = Reference<ClientInfo>(new ClientInfo(new DLApi(lib), lib));
|
||||
}
|
||||
externalClientDescriptions.emplace(std::make_pair(filename, ClientDesc(lib, true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(__unixish__)
|
||||
std::vector<std::pair<std::string, bool>> MultiVersionApi::copyExternalLibraryPerThread(std::string path) {
|
||||
ASSERT_GE(threadCount, 1);
|
||||
// Copy library for each thread configured per version
|
||||
std::vector<std::pair<std::string, bool>> paths;
|
||||
// It's tempting to use the so once without copying. However, we don't know
|
||||
// if the thing we're about to copy is the shared object executing this code
|
||||
// or not, so this optimization is unsafe.
|
||||
// paths.push_back({path, false});
|
||||
for (int ii = 0; ii < threadCount; ++ii) {
|
||||
std::string filename = basename(path);
|
||||
|
||||
char tempName[PATH_MAX + 12];
|
||||
sprintf(tempName, "/tmp/%s-XXXXXX", filename.c_str());
|
||||
int tempFd = mkstemp(tempName);
|
||||
int fd;
|
||||
|
||||
if ((fd = open(path.c_str(), O_RDONLY)) == -1) {
|
||||
TraceEvent("ExternalClientNotFound").detail("LibraryPath", path);
|
||||
throw file_not_found();
|
||||
}
|
||||
|
||||
constexpr size_t buf_sz = 4096;
|
||||
char buf[buf_sz];
|
||||
while (1) {
|
||||
ssize_t readCount = read(fd, buf, buf_sz);
|
||||
if (readCount == 0) {
|
||||
// eof
|
||||
break;
|
||||
}
|
||||
if (readCount == -1) {
|
||||
TraceEvent(SevError, "ExternalClientCopyFailedReadError").GetLastError().detail("LibraryPath", path);
|
||||
throw platform_error();
|
||||
}
|
||||
ssize_t written = 0;
|
||||
while (written != readCount) {
|
||||
ssize_t writeCount = write(tempFd, buf + written, readCount - written);
|
||||
if (writeCount == -1) {
|
||||
TraceEvent(SevError, "ExternalClientCopyFailedWriteError").GetLastError().detail("LibraryPath", path);
|
||||
throw platform_error();
|
||||
}
|
||||
written += writeCount;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
close(tempFd);
|
||||
|
||||
paths.push_back({tempName, true}); // use + delete temporary copies of the library.
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
#else
|
||||
std::vector<std::pair< std::string, bool> > MultiVersionApi::copyExternalLibraryPerThread(std::string path) {
|
||||
if (threadCount > 1) {
|
||||
TraceEvent(SevError, "MultipleClientThreadsUnsupportedOnWindows");
|
||||
throw unsupported_operation();
|
||||
}
|
||||
std::vector<std::pair<std::string, bool>> paths;
|
||||
paths.push_back({ path , false });
|
||||
return paths;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MultiVersionApi::disableLocalClient() {
|
||||
MutexHolder holder(lock);
|
||||
if(networkStartSetup || bypassMultiClientApi) {
|
||||
throw invalid_option();
|
||||
}
|
||||
|
||||
threadCount = std::max(threadCount, 1);
|
||||
localClientDisabled = true;
|
||||
}
|
||||
|
||||
|
|
@ -1040,13 +1131,14 @@ void MultiVersionApi::setSupportedClientVersions(Standalone<StringRef> versions)
|
|||
MutexHolder holder(lock);
|
||||
ASSERT(networkSetup);
|
||||
|
||||
// This option must be set on the main thread because it modifes structures that can be used concurrently by the main thread
|
||||
// This option must be set on the main thread because it modifies structures that can be used concurrently by the
|
||||
// main thread
|
||||
onMainThreadVoid([this, versions](){
|
||||
localClient->api->setNetworkOption(FDBNetworkOptions::SUPPORTED_CLIENT_VERSIONS, versions);
|
||||
}, NULL);
|
||||
|
||||
if(!bypassMultiClientApi) {
|
||||
runOnExternalClients([versions](Reference<ClientInfo> client) {
|
||||
runOnExternalClientsAllThreads([versions](Reference<ClientInfo> client) {
|
||||
client->api->setNetworkOption(FDBNetworkOptions::SUPPORTED_CLIENT_VERSIONS, versions);
|
||||
});
|
||||
}
|
||||
|
|
@ -1099,14 +1191,26 @@ void MultiVersionApi::setNetworkOptionInternal(FDBNetworkOptions::Option option,
|
|||
ASSERT(!value.present() && !networkStartSetup);
|
||||
externalClient = true;
|
||||
bypassMultiClientApi = true;
|
||||
}
|
||||
else {
|
||||
} else if (option == FDBNetworkOptions::CLIENT_THREADS_PER_VERSION) {
|
||||
MutexHolder holder(lock);
|
||||
validateOption(value, true, false, false);
|
||||
ASSERT(!networkStartSetup);
|
||||
#if defined(__unixish__)
|
||||
threadCount = extractIntOption(value, 1, 1024);
|
||||
#else
|
||||
// multiple client threads are not supported on windows.
|
||||
threadCount = extractIntOption(value, 1, 1);
|
||||
#endif
|
||||
if (threadCount > 1) {
|
||||
disableLocalClient();
|
||||
}
|
||||
} else {
|
||||
MutexHolder holder(lock);
|
||||
localClient->api->setNetworkOption(option, value);
|
||||
|
||||
if(!bypassMultiClientApi) {
|
||||
if(networkSetup) {
|
||||
runOnExternalClients(
|
||||
runOnExternalClientsAllThreads(
|
||||
[option, value](Reference<ClientInfo> client) { client->api->setNetworkOption(option, value); });
|
||||
}
|
||||
else {
|
||||
|
|
@ -1128,6 +1232,24 @@ void MultiVersionApi::setupNetwork() {
|
|||
throw network_already_setup();
|
||||
}
|
||||
|
||||
for (auto i : externalClientDescriptions) {
|
||||
std::string path = i.second.libPath;
|
||||
std::string filename = basename(path);
|
||||
|
||||
// Copy external lib for each thread
|
||||
if (externalClients.count(filename) == 0) {
|
||||
externalClients[filename] = {};
|
||||
for (const auto& tmp : copyExternalLibraryPerThread(path)) {
|
||||
TraceEvent("AddingExternalClient")
|
||||
.detail("FileName", filename)
|
||||
.detail("LibraryPath", path)
|
||||
.detail("TempPath", tmp.first);
|
||||
externalClients[filename].push_back(
|
||||
Reference<ClientInfo>(new ClientInfo(new DLApi(tmp.first, tmp.second /*unlink on load*/), path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
networkStartSetup = true;
|
||||
|
||||
if(externalClients.empty()) {
|
||||
|
|
@ -1145,14 +1267,14 @@ void MultiVersionApi::setupNetwork() {
|
|||
localClient->loadProtocolVersion();
|
||||
|
||||
if(!bypassMultiClientApi) {
|
||||
runOnExternalClients([this](Reference<ClientInfo> client) {
|
||||
runOnExternalClientsAllThreads([this](Reference<ClientInfo> client) {
|
||||
TraceEvent("InitializingExternalClient").detail("LibraryPath", client->libPath);
|
||||
client->api->selectApiVersion(apiVersion);
|
||||
client->loadProtocolVersion();
|
||||
});
|
||||
|
||||
MutexHolder holder(lock);
|
||||
runOnExternalClients([this, transportId](Reference<ClientInfo> client) {
|
||||
runOnExternalClientsAllThreads([this, transportId](Reference<ClientInfo> client) {
|
||||
for(auto option : options) {
|
||||
client->api->setNetworkOption(option.first, option.second.castTo<StringRef>());
|
||||
}
|
||||
|
|
@ -1193,7 +1315,7 @@ void MultiVersionApi::runNetwork() {
|
|||
|
||||
std::vector<THREAD_HANDLE> handles;
|
||||
if(!bypassMultiClientApi) {
|
||||
runOnExternalClients([&handles](Reference<ClientInfo> client) {
|
||||
runOnExternalClientsAllThreads([&handles](Reference<ClientInfo> client) {
|
||||
if(client->external) {
|
||||
handles.push_back(g_network->startThread(&runNetworkThread, client.getPtr()));
|
||||
}
|
||||
|
|
@ -1218,9 +1340,7 @@ void MultiVersionApi::stopNetwork() {
|
|||
localClient->api->stopNetwork();
|
||||
|
||||
if(!bypassMultiClientApi) {
|
||||
runOnExternalClients([](Reference<ClientInfo> client) {
|
||||
client->api->stopNetwork();
|
||||
}, true);
|
||||
runOnExternalClientsAllThreads([](Reference<ClientInfo> client) { client->api->stopNetwork(); }, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1235,7 +1355,7 @@ void MultiVersionApi::addNetworkThreadCompletionHook(void (*hook)(void*), void *
|
|||
localClient->api->addNetworkThreadCompletionHook(hook, hookParameter);
|
||||
|
||||
if(!bypassMultiClientApi) {
|
||||
runOnExternalClients([hook, hookParameter](Reference<ClientInfo> client) {
|
||||
runOnExternalClientsAllThreads([hook, hookParameter](Reference<ClientInfo> client) {
|
||||
client->api->addNetworkThreadCompletionHook(hook, hookParameter);
|
||||
});
|
||||
}
|
||||
|
|
@ -1247,22 +1367,36 @@ Reference<IDatabase> MultiVersionApi::createDatabase(const char *clusterFilePath
|
|||
lock.leave();
|
||||
throw network_not_setup();
|
||||
}
|
||||
lock.leave();
|
||||
|
||||
std::string clusterFile(clusterFilePath);
|
||||
if(localClientDisabled) {
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(this, clusterFile, Reference<IDatabase>()));
|
||||
|
||||
if (threadCount > 1 || localClientDisabled) {
|
||||
ASSERT(localClientDisabled);
|
||||
ASSERT(!bypassMultiClientApi);
|
||||
|
||||
int threadIdx = nextThread;
|
||||
nextThread = (nextThread + 1) % threadCount;
|
||||
lock.leave();
|
||||
for (auto it : externalClients) {
|
||||
TraceEvent("CreatingDatabaseOnExternalClient")
|
||||
.detail("LibraryPath", it.first)
|
||||
.detail("Failed", it.second[threadIdx]->failed);
|
||||
}
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(this, threadIdx, clusterFile, Reference<IDatabase>()));
|
||||
}
|
||||
|
||||
lock.leave();
|
||||
|
||||
auto db = localClient->api->createDatabase(clusterFilePath);
|
||||
if(bypassMultiClientApi) {
|
||||
return db;
|
||||
}
|
||||
else {
|
||||
for(auto it : externalClients) {
|
||||
TraceEvent("CreatingDatabaseOnExternalClient").detail("LibraryPath", it.second->libPath).detail("Failed", it.second->failed);
|
||||
TraceEvent("CreatingDatabaseOnExternalClient")
|
||||
.detail("LibraryPath", it.first)
|
||||
.detail("Failed", it.second[0]->failed);
|
||||
}
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(this, clusterFile, db));
|
||||
return Reference<IDatabase>(new MultiVersionDatabase(this, 0, clusterFile, db));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1270,7 +1404,9 @@ void MultiVersionApi::updateSupportedVersions() {
|
|||
if(networkSetup) {
|
||||
Standalone<VectorRef<uint8_t>> versionStr;
|
||||
|
||||
runOnExternalClients([&versionStr](Reference<ClientInfo> client){
|
||||
// not mutating the client, so just call on one instance of each client version.
|
||||
// thread 0 always exists.
|
||||
runOnExternalClients(0, [&versionStr](Reference<ClientInfo> client) {
|
||||
const char *ver = client->api->getClientVersion();
|
||||
versionStr.append(versionStr.arena(), (uint8_t*)ver, (int)strlen(ver));
|
||||
versionStr.append(versionStr.arena(), (uint8_t*)";", 1);
|
||||
|
|
@ -1365,7 +1501,9 @@ void MultiVersionApi::loadEnvironmentVariableNetworkOptions() {
|
|||
envOptionsLoaded = true;
|
||||
}
|
||||
|
||||
MultiVersionApi::MultiVersionApi() : bypassMultiClientApi(false), networkStartSetup(false), networkSetup(false), callbackOnMainThread(true), externalClient(false), localClientDisabled(false), apiVersion(0), envOptionsLoaded(false) {}
|
||||
MultiVersionApi::MultiVersionApi()
|
||||
: bypassMultiClientApi(false), networkStartSetup(false), networkSetup(false), callbackOnMainThread(true),
|
||||
externalClient(false), localClientDisabled(false), apiVersion(0), envOptionsLoaded(false), threadCount(0) {}
|
||||
|
||||
MultiVersionApi* MultiVersionApi::api = new MultiVersionApi();
|
||||
|
||||
|
|
@ -1382,7 +1520,7 @@ void ClientInfo::loadProtocolVersion() {
|
|||
protocolVersion = ProtocolVersion(strtoull(protocolVersionStr.c_str(), &next, 16));
|
||||
|
||||
ASSERT(protocolVersion.version() != 0 && protocolVersion.version() != ULLONG_MAX);
|
||||
ASSERT(next == &protocolVersionStr[protocolVersionStr.length()]);
|
||||
ASSERT_EQ(next, &protocolVersionStr[protocolVersionStr.length()]);
|
||||
}
|
||||
|
||||
bool ClientInfo::canReplace(Reference<ClientInfo> other) const {
|
||||
|
|
@ -1430,7 +1568,7 @@ TEST_CASE("/fdbclient/multiversionclient/EnvironmentVariableParsing" ) {
|
|||
ASSERT(false);
|
||||
}
|
||||
catch(Error &e) {
|
||||
ASSERT(e.code() == error_code_invalid_option_value);
|
||||
ASSERT_EQ(e.code(), error_code_invalid_option_value);
|
||||
}
|
||||
|
||||
return Void();
|
||||
|
|
@ -1589,7 +1727,7 @@ ACTOR Future<Void> checkUndestroyedFutures(std::vector<ThreadSingleAssignmentVar
|
|||
for(fNum = 0; fNum < undestroyed.size(); ++fNum) {
|
||||
f = undestroyed[fNum];
|
||||
|
||||
ASSERT(f->debugGetReferenceCount() == 1);
|
||||
ASSERT_EQ(f->debugGetReferenceCount(), 1);
|
||||
ASSERT(f->isReady());
|
||||
|
||||
f->cancel();
|
||||
|
|
@ -1673,7 +1811,7 @@ struct AbortableTest {
|
|||
auto newFuture = FutureInfo(abortableFuture(f.future, ThreadFuture<Void>(abort)), f.expectedValue, f.legalErrors);
|
||||
|
||||
if(!abort->isReady() && deterministicRandom()->coinflip()) {
|
||||
ASSERT(abort->status == ThreadSingleAssignmentVarBase::Unset);
|
||||
ASSERT_EQ(abort->status, ThreadSingleAssignmentVarBase::Unset);
|
||||
newFuture.threads.push_back(g_network->startThread(setAbort, abort));
|
||||
}
|
||||
|
||||
|
|
@ -1717,7 +1855,7 @@ private:
|
|||
struct DLTest {
|
||||
static FutureInfo createThreadFuture(FutureInfo f) {
|
||||
return FutureInfo(toThreadFuture<int>(getApi(), (FdbCApi::FDBFuture*)f.future.extractPtr(), [](FdbCApi::FDBFuture *f, FdbCApi *api) {
|
||||
ASSERT(((ThreadSingleAssignmentVar<int>*)f)->debugGetReferenceCount() >= 1);
|
||||
ASSERT_GE(((ThreadSingleAssignmentVar<int>*)f)->debugGetReferenceCount(), 1);
|
||||
return ((ThreadSingleAssignmentVar<int>*)f)->get();
|
||||
}), f.expectedValue, f.legalErrors);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ private:
|
|||
|
||||
class DLApi : public IClientApi {
|
||||
public:
|
||||
DLApi(std::string fdbCPath);
|
||||
DLApi(std::string fdbCPath, bool unlinkOnLoad = false);
|
||||
|
||||
void selectApiVersion(int apiVersion) override;
|
||||
const char* getClientVersion() override;
|
||||
|
|
@ -207,6 +207,7 @@ public:
|
|||
private:
|
||||
const std::string fdbCPath;
|
||||
const Reference<FdbCApi> api;
|
||||
const bool unlinkOnLoad;
|
||||
int headerVersion;
|
||||
bool networkSetup;
|
||||
|
||||
|
|
@ -278,17 +279,23 @@ private:
|
|||
std::vector<std::pair<FDBTransactionOptions::Option, Optional<Standalone<StringRef>>>> persistentOptions;
|
||||
};
|
||||
|
||||
struct ClientInfo : ThreadSafeReferenceCounted<ClientInfo> {
|
||||
struct ClientDesc {
|
||||
std::string const libPath;
|
||||
bool const external;
|
||||
|
||||
ClientDesc(std::string libPath, bool external) : libPath(libPath), external(external) {}
|
||||
};
|
||||
|
||||
struct ClientInfo : ClientDesc, ThreadSafeReferenceCounted<ClientInfo> {
|
||||
ProtocolVersion protocolVersion;
|
||||
IClientApi *api;
|
||||
std::string libPath;
|
||||
bool external;
|
||||
bool failed;
|
||||
std::vector<std::pair<void (*)(void*), void*>> threadCompletionHooks;
|
||||
|
||||
ClientInfo() : protocolVersion(0), api(NULL), external(false), failed(true) {}
|
||||
ClientInfo(IClientApi *api) : protocolVersion(0), api(api), libPath("internal"), external(false), failed(false) {}
|
||||
ClientInfo(IClientApi *api, std::string libPath) : protocolVersion(0), api(api), libPath(libPath), external(true), failed(false) {}
|
||||
ClientInfo() : ClientDesc(std::string(), false), protocolVersion(0), api(NULL), failed(true) {}
|
||||
ClientInfo(IClientApi* api) : ClientDesc("internal", false), protocolVersion(0), api(api), failed(false) {}
|
||||
ClientInfo(IClientApi* api, std::string libPath)
|
||||
: ClientDesc(libPath, true), protocolVersion(0), api(api), failed(false) {}
|
||||
|
||||
void loadProtocolVersion();
|
||||
bool canReplace(Reference<ClientInfo> other) const;
|
||||
|
|
@ -298,7 +305,8 @@ class MultiVersionApi;
|
|||
|
||||
class MultiVersionDatabase : public IDatabase, ThreadSafeReferenceCounted<MultiVersionDatabase> {
|
||||
public:
|
||||
MultiVersionDatabase(MultiVersionApi *api, std::string clusterFilePath, Reference<IDatabase> db, bool openConnectors=true);
|
||||
MultiVersionDatabase(MultiVersionApi* api, int threadIdx, std::string clusterFilePath, Reference<IDatabase> db,
|
||||
bool openConnectors = true);
|
||||
~MultiVersionDatabase();
|
||||
|
||||
Reference<ITransaction> createTransaction() override;
|
||||
|
|
@ -361,6 +369,7 @@ private:
|
|||
};
|
||||
|
||||
const Reference<DatabaseState> dbState;
|
||||
const int threadIdx;
|
||||
friend class MultiVersionTransaction;
|
||||
};
|
||||
|
||||
|
|
@ -379,7 +388,9 @@ public:
|
|||
static MultiVersionApi* api;
|
||||
|
||||
Reference<ClientInfo> getLocalClient();
|
||||
void runOnExternalClients(std::function<void(Reference<ClientInfo>)>, bool runOnFailedClients=false);
|
||||
void runOnExternalClients(int threadId, std::function<void(Reference<ClientInfo>)>,
|
||||
bool runOnFailedClients = false);
|
||||
void runOnExternalClientsAllThreads(std::function<void(Reference<ClientInfo>)>, bool runOnFailedClients = false);
|
||||
|
||||
void updateSupportedVersions();
|
||||
|
||||
|
|
@ -397,13 +408,17 @@ private:
|
|||
void setCallbacksOnExternalThreads();
|
||||
void addExternalLibrary(std::string path);
|
||||
void addExternalLibraryDirectory(std::string path);
|
||||
// Return a vector of (pathname, unlink_on_close) pairs. Makes threadCount - 1 copies of the library stored in path,
|
||||
// and returns a vector of length threadCount.
|
||||
std::vector<std::pair<std::string, bool>> copyExternalLibraryPerThread(std::string path);
|
||||
void disableLocalClient();
|
||||
void setSupportedClientVersions(Standalone<StringRef> versions);
|
||||
|
||||
void setNetworkOptionInternal(FDBNetworkOptions::Option option, Optional<StringRef> value);
|
||||
|
||||
Reference<ClientInfo> localClient;
|
||||
std::map<std::string, Reference<ClientInfo>> externalClients;
|
||||
std::map<std::string, ClientDesc> externalClientDescriptions;
|
||||
std::map<std::string, std::vector<Reference<ClientInfo>>> externalClients;
|
||||
|
||||
bool networkStartSetup;
|
||||
volatile bool networkSetup;
|
||||
|
|
@ -411,6 +426,9 @@ private:
|
|||
volatile bool externalClient;
|
||||
int apiVersion;
|
||||
|
||||
int nextThread = 0;
|
||||
int threadCount;
|
||||
|
||||
Mutex lock;
|
||||
std::vector<std::pair<FDBNetworkOptions::Option, Optional<Standalone<StringRef>>>> options;
|
||||
std::map<FDBNetworkOptions::Option, std::set<Standalone<StringRef>>> setEnvOptions;
|
||||
|
|
|
|||
|
|
@ -651,8 +651,7 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<ClusterConnectionF
|
|||
: connectionFile(connectionFile), clientInfo(clientInfo), clientInfoMonitor(clientInfoMonitor), taskID(taskID),
|
||||
clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), lockAware(lockAware),
|
||||
apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"),
|
||||
transactionReadVersions("ReadVersions", cc),
|
||||
transactionReadVersionsThrottled("ReadVersionsThrottled", cc),
|
||||
transactionReadVersions("ReadVersions", cc), transactionReadVersionsThrottled("ReadVersionsThrottled", cc),
|
||||
transactionReadVersionsCompleted("ReadVersionsCompleted", cc),
|
||||
transactionReadVersionBatches("ReadVersionBatches", cc),
|
||||
transactionBatchReadVersions("BatchPriorityReadVersions", cc),
|
||||
|
|
@ -673,12 +672,13 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<ClusterConnectionF
|
|||
transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc),
|
||||
transactionKeyServerLocationRequests("KeyServerLocationRequests", cc),
|
||||
transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc),
|
||||
transactionsTooOld("TooOld", cc), transactionsFutureVersions("FutureVersions", cc),
|
||||
transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc),
|
||||
transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc),
|
||||
transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000),
|
||||
commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0),
|
||||
healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal),
|
||||
transactionStatusRequests("StatusRequests", cc), transactionsTooOld("TooOld", cc),
|
||||
transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc),
|
||||
transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc),
|
||||
transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0),
|
||||
latencies(1000), readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000),
|
||||
bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0),
|
||||
internal(internal),
|
||||
specialKeySpace(std::make_unique<SpecialKeySpace>(specialKeys.begin, specialKeys.end, /* test */ false)) {
|
||||
dbId = deterministicRandom()->randomUniqueID();
|
||||
connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange();
|
||||
|
|
@ -716,6 +716,7 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<ClusterConnectionF
|
|||
[](ReadYourWritesTransaction* ryw) -> Future<Optional<Value>> {
|
||||
if (ryw->getDatabase().getPtr() &&
|
||||
ryw->getDatabase()->getConnectionFile()) {
|
||||
++ryw->getDatabase()->transactionStatusRequests;
|
||||
return getJSON(ryw->getDatabase());
|
||||
} else {
|
||||
return Optional<Value>();
|
||||
|
|
@ -761,22 +762,35 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<ClusterConnectionF
|
|||
}
|
||||
}
|
||||
|
||||
DatabaseContext::DatabaseContext( const Error &err ) : deferredError(err), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), transactionReadVersionsThrottled("ReadVersionsThrottled", cc),
|
||||
transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc),
|
||||
transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc),
|
||||
transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc),
|
||||
transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc),
|
||||
transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc),
|
||||
transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc),
|
||||
transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc),
|
||||
transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc), transactionClearMutations("ClearMutations", cc),
|
||||
transactionAtomicMutations("AtomicMutations", cc), transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc),
|
||||
transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), transactionsTooOld("TooOld", cc),
|
||||
transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc),
|
||||
transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), latencies(1000), readLatencies(1000), commitLatencies(1000),
|
||||
GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000),
|
||||
internal(false) {}
|
||||
|
||||
DatabaseContext::DatabaseContext(const Error& err)
|
||||
: deferredError(err), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc),
|
||||
transactionReadVersionsThrottled("ReadVersionsThrottled", cc),
|
||||
transactionReadVersionsCompleted("ReadVersionsCompleted", cc),
|
||||
transactionReadVersionBatches("ReadVersionBatches", cc),
|
||||
transactionBatchReadVersions("BatchPriorityReadVersions", cc),
|
||||
transactionDefaultReadVersions("DefaultPriorityReadVersions", cc),
|
||||
transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc),
|
||||
transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc),
|
||||
transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc),
|
||||
transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc),
|
||||
transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc),
|
||||
transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc),
|
||||
transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc),
|
||||
transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc),
|
||||
transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), transactionBytesRead("BytesRead", cc),
|
||||
transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc),
|
||||
transactionCommittedMutations("CommittedMutations", cc),
|
||||
transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc),
|
||||
transactionClearMutations("ClearMutations", cc), transactionAtomicMutations("AtomicMutations", cc),
|
||||
transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc),
|
||||
transactionKeyServerLocationRequests("KeyServerLocationRequests", cc),
|
||||
transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc),
|
||||
transactionStatusRequests("StatusRequests", cc), transactionsTooOld("TooOld", cc),
|
||||
transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc),
|
||||
transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc),
|
||||
transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), latencies(1000),
|
||||
readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000),
|
||||
internal(false) {}
|
||||
|
||||
Database DatabaseContext::create(Reference<AsyncVar<ClientDBInfo>> clientInfo, Future<Void> clientInfoMonitor, LocalityData clientLocality, bool enableLocalityLoadBalance, TaskPriority taskID, bool lockAware, int apiVersion, bool switchable) {
|
||||
return Database( new DatabaseContext( Reference<AsyncVar<Reference<ClusterConnectionFile>>>(), clientInfo, clientInfoMonitor, taskID, clientLocality, enableLocalityLoadBalance, lockAware, true, apiVersion, switchable ) );
|
||||
|
|
@ -1515,6 +1529,12 @@ ACTOR Future< vector< pair<KeyRange,Reference<LocationInfo>> > > getKeyRangeLoca
|
|||
}
|
||||
}
|
||||
|
||||
// Get the SS locations for each shard in the 'keys' key-range;
|
||||
// Returned vector size is the number of shards in the input keys key-range.
|
||||
// Returned vector element is <ShardRange, storage server location info> pairs, where
|
||||
// ShardRange is the whole shard key-range, not a part of the given key range.
|
||||
// Example: If query the function with key range (b, d), the returned list of pairs could be something like:
|
||||
// [([a, b1), locationInfo), ([b1, c), locationInfo), ([c, d1), locationInfo)].
|
||||
template <class F>
|
||||
Future< vector< pair<KeyRange,Reference<LocationInfo>> > > getKeyRangeLocations( Database const& cx, KeyRange const& keys, int limit, bool reverse, F StorageServerInterface::*member, TransactionInfo const& info ) {
|
||||
ASSERT (!keys.empty());
|
||||
|
|
@ -2191,7 +2211,6 @@ ACTOR Future<Standalone<RangeResultRef>> getRange( Database cx, Reference<Transa
|
|||
}
|
||||
|
||||
++cx->transactionPhysicalReads;
|
||||
++cx->transactionGetRangeRequests;
|
||||
state GetKeyValuesReply rep;
|
||||
try {
|
||||
if (CLIENT_BUGGIFY) {
|
||||
|
|
|
|||
|
|
@ -1234,6 +1234,7 @@ Future< Optional<Value> > ReadYourWritesTransaction::get( const Key& key, bool s
|
|||
} else {
|
||||
if (key == LiteralStringRef("\xff\xff/status/json")) {
|
||||
if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionFile()) {
|
||||
++tr.getDatabase()->transactionStatusRequests;
|
||||
return getJSON(tr.getDatabase());
|
||||
} else {
|
||||
return Optional<Value>();
|
||||
|
|
|
|||
|
|
@ -116,6 +116,11 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"low_priority_queries":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"bytes_queried":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
|
|
@ -521,6 +526,11 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"low_priority_reads":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"location_requests":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
|
|
@ -577,6 +587,11 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"rejected_for_queued_too_long":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
"roughness":0.0
|
||||
},
|
||||
"committed":{
|
||||
"hz":0.0,
|
||||
"counter":0,
|
||||
|
|
|
|||
|
|
@ -115,27 +115,46 @@ ACTOR Future<Void> normalizeKeySelectorActor(SpecialKeySpace* sks, ReadYourWrite
|
|||
KeyRangeRef boundary, int* actualOffset,
|
||||
Standalone<RangeResultRef>* result,
|
||||
Optional<Standalone<RangeResultRef>>* cache) {
|
||||
// If offset < 1, where we need to move left, iter points to the range containing at least one smaller key
|
||||
// (It's a wasting of time to walk through the range whose begin key is same as ks->key)
|
||||
// (rangeContainingKeyBefore itself handles the case where ks->key == Key())
|
||||
// Otherwise, we only need to move right if offset > 1, iter points to the range containing the key
|
||||
// Since boundary.end is always a key in the RangeMap, it is always safe to move right
|
||||
state RangeMap<Key, SpecialKeyRangeBaseImpl*, KeyRangeRef>::Iterator iter =
|
||||
ks->offset < 1 ? sks->getImpls().rangeContainingKeyBefore(ks->getKey())
|
||||
: sks->getImpls().rangeContaining(ks->getKey());
|
||||
while ((ks->offset < 1 && iter->begin() > boundary.begin) || (ks->offset > 1 && iter->begin() < boundary.end)) {
|
||||
while ((ks->offset < 1 && iter->begin() >= boundary.begin) || (ks->offset > 1 && iter->begin() < boundary.end)) {
|
||||
if (iter->value() != nullptr) {
|
||||
wait(moveKeySelectorOverRangeActor(iter->value(), ryw, ks, cache));
|
||||
}
|
||||
ks->offset < 1 ? --iter : ++iter;
|
||||
// Check if we can still move the iterator left
|
||||
if (ks->offset < 1) {
|
||||
if (iter == sks->getImpls().ranges().begin()) {
|
||||
break;
|
||||
} else {
|
||||
--iter;
|
||||
}
|
||||
} else if (ks->offset > 1) {
|
||||
// Always safe to move right
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
*actualOffset = ks->offset;
|
||||
if (iter->begin() == boundary.begin || iter->begin() == boundary.end) ks->setKey(iter->begin());
|
||||
|
||||
if (!ks->isFirstGreaterOrEqual()) {
|
||||
// The Key Selector clamps up to the legal key space
|
||||
TraceEvent(SevDebug, "ReadToBoundary")
|
||||
.detail("TerminateKey", ks->getKey())
|
||||
.detail("TerminateOffset", ks->offset);
|
||||
if (ks->offset < 1)
|
||||
// If still not normalized after moving to the boundary,
|
||||
// let key selector clamp up to the boundary
|
||||
if (ks->offset < 1) {
|
||||
result->readToBegin = true;
|
||||
else
|
||||
ks->setKey(boundary.begin);
|
||||
}
|
||||
else {
|
||||
result->readThroughEnd = true;
|
||||
ks->setKey(boundary.end);
|
||||
}
|
||||
ks->offset = 1;
|
||||
}
|
||||
return Void();
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ struct StorageInfo : NonCopyable, public ReferenceCounted<StorageInfo> {
|
|||
};
|
||||
|
||||
struct ServerCacheInfo {
|
||||
std::vector<Tag> tags;
|
||||
std::vector<Tag> tags; // all tags in both primary and remote DC for the key-range
|
||||
std::vector<Reference<StorageInfo>> src_info;
|
||||
std::vector<Reference<StorageInfo>> dest_info;
|
||||
|
||||
|
|
@ -426,7 +426,12 @@ struct SplitMetricsRequest {
|
|||
// Should always be used inside a `Standalone`.
|
||||
struct ReadHotRangeWithMetrics {
|
||||
KeyRangeRef keys;
|
||||
// density refers to the ratio of bytes sent(because of the read) and bytes on disk.
|
||||
// For example if key range [A, B) and [B, C) respectively has byte size 100 bytes on disk.
|
||||
// Key range [A,B) was read 30 times.
|
||||
// The density for key range [A,C) is 30 * 100 / 200 = 15
|
||||
double density;
|
||||
// How many bytes of data was sent in a period of time because of read requests.
|
||||
double readBandwidth;
|
||||
|
||||
ReadHotRangeWithMetrics() = default;
|
||||
|
|
|
|||
|
|
@ -572,6 +572,8 @@ ProcessClass decodeProcessClassValue( ValueRef const& value ) {
|
|||
const KeyRangeRef configKeys( LiteralStringRef("\xff/conf/"), LiteralStringRef("\xff/conf0") );
|
||||
const KeyRef configKeysPrefix = configKeys.begin;
|
||||
|
||||
const KeyRef triggerDDTeamInfoPrintKey(LiteralStringRef("\xff/triggerDDTeamInfoPrint"));
|
||||
|
||||
const KeyRangeRef excludedServersKeys( LiteralStringRef("\xff/conf/excluded/"), LiteralStringRef("\xff/conf/excluded0") );
|
||||
const KeyRef excludedServersPrefix = excludedServersKeys.begin;
|
||||
const KeyRef excludedServersVersionKey = LiteralStringRef("\xff/conf/excluded");
|
||||
|
|
|
|||
|
|
@ -167,6 +167,9 @@ UID decodeProcessClassKeyOld( KeyRef const& key );
|
|||
extern const KeyRangeRef configKeys;
|
||||
extern const KeyRef configKeysPrefix;
|
||||
|
||||
// Change the value of this key to anything and that will trigger detailed data distribution team info log.
|
||||
extern const KeyRef triggerDDTeamInfoPrintKey;
|
||||
|
||||
// "\xff/conf/excluded/1.2.3.4" := ""
|
||||
// "\xff/conf/excluded/1.2.3.4:4000" := ""
|
||||
// These are inside configKeysPrefix since they represent a form of configuration and they are convenient
|
||||
|
|
|
|||
|
|
@ -108,7 +108,10 @@ description is not currently required but encouraged.
|
|||
paramType="String" paramDescription="path to directory containing client libraries"
|
||||
description="Searches the specified path for dynamic libraries and adds them to the list of client libraries for use by the multi-version client API. Must be set before setting up the network." />
|
||||
<Option name="disable_local_client" code="64"
|
||||
description="Prevents connections through the local client, allowing only connections through externally loaded client libraries. Intended primarily for testing." />
|
||||
description="Prevents connections through the local client, allowing only connections through externally loaded client libraries." />
|
||||
<Option name="client_threads_per_version" code="65"
|
||||
paramType="Int" paramDescription="Number of client threads to be spawned. Each cluster will be serviced by a single client thread."
|
||||
description="Spawns multiple worker threads for each version of the client that is loaded. Setting this to a number greater than one implies disable_local_client." />
|
||||
<Option name="disable_client_statistics_logging" code="70"
|
||||
description="Disables logging of client statistics, such as sampled transaction activity." />
|
||||
<Option name="enable_slow_task_profiling" code="71"
|
||||
|
|
|
|||
|
|
@ -231,6 +231,10 @@ public:
|
|||
return filename;
|
||||
}
|
||||
|
||||
void setRateControl(Reference<IRateControl> const& rc) override { rateControl = rc; }
|
||||
|
||||
Reference<IRateControl> const& getRateControl() override { return rateControl; }
|
||||
|
||||
virtual void addref() {
|
||||
ReferenceCounted<AsyncFileCached>::addref();
|
||||
//TraceEvent("AsyncFileCachedAddRef").detail("Filename", filename).detail("Refcount", debugGetReferenceCount()).backtrace();
|
||||
|
|
@ -240,9 +244,15 @@ public:
|
|||
// If this is ever ThreadSafeReferenceCounted...
|
||||
// setrefCountUnsafe(0);
|
||||
|
||||
if(rateControl) {
|
||||
TraceEvent(SevDebug, "AsyncFileCachedKillWaiters")
|
||||
.detail("Filename", filename);
|
||||
rateControl->killWaiters(io_error());
|
||||
}
|
||||
|
||||
auto f = quiesce();
|
||||
//TraceEvent("AsyncFileCachedDel").detail("Filename", filename)
|
||||
// .detail("Refcount", debugGetReferenceCount()).detail("CanDie", f.isReady()).backtrace();
|
||||
TraceEvent("AsyncFileCachedDel").detail("Filename", filename)
|
||||
.detail("Refcount", debugGetReferenceCount()).detail("CanDie", f.isReady()).backtrace();
|
||||
if (f.isReady())
|
||||
delete this;
|
||||
else
|
||||
|
|
@ -263,6 +273,7 @@ private:
|
|||
Reference<EvictablePageCache> pageCache;
|
||||
Future<Void> currentTruncate;
|
||||
int64_t currentTruncateSize;
|
||||
Reference<IRateControl> rateControl;
|
||||
|
||||
// Map of pointers which hold page buffers for pages which have been overwritten
|
||||
// but at the time of write there were still readZeroCopy holders.
|
||||
|
|
@ -288,8 +299,10 @@ private:
|
|||
Int64MetricHandle countCachePageReadsMerged;
|
||||
Int64MetricHandle countCacheReadBytes;
|
||||
|
||||
AsyncFileCached( Reference<IAsyncFile> uncached, const std::string& filename, int64_t length, Reference<EvictablePageCache> pageCache )
|
||||
: uncached(uncached), filename(filename), length(length), prevLength(length), pageCache(pageCache), currentTruncate(Void()), currentTruncateSize(0) {
|
||||
AsyncFileCached(Reference<IAsyncFile> uncached, const std::string& filename, int64_t length,
|
||||
Reference<EvictablePageCache> pageCache)
|
||||
: uncached(uncached), filename(filename), length(length), prevLength(length), pageCache(pageCache),
|
||||
currentTruncate(Void()), currentTruncateSize(0), rateControl(nullptr) {
|
||||
if( !g_network->isSimulated() ) {
|
||||
countFileCacheWrites.init(LiteralStringRef("AsyncFile.CountFileCacheWrites"), filename);
|
||||
countFileCacheReads.init(LiteralStringRef("AsyncFile.CountFileCacheReads"), filename);
|
||||
|
|
@ -507,6 +520,18 @@ struct AFCPage : public EvictablePage, public FastAllocated<AFCPage> {
|
|||
wait( self->notReading && self->notFlushing );
|
||||
|
||||
if (dirty) {
|
||||
// Wait for rate control if it is set
|
||||
if (self->owner->getRateControl()) {
|
||||
int allowance = 1;
|
||||
// If I/O size is defined, wait for the calculated I/O quota
|
||||
if (FLOW_KNOBS->FLOW_CACHEDFILE_WRITE_IO_SIZE > 0) {
|
||||
allowance = (self->pageCache->pageSize + FLOW_KNOBS->FLOW_CACHEDFILE_WRITE_IO_SIZE - 1) /
|
||||
FLOW_KNOBS->FLOW_CACHEDFILE_WRITE_IO_SIZE; // round up
|
||||
ASSERT(allowance > 0);
|
||||
}
|
||||
wait(self->owner->getRateControl()->getAllowance(allowance));
|
||||
}
|
||||
|
||||
if ( self->pageOffset + self->pageCache->pageSize > self->owner->length ) {
|
||||
ASSERT(self->pageOffset < self->owner->length);
|
||||
memset( static_cast<uint8_t *>(self->data) + self->owner->length - self->pageOffset, 0, self->pageCache->pageSize - (self->owner->length - self->pageOffset) );
|
||||
|
|
|
|||
|
|
@ -260,18 +260,36 @@ ACTOR Future<Void> pingLatencyLogger(TransportData* self) {
|
|||
if(!peer) {
|
||||
TraceEvent(SevWarnAlways, "MissingNetworkAddress").suppressFor(10.0).detail("PeerAddr", lastAddress);
|
||||
}
|
||||
if (peer->lastLoggedTime <= 0.0) {
|
||||
peer->lastLoggedTime = peer->lastConnectTime;
|
||||
}
|
||||
|
||||
if(peer && peer->pingLatencies.getPopulationSize() >= 10) {
|
||||
TraceEvent("PingLatency")
|
||||
.detail("PeerAddr", lastAddress)
|
||||
.detail("MinLatency", peer->pingLatencies.min())
|
||||
.detail("MaxLatency", peer->pingLatencies.max())
|
||||
.detail("MeanLatency", peer->pingLatencies.mean())
|
||||
.detail("MedianLatency", peer->pingLatencies.median())
|
||||
.detail("P90Latency", peer->pingLatencies.percentile(0.90))
|
||||
.detail("Count", peer->pingLatencies.getPopulationSize())
|
||||
.detail("BytesReceived", peer->bytesReceived - peer->lastLoggedBytesReceived)
|
||||
.detail("BytesSent", peer->bytesSent - peer->lastLoggedBytesSent);
|
||||
.detail("Elapsed", now() - peer->lastLoggedTime)
|
||||
.detail("PeerAddr", lastAddress)
|
||||
.detail("MinLatency", peer->pingLatencies.min())
|
||||
.detail("MaxLatency", peer->pingLatencies.max())
|
||||
.detail("MeanLatency", peer->pingLatencies.mean())
|
||||
.detail("MedianLatency", peer->pingLatencies.median())
|
||||
.detail("P90Latency", peer->pingLatencies.percentile(0.90))
|
||||
.detail("Count", peer->pingLatencies.getPopulationSize())
|
||||
.detail("BytesReceived", peer->bytesReceived - peer->lastLoggedBytesReceived)
|
||||
.detail("BytesSent", peer->bytesSent - peer->lastLoggedBytesSent)
|
||||
.detail("ConnectOutgoingCount", peer->connectOutgoingCount)
|
||||
.detail("ConnectIncomingCount", peer->connectIncomingCount)
|
||||
.detail("ConnectFailedCount", peer->connectFailedCount)
|
||||
.detail("ConnectMinLatency", peer->connectLatencies.min())
|
||||
.detail("ConnectMaxLatency", peer->connectLatencies.max())
|
||||
.detail("ConnectMeanLatency", peer->connectLatencies.mean())
|
||||
.detail("ConnectMedianLatency", peer->connectLatencies.median())
|
||||
.detail("ConnectP90Latency", peer->connectLatencies.percentile(0.90));
|
||||
peer->lastLoggedTime = now();
|
||||
peer->connectOutgoingCount = 0;
|
||||
peer->connectIncomingCount = 0;
|
||||
peer->connectFailedCount = 0;
|
||||
peer->pingLatencies.clear();
|
||||
peer->connectLatencies.clear();
|
||||
peer->lastLoggedBytesReceived = peer->bytesReceived;
|
||||
peer->lastLoggedBytesSent = peer->bytesSent;
|
||||
wait(delay(FLOW_KNOBS->PING_LOGGING_INTERVAL));
|
||||
|
|
@ -567,6 +585,7 @@ ACTOR Future<Void> connectionKeeper( Reference<Peer> self,
|
|||
.detail("FailureStatus", IFailureMonitor::failureMonitor().getState(self->destination).isAvailable()
|
||||
? "OK"
|
||||
: "FAILED");
|
||||
++self->connectOutgoingCount;
|
||||
|
||||
try {
|
||||
choose {
|
||||
|
|
@ -574,6 +593,10 @@ ACTOR Future<Void> connectionKeeper( Reference<Peer> self,
|
|||
wait(INetworkConnections::net()->connect(self->destination))) {
|
||||
conn = _conn;
|
||||
wait(conn->connectHandshake());
|
||||
self->connectLatencies.addSample(now() - self->lastConnectTime);
|
||||
if (FlowTransport::isClient()) {
|
||||
IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(false));
|
||||
}
|
||||
if (self->unsent.empty()) {
|
||||
delayedHealthUpdateF = delayedHealthUpdate(self->destination);
|
||||
choose {
|
||||
|
|
@ -597,8 +620,9 @@ ACTOR Future<Void> connectionKeeper( Reference<Peer> self,
|
|||
throw connection_failed();
|
||||
}
|
||||
}
|
||||
} catch (Error& e) {
|
||||
if (e.code() != error_code_connection_failed) {
|
||||
} catch(Error &e) {
|
||||
++self->connectFailedCount;
|
||||
if(e.code() != error_code_connection_failed) {
|
||||
throw;
|
||||
}
|
||||
TraceEvent("ConnectionTimedOut", conn ? conn->getDebugID() : UID())
|
||||
|
|
@ -728,7 +752,8 @@ Peer::Peer(TransportData* transport, NetworkAddress const& destination)
|
|||
reconnectionDelay(FLOW_KNOBS->INITIAL_RECONNECTION_TIME), compatible(true), outstandingReplies(0),
|
||||
incompatibleProtocolVersionNewer(false), peerReferences(-1), bytesReceived(0), lastDataPacketSentTime(now()),
|
||||
pingLatencies(destination.isPublic() ? FLOW_KNOBS->PING_SAMPLE_AMOUNT : 1), lastLoggedBytesReceived(0),
|
||||
bytesSent(0), lastLoggedBytesSent(0) {
|
||||
bytesSent(0), lastLoggedBytesSent(0), lastLoggedTime(0.0), connectOutgoingCount(0), connectIncomingCount(0),
|
||||
connectFailedCount(0), connectLatencies(destination.isPublic() ? FLOW_KNOBS->NETWORK_CONNECT_SAMPLE_AMOUNT : 1) {
|
||||
IFailureMonitor::failureMonitor().setStatus(destination, FailureStatus(false));
|
||||
}
|
||||
|
||||
|
|
@ -779,6 +804,7 @@ void Peer::discardUnreliablePackets() {
|
|||
void Peer::onIncomingConnection( Reference<Peer> self, Reference<IConnection> conn, Future<Void> reader ) {
|
||||
// In case two processes are trying to connect to each other simultaneously, the process with the larger canonical NetworkAddress
|
||||
// gets to keep its outgoing connection.
|
||||
++self->connectIncomingCount;
|
||||
if ( !destination.isPublic() && !outgoingConnectionIdle ) throw address_in_use();
|
||||
NetworkAddress compatibleAddr = transport->localAddresses.address;
|
||||
if(transport->localAddresses.secondaryAddress.present() && transport->localAddresses.secondaryAddress.get().isTLS() == destination.isTLS()) {
|
||||
|
|
|
|||
|
|
@ -145,8 +145,14 @@ struct Peer : public ReferenceCounted<Peer> {
|
|||
double lastDataPacketSentTime;
|
||||
int outstandingReplies;
|
||||
ContinuousSample<double> pingLatencies;
|
||||
double lastLoggedTime;
|
||||
int64_t lastLoggedBytesReceived;
|
||||
int64_t lastLoggedBytesSent;
|
||||
// Cleared every time stats are logged for this peer.
|
||||
int connectOutgoingCount;
|
||||
int connectIncomingCount;
|
||||
int connectFailedCount;
|
||||
ContinuousSample<double> connectLatencies;
|
||||
|
||||
explicit Peer(TransportData* transport, NetworkAddress const& destination);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <ctime>
|
||||
#include "flow/flow.h"
|
||||
#include "fdbrpc/IRateControl.h"
|
||||
|
||||
// All outstanding operations must be cancelled before the destructor of IAsyncFile is called.
|
||||
// The desirability of the above semantic is disputed. Some classes (AsyncFileBlobStore,
|
||||
|
|
@ -81,6 +82,10 @@ public:
|
|||
virtual void releaseZeroCopy( void* data, int length, int64_t offset ) {}
|
||||
|
||||
virtual int64_t debugFD() = 0;
|
||||
|
||||
// Used for rate control, at present, only AsyncFileCached supports it
|
||||
virtual Reference<IRateControl> const& getRateControl() { throw unsupported_operation(); }
|
||||
virtual void setRateControl(Reference<IRateControl> const& rc) { throw unsupported_operation(); }
|
||||
};
|
||||
|
||||
typedef void (*runCycleFuncPtr)();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public:
|
|||
// If all of the allowance is not used the unused units can be given back.
|
||||
// For convenience, n can safely be negative.
|
||||
virtual void returnUnused(int n) = 0;
|
||||
virtual void killWaiters(const Error &e) = 0;
|
||||
virtual void wakeWaiters() = 0;
|
||||
virtual void addref() = 0;
|
||||
virtual void delref() = 0;
|
||||
};
|
||||
|
|
@ -37,18 +39,19 @@ public:
|
|||
// An IRateControl implemenation that allows at most hands out at most windowLimit units of 'credit' in windowSeconds seconds
|
||||
class SpeedLimit : public IRateControl, ReferenceCounted<SpeedLimit> {
|
||||
public:
|
||||
SpeedLimit(int windowLimit, int windowSeconds) : m_limit(windowLimit), m_seconds(windowSeconds), m_last_update(0), m_budget(0) {
|
||||
m_budget_max = m_limit * m_seconds;
|
||||
m_last_update = timer();
|
||||
SpeedLimit(int windowLimit, double windowSeconds) : m_limit(windowLimit), m_seconds(windowSeconds), m_last_update(0), m_budget(0) {
|
||||
m_last_update = now();
|
||||
}
|
||||
virtual ~SpeedLimit() {
|
||||
m_stop.send(Never());
|
||||
}
|
||||
virtual ~SpeedLimit() {}
|
||||
|
||||
virtual void addref() { ReferenceCounted<SpeedLimit>::addref(); }
|
||||
virtual void delref() { ReferenceCounted<SpeedLimit>::delref(); }
|
||||
void addref() override { ReferenceCounted<SpeedLimit>::addref(); }
|
||||
void delref() override { ReferenceCounted<SpeedLimit>::delref(); }
|
||||
|
||||
virtual Future<Void> getAllowance(unsigned int n) {
|
||||
Future<Void> getAllowance(unsigned int n) override {
|
||||
// Replenish budget based on time since last update
|
||||
double ts = timer();
|
||||
double ts = now();
|
||||
// returnUnused happens to do exactly what we want here
|
||||
returnUnused((ts - m_last_update) / m_seconds * m_limit);
|
||||
m_last_update = ts;
|
||||
|
|
@ -57,13 +60,25 @@ public:
|
|||
if(m_budget >= 0)
|
||||
return Void();
|
||||
// Otherise return the amount of time it will take for the budget to rise to 0.
|
||||
return delay(m_seconds * -m_budget / m_limit);
|
||||
return m_stop.getFuture() || delay(m_seconds * -m_budget / m_limit);
|
||||
}
|
||||
|
||||
virtual void returnUnused(int n) {
|
||||
void returnUnused(int n) override {
|
||||
if(n < 0)
|
||||
return;
|
||||
m_budget = std::min<int64_t>(m_budget + n, m_budget_max);
|
||||
m_budget = std::min<int64_t>(m_budget + n, m_limit);
|
||||
}
|
||||
|
||||
void wakeWaiters() override {
|
||||
Promise<Void> p;
|
||||
p.swap(m_stop);
|
||||
p.send(Void());
|
||||
}
|
||||
|
||||
void killWaiters(const Error &e) override {
|
||||
Promise<Void> p;
|
||||
p.swap(m_stop);
|
||||
p.sendError(e);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -71,7 +86,7 @@ private:
|
|||
double m_seconds;
|
||||
double m_last_update;
|
||||
int64_t m_budget;
|
||||
int64_t m_budget_max;
|
||||
Promise<Void> m_stop;
|
||||
};
|
||||
|
||||
// An IRateControl implemenation that enforces no limit
|
||||
|
|
@ -79,9 +94,11 @@ class Unlimited : public IRateControl, ReferenceCounted<Unlimited> {
|
|||
public:
|
||||
Unlimited() {}
|
||||
virtual ~Unlimited() {}
|
||||
virtual void addref() { ReferenceCounted<Unlimited>::addref(); }
|
||||
virtual void delref() { ReferenceCounted<Unlimited>::delref(); }
|
||||
void addref() override { ReferenceCounted<Unlimited>::addref(); }
|
||||
void delref() override { ReferenceCounted<Unlimited>::delref(); }
|
||||
|
||||
virtual Future<Void> getAllowance(unsigned int n) { return Void(); }
|
||||
virtual void returnUnused(int n) {}
|
||||
Future<Void> getAllowance(unsigned int n) override { return Void(); }
|
||||
void returnUnused(int n) override {}
|
||||
void wakeWaiters() override {}
|
||||
void killWaiters(const Error &e) override {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -169,16 +169,15 @@ void addLaggingRequest(Future<Optional<Reply>> reply, Promise<Void> requestFinis
|
|||
|
||||
// Keep trying to get a reply from any of servers until success or cancellation; tries to take into account
|
||||
// failMon's information for load balancing and avoiding failed servers
|
||||
// If ALL the servers are failed and the list of servers is not fresh, throws an exception to let the caller refresh the list of servers
|
||||
// If ALL the servers are failed and the list of servers is not fresh, throws an exception to let the caller refresh the
|
||||
// list of servers. When model is set, load balance among alternatives in the same DC, aiming to balance request queue
|
||||
// length on these interfaces. If too many interfaces in the same DC are bad, try remote interfaces.
|
||||
ACTOR template <class Interface, class Request, class Multi>
|
||||
Future< REPLY_TYPE(Request) > loadBalance(
|
||||
Reference<MultiInterface<Multi>> alternatives,
|
||||
RequestStream<Request> Interface::* channel,
|
||||
Request request = Request(),
|
||||
TaskPriority taskID = TaskPriority::DefaultPromiseEndpoint,
|
||||
bool atMostOnce = false, // if true, throws request_maybe_delivered() instead of retrying automatically
|
||||
QueueModel* model = NULL)
|
||||
{
|
||||
Future<REPLY_TYPE(Request)> loadBalance(
|
||||
Reference<MultiInterface<Multi>> alternatives, RequestStream<Request> Interface::*channel,
|
||||
Request request = Request(), TaskPriority taskID = TaskPriority::DefaultPromiseEndpoint,
|
||||
bool atMostOnce = false, // if true, throws request_maybe_delivered() instead of retrying automatically
|
||||
QueueModel* model = NULL) {
|
||||
state Future<Optional<REPLY_TYPE(Request)>> firstRequest;
|
||||
state Optional<uint64_t> firstRequestEndpoint;
|
||||
state Future<Optional<REPLY_TYPE(Request)>> secondRequest;
|
||||
|
|
@ -206,10 +205,15 @@ Future< REPLY_TYPE(Request) > loadBalance(
|
|||
int badServers = 0;
|
||||
|
||||
for(int i=0; i<alternatives->size(); i++) {
|
||||
// countBest(): the number of alternatives in the same locality (i.e., DC by default) as alternatives[0].
|
||||
// if the if-statement is correct, it won't try to send requests to the remote ones.
|
||||
if(badServers < std::min(i, FLOW_KNOBS->LOAD_BALANCE_MAX_BAD_OPTIONS + 1) && i == alternatives->countBest()) {
|
||||
// If the number of local bad servers is limited,
|
||||
// we won't even try to send requests to remote servers.
|
||||
// An interface is bad if its endpoint fails or if reply from the interface has a high penalty.
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RequestStream<Request> const* thisStream = &alternatives->get( i, channel );
|
||||
if (!IFailureMonitor::failureMonitor().getState( thisStream->getEndpoint() ).failed) {
|
||||
auto& qd = model->getMeasurement(thisStream->getEndpoint().token.first());
|
||||
|
|
@ -217,9 +221,10 @@ Future< REPLY_TYPE(Request) > loadBalance(
|
|||
double thisMetric = qd.smoothOutstanding.smoothTotal();
|
||||
double thisTime = qd.latency;
|
||||
if(FLOW_KNOBS->LOAD_BALANCE_PENALTY_IS_BAD && qd.penalty > 1.001) {
|
||||
// penalty is sent from server.
|
||||
++badServers;
|
||||
}
|
||||
|
||||
|
||||
if(thisMetric < bestMetric) {
|
||||
if(i != bestAlt) {
|
||||
nextAlt = bestAlt;
|
||||
|
|
@ -249,7 +254,7 @@ Future< REPLY_TYPE(Request) > loadBalance(
|
|||
if(now() > qd.failedUntil) {
|
||||
double thisMetric = qd.smoothOutstanding.smoothTotal();
|
||||
double thisTime = qd.latency;
|
||||
|
||||
|
||||
if( thisMetric < nextMetric ) {
|
||||
nextAlt = i;
|
||||
nextMetric = thisMetric;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ std::string describe( KVPair<K,V> const& p ) { return format("%d ", p.k) + descr
|
|||
template <class T>
|
||||
struct ReferencedInterface : public ReferenceCounted<ReferencedInterface<T>> {
|
||||
T interf;
|
||||
int8_t distance;
|
||||
int8_t distance; // one of enum values in struct LBDistance
|
||||
std::string toString() const {
|
||||
return interf.toString();
|
||||
}
|
||||
|
|
@ -222,7 +222,8 @@ public:
|
|||
}
|
||||
private:
|
||||
vector<Reference<ReferencedInterface<T>>> alternatives;
|
||||
int16_t bestCount;
|
||||
int16_t bestCount; // The number of interfaces in the same location as alternatives[0]. The same location means
|
||||
// DC by default and machine if more than one alternatives are on the same machine).
|
||||
};
|
||||
|
||||
template <class Ar, class T> void load(Ar& ar, Reference<MultiInterface<T>>&) { ASSERT(false); } //< required for Future<T>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ void QueueModel::endRequest( uint64_t id, double latency, double penalty, double
|
|||
}
|
||||
|
||||
QueueData& QueueModel::getMeasurement( uint64_t id ) {
|
||||
return data[id];
|
||||
return data[id]; // return smoothed penalty
|
||||
}
|
||||
|
||||
double QueueModel::addRequest( uint64_t id ) {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,9 @@ void CounterCollection::logToTraceEvent(TraceEvent &te) const {
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> traceCounters(std::string traceEventName, UID traceEventID, double interval, CounterCollection* counters, std::string trackLatestName) {
|
||||
ACTOR Future<Void> traceCounters(std::string traceEventName, UID traceEventID, double interval,
|
||||
CounterCollection* counters, std::string trackLatestName,
|
||||
std::function<void(TraceEvent&)> decorator) {
|
||||
wait(delay(0)); // Give an opportunity for all members used in special counters to be initialized
|
||||
|
||||
for (ICounter* c : counters->counters)
|
||||
|
|
@ -100,6 +102,7 @@ ACTOR Future<Void> traceCounters(std::string traceEventName, UID traceEventID, d
|
|||
te.detail("Elapsed", now() - last_interval);
|
||||
|
||||
counters->logToTraceEvent(te);
|
||||
decorator(te);
|
||||
|
||||
if (!trackLatestName.empty()) {
|
||||
te.trackLatest(trackLatestName);
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ struct SpecialCounter : ICounter, FastAllocated<SpecialCounter<F>>, NonCopyable
|
|||
template <class F>
|
||||
static void specialCounter(CounterCollection& collection, std::string const& name, F && f) { new SpecialCounter<F>(collection, name, std::move(f)); }
|
||||
|
||||
Future<Void> traceCounters(std::string const& traceEventName, UID const& traceEventID, double const& interval, CounterCollection* const& counters, std::string const& trackLatestName = std::string());
|
||||
Future<Void> traceCounters(std::string const& traceEventName, UID const& traceEventID, double const& interval,
|
||||
CounterCollection* const& counters, std::string const& trackLatestName = std::string(),
|
||||
std::function<void(TraceEvent&)> const& decorator = [](TraceEvent& te) {});
|
||||
|
||||
class LatencyBands {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -87,8 +87,6 @@ void ISimulator::displayWorkers() const
|
|||
|
||||
const UID TOKEN_ENDPOINT_NOT_FOUND(-1, -1);
|
||||
|
||||
ISimulator* g_pSimulator = 0;
|
||||
thread_local ISimulator::ProcessInfo* ISimulator::currentProcess = 0;
|
||||
int openCount = 0;
|
||||
|
||||
struct SimClogging {
|
||||
|
|
@ -1116,7 +1114,7 @@ public:
|
|||
int nQuorum = ((desiredCoordinators+1)/2)*2-1;
|
||||
|
||||
KillType newKt = kt;
|
||||
if ((kt == KillInstantly) || (kt == InjectFaults) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete))
|
||||
if ((kt == KillInstantly) || (kt == InjectFaults) || (kt == FailDisk) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete))
|
||||
{
|
||||
LocalityGroup primaryProcessesLeft, primaryProcessesDead;
|
||||
LocalityGroup primarySatelliteProcessesLeft, primarySatelliteProcessesDead;
|
||||
|
|
@ -1257,6 +1255,7 @@ public:
|
|||
TEST( true ); // Simulated machine was killed with any kill type
|
||||
TEST( kt == KillInstantly ); // Simulated machine was killed instantly
|
||||
TEST( kt == InjectFaults ); // Simulated machine was killed with faults
|
||||
TEST( kt == FailDisk ); // Simulated machine was killed with a failed disk
|
||||
|
||||
if (kt == KillInstantly) {
|
||||
TraceEvent(SevWarn, "FailMachine")
|
||||
|
|
@ -1283,6 +1282,9 @@ public:
|
|||
machine->fault_injection_r = deterministicRandom()->randomUniqueID().first();
|
||||
machine->fault_injection_p1 = 0.1;
|
||||
machine->fault_injection_p2 = deterministicRandom()->random01();
|
||||
} else if (kt == FailDisk) {
|
||||
TraceEvent(SevWarn, "FailDiskMachine").detail("Name", machine->name).detail("Address", machine->address).detail("ZoneId", machine->locality.zoneId()).detail("Process", machine->toString()).detail("Rebooting", machine->rebooting).detail("Protected", protectedAddresses.count(machine->address)).backtrace();
|
||||
machine->failedDisk = true;
|
||||
} else {
|
||||
ASSERT( false );
|
||||
}
|
||||
|
|
@ -1373,7 +1375,7 @@ public:
|
|||
}
|
||||
|
||||
// Check if machine can be removed, if requested
|
||||
if (!forceKill && ((kt == KillInstantly) || (kt == InjectFaults) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete)))
|
||||
if (!forceKill && ((kt == KillInstantly) || (kt == InjectFaults) || (kt == FailDisk) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete)))
|
||||
{
|
||||
std::vector<ProcessInfo*> processesLeft, processesDead;
|
||||
int protectedWorker = 0, unavailable = 0, excluded = 0, cleared = 0;
|
||||
|
|
@ -1406,7 +1408,7 @@ public:
|
|||
if (!canKillProcesses(processesLeft, processesDead, kt, &kt)) {
|
||||
TraceEvent("ChangedKillMachine").detail("MachineId", machineId).detail("KillType", kt).detail("OrigKillType", ktOrig).detail("ProcessesLeft", processesLeft.size()).detail("ProcessesDead", processesDead.size()).detail("TotalProcesses", machines.size()).detail("ProcessesPerMachine", processesPerMachine).detail("Protected", protectedWorker).detail("Unavailable", unavailable).detail("Excluded", excluded).detail("Cleared", cleared).detail("ProtectedTotal", protectedAddresses.size()).detail("TLogPolicy", tLogPolicy->info()).detail("StoragePolicy", storagePolicy->info());
|
||||
}
|
||||
else if ((kt == KillInstantly) || (kt == InjectFaults)) {
|
||||
else if ((kt == KillInstantly) || (kt == InjectFaults) || (kt == FailDisk)) {
|
||||
TraceEvent("DeadMachine").detail("MachineId", machineId).detail("KillType", kt).detail("ProcessesLeft", processesLeft.size()).detail("ProcessesDead", processesDead.size()).detail("TotalProcesses", machines.size()).detail("ProcessesPerMachine", processesPerMachine).detail("TLogPolicy", tLogPolicy->info()).detail("StoragePolicy", storagePolicy->info());
|
||||
for (auto process : processesLeft) {
|
||||
TraceEvent("DeadMachineSurvivors").detail("MachineId", machineId).detail("KillType", kt).detail("ProcessesLeft", processesLeft.size()).detail("ProcessesDead", processesDead.size()).detail("SurvivingProcess", process->toString());
|
||||
|
|
@ -1446,7 +1448,7 @@ public:
|
|||
|
||||
TraceEvent("KillMachine").detail("MachineId", machineId).detail("Kt", kt).detail("KtOrig", ktOrig).detail("KillableMachines", processesOnMachine).detail("ProcessPerMachine", processesPerMachine).detail("KillChanged", kt!=ktOrig);
|
||||
if ( kt < RebootAndDelete ) {
|
||||
if(kt == InjectFaults && machines[machineId].machineProcess != nullptr)
|
||||
if((kt == InjectFaults || kt == FailDisk) && machines[machineId].machineProcess != nullptr)
|
||||
killProcess_internal( machines[machineId].machineProcess, kt );
|
||||
for (auto& process : machines[machineId].processes) {
|
||||
TraceEvent("KillMachineProcess").detail("KillType", kt).detail("Process", process->toString()).detail("StartingClass", process->startingClass.toString()).detail("Failed", process->failed).detail("Excluded", process->excluded).detail("Cleared", process->cleared).detail("Rebooting", process->rebooting);
|
||||
|
|
@ -1494,7 +1496,7 @@ public:
|
|||
}
|
||||
|
||||
// Check if machine can be removed, if requested
|
||||
if (!forceKill && ((kt == KillInstantly) || (kt == InjectFaults) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete)))
|
||||
if (!forceKill && ((kt == KillInstantly) || (kt == InjectFaults) || (kt == FailDisk) || (kt == RebootAndDelete) || (kt == RebootProcessAndDelete)))
|
||||
{
|
||||
std::vector<ProcessInfo*> processesLeft, processesDead;
|
||||
for (auto processInfo : getAllProcesses()) {
|
||||
|
|
@ -1783,6 +1785,9 @@ ACTOR void doReboot( ISimulator::ProcessInfo *p, ISimulator::KillType kt ) {
|
|||
|
||||
//Simulates delays for performing operations on disk
|
||||
Future<Void> waitUntilDiskReady( Reference<DiskParameters> diskParameters, int64_t size, bool sync ) {
|
||||
if(g_simulator.getCurrentProcess()->failedDisk) {
|
||||
return Never();
|
||||
}
|
||||
if(g_simulator.connectionFailuresDisableDuration > 1e4)
|
||||
return delay(0.0001);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "flow/flow.h"
|
||||
#include "flow/Histogram.h"
|
||||
#include "fdbrpc/FailureMonitor.h"
|
||||
#include "fdbrpc/Locality.h"
|
||||
#include "fdbrpc/IAsyncFile.h"
|
||||
|
|
@ -37,7 +38,7 @@ public:
|
|||
ISimulator() : desiredCoordinators(1), physicalDatacenters(1), processesPerMachine(0), listenersPerProcess(1), isStopped(false), lastConnectionFailure(0), connectionFailuresDisableDuration(0), speedUpSimulation(false), allSwapsDisabled(false), backupAgents(WaitForType), drAgents(WaitForType), extraDB(NULL), allowLogSetKills(true), usableRegions(1) {}
|
||||
|
||||
// Order matters!
|
||||
enum KillType { KillInstantly, InjectFaults, RebootAndDelete, RebootProcessAndDelete, Reboot, RebootProcess, None };
|
||||
enum KillType { KillInstantly, InjectFaults, FailDisk, RebootAndDelete, RebootProcessAndDelete, Reboot, RebootProcess, None };
|
||||
|
||||
enum BackupAgentType { NoBackupAgents, WaitForType, BackupToFile, BackupToDB };
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ public:
|
|||
LocalityData locality;
|
||||
ProcessClass startingClass;
|
||||
TDMetricCollection tdmetrics;
|
||||
HistogramRegistry histograms;
|
||||
std::map<NetworkAddress, Reference<IListener>> listenerMap;
|
||||
bool failed;
|
||||
bool excluded;
|
||||
|
|
@ -66,6 +68,7 @@ public:
|
|||
|
||||
uint64_t fault_injection_r;
|
||||
double fault_injection_p1, fault_injection_p2;
|
||||
bool failedDisk;
|
||||
|
||||
UID uid;
|
||||
|
||||
|
|
@ -74,13 +77,13 @@ public:
|
|||
: name(name), locality(locality), startingClass(startingClass), addresses(addresses),
|
||||
address(addresses.address), dataFolder(dataFolder), network(net), coordinationFolder(coordinationFolder),
|
||||
failed(false), excluded(false), cpuTicks(0), rebooting(false), fault_injection_p1(0), fault_injection_p2(0),
|
||||
fault_injection_r(0), machine(0), cleared(false) {
|
||||
fault_injection_r(0), machine(0), cleared(false), failedDisk(false) {
|
||||
uid = deterministicRandom()->randomUniqueID();
|
||||
}
|
||||
|
||||
Future<KillType> onShutdown() { return shutdownSignal.getFuture(); }
|
||||
|
||||
bool isReliable() const { return !failed && fault_injection_p1 == 0 && fault_injection_p2 == 0; }
|
||||
bool isReliable() const { return !failed && fault_injection_p1 == 0 && fault_injection_p2 == 0 && !failedDisk; }
|
||||
bool isAvailable() const { return !isExcluded() && isReliable(); }
|
||||
bool isExcluded() const { return excluded; }
|
||||
bool isCleared() const { return cleared; }
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ set(FDBSERVER_SRCS
|
|||
TLogInterface.h
|
||||
TLogServer.actor.cpp
|
||||
VersionedBTree.actor.cpp
|
||||
VFSAsync.h
|
||||
VFSAsync.cpp
|
||||
WaitFailure.actor.cpp
|
||||
WaitFailure.h
|
||||
|
|
|
|||
|
|
@ -945,6 +945,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Check if txn system is recruited successfully in each region
|
||||
void checkRegions(const std::vector<RegionInfo>& regions) {
|
||||
if(desiredDcIds.get().present() && desiredDcIds.get().get().size() == 2 && desiredDcIds.get().get()[0].get() == regions[0].dcId && desiredDcIds.get().get()[1].get() == regions[1].dcId) {
|
||||
return;
|
||||
|
|
@ -2090,6 +2091,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Recruit storage cache role. The storage cache project is paused. Its code path is unlikely used.
|
||||
Optional<uint16_t> newStorageCache = req.storageCacheInterf.present() ? req.storageCacheInterf.get().first : Optional<uint16_t>();
|
||||
auto& cacheInfo = self->id_worker[w.locality.processId()].storageCacheInfo;
|
||||
if (req.storageCacheInterf.present()) {
|
||||
|
|
|
|||
|
|
@ -122,8 +122,10 @@ public:
|
|||
vector<Reference<TCMachineInfo>> machines;
|
||||
vector<Standalone<StringRef>> machineIDs;
|
||||
vector<Reference<TCTeamInfo>> serverTeams;
|
||||
UID id;
|
||||
|
||||
explicit TCMachineTeamInfo(vector<Reference<TCMachineInfo>> const& machines) : machines(machines) {
|
||||
explicit TCMachineTeamInfo(vector<Reference<TCMachineInfo>> const& machines)
|
||||
: machines(machines), id(deterministicRandom()->randomUniqueID()) {
|
||||
machineIDs.reserve(machines.size());
|
||||
for (int i = 0; i < machines.size(); i++) {
|
||||
machineIDs.push_back(machines[i]->machineID);
|
||||
|
|
@ -158,13 +160,15 @@ class TCTeamInfo : public ReferenceCounted<TCTeamInfo>, public IDataDistribution
|
|||
bool healthy;
|
||||
bool wrongConfiguration; //True if any of the servers in the team have the wrong configuration
|
||||
int priority;
|
||||
UID id;
|
||||
|
||||
public:
|
||||
Reference<TCMachineTeamInfo> machineTeam;
|
||||
Future<Void> tracker;
|
||||
|
||||
explicit TCTeamInfo(vector<Reference<TCServerInfo>> const& servers)
|
||||
: servers(servers), healthy(true), priority(SERVER_KNOBS->PRIORITY_TEAM_HEALTHY), wrongConfiguration(false) {
|
||||
: servers(servers), healthy(true), priority(SERVER_KNOBS->PRIORITY_TEAM_HEALTHY), wrongConfiguration(false),
|
||||
id(deterministicRandom()->randomUniqueID()) {
|
||||
if (servers.empty()) {
|
||||
TraceEvent(SevInfo, "ConstructTCTeamFromEmptyServers");
|
||||
}
|
||||
|
|
@ -174,6 +178,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
std::string getTeamID() const override { return id.shortString(); }
|
||||
|
||||
vector<StorageServerInterface> getLastKnownServerInterfaces() const override {
|
||||
vector<StorageServerInterface> v;
|
||||
v.reserve(servers.size());
|
||||
|
|
@ -616,6 +622,7 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
int highestUtilizationTeam;
|
||||
|
||||
AsyncTrigger printDetailedTeamsInfo;
|
||||
PromiseStream<GetMetricsRequest> getShardMetrics;
|
||||
|
||||
void resetLocalitySet() {
|
||||
storageServerSet = Reference<LocalitySet>(new LocalityMap<UID>());
|
||||
|
|
@ -647,7 +654,7 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
DatabaseConfiguration configuration, std::vector<Optional<Key>> includedDCs,
|
||||
Optional<std::vector<Optional<Key>>> otherTrackedDCs, Future<Void> readyToStart,
|
||||
Reference<AsyncVar<bool>> zeroHealthyTeams, bool primary,
|
||||
Reference<AsyncVar<bool>> processingUnhealthy)
|
||||
Reference<AsyncVar<bool>> processingUnhealthy, PromiseStream<GetMetricsRequest> getShardMetrics)
|
||||
: cx(cx), distributorId(distributorId), lock(lock), output(output),
|
||||
shardsAffectedByTeamFailure(shardsAffectedByTeamFailure), doBuildTeams(true), lastBuildTeamsFailed(false),
|
||||
teamBuilder(Void()), badTeamRemover(Void()), checkInvalidLocalities(Void()), wrongStoreTypeRemover(Void()), configuration(configuration),
|
||||
|
|
@ -659,8 +666,10 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
initializationDoneActor(logOnCompletion(readyToStart && initialFailureReactionDelay, this)),
|
||||
optimalTeamCount(0), recruitingStream(0), restartRecruiting(SERVER_KNOBS->DEBOUNCE_RECRUITING_DELAY),
|
||||
unhealthyServers(0), includedDCs(includedDCs), otherTrackedDCs(otherTrackedDCs),
|
||||
zeroHealthyTeams(zeroHealthyTeams), zeroOptimalTeams(true), primary(primary), medianAvailableSpace(SERVER_KNOBS->MIN_AVAILABLE_SPACE_RATIO),
|
||||
lastMedianAvailableSpaceUpdate(0), processingUnhealthy(processingUnhealthy), lowestUtilizationTeam(0), highestUtilizationTeam(0) {
|
||||
zeroHealthyTeams(zeroHealthyTeams), zeroOptimalTeams(true), primary(primary),
|
||||
medianAvailableSpace(SERVER_KNOBS->MIN_AVAILABLE_SPACE_RATIO), lastMedianAvailableSpaceUpdate(0),
|
||||
processingUnhealthy(processingUnhealthy), lowestUtilizationTeam(0), highestUtilizationTeam(0),
|
||||
getShardMetrics(getShardMetrics) {
|
||||
if(!primary || configuration.usableRegions == 1) {
|
||||
TraceEvent("DDTrackerStarting", distributorId)
|
||||
.detail( "State", "Inactive" )
|
||||
|
|
@ -1395,7 +1404,8 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
.detail("TeamIndex", i++)
|
||||
.detail("Healthy", team->isHealthy())
|
||||
.detail("TeamSize", team->size())
|
||||
.detail("MemberIDs", team->getServerIDsStr());
|
||||
.detail("MemberIDs", team->getServerIDsStr())
|
||||
.detail("TeamID", team->getTeamID());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2151,7 +2161,7 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
.detail("Primary", primary)
|
||||
.detail("AddedTeams", 0)
|
||||
.detail("TeamsToBuild", 0)
|
||||
.detail("CurrentTeams", teams.size())
|
||||
.detail("CurrentServerTeams", teams.size())
|
||||
.detail("DesiredTeams", desiredServerTeams)
|
||||
.detail("MaxTeams", maxServerTeams)
|
||||
.detail("StorageTeamSize", configuration.storageTeamSize)
|
||||
|
|
@ -2200,11 +2210,11 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
}
|
||||
}
|
||||
uniqueMachines = machines.size();
|
||||
TraceEvent("BuildTeams")
|
||||
.detail("ServerCount", self->server_info.size())
|
||||
.detail("UniqueMachines", uniqueMachines)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("StorageTeamSize", self->configuration.storageTeamSize);
|
||||
TraceEvent("BuildTeams", self->distributorId)
|
||||
.detail("ServerCount", self->server_info.size())
|
||||
.detail("UniqueMachines", uniqueMachines)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("StorageTeamSize", self->configuration.storageTeamSize);
|
||||
|
||||
// If there are too few machines to even build teams or there are too few represented datacenters, build no new teams
|
||||
if( uniqueMachines >= self->configuration.storageTeamSize ) {
|
||||
|
|
@ -2231,11 +2241,11 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
.detail("TeamsToBuild", teamsToBuild)
|
||||
.detail("DesiredTeams", desiredTeams)
|
||||
.detail("MaxTeams", maxTeams)
|
||||
.detail("BadTeams", self->badTeams.size())
|
||||
.detail("BadServerTeams", self->badTeams.size())
|
||||
.detail("UniqueMachines", uniqueMachines)
|
||||
.detail("TeamSize", self->configuration.storageTeamSize)
|
||||
.detail("Servers", serverCount)
|
||||
.detail("CurrentTrackedTeams", self->teams.size())
|
||||
.detail("CurrentTrackedServerTeams", self->teams.size())
|
||||
.detail("HealthyTeamCount", teamCount)
|
||||
.detail("TotalTeamCount", totalTeamCount)
|
||||
.detail("MachineTeamCount", self->machineTeams.size())
|
||||
|
|
@ -2252,9 +2262,9 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
int addedTeams = self->addTeamsBestOf(teamsToBuild, desiredTeams, maxTeams);
|
||||
|
||||
if (addedTeams <= 0 && self->teams.size() == 0) {
|
||||
TraceEvent(SevWarn, "NoTeamAfterBuildTeam")
|
||||
.detail("TeamNum", self->teams.size())
|
||||
.detail("Debug", "Check information below");
|
||||
TraceEvent(SevWarn, "NoTeamAfterBuildTeam", self->distributorId)
|
||||
.detail("ServerTeamNum", self->teams.size())
|
||||
.detail("Debug", "Check information below");
|
||||
// Debug: set true for traceAllInfo() to print out more information
|
||||
self->traceAllInfo();
|
||||
}
|
||||
|
|
@ -2272,7 +2282,7 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
.detail("Primary", self->primary)
|
||||
.detail("AddedTeams", 0)
|
||||
.detail("TeamsToBuild", teamsToBuild)
|
||||
.detail("CurrentTeams", self->teams.size())
|
||||
.detail("CurrentServerTeams", self->teams.size())
|
||||
.detail("DesiredTeams", desiredTeams)
|
||||
.detail("MaxTeams", maxTeams)
|
||||
.detail("StorageTeamSize", self->configuration.storageTeamSize)
|
||||
|
|
@ -2313,9 +2323,9 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
}
|
||||
|
||||
TraceEvent(SevWarn, "NoHealthyTeams", distributorId)
|
||||
.detail("CurrentTeamCount", teams.size())
|
||||
.detail("ServerCount", server_info.size())
|
||||
.detail("NonFailedServerCount", desiredServerSet.size());
|
||||
.detail("CurrentServerTeamCount", teams.size())
|
||||
.detail("ServerCount", server_info.size())
|
||||
.detail("NonFailedServerCount", desiredServerSet.size());
|
||||
}
|
||||
|
||||
bool shouldHandleServer(const StorageServerInterface &newServer) {
|
||||
|
|
@ -2343,7 +2353,7 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
}
|
||||
|
||||
bool removeTeam( Reference<TCTeamInfo> team ) {
|
||||
TraceEvent("RemovedTeam", distributorId).detail("Team", team->getDesc());
|
||||
TraceEvent("RemovedServerTeam", distributorId).detail("Team", team->getDesc());
|
||||
bool found = false;
|
||||
for(int t=0; t<teams.size(); t++) {
|
||||
if( teams[t] == team ) {
|
||||
|
|
@ -2537,9 +2547,10 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
int removedCount = 0;
|
||||
for (int t = 0; t < teams.size(); t++) {
|
||||
if ( std::count( teams[t]->getServerIDs().begin(), teams[t]->getServerIDs().end(), removedServer ) ) {
|
||||
TraceEvent("TeamRemoved")
|
||||
TraceEvent("ServerTeamRemoved")
|
||||
.detail("Primary", primary)
|
||||
.detail("TeamServerIDs", teams[t]->getServerIDsStr());
|
||||
.detail("TeamServerIDs", teams[t]->getServerIDsStr())
|
||||
.detail("TeamID", teams[t]->getTeamID());
|
||||
// removeTeam also needs to remove the team from the machine team info.
|
||||
removeTeam(teams[t]);
|
||||
t--;
|
||||
|
|
@ -2612,8 +2623,8 @@ struct DDTeamCollection : ReferenceCounted<DDTeamCollection> {
|
|||
restartTeamBuilder.trigger();
|
||||
|
||||
TraceEvent("DataDistributionTeamCollectionUpdate", distributorId)
|
||||
.detail("Teams", teams.size())
|
||||
.detail("BadTeams", badTeams.size())
|
||||
.detail("ServerTeams", teams.size())
|
||||
.detail("BadServerTeams", badTeams.size())
|
||||
.detail("Servers", allServers.size())
|
||||
.detail("Machines", machine_info.size())
|
||||
.detail("MachineTeams", machineTeams.size())
|
||||
|
|
@ -2732,178 +2743,189 @@ ACTOR Future<Void> printSnapshotTeamsInfo(Reference<DDTeamCollection> self) {
|
|||
state int traceEventsPrinted = 0;
|
||||
state std::vector<const UID*> serverIDs;
|
||||
state double lastPrintTime = 0;
|
||||
state ReadYourWritesTransaction tr(self->cx);
|
||||
loop {
|
||||
wait(self->printDetailedTeamsInfo.onTrigger());
|
||||
if (now() - lastPrintTime < SERVER_KNOBS->DD_TEAMS_INFO_PRINT_INTERVAL) {
|
||||
continue;
|
||||
}
|
||||
lastPrintTime = now();
|
||||
try {
|
||||
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
||||
state Future<Void> watchFuture = tr.watch(triggerDDTeamInfoPrintKey);
|
||||
wait(tr.commit());
|
||||
wait(self->printDetailedTeamsInfo.onTrigger() || watchFuture);
|
||||
tr.reset();
|
||||
if (now() - lastPrintTime < SERVER_KNOBS->DD_TEAMS_INFO_PRINT_INTERVAL) {
|
||||
continue;
|
||||
}
|
||||
lastPrintTime = now();
|
||||
|
||||
traceEventsPrinted = 0;
|
||||
traceEventsPrinted = 0;
|
||||
|
||||
double snapshotStart = now();
|
||||
double snapshotStart = now();
|
||||
|
||||
configuration = self->configuration;
|
||||
server_info = self->server_info;
|
||||
teams = self->teams;
|
||||
machine_info = self->machine_info;
|
||||
machineTeams = self->machineTeams;
|
||||
// internedLocalityRecordKeyNameStrings = self->machineLocalityMap._keymap->_lookuparray;
|
||||
// machineLocalityMapEntryArraySize = self->machineLocalityMap.size();
|
||||
// machineLocalityMapRecordArray = self->machineLocalityMap.getRecordArray();
|
||||
std::vector<const UID*> _uids = self->machineLocalityMap.getObjects();
|
||||
serverIDs = _uids;
|
||||
configuration = self->configuration;
|
||||
server_info = self->server_info;
|
||||
teams = self->teams;
|
||||
machine_info = self->machine_info;
|
||||
machineTeams = self->machineTeams;
|
||||
// internedLocalityRecordKeyNameStrings = self->machineLocalityMap._keymap->_lookuparray;
|
||||
// machineLocalityMapEntryArraySize = self->machineLocalityMap.size();
|
||||
// machineLocalityMapRecordArray = self->machineLocalityMap.getRecordArray();
|
||||
std::vector<const UID*> _uids = self->machineLocalityMap.getObjects();
|
||||
serverIDs = _uids;
|
||||
|
||||
auto const& keys = self->server_status.getKeys();
|
||||
for (auto const& key : keys) {
|
||||
server_status.emplace(key, self->server_status.get(key));
|
||||
}
|
||||
auto const& keys = self->server_status.getKeys();
|
||||
for (auto const& key : keys) {
|
||||
server_status.emplace(key, self->server_status.get(key));
|
||||
}
|
||||
|
||||
TraceEvent("DDPrintSnapshotTeasmInfo", self->distributorId)
|
||||
.detail("SnapshotSpeed", now() - snapshotStart)
|
||||
.detail("Primary", self->primary);
|
||||
TraceEvent("DDPrintSnapshotTeasmInfo", self->distributorId)
|
||||
.detail("SnapshotSpeed", now() - snapshotStart)
|
||||
.detail("Primary", self->primary);
|
||||
|
||||
// Print to TraceEvents
|
||||
TraceEvent("DDConfig", self->distributorId)
|
||||
.detail("StorageTeamSize", configuration.storageTeamSize)
|
||||
.detail("DesiredTeamsPerServer", SERVER_KNOBS->DESIRED_TEAMS_PER_SERVER)
|
||||
.detail("MaxTeamsPerServer", SERVER_KNOBS->MAX_TEAMS_PER_SERVER)
|
||||
.detail("Primary", self->primary);
|
||||
// Print to TraceEvents
|
||||
TraceEvent("DDConfig", self->distributorId)
|
||||
.detail("StorageTeamSize", configuration.storageTeamSize)
|
||||
.detail("DesiredTeamsPerServer", SERVER_KNOBS->DESIRED_TEAMS_PER_SERVER)
|
||||
.detail("MaxTeamsPerServer", SERVER_KNOBS->MAX_TEAMS_PER_SERVER)
|
||||
.detail("Primary", self->primary);
|
||||
|
||||
TraceEvent("ServerInfo", self->distributorId)
|
||||
.detail("Size", server_info.size())
|
||||
.detail("Primary", self->primary);
|
||||
state int i;
|
||||
state std::map<UID, Reference<TCServerInfo>>::iterator server = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
TraceEvent("ServerInfo", self->distributorId)
|
||||
.detail("ServerInfoIndex", i)
|
||||
.detail("ServerID", server->first.toString())
|
||||
.detail("ServerTeamOwned", server->second->teams.size())
|
||||
.detail("MachineID", server->second->machine->machineID.contents().toString())
|
||||
.detail("Size", server_info.size())
|
||||
.detail("Primary", self->primary);
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
server = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
const UID& uid = server->first;
|
||||
TraceEvent("ServerStatus", self->distributorId)
|
||||
.detail("ServerUID", uid)
|
||||
.detail("Healthy", !server_status.at(uid).isUnhealthy())
|
||||
.detail("MachineIsValid", server_info[uid]->machine.isValid())
|
||||
.detail("MachineTeamSize",
|
||||
server_info[uid]->machine.isValid() ? server_info[uid]->machine->machineTeams.size() : -1)
|
||||
.detail("Primary", self->primary);
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvent("ServerTeamInfo", self->distributorId).detail("Size", teams.size()).detail("Primary", self->primary);
|
||||
for (i = 0; i < teams.size(); i++) {
|
||||
const auto& team = teams[i];
|
||||
TraceEvent("ServerTeamInfo", self->distributorId)
|
||||
.detail("TeamIndex", i)
|
||||
.detail("Healthy", team->isHealthy())
|
||||
.detail("TeamSize", team->size())
|
||||
.detail("MemberIDs", team->getServerIDsStr())
|
||||
.detail("Primary", self->primary);
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvent("MachineInfo", self->distributorId)
|
||||
.detail("Size", machine_info.size())
|
||||
.detail("Primary", self->primary);
|
||||
state std::map<Standalone<StringRef>, Reference<TCMachineInfo>>::iterator machine = machine_info.begin();
|
||||
state bool isMachineHealthy = false;
|
||||
for (i = 0; i < machine_info.size(); i++) {
|
||||
Reference<TCMachineInfo> _machine = machine->second;
|
||||
if (!_machine.isValid() || machine_info.find(_machine->machineID) == machine_info.end() ||
|
||||
_machine->serversOnMachine.empty()) {
|
||||
isMachineHealthy = false;
|
||||
}
|
||||
|
||||
// Healthy machine has at least one healthy server
|
||||
for (auto& server : _machine->serversOnMachine) {
|
||||
if (!server_status.at(server->id).isUnhealthy()) {
|
||||
isMachineHealthy = true;
|
||||
state int i;
|
||||
state std::map<UID, Reference<TCServerInfo>>::iterator server = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
TraceEvent("ServerInfo", self->distributorId)
|
||||
.detail("ServerInfoIndex", i)
|
||||
.detail("ServerID", server->first.toString())
|
||||
.detail("ServerTeamOwned", server->second->teams.size())
|
||||
.detail("MachineID", server->second->machine->machineID.contents().toString())
|
||||
.detail("Primary", self->primary);
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
server = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
const UID& uid = server->first;
|
||||
TraceEvent("ServerStatus", self->distributorId)
|
||||
.detail("ServerUID", uid)
|
||||
.detail("Healthy", !server_status.at(uid).isUnhealthy())
|
||||
.detail("MachineIsValid", server_info[uid]->machine.isValid())
|
||||
.detail("MachineTeamSize",
|
||||
server_info[uid]->machine.isValid() ? server_info[uid]->machine->machineTeams.size() : -1)
|
||||
.detail("Primary", self->primary);
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvent("ServerTeamInfo", self->distributorId)
|
||||
.detail("Size", teams.size())
|
||||
.detail("Primary", self->primary);
|
||||
for (i = 0; i < teams.size(); i++) {
|
||||
const auto& team = teams[i];
|
||||
TraceEvent("ServerTeamInfo", self->distributorId)
|
||||
.detail("TeamIndex", i)
|
||||
.detail("Healthy", team->isHealthy())
|
||||
.detail("TeamSize", team->size())
|
||||
.detail("MemberIDs", team->getServerIDsStr())
|
||||
.detail("Primary", self->primary);
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
isMachineHealthy = false;
|
||||
TraceEvent("MachineInfo", self->distributorId)
|
||||
.detail("MachineInfoIndex", i)
|
||||
.detail("Healthy", isMachineHealthy)
|
||||
.detail("MachineID", machine->first.contents().toString())
|
||||
.detail("MachineTeamOwned", machine->second->machineTeams.size())
|
||||
.detail("ServerNumOnMachine", machine->second->serversOnMachine.size())
|
||||
.detail("ServersID", machine->second->getServersIDStr())
|
||||
.detail("Size", machine_info.size())
|
||||
.detail("Primary", self->primary);
|
||||
machine++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
state std::map<Standalone<StringRef>, Reference<TCMachineInfo>>::iterator machine = machine_info.begin();
|
||||
state bool isMachineHealthy = false;
|
||||
for (i = 0; i < machine_info.size(); i++) {
|
||||
Reference<TCMachineInfo> _machine = machine->second;
|
||||
if (!_machine.isValid() || machine_info.find(_machine->machineID) == machine_info.end() ||
|
||||
_machine->serversOnMachine.empty()) {
|
||||
isMachineHealthy = false;
|
||||
}
|
||||
|
||||
// Healthy machine has at least one healthy server
|
||||
for (auto& server : _machine->serversOnMachine) {
|
||||
if (!server_status.at(server->id).isUnhealthy()) {
|
||||
isMachineHealthy = true;
|
||||
}
|
||||
}
|
||||
|
||||
isMachineHealthy = false;
|
||||
TraceEvent("MachineInfo", self->distributorId)
|
||||
.detail("MachineInfoIndex", i)
|
||||
.detail("Healthy", isMachineHealthy)
|
||||
.detail("MachineID", machine->first.contents().toString())
|
||||
.detail("MachineTeamOwned", machine->second->machineTeams.size())
|
||||
.detail("ServerNumOnMachine", machine->second->serversOnMachine.size())
|
||||
.detail("ServersID", machine->second->getServersIDStr())
|
||||
.detail("Primary", self->primary);
|
||||
machine++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvent("MachineTeamInfo", self->distributorId)
|
||||
.detail("Size", machineTeams.size())
|
||||
.detail("Primary", self->primary);
|
||||
for (i = 0; i < machineTeams.size(); i++) {
|
||||
const auto& team = machineTeams[i];
|
||||
TraceEvent("MachineTeamInfo", self->distributorId)
|
||||
.detail("TeamIndex", i)
|
||||
.detail("MachineIDs", team->getMachineIDsStr())
|
||||
.detail("ServerTeams", team->serverTeams.size())
|
||||
.detail("Size", machineTeams.size())
|
||||
.detail("Primary", self->primary);
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
for (i = 0; i < machineTeams.size(); i++) {
|
||||
const auto& team = machineTeams[i];
|
||||
TraceEvent("MachineTeamInfo", self->distributorId)
|
||||
.detail("TeamIndex", i)
|
||||
.detail("MachineIDs", team->getMachineIDsStr())
|
||||
.detail("ServerTeams", team->serverTeams.size())
|
||||
.detail("Primary", self->primary);
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: re-enable the following logging or remove them.
|
||||
// TraceEvent("LocalityRecordKeyName", self->distributorId)
|
||||
// .detail("Size", internedLocalityRecordKeyNameStrings.size())
|
||||
// .detail("Primary", self->primary);
|
||||
// for (i = 0; i < internedLocalityRecordKeyNameStrings.size(); i++) {
|
||||
// TraceEvent("LocalityRecordKeyIndexName", self->distributorId)
|
||||
// .detail("KeyIndex", i)
|
||||
// .detail("KeyName", internedLocalityRecordKeyNameStrings[i])
|
||||
// .detail("Primary", self->primary);
|
||||
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
// wait(yield());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("Size", machineLocalityMapEntryArraySize)
|
||||
// .detail("Primary", self->primary);
|
||||
// for (i = 0; i < serverIDs.size(); i++) {
|
||||
// const auto& serverID = serverIDs[i];
|
||||
// Reference<LocalityRecord> record = machineLocalityMapRecordArray[i];
|
||||
// if (record.isValid()) {
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("LocalityIndex", i)
|
||||
// .detail("UID", serverID->toString())
|
||||
// .detail("LocalityRecord", record->toString())
|
||||
// .detail("Primary", self->primary);
|
||||
// } else {
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("LocalityIndex", i)
|
||||
// .detail("UID", serverID->toString())
|
||||
// .detail("LocalityRecord", "[NotFound]")
|
||||
// .detail("Primary", self->primary);
|
||||
// }
|
||||
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
// wait(yield());
|
||||
// }
|
||||
// }
|
||||
} catch (Error& e) {
|
||||
wait(tr.onError(e));
|
||||
}
|
||||
|
||||
// TODO: re-enable the following logging or remove them.
|
||||
// TraceEvent("LocalityRecordKeyName", self->distributorId)
|
||||
// .detail("Size", internedLocalityRecordKeyNameStrings.size())
|
||||
// .detail("Primary", self->primary);
|
||||
// for (i = 0; i < internedLocalityRecordKeyNameStrings.size(); i++) {
|
||||
// TraceEvent("LocalityRecordKeyIndexName", self->distributorId)
|
||||
// .detail("KeyIndex", i)
|
||||
// .detail("KeyName", internedLocalityRecordKeyNameStrings[i])
|
||||
// .detail("Primary", self->primary);
|
||||
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
// wait(yield());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("Size", machineLocalityMapEntryArraySize)
|
||||
// .detail("Primary", self->primary);
|
||||
// for (i = 0; i < serverIDs.size(); i++) {
|
||||
// const auto& serverID = serverIDs[i];
|
||||
// Reference<LocalityRecord> record = machineLocalityMapRecordArray[i];
|
||||
// if (record.isValid()) {
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("LocalityIndex", i)
|
||||
// .detail("UID", serverID->toString())
|
||||
// .detail("LocalityRecord", record->toString())
|
||||
// .detail("Primary", self->primary);
|
||||
// } else {
|
||||
// TraceEvent("MachineLocalityMap", self->distributorId)
|
||||
// .detail("LocalityIndex", i)
|
||||
// .detail("UID", serverID->toString())
|
||||
// .detail("LocalityRecord", "[NotFound]")
|
||||
// .detail("Primary", self->primary);
|
||||
// }
|
||||
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
// wait(yield());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2911,7 +2933,7 @@ ACTOR Future<Void> removeBadTeams(DDTeamCollection* self) {
|
|||
wait(self->initialFailureReactionDelay);
|
||||
wait(waitUntilHealthy(self));
|
||||
wait(self->addSubsetComplete.getFuture());
|
||||
TraceEvent("DDRemovingBadTeams", self->distributorId).detail("Primary", self->primary);
|
||||
TraceEvent("DDRemovingBadServerTeams", self->distributorId).detail("Primary", self->primary);
|
||||
for(auto it : self->badTeams) {
|
||||
it->tracker.cancel();
|
||||
}
|
||||
|
|
@ -3025,9 +3047,9 @@ ACTOR Future<Void> machineTeamRemover(DDTeamCollection* self) {
|
|||
// Check if a server will have 0 team after the team is removed
|
||||
for (auto& s : team->getServers()) {
|
||||
if (s->teams.size() == 0) {
|
||||
TraceEvent(SevError, "TeamRemoverTooAggressive")
|
||||
TraceEvent(SevError, "MachineTeamRemoverTooAggressive", self->distributorId)
|
||||
.detail("Server", s->id)
|
||||
.detail("Team", team->getServerIDsStr());
|
||||
.detail("ServerTeam", team->getDesc());
|
||||
self->traceAllInfo(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -3050,6 +3072,7 @@ ACTOR Future<Void> machineTeamRemover(DDTeamCollection* self) {
|
|||
}
|
||||
|
||||
TraceEvent("MachineTeamRemover", self->distributorId)
|
||||
.detail("MachineTeamIDToRemove", mt->id.shortString())
|
||||
.detail("MachineTeamToRemove", mt->getMachineIDsStr())
|
||||
.detail("NumProcessTeamsOnTheMachineTeam", minNumProcessTeams)
|
||||
.detail("CurrentMachineTeams", self->machineTeams.size())
|
||||
|
|
@ -3065,7 +3088,7 @@ ACTOR Future<Void> machineTeamRemover(DDTeamCollection* self) {
|
|||
} else {
|
||||
if (numMachineTeamRemoved > 0) {
|
||||
// Only trace the information when we remove a machine team
|
||||
TraceEvent("TeamRemoverDone")
|
||||
TraceEvent("MachineTeamRemoverDone", self->distributorId)
|
||||
.detail("HealthyMachines", healthyMachineCount)
|
||||
// .detail("CurrentHealthyMachineTeams", currentHealthyMTCount)
|
||||
.detail("CurrentMachineTeams", self->machineTeams.size())
|
||||
|
|
@ -3129,6 +3152,7 @@ ACTOR Future<Void> serverTeamRemover(DDTeamCollection* self) {
|
|||
|
||||
TraceEvent("ServerTeamRemover", self->distributorId)
|
||||
.detail("ServerTeamToRemove", st->getServerIDsStr())
|
||||
.detail("ServerTeamID", st->getTeamID())
|
||||
.detail("NumProcessTeamsOnTheServerTeam", maxNumProcessTeams)
|
||||
.detail("CurrentServerTeams", self->teams.size())
|
||||
.detail("DesiredServerTeams", desiredServerTeams);
|
||||
|
|
@ -3148,6 +3172,35 @@ ACTOR Future<Void> serverTeamRemover(DDTeamCollection* self) {
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> zeroServerLeftLogger_impl(DDTeamCollection* self, Reference<TCTeamInfo> team) {
|
||||
wait(delay(SERVER_KNOBS->DD_TEAM_ZERO_SERVER_LEFT_LOG_DELAY));
|
||||
state vector<KeyRange> shards = self->shardsAffectedByTeamFailure->getShardsFor(
|
||||
ShardsAffectedByTeamFailure::Team(team->getServerIDs(), self->primary));
|
||||
state std::vector<Future<StorageMetrics>> sizes;
|
||||
sizes.reserve(shards.size());
|
||||
|
||||
for (auto const& shard : shards) {
|
||||
sizes.emplace_back(brokenPromiseToNever(self->getShardMetrics.getReply(GetMetricsRequest(shard))));
|
||||
TraceEvent(SevWarnAlways, "DDShardLost", self->distributorId)
|
||||
.detail("ServerTeamID", team->getTeamID())
|
||||
.detail("ShardBegin", shard.begin)
|
||||
.detail("ShardEnd", shard.end);
|
||||
}
|
||||
|
||||
wait(waitForAll(sizes));
|
||||
|
||||
int64_t bytesLost = 0;
|
||||
for (auto const& size : sizes) {
|
||||
bytesLost += size.get().bytes;
|
||||
}
|
||||
|
||||
TraceEvent(SevWarnAlways, "DDZeroServerLeftInTeam", self->distributorId)
|
||||
.detail("Team", team->getDesc())
|
||||
.detail("TotalBytesLost", bytesLost);
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
bool teamContainsFailedServer(DDTeamCollection* self, Reference<TCTeamInfo> team) {
|
||||
auto ssis = team->getLastKnownServerInterfaces();
|
||||
for (const auto &ssi : ssis) {
|
||||
|
|
@ -3183,18 +3236,22 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
state bool lastZeroHealthy = self->zeroHealthyTeams->get();
|
||||
state bool firstCheck = true;
|
||||
|
||||
state Future<Void> zeroServerLeftLogger;
|
||||
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamTrackerStarting", self->distributorId).detail("Reason", "Initial wait complete (sc)").detail("Team", team->getDesc());
|
||||
TraceEvent("ServerTeamTrackerStarting", self->distributorId)
|
||||
.detail("Reason", "Initial wait complete (sc)")
|
||||
.detail("ServerTeam", team->getDesc());
|
||||
}
|
||||
self->priority_teams[team->getPriority()]++;
|
||||
|
||||
try {
|
||||
loop {
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamHealthChangeDetected", self->distributorId)
|
||||
.detail("Team", team->getDesc())
|
||||
.detail("Primary", self->primary)
|
||||
.detail("IsReady", self->initialFailureReactionDelay.isReady());
|
||||
TraceEvent("ServerTeamHealthChangeDetected", self->distributorId)
|
||||
.detail("ServerTeam", team->getDesc())
|
||||
.detail("Primary", self->primary)
|
||||
.detail("IsReady", self->initialFailureReactionDelay.isReady());
|
||||
self->traceTeamCollectionInfo();
|
||||
}
|
||||
// Check if the number of degraded machines has changed
|
||||
|
|
@ -3270,10 +3327,13 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
if (serversLeft != lastServersLeft || anyUndesired != lastAnyUndesired ||
|
||||
anyWrongConfiguration != lastWrongConfiguration || recheck) { // NOTE: do not check wrongSize
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamHealthChanged", self->distributorId)
|
||||
.detail("Team", team->getDesc()).detail("ServersLeft", serversLeft)
|
||||
.detail("LastServersLeft", lastServersLeft).detail("ContainsUndesiredServer", anyUndesired)
|
||||
.detail("HealthyTeamsCount", self->healthyTeamCount).detail("IsWrongConfiguration", anyWrongConfiguration);
|
||||
TraceEvent("ServerTeamHealthChanged", self->distributorId)
|
||||
.detail("ServerTeam", team->getDesc())
|
||||
.detail("ServersLeft", serversLeft)
|
||||
.detail("LastServersLeft", lastServersLeft)
|
||||
.detail("ContainsUndesiredServer", anyUndesired)
|
||||
.detail("HealthyTeamsCount", self->healthyTeamCount)
|
||||
.detail("IsWrongConfiguration", anyWrongConfiguration);
|
||||
}
|
||||
|
||||
team->setWrongConfiguration( anyWrongConfiguration );
|
||||
|
|
@ -3295,18 +3355,18 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
self->zeroHealthyTeams->set(self->healthyTeamCount == 0);
|
||||
|
||||
if( self->healthyTeamCount == 0 ) {
|
||||
TraceEvent(SevWarn, "ZeroTeamsHealthySignalling", self->distributorId)
|
||||
.detail("SignallingTeam", team->getDesc())
|
||||
.detail("Primary", self->primary);
|
||||
TraceEvent(SevWarn, "ZeroServerTeamsHealthySignalling", self->distributorId)
|
||||
.detail("SignallingTeam", team->getDesc())
|
||||
.detail("Primary", self->primary);
|
||||
}
|
||||
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamHealthDifference", self->distributorId)
|
||||
.detail("Team", team->getDesc())
|
||||
.detail("LastOptimal", lastOptimal)
|
||||
.detail("LastHealthy", lastHealthy)
|
||||
.detail("Optimal", optimal)
|
||||
.detail("OptimalTeamCount", self->optimalTeamCount);
|
||||
TraceEvent("ServerTeamHealthDifference", self->distributorId)
|
||||
.detail("ServerTeam", team->getDesc())
|
||||
.detail("LastOptimal", lastOptimal)
|
||||
.detail("LastHealthy", lastHealthy)
|
||||
.detail("Optimal", optimal)
|
||||
.detail("OptimalTeamCount", self->optimalTeamCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3343,12 +3403,26 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
if(lastPriority != team->getPriority()) {
|
||||
self->priority_teams[lastPriority]--;
|
||||
self->priority_teams[team->getPriority()]++;
|
||||
if (lastPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT &&
|
||||
team->getPriority() < SERVER_KNOBS->PRIORITY_TEAM_0_LEFT) {
|
||||
zeroServerLeftLogger = Void();
|
||||
}
|
||||
if (logTeamEvents) {
|
||||
int dataLoss = team->getPriority() == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT;
|
||||
Severity severity = dataLoss ? SevWarnAlways : SevInfo;
|
||||
TraceEvent(severity, "ServerTeamPriorityChange", self->distributorId)
|
||||
.detail("Priority", team->getPriority())
|
||||
.detail("Info", team->getDesc())
|
||||
.detail("ZeroHealthyServerTeams", self->zeroHealthyTeams->get())
|
||||
.detail("Hint", severity == SevWarnAlways ? "No replicas remain of some data"
|
||||
: "The priority of this team changed");
|
||||
if (team->getPriority() == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT) {
|
||||
// 0 servers left in this team, data might be lost.
|
||||
zeroServerLeftLogger = zeroServerLeftLogger_impl(self, team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamPriorityChange", self->distributorId).detail("Priority", team->getPriority())
|
||||
.detail("Info", team->getDesc()).detail("ZeroHealthyTeams", self->zeroHealthyTeams->get());
|
||||
}
|
||||
|
||||
lastZeroHealthy = self->zeroHealthyTeams->get(); //set this again in case it changed from this teams health changing
|
||||
if ((self->initialFailureReactionDelay.isReady() && !self->zeroHealthyTeams->get()) || containsFailed) {
|
||||
|
|
@ -3417,17 +3491,19 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
self->output.send(rs);
|
||||
TraceEvent("SendRelocateToDDQueue", self->distributorId)
|
||||
.suppressFor(1.0)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("Team", team->getDesc())
|
||||
.detail("ServerPrimary", self->primary)
|
||||
.detail("ServerTeam", team->getDesc())
|
||||
.detail("KeyBegin", rs.keys.begin)
|
||||
.detail("KeyEnd", rs.keys.end)
|
||||
.detail("Priority", rs.priority)
|
||||
.detail("TeamFailedMachines", team->size() - serversLeft)
|
||||
.detail("TeamOKMachines", serversLeft);
|
||||
.detail("ServerTeamFailedMachines", team->size() - serversLeft)
|
||||
.detail("ServerTeamOKMachines", serversLeft);
|
||||
}
|
||||
} else {
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamHealthNotReady", self->distributorId).detail("HealthyTeamCount", self->healthyTeamCount);
|
||||
TraceEvent("ServerTeamHealthNotReady", self->distributorId)
|
||||
.detail("HealthyServerTeamCount", self->healthyTeamCount)
|
||||
.detail("ServerTeamID", team->getTeamID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3439,7 +3515,7 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
} catch(Error& e) {
|
||||
if(logTeamEvents) {
|
||||
TraceEvent("TeamTrackerStopping", self->distributorId)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("ServerPrimary", self->primary)
|
||||
.detail("Team", team->getDesc())
|
||||
.detail("Priority", team->getPriority());
|
||||
}
|
||||
|
|
@ -3450,8 +3526,8 @@ ACTOR Future<Void> teamTracker(DDTeamCollection* self, Reference<TCTeamInfo> tea
|
|||
|
||||
if( self->healthyTeamCount == 0 ) {
|
||||
TraceEvent(SevWarn, "ZeroTeamsHealthySignalling", self->distributorId)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("SignallingTeam", team->getDesc());
|
||||
.detail("ServerPrimary", self->primary)
|
||||
.detail("SignallingServerTeam", team->getDesc());
|
||||
self->zeroHealthyTeams->set(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -4716,7 +4792,9 @@ ACTOR Future<Void> dataDistribution(Reference<DataDistributorData> self, Promise
|
|||
state MoveKeysLock lock;
|
||||
state Reference<DDTeamCollection> primaryTeamCollection;
|
||||
state Reference<DDTeamCollection> remoteTeamCollection;
|
||||
state bool trackerCancelled;
|
||||
loop {
|
||||
trackerCancelled = false;
|
||||
try {
|
||||
loop {
|
||||
TraceEvent("DDInitTakingMoveKeysLock", self->ddId);
|
||||
|
|
@ -4880,7 +4958,7 @@ ACTOR Future<Void> dataDistribution(Reference<DataDistributorData> self, Promise
|
|||
actors.push_back(reportErrorsExcept(
|
||||
dataDistributionTracker(initData, cx, output, shardsAffectedByTeamFailure, getShardMetrics,
|
||||
getShardMetricsList, getAverageShardBytes.getFuture(), readyToStart,
|
||||
anyZeroHealthyTeams, self->ddId, &shards),
|
||||
anyZeroHealthyTeams, self->ddId, &shards, &trackerCancelled),
|
||||
"DDTracker", self->ddId, &normalDDQueueErrors()));
|
||||
actors.push_back(reportErrorsExcept(
|
||||
dataDistributionQueue(cx, output, input.getFuture(), getShardMetrics, processingUnhealthy, tcis,
|
||||
|
|
@ -4889,10 +4967,16 @@ ACTOR Future<Void> dataDistribution(Reference<DataDistributorData> self, Promise
|
|||
"DDQueue", self->ddId, &normalDDQueueErrors()));
|
||||
|
||||
vector<DDTeamCollection*> teamCollectionsPtrs;
|
||||
primaryTeamCollection = Reference<DDTeamCollection>( new DDTeamCollection(cx, self->ddId, lock, output, shardsAffectedByTeamFailure, configuration, primaryDcId, configuration.usableRegions > 1 ? remoteDcIds : std::vector<Optional<Key>>(), readyToStart.getFuture(), zeroHealthyTeams[0], true, processingUnhealthy) );
|
||||
primaryTeamCollection = Reference<DDTeamCollection>(new DDTeamCollection(
|
||||
cx, self->ddId, lock, output, shardsAffectedByTeamFailure, configuration, primaryDcId,
|
||||
configuration.usableRegions > 1 ? remoteDcIds : std::vector<Optional<Key>>(), readyToStart.getFuture(),
|
||||
zeroHealthyTeams[0], true, processingUnhealthy, getShardMetrics));
|
||||
teamCollectionsPtrs.push_back(primaryTeamCollection.getPtr());
|
||||
if (configuration.usableRegions > 1) {
|
||||
remoteTeamCollection = Reference<DDTeamCollection>( new DDTeamCollection(cx, self->ddId, lock, output, shardsAffectedByTeamFailure, configuration, remoteDcIds, Optional<std::vector<Optional<Key>>>(), readyToStart.getFuture() && remoteRecovered(self->dbInfo), zeroHealthyTeams[1], false, processingUnhealthy) );
|
||||
remoteTeamCollection = Reference<DDTeamCollection>(new DDTeamCollection(
|
||||
cx, self->ddId, lock, output, shardsAffectedByTeamFailure, configuration, remoteDcIds,
|
||||
Optional<std::vector<Optional<Key>>>(), readyToStart.getFuture() && remoteRecovered(self->dbInfo),
|
||||
zeroHealthyTeams[1], false, processingUnhealthy, getShardMetrics));
|
||||
teamCollectionsPtrs.push_back(remoteTeamCollection.getPtr());
|
||||
remoteTeamCollection->teamCollections = teamCollectionsPtrs;
|
||||
actors.push_back( reportErrorsExcept( dataDistributionTeamCollection( remoteTeamCollection, initData, tcis[1], self->dbInfo ), "DDTeamCollectionSecondary", self->ddId, &normalDDQueueErrors() ) );
|
||||
|
|
@ -4908,6 +4992,7 @@ ACTOR Future<Void> dataDistribution(Reference<DataDistributorData> self, Promise
|
|||
return Void();
|
||||
}
|
||||
catch( Error &e ) {
|
||||
trackerCancelled = true;
|
||||
state Error err = e;
|
||||
TraceEvent("DataDistributorDestroyTeamCollections").error(e);
|
||||
self->teamCollection = nullptr;
|
||||
|
|
@ -5015,9 +5100,8 @@ ACTOR Future<Void> ddSnapCreateCore(DistributorSnapRequest snapReq, Reference<As
|
|||
.detail("SnapPayload", snapReq.snapPayload)
|
||||
.detail("SnapUID", snapReq.snapUID)
|
||||
.error(e, true /*includeCancelled */);
|
||||
if (e.code() == error_code_snap_storage_failed
|
||||
|| e.code() == error_code_snap_tlog_failed
|
||||
|| e.code() == error_code_operation_cancelled) {
|
||||
if (e.code() == error_code_snap_storage_failed || e.code() == error_code_snap_tlog_failed ||
|
||||
e.code() == error_code_operation_cancelled || e.code() == error_code_snap_disable_tlog_pop_failed) {
|
||||
// enable tlog pop on local tlog nodes
|
||||
std::vector<TLogInterface> tlogs = db->get().logSystemConfig.allLocalLogs(false);
|
||||
try {
|
||||
|
|
@ -5209,20 +5293,11 @@ DDTeamCollection* testTeamCollection(int teamSize, Reference<IReplicationPolicy>
|
|||
conf.storageTeamSize = teamSize;
|
||||
conf.storagePolicy = policy;
|
||||
|
||||
DDTeamCollection* collection = new DDTeamCollection(
|
||||
database,
|
||||
UID(0, 0),
|
||||
MoveKeysLock(),
|
||||
PromiseStream<RelocateShard>(),
|
||||
Reference<ShardsAffectedByTeamFailure>(new ShardsAffectedByTeamFailure()),
|
||||
conf,
|
||||
{},
|
||||
{},
|
||||
Future<Void>(Void()),
|
||||
Reference<AsyncVar<bool>>( new AsyncVar<bool>(true) ),
|
||||
true,
|
||||
Reference<AsyncVar<bool>>( new AsyncVar<bool>(false) )
|
||||
);
|
||||
DDTeamCollection* collection =
|
||||
new DDTeamCollection(database, UID(0, 0), MoveKeysLock(), PromiseStream<RelocateShard>(),
|
||||
Reference<ShardsAffectedByTeamFailure>(new ShardsAffectedByTeamFailure()), conf, {}, {},
|
||||
Future<Void>(Void()), Reference<AsyncVar<bool>>(new AsyncVar<bool>(true)), true,
|
||||
Reference<AsyncVar<bool>>(new AsyncVar<bool>(false)), PromiseStream<GetMetricsRequest>());
|
||||
|
||||
for (int id = 1; id <= processCount; ++id) {
|
||||
UID uid(id, 0);
|
||||
|
|
@ -5250,9 +5325,8 @@ DDTeamCollection* testMachineTeamCollection(int teamSize, Reference<IReplication
|
|||
DDTeamCollection* collection =
|
||||
new DDTeamCollection(database, UID(0, 0), MoveKeysLock(), PromiseStream<RelocateShard>(),
|
||||
Reference<ShardsAffectedByTeamFailure>(new ShardsAffectedByTeamFailure()), conf, {}, {},
|
||||
Future<Void>(Void()),
|
||||
Reference<AsyncVar<bool>>(new AsyncVar<bool>(true)), true,
|
||||
Reference<AsyncVar<bool>>(new AsyncVar<bool>(false)));
|
||||
Future<Void>(Void()), Reference<AsyncVar<bool>>(new AsyncVar<bool>(true)), true,
|
||||
Reference<AsyncVar<bool>>(new AsyncVar<bool>(false)), PromiseStream<GetMetricsRequest>());
|
||||
|
||||
for (int id = 1; id <= processCount; id++) {
|
||||
UID uid(id, 0);
|
||||
|
|
|
|||
|
|
@ -58,10 +58,12 @@ struct IDataDistributionTeam {
|
|||
virtual bool isWrongConfiguration() const = 0;
|
||||
virtual void setWrongConfiguration(bool) = 0;
|
||||
virtual void addServers(const vector<UID> &servers) = 0;
|
||||
virtual std::string getTeamID() const = 0;
|
||||
|
||||
std::string getDesc() const {
|
||||
const auto& servers = getLastKnownServerInterfaces();
|
||||
std::string s = format("Size %d; ", servers.size());
|
||||
std::string s = format("TeamID:%s", getTeamID().c_str());
|
||||
s += format("Size %d; ", servers.size());
|
||||
for(int i=0; i<servers.size(); i++) {
|
||||
if (i) s += ", ";
|
||||
s += servers[i].address().toString() + " " + servers[i].id().shortString();
|
||||
|
|
@ -210,7 +212,7 @@ struct InitialDataDistribution : ReferenceCounted<InitialDataDistribution> {
|
|||
struct ShardMetrics {
|
||||
StorageMetrics metrics;
|
||||
double lastLowBandwidthStartTime;
|
||||
int shardCount;
|
||||
int shardCount; // number of smaller shards whose metrics are aggregated in the ShardMetrics
|
||||
|
||||
bool operator==(ShardMetrics const& rhs) const {
|
||||
return metrics == rhs.metrics && lastLowBandwidthStartTime == rhs.lastLowBandwidthStartTime &&
|
||||
|
|
@ -227,33 +229,22 @@ struct ShardTrackedData {
|
|||
Reference<AsyncVar<Optional<ShardMetrics>>> stats;
|
||||
};
|
||||
|
||||
Future<Void> dataDistributionTracker(
|
||||
Reference<InitialDataDistribution> const& initData,
|
||||
Database const& cx,
|
||||
PromiseStream<RelocateShard> const& output,
|
||||
Reference<ShardsAffectedByTeamFailure> const& shardsAffectedByTeamFailure,
|
||||
PromiseStream<GetMetricsRequest> const& getShardMetrics,
|
||||
PromiseStream<GetMetricsListRequest> const& getShardMetricsList,
|
||||
FutureStream<Promise<int64_t>> const& getAverageShardBytes,
|
||||
Promise<Void> const& readyToStart,
|
||||
Reference<AsyncVar<bool>> const& zeroHealthyTeams,
|
||||
UID const& distributorId,
|
||||
KeyRangeMap<ShardTrackedData>* const& shards);
|
||||
ACTOR Future<Void> dataDistributionTracker(Reference<InitialDataDistribution> initData, Database cx,
|
||||
PromiseStream<RelocateShard> output,
|
||||
Reference<ShardsAffectedByTeamFailure> shardsAffectedByTeamFailure,
|
||||
PromiseStream<GetMetricsRequest> getShardMetrics,
|
||||
PromiseStream<GetMetricsListRequest> getShardMetricsList,
|
||||
FutureStream<Promise<int64_t>> getAverageShardBytes,
|
||||
Promise<Void> readyToStart, Reference<AsyncVar<bool>> anyZeroHealthyTeams,
|
||||
UID distributorId, KeyRangeMap<ShardTrackedData>* shards,
|
||||
bool* trackerCancelled);
|
||||
|
||||
Future<Void> dataDistributionQueue(
|
||||
Database const& cx,
|
||||
PromiseStream<RelocateShard> const& output,
|
||||
FutureStream<RelocateShard> const& input,
|
||||
PromiseStream<GetMetricsRequest> const& getShardMetrics,
|
||||
Reference<AsyncVar<bool>> const& processingUnhealthy,
|
||||
vector<TeamCollectionInterface> const& teamCollection,
|
||||
Reference<ShardsAffectedByTeamFailure> const& shardsAffectedByTeamFailure,
|
||||
MoveKeysLock const& lock,
|
||||
PromiseStream<Promise<int64_t>> const& getAverageShardBytes,
|
||||
UID const& distributorId,
|
||||
int const& teamSize,
|
||||
int const& singleRegionTeamSize,
|
||||
double* const& lastLimited);
|
||||
ACTOR Future<Void> dataDistributionQueue(
|
||||
Database cx, PromiseStream<RelocateShard> output, FutureStream<RelocateShard> input,
|
||||
PromiseStream<GetMetricsRequest> getShardMetrics, Reference<AsyncVar<bool>> processingUnhealthy,
|
||||
vector<TeamCollectionInterface> teamCollection, Reference<ShardsAffectedByTeamFailure> shardsAffectedByTeamFailure,
|
||||
MoveKeysLock lock, PromiseStream<Promise<int64_t>> getAverageShardBytes, UID distributorId, int teamSize,
|
||||
int singleRegionTeamSize, double* lastLimited);
|
||||
|
||||
//Holds the permitted size and IO Bounds for a shard
|
||||
struct ShardSizeBounds {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <numeric>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "flow/ActorCollection.h"
|
||||
#include "flow/Util.h"
|
||||
|
|
@ -82,7 +83,8 @@ struct RelocateData {
|
|||
};
|
||||
|
||||
class ParallelTCInfo : public ReferenceCounted<ParallelTCInfo>, public IDataDistributionTeam {
|
||||
vector<Reference<IDataDistributionTeam>> teams;
|
||||
std::vector<Reference<IDataDistributionTeam>> teams;
|
||||
std::vector<UID> tempServerIDs;
|
||||
|
||||
int64_t sum(std::function<int64_t(IDataDistributionTeam const&)> func) const {
|
||||
int64_t result = 0;
|
||||
|
|
@ -93,11 +95,11 @@ class ParallelTCInfo : public ReferenceCounted<ParallelTCInfo>, public IDataDist
|
|||
}
|
||||
|
||||
template <class T>
|
||||
vector<T> collect(std::function<vector<T>(IDataDistributionTeam const&)> func) const {
|
||||
vector<T> result;
|
||||
std::vector<T> collect(std::function<vector<T>(IDataDistributionTeam const&)> func) const {
|
||||
std::vector<T> result;
|
||||
|
||||
for (const auto& team : teams) {
|
||||
vector<T> newItems = func(*team);
|
||||
std::vector<T> newItems = func(*team);
|
||||
result.insert(result.end(), newItems.begin(), newItems.end());
|
||||
}
|
||||
return result;
|
||||
|
|
@ -123,7 +125,7 @@ public:
|
|||
return !any([func](IDataDistributionTeam const& team) { return !func(team); });
|
||||
}
|
||||
|
||||
vector<StorageServerInterface> getLastKnownServerInterfaces() const override {
|
||||
std::vector<StorageServerInterface> getLastKnownServerInterfaces() const override {
|
||||
return collect<StorageServerInterface>(
|
||||
[](IDataDistributionTeam const& team) { return team.getLastKnownServerInterfaces(); });
|
||||
}
|
||||
|
|
@ -136,11 +138,11 @@ public:
|
|||
return totalSize;
|
||||
}
|
||||
|
||||
vector<UID> const& getServerIDs() const override {
|
||||
std::vector<UID> const& getServerIDs() const override {
|
||||
static vector<UID> tempServerIDs;
|
||||
tempServerIDs.clear();
|
||||
for (const auto& team : teams) {
|
||||
vector<UID> const &childIDs = team->getServerIDs();
|
||||
std::vector<UID> const& childIDs = team->getServerIDs();
|
||||
tempServerIDs.insert(tempServerIDs.end(), childIDs.begin(), childIDs.end());
|
||||
}
|
||||
return tempServerIDs;
|
||||
|
|
@ -183,7 +185,7 @@ public:
|
|||
}
|
||||
|
||||
virtual Future<Void> updateStorageMetrics() {
|
||||
vector<Future<Void>> futures;
|
||||
std::vector<Future<Void>> futures;
|
||||
|
||||
for (auto& team : teams) {
|
||||
futures.push_back(team->updateStorageMetrics());
|
||||
|
|
@ -234,10 +236,19 @@ public:
|
|||
ASSERT(!teams.empty());
|
||||
teams[0]->addServers(servers);
|
||||
}
|
||||
|
||||
std::string getTeamID() const override {
|
||||
std::string id;
|
||||
for (int i = 0; i < teams.size(); i++) {
|
||||
auto const& team = teams[i];
|
||||
id += (i == teams.size() - 1) ? team->getTeamID() : format("%s, ", team->getTeamID().c_str());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
struct Busyness {
|
||||
vector<int> ledger;
|
||||
std::vector<int> ledger;
|
||||
|
||||
Busyness() : ledger( 10, 0 ) {}
|
||||
|
||||
|
|
@ -541,8 +552,8 @@ struct DDQueueData {
|
|||
|
||||
if(keyServersEntries.size() < SERVER_KNOBS->DD_QUEUE_MAX_KEY_SERVERS) {
|
||||
for( int shard = 0; shard < keyServersEntries.size(); shard++ ) {
|
||||
vector<UID> src, dest;
|
||||
decodeKeyServersValue( UIDtoTagMap, keyServersEntries[shard].value, src, dest );
|
||||
std::vector<UID> src, dest;
|
||||
decodeKeyServersValue(UIDtoTagMap, keyServersEntries[shard].value, src, dest);
|
||||
ASSERT( src.size() );
|
||||
for( int i = 0; i < src.size(); i++ ) {
|
||||
servers.insert( src[i] );
|
||||
|
|
@ -845,7 +856,7 @@ struct DDQueueData {
|
|||
startedHere++;
|
||||
|
||||
// update both inFlightActors and inFlight key range maps, cancelling deleted RelocateShards
|
||||
vector<KeyRange> ranges;
|
||||
std::vector<KeyRange> ranges;
|
||||
inFlightActors.getRangesAffectedByInsertion( rd.keys, ranges );
|
||||
inFlightActors.cancel( KeyRangeRef( ranges.front().begin, ranges.back().end ) );
|
||||
inFlight.insert( rd.keys, rd );
|
||||
|
|
@ -1036,6 +1047,9 @@ ACTOR Future<Void> dataDistributionRelocator( DDQueueData *self, RelocateData rd
|
|||
} else {
|
||||
TraceEvent(relocateShardInterval.severity, "RelocateShardHasDestination", distributorId)
|
||||
.detail("PairId", relocateShardInterval.pairID)
|
||||
.detail("KeyBegin", rd.keys.begin)
|
||||
.detail("KeyEnd", rd.keys.end)
|
||||
.detail("SourceServers", describe(rd.src))
|
||||
.detail("DestinationTeam", describe(destIds))
|
||||
.detail("ExtraIds", describe(extraIds));
|
||||
}
|
||||
|
|
@ -1424,7 +1438,7 @@ ACTOR Future<Void> dataDistributionQueue(
|
|||
state RelocateData launchData;
|
||||
state Future<Void> recordMetrics = delay(SERVER_KNOBS->DD_QUEUE_LOGGING_INTERVAL);
|
||||
|
||||
state vector<Future<Void>> balancingFutures;
|
||||
state std::vector<Future<Void>> balancingFutures;
|
||||
|
||||
state ActorCollectionNoErrors actors;
|
||||
state PromiseStream<KeyRange> rangesComplete;
|
||||
|
|
|
|||
|
|
@ -91,17 +91,47 @@ struct DataDistributionTracker {
|
|||
// Read hot detection
|
||||
PromiseStream<KeyRange> readHotShard;
|
||||
|
||||
// The reference to trackerCancelled must be extracted by actors,
|
||||
// because by the time (trackerCancelled == true) this memory cannot
|
||||
// be accessed
|
||||
bool& trackerCancelled;
|
||||
|
||||
// This class extracts the trackerCancelled reference from a DataDistributionTracker object
|
||||
// Because some actors spawned by the dataDistributionTracker outlive the DataDistributionTracker
|
||||
// object, we must guard against memory errors by using a GetTracker functor to access
|
||||
// the DataDistributionTracker object.
|
||||
class SafeAccessor {
|
||||
bool const& trackerCancelled;
|
||||
DataDistributionTracker& tracker;
|
||||
|
||||
public:
|
||||
SafeAccessor(DataDistributionTracker* tracker)
|
||||
: trackerCancelled(tracker->trackerCancelled), tracker(*tracker) {
|
||||
ASSERT(!trackerCancelled);
|
||||
}
|
||||
|
||||
DataDistributionTracker* operator()() {
|
||||
if (trackerCancelled) {
|
||||
TEST(true); // Trying to access DataDistributionTracker after tracker has been cancelled
|
||||
throw dd_tracker_cancelled();
|
||||
}
|
||||
return &tracker;
|
||||
}
|
||||
};
|
||||
|
||||
DataDistributionTracker(Database cx, UID distributorId, Promise<Void> const& readyToStart,
|
||||
PromiseStream<RelocateShard> const& output,
|
||||
Reference<ShardsAffectedByTeamFailure> shardsAffectedByTeamFailure,
|
||||
Reference<AsyncVar<bool>> anyZeroHealthyTeams, KeyRangeMap<ShardTrackedData>& shards)
|
||||
Reference<AsyncVar<bool>> anyZeroHealthyTeams, KeyRangeMap<ShardTrackedData>& shards,
|
||||
bool& trackerCancelled)
|
||||
: cx(cx), distributorId(distributorId), dbSizeEstimate(new AsyncVar<int64_t>()), systemSizeEstimate(0),
|
||||
maxShardSize(new AsyncVar<Optional<int64_t>>()), sizeChanges(false), readyToStart(readyToStart), output(output),
|
||||
shardsAffectedByTeamFailure(shardsAffectedByTeamFailure), anyZeroHealthyTeams(anyZeroHealthyTeams),
|
||||
shards(shards) {}
|
||||
shards(shards), trackerCancelled(trackerCancelled) {}
|
||||
|
||||
~DataDistributionTracker()
|
||||
{
|
||||
trackerCancelled = true;
|
||||
//Cancel all actors so they aren't waiting on sizeChanged broken promise
|
||||
sizeChanges.clear(false);
|
||||
}
|
||||
|
|
@ -150,7 +180,7 @@ int64_t getMaxShardSize( double dbSizeEstimate ) {
|
|||
(int64_t)SERVER_KNOBS->MAX_SHARD_BYTES);
|
||||
}
|
||||
|
||||
ACTOR Future<Void> trackShardMetrics(DataDistributionTracker* self, KeyRange keys,
|
||||
ACTOR Future<Void> trackShardMetrics(DataDistributionTracker::SafeAccessor self, KeyRange keys,
|
||||
Reference<AsyncVar<Optional<ShardMetrics>>> shardMetrics) {
|
||||
state BandwidthStatus bandwidthStatus = shardMetrics->get().present() ? getBandwidthStatus( shardMetrics->get().get().metrics ) : BandwidthStatusNormal;
|
||||
state double lastLowBandwidthStartTime = shardMetrics->get().present() ? shardMetrics->get().get().lastLowBandwidthStartTime : now();
|
||||
|
|
@ -209,7 +239,7 @@ ACTOR Future<Void> trackShardMetrics(DataDistributionTracker* self, KeyRange key
|
|||
// TraceEvent("RHDTriggerReadHotLoggingForShard")
|
||||
// .detail("ShardBegin", keys.begin.printable().c_str())
|
||||
// .detail("ShardEnd", keys.end.printable().c_str());
|
||||
self->readHotShard.send(keys);
|
||||
self()->readHotShard.send(keys);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
|
@ -230,7 +260,8 @@ ACTOR Future<Void> trackShardMetrics(DataDistributionTracker* self, KeyRange key
|
|||
bounds.permittedError.iosPerKSecond = bounds.permittedError.infinity;
|
||||
|
||||
loop {
|
||||
Transaction tr(self->cx);
|
||||
Transaction tr(self()->cx);
|
||||
// metrics.second is the number of key-ranges (i.e., shards) in the 'keys' key-range
|
||||
std::pair<Optional<StorageMetrics>, int> metrics = wait( tr.waitStorageMetrics( keys, bounds.min, bounds.max, bounds.permittedError, CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT, shardCount ) );
|
||||
if(metrics.first.present()) {
|
||||
BandwidthStatus newBandwidthStatus = getBandwidthStatus( metrics.first.get() );
|
||||
|
|
@ -253,9 +284,11 @@ ACTOR Future<Void> trackShardMetrics(DataDistributionTracker* self, KeyRange key
|
|||
.detail("TrackerID", trackerID);*/
|
||||
|
||||
if( shardMetrics->get().present() ) {
|
||||
self->dbSizeEstimate->set( self->dbSizeEstimate->get() + metrics.first.get().bytes - shardMetrics->get().get().metrics.bytes );
|
||||
self()->dbSizeEstimate->set(self()->dbSizeEstimate->get() + metrics.first.get().bytes -
|
||||
shardMetrics->get().get().metrics.bytes);
|
||||
if(keys.begin >= systemKeys.begin) {
|
||||
self->systemSizeEstimate += metrics.first.get().bytes - shardMetrics->get().get().metrics.bytes;
|
||||
self()->systemSizeEstimate +=
|
||||
metrics.first.get().bytes - shardMetrics->get().get().metrics.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,8 +305,9 @@ ACTOR Future<Void> trackShardMetrics(DataDistributionTracker* self, KeyRange key
|
|||
}
|
||||
}
|
||||
} catch( Error &e ) {
|
||||
if (e.code() != error_code_actor_cancelled)
|
||||
self->output.sendError(e); // Propagate failure to dataDistributionTracker
|
||||
if (e.code() != error_code_actor_cancelled && e.code() != error_code_dd_tracker_cancelled) {
|
||||
self()->output.sendError(e); // Propagate failure to dataDistributionTracker
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
@ -382,16 +416,19 @@ ACTOR Future<Void> changeSizes( DataDistributionTracker* self, KeyRange keys, in
|
|||
}
|
||||
|
||||
struct HasBeenTrueFor : ReferenceCounted<HasBeenTrueFor> {
|
||||
explicit HasBeenTrueFor( Optional<ShardMetrics> value ) {
|
||||
explicit HasBeenTrueFor(const Optional<ShardMetrics>& value) {
|
||||
if(value.present()) {
|
||||
trigger = delayJittered(std::max(0.0, SERVER_KNOBS->DD_MERGE_COALESCE_DELAY + value.get().lastLowBandwidthStartTime - now()), TaskPriority::DataDistributionLow ) || cleared.getFuture();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Void> set() {
|
||||
Future<Void> set(double lastLowBandwidthStartTime) {
|
||||
if( !trigger.isValid() ) {
|
||||
cleared = Promise<Void>();
|
||||
trigger = delayJittered( SERVER_KNOBS->DD_MERGE_COALESCE_DELAY, TaskPriority::DataDistributionLow ) || cleared.getFuture();
|
||||
trigger =
|
||||
delayJittered(SERVER_KNOBS->DD_MERGE_COALESCE_DELAY + std::max(lastLowBandwidthStartTime - now(), 0.0),
|
||||
TaskPriority::DataDistributionLow) ||
|
||||
cleared.getFuture();
|
||||
}
|
||||
return trigger;
|
||||
}
|
||||
|
|
@ -558,6 +595,8 @@ Future<Void> shardMerger(
|
|||
shardsMerged++;
|
||||
|
||||
auto shardBounds = getShardSizeBounds( merged, maxShardSize );
|
||||
// If we just recently get the current shard's metrics (i.e., less than DD_LOW_BANDWIDTH_DELAY ago), it means
|
||||
// the shard's metric may not be stable yet. So we cannot continue merging in this direction.
|
||||
if( endingStats.bytes >= shardBounds.min.bytes ||
|
||||
getBandwidthStatus( endingStats ) != BandwidthStatusLow ||
|
||||
now() - lastLowBandwidthStartTime < SERVER_KNOBS->DD_LOW_BANDWIDTH_DELAY ||
|
||||
|
|
@ -588,13 +627,21 @@ Future<Void> shardMerger(
|
|||
//restarting shard tracker will derefenced values in the shard map, so make a copy
|
||||
KeyRange mergeRange = merged;
|
||||
|
||||
// OldKeys: Shards in the key range are merged as one shard defined by NewKeys;
|
||||
// NewKeys: New key range after shards are merged;
|
||||
// EndingSize: The new merged shard size in bytes;
|
||||
// BatchedMerges: The number of shards merged. Each shard is defined in self->shards;
|
||||
// LastLowBandwidthStartTime: When does a shard's bandwidth status becomes BandwidthStatusLow. If a shard's status
|
||||
// becomes BandwidthStatusLow less than DD_LOW_BANDWIDTH_DELAY ago, the merging logic will stop at the shard;
|
||||
// ShardCount: The number of non-splittable shards that are merged. Each shard is defined in self->shards may have
|
||||
// more than 1 shards.
|
||||
TraceEvent("RelocateShardMergeMetrics", self->distributorId)
|
||||
.detail("OldKeys", keys)
|
||||
.detail("NewKeys", mergeRange)
|
||||
.detail("EndingSize", endingStats.bytes)
|
||||
.detail("BatchedMerges", shardsMerged)
|
||||
.detail("LastLowBandwidthStartTime", lastLowBandwidthStartTime)
|
||||
.detail("ShardCount", shardCount);
|
||||
.detail("OldKeys", keys)
|
||||
.detail("NewKeys", mergeRange)
|
||||
.detail("EndingSize", endingStats.bytes)
|
||||
.detail("BatchedMerges", shardsMerged)
|
||||
.detail("LastLowBandwidthStartTime", lastLowBandwidthStartTime)
|
||||
.detail("ShardCount", shardCount);
|
||||
|
||||
if(mergeRange.begin < systemKeys.begin) {
|
||||
self->systemSizeEstimate -= systemBytes;
|
||||
|
|
@ -629,7 +676,7 @@ ACTOR Future<Void> shardEvaluator(
|
|||
|
||||
// Every invocation must set this or clear it
|
||||
if(shouldMerge && !self->anyZeroHealthyTeams->get()) {
|
||||
auto whenLongEnough = wantsToMerge->set();
|
||||
auto whenLongEnough = wantsToMerge->set(shardSize->get().get().lastLowBandwidthStartTime);
|
||||
if( !wantsToMerge->hasBeenTrueForLongEnough() ) {
|
||||
onChange = onChange || whenLongEnough;
|
||||
}
|
||||
|
|
@ -664,18 +711,14 @@ ACTOR Future<Void> shardEvaluator(
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> shardTracker(
|
||||
DataDistributionTracker* self,
|
||||
KeyRange keys,
|
||||
Reference<AsyncVar<Optional<ShardMetrics>>> shardSize)
|
||||
{
|
||||
wait( yieldedFuture(self->readyToStart.getFuture()) );
|
||||
ACTOR Future<Void> shardTracker(DataDistributionTracker::SafeAccessor self, KeyRange keys,
|
||||
Reference<AsyncVar<Optional<ShardMetrics>>> shardSize) {
|
||||
wait(yieldedFuture(self()->readyToStart.getFuture()));
|
||||
|
||||
if( !shardSize->get().present() )
|
||||
wait( shardSize->onChange() );
|
||||
|
||||
if( !self->maxShardSize->get().present() )
|
||||
wait( yieldedFuture(self->maxShardSize->onChange()) );
|
||||
if (!self()->maxShardSize->get().present()) wait(yieldedFuture(self()->maxShardSize->onChange()));
|
||||
|
||||
// Since maxShardSize will become present for all shards at once, avoid slow tasks with a short delay
|
||||
wait( delay( 0, TaskPriority::DataDistribution ) );
|
||||
|
|
@ -683,26 +726,27 @@ ACTOR Future<Void> shardTracker(
|
|||
// Survives multiple calls to shardEvaluator and keeps merges from happening too quickly.
|
||||
state Reference<HasBeenTrueFor> wantsToMerge( new HasBeenTrueFor( shardSize->get() ) );
|
||||
|
||||
/*TraceEvent("ShardTracker", self->distributorId)
|
||||
.detail("Begin", keys.begin)
|
||||
.detail("End", keys.end)
|
||||
.detail("TrackerID", trackerID)
|
||||
.detail("MaxBytes", self->maxShardSize->get().get())
|
||||
.detail("ShardSize", shardSize->get().get().bytes)
|
||||
.detail("BytesPerKSec", shardSize->get().get().bytesPerKSecond);*/
|
||||
/*TraceEvent("ShardTracker", self()->distributorId)
|
||||
.detail("Begin", keys.begin)
|
||||
.detail("End", keys.end)
|
||||
.detail("TrackerID", trackerID)
|
||||
.detail("MaxBytes", self()->maxShardSize->get().get())
|
||||
.detail("ShardSize", shardSize->get().get().bytes)
|
||||
.detail("BytesPerKSec", shardSize->get().get().bytesPerKSecond);*/
|
||||
|
||||
try {
|
||||
loop {
|
||||
// Use the current known size to check for (and start) splits and merges.
|
||||
wait( shardEvaluator( self, keys, shardSize, wantsToMerge ) );
|
||||
wait(shardEvaluator(self(), keys, shardSize, wantsToMerge));
|
||||
|
||||
// We could have a lot of actors being released from the previous wait at the same time. Immediately calling
|
||||
// delay(0) mitigates the resulting SlowTask
|
||||
wait( delay(0, TaskPriority::DataDistribution) );
|
||||
}
|
||||
} catch (Error& e) {
|
||||
if (e.code() != error_code_actor_cancelled)
|
||||
self->output.sendError(e); // Propagate failure to dataDistributionTracker
|
||||
if (e.code() != error_code_actor_cancelled && e.code() != error_code_dd_tracker_cancelled) {
|
||||
self()->output.sendError(e); // Propagate failure to dataDistributionTracker
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
@ -733,8 +777,8 @@ void restartShardTrackers(DataDistributionTracker* self, KeyRangeRef keys, Optio
|
|||
|
||||
ShardTrackedData data;
|
||||
data.stats = shardMetrics;
|
||||
data.trackShard = shardTracker(self, ranges[i], shardMetrics);
|
||||
data.trackBytes = trackShardMetrics(self, ranges[i], shardMetrics);
|
||||
data.trackShard = shardTracker(DataDistributionTracker::SafeAccessor(self), ranges[i], shardMetrics);
|
||||
data.trackBytes = trackShardMetrics(DataDistributionTracker::SafeAccessor(self), ranges[i], shardMetrics);
|
||||
self->shards.insert( ranges[i], data );
|
||||
}
|
||||
}
|
||||
|
|
@ -857,9 +901,10 @@ ACTOR Future<Void> dataDistributionTracker(Reference<InitialDataDistribution> in
|
|||
PromiseStream<GetMetricsListRequest> getShardMetricsList,
|
||||
FutureStream<Promise<int64_t>> getAverageShardBytes,
|
||||
Promise<Void> readyToStart, Reference<AsyncVar<bool>> anyZeroHealthyTeams,
|
||||
UID distributorId, KeyRangeMap<ShardTrackedData>* shards) {
|
||||
UID distributorId, KeyRangeMap<ShardTrackedData>* shards,
|
||||
bool* trackerCancelled) {
|
||||
state DataDistributionTracker self(cx, distributorId, readyToStart, output, shardsAffectedByTeamFailure,
|
||||
anyZeroHealthyTeams, *shards);
|
||||
anyZeroHealthyTeams, *shards, *trackerCancelled);
|
||||
state Future<Void> loggingTrigger = Void();
|
||||
state Future<Void> readHotDetect = readHotDetector(&self);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#ifdef SSD_ROCKSDB_EXPERIMENTAL
|
||||
|
||||
#include <rocksdb/cache.h>
|
||||
#include <rocksdb/db.h>
|
||||
#include <rocksdb/filter_policy.h>
|
||||
#include <rocksdb/options.h>
|
||||
#include <rocksdb/slice_transform.h>
|
||||
#include <rocksdb/table.h>
|
||||
#include <rocksdb/utilities/table_properties_collectors.h>
|
||||
#include "flow/flow.h"
|
||||
#include "flow/IThreadPool.h"
|
||||
|
|
@ -27,6 +31,9 @@ rocksdb::ColumnFamilyOptions getCFOptions() {
|
|||
rocksdb::ColumnFamilyOptions options;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.OptimizeLevelStyleCompaction(SERVER_KNOBS->ROCKSDB_MEMTABLE_BYTES);
|
||||
if (SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS > 0) {
|
||||
options.periodic_compaction_seconds = SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS;
|
||||
}
|
||||
// Compact sstables when there's too much deleted stuff.
|
||||
options.table_properties_collector_factories = { rocksdb::NewCompactOnDeletionCollectorFactory(128, 1) };
|
||||
return options;
|
||||
|
|
@ -39,6 +46,45 @@ rocksdb::Options getOptions() {
|
|||
if (SERVER_KNOBS->ROCKSDB_BACKGROUND_PARALLELISM > 0) {
|
||||
options.IncreaseParallelism(SERVER_KNOBS->ROCKSDB_BACKGROUND_PARALLELISM);
|
||||
}
|
||||
|
||||
rocksdb::BlockBasedTableOptions bbOpts;
|
||||
// TODO: Add a knob for the block cache size. (Default is 8 MB)
|
||||
if (SERVER_KNOBS->ROCKSDB_PREFIX_LEN > 0) {
|
||||
// Prefix blooms are used during Seek.
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(SERVER_KNOBS->ROCKSDB_PREFIX_LEN));
|
||||
|
||||
// Also turn on bloom filters in the memtable.
|
||||
// TODO: Make a knob for this as well.
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
|
||||
// 5 -- Can be read by RocksDB's versions since 6.6.0. Full and partitioned
|
||||
// filters use a generally faster and more accurate Bloom filter
|
||||
// implementation, with a different schema.
|
||||
// https://github.com/facebook/rocksdb/blob/b77569f18bfc77fb1d8a0b3218f6ecf571bc4988/include/rocksdb/table.h#L391
|
||||
bbOpts.format_version = 5;
|
||||
|
||||
// Create and apply a bloom filter using the 10 bits
|
||||
// which should yield a ~1% false positive rate:
|
||||
// https://github.com/facebook/rocksdb/wiki/RocksDB-Bloom-Filter#full-filters-new-format
|
||||
bbOpts.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
|
||||
|
||||
// The whole key blooms are only used for point lookups.
|
||||
// https://github.com/facebook/rocksdb/wiki/RocksDB-Bloom-Filter#prefix-vs-whole-key
|
||||
bbOpts.whole_key_filtering = false;
|
||||
}
|
||||
|
||||
if (SERVER_KNOBS->ROCKSDB_BLOCK_CACHE_SIZE > 0) {
|
||||
bbOpts.block_cache = rocksdb::NewLRUCache(SERVER_KNOBS->ROCKSDB_BLOCK_CACHE_SIZE);
|
||||
}
|
||||
|
||||
options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(bbOpts));
|
||||
return options;
|
||||
}
|
||||
|
||||
// Set some useful defaults desired for all reads.
|
||||
rocksdb::ReadOptions getReadOptions() {
|
||||
rocksdb::ReadOptions options;
|
||||
options.background_purge_on_iterator_cleanup = true;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +98,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
|
||||
explicit Writer(DB& db, UID id) : db(db), id(id) {}
|
||||
|
||||
~Writer() {
|
||||
~Writer() override {
|
||||
if (db) {
|
||||
delete db;
|
||||
}
|
||||
|
|
@ -85,24 +131,49 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
TraceEvent(SevError, "RocksDBError").detail("Error", status.ToString()).detail("Method", "Open");
|
||||
a.done.sendError(statusToError(status));
|
||||
} else {
|
||||
TraceEvent(SevInfo, "RocksDB").detail("Path", a.path).detail("Method", "Open");
|
||||
a.done.send(Void());
|
||||
}
|
||||
}
|
||||
|
||||
struct DeleteVisitor : public rocksdb::WriteBatch::Handler {
|
||||
VectorRef<KeyRangeRef>& deletes;
|
||||
Arena& arena;
|
||||
|
||||
DeleteVisitor(VectorRef<KeyRangeRef>& deletes, Arena& arena) : deletes(deletes), arena(arena) {}
|
||||
|
||||
rocksdb::Status DeleteRangeCF(uint32_t /*column_family_id*/, const rocksdb::Slice& begin,
|
||||
const rocksdb::Slice& end) override {
|
||||
KeyRangeRef kr(toStringRef(begin), toStringRef(end));
|
||||
deletes.push_back_deep(arena, kr);
|
||||
return rocksdb::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
struct CommitAction : TypedAction<Writer, CommitAction> {
|
||||
std::unique_ptr<rocksdb::WriteBatch> batchToCommit;
|
||||
ThreadReturnPromise<Void> done;
|
||||
double getTimeEstimate() override { return SERVER_KNOBS->COMMIT_TIME_ESTIMATE; }
|
||||
};
|
||||
void action(CommitAction& a) {
|
||||
Standalone<VectorRef<KeyRangeRef>> deletes;
|
||||
DeleteVisitor dv(deletes, deletes.arena());
|
||||
ASSERT(a.batchToCommit->Iterate(&dv).ok());
|
||||
// If there are any range deletes, we should have added them to be deleted.
|
||||
ASSERT(!deletes.empty() || !a.batchToCommit->HasDeleteRange());
|
||||
rocksdb::WriteOptions options;
|
||||
options.sync = true;
|
||||
options.sync = !SERVER_KNOBS->ROCKSDB_UNSAFE_AUTO_FSYNC;
|
||||
auto s = db->Write(options, a.batchToCommit.get());
|
||||
if (!s.ok()) {
|
||||
TraceEvent(SevError, "RocksDBError").detail("Error", s.ToString()).detail("Method", "Commit");
|
||||
a.done.sendError(statusToError(s));
|
||||
} else {
|
||||
a.done.send(Void());
|
||||
for (const auto& keyRange : deletes) {
|
||||
auto begin = toSlice(keyRange.begin);
|
||||
auto end = toSlice(keyRange.end);
|
||||
ASSERT(db->SuggestCompactRange(db->DefaultColumnFamily(), &begin, &end).ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +185,10 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
double getTimeEstimate() override { return SERVER_KNOBS->COMMIT_TIME_ESTIMATE; }
|
||||
};
|
||||
void action(CloseAction& a) {
|
||||
if (db == nullptr) {
|
||||
a.done.send(Void());
|
||||
return;
|
||||
}
|
||||
auto s = db->Close();
|
||||
if (!s.ok()) {
|
||||
TraceEvent(SevError, "RocksDBError").detail("Error", s.ToString()).detail("Method", "Close");
|
||||
|
|
@ -121,8 +196,14 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
if (a.deleteOnClose) {
|
||||
std::vector<rocksdb::ColumnFamilyDescriptor> defaultCF = { rocksdb::ColumnFamilyDescriptor{
|
||||
"default", getCFOptions() } };
|
||||
rocksdb::DestroyDB(a.path, getOptions(), defaultCF);
|
||||
s = rocksdb::DestroyDB(a.path, getOptions(), defaultCF);
|
||||
if (!s.ok()) {
|
||||
TraceEvent(SevError, "RocksDBError").detail("Error", s.ToString()).detail("Method", "Destroy");
|
||||
} else {
|
||||
TraceEvent(SevInfo, "RocksDB").detail("Path", a.path).detail("Method", "Destroy");
|
||||
}
|
||||
}
|
||||
TraceEvent(SevInfo, "RocksDB").detail("Path", a.path).detail("Method", "Close");
|
||||
a.done.send(Void());
|
||||
}
|
||||
};
|
||||
|
|
@ -150,7 +231,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
traceBatch.get().addEvent("GetValueDebug", a.debugID.get().first(), "Reader.Before");
|
||||
}
|
||||
rocksdb::PinnableSlice value;
|
||||
auto s = db->Get({}, db->DefaultColumnFamily(), toSlice(a.key), &value);
|
||||
auto s = db->Get(getReadOptions(), db->DefaultColumnFamily(), toSlice(a.key), &value);
|
||||
if (a.debugID.present()) {
|
||||
traceBatch.get().addEvent("GetValueDebug", a.debugID.get().first(), "Reader.After");
|
||||
traceBatch.get().dump();
|
||||
|
|
@ -181,7 +262,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
traceBatch.get().addEvent("GetValuePrefixDebug", a.debugID.get().first(),
|
||||
"Reader.Before"); //.detail("TaskID", g_network->getCurrentTask());
|
||||
}
|
||||
auto s = db->Get({}, db->DefaultColumnFamily(), toSlice(a.key), &value);
|
||||
auto s = db->Get(getReadOptions(), db->DefaultColumnFamily(), toSlice(a.key), &value);
|
||||
if (a.debugID.present()) {
|
||||
traceBatch.get().addEvent("GetValuePrefixDebug", a.debugID.get().first(),
|
||||
"Reader.After"); //.detail("TaskID", g_network->getCurrentTask());
|
||||
|
|
@ -210,8 +291,11 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
}
|
||||
int accumulatedBytes = 0;
|
||||
rocksdb::Status s;
|
||||
auto options = getReadOptions();
|
||||
// When using a prefix extractor, ensure that keys are returned in order even if they cross
|
||||
// a prefix boundary.
|
||||
options.auto_prefix_mode = (SERVER_KNOBS->ROCKSDB_PREFIX_LEN > 0);
|
||||
if (a.rowLimit >= 0) {
|
||||
rocksdb::ReadOptions options;
|
||||
auto endSlice = toSlice(a.keys.end);
|
||||
options.iterate_upper_bound = &endSlice;
|
||||
auto cursor = std::unique_ptr<rocksdb::Iterator>(db->NewIterator(options));
|
||||
|
|
@ -228,7 +312,6 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
}
|
||||
s = cursor->status();
|
||||
} else {
|
||||
rocksdb::ReadOptions options;
|
||||
auto beginSlice = toSlice(a.keys.begin);
|
||||
options.iterate_lower_bound = &beginSlice;
|
||||
auto cursor = std::unique_ptr<rocksdb::Iterator>(db->NewIterator(options));
|
||||
|
|
@ -266,7 +349,6 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
UID id;
|
||||
Reference<IThreadPool> writeThread;
|
||||
Reference<IThreadPool> readThreads;
|
||||
unsigned nReaders = 16;
|
||||
Promise<Void> errorPromise;
|
||||
Promise<Void> closePromise;
|
||||
std::unique_ptr<rocksdb::WriteBatch> writeBatch;
|
||||
|
|
@ -278,7 +360,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
writeThread = createGenericThreadPool();
|
||||
readThreads = createGenericThreadPool();
|
||||
writeThread->addThread(new Writer(db, id));
|
||||
for (unsigned i = 0; i < nReaders; ++i) {
|
||||
for (unsigned i = 0; i < SERVER_KNOBS->ROCKSDB_READ_PARALLELISM; ++i) {
|
||||
readThreads->addThread(new Reader(db));
|
||||
}
|
||||
}
|
||||
|
|
@ -372,16 +454,14 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
}
|
||||
|
||||
StorageBytes getStorageBytes() override {
|
||||
uint64_t live = 0;
|
||||
ASSERT(db->GetIntProperty(rocksdb::DB::Properties::kEstimateLiveDataSize, &live));
|
||||
|
||||
int64_t free;
|
||||
int64_t total;
|
||||
|
||||
uint64_t sstBytes = 0;
|
||||
ASSERT(db->GetIntProperty(rocksdb::DB::Properties::kTotalSstFilesSize, &sstBytes));
|
||||
uint64_t memtableBytes = 0;
|
||||
ASSERT(db->GetIntProperty(rocksdb::DB::Properties::kSizeAllMemTables, &memtableBytes));
|
||||
g_network->getDiskBytes(path, free, total);
|
||||
|
||||
return StorageBytes(free, total, sstBytes + memtableBytes, free);
|
||||
return StorageBytes(free, total, live, free);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@
|
|||
#include "fdbserver/CoroFlow.h"
|
||||
#include "fdbserver/Knobs.h"
|
||||
#include "flow/Hash3.h"
|
||||
#include "flow/xxhash.h"
|
||||
|
||||
extern "C" {
|
||||
#include "fdbserver/sqlite/sqliteInt.h"
|
||||
u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
|
||||
}
|
||||
#include "flow/ThreadPrimitives.h"
|
||||
#include "fdbserver/VFSAsync.h"
|
||||
#include "fdbserver/template_fdb.h"
|
||||
#include "fdbrpc/simulator.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
|
@ -100,34 +102,58 @@ struct PageChecksumCodec {
|
|||
return true;
|
||||
}
|
||||
|
||||
SumType sum;
|
||||
SumType crc32Sum;
|
||||
if (pSumInPage->part1 == 0) {
|
||||
// part1 being 0 indicates with high probability that a CRC32 checksum
|
||||
// part1 being 0 indicates with very high probability that a CRC32 checksum
|
||||
// was used, so check that first. If this checksum fails, there is still
|
||||
// some chance the page was written with hashlittle2, so fall back to checking
|
||||
// hashlittle2
|
||||
sum.part1 = 0;
|
||||
sum.part2 = crc32c_append(0xfdbeefdb, static_cast<uint8_t*>(data), dataLen);
|
||||
if (sum == *pSumInPage) return true;
|
||||
// some chance the page was written with another checksum algorithm
|
||||
crc32Sum.part1 = 0;
|
||||
crc32Sum.part2 = crc32c_append(0xfdbeefdb, static_cast<uint8_t*>(data), dataLen);
|
||||
if (crc32Sum == *pSumInPage) return true;
|
||||
}
|
||||
|
||||
// Try xxhash3
|
||||
SumType xxHash3Sum;
|
||||
if ((pSumInPage->part1 >> 24) == 0) {
|
||||
// The first 8 bits of part1 being 0 indicates with high probability that an
|
||||
// xxHash3 checksum was used, so check that next. If this checksum fails, there is
|
||||
// still some chance the page was written with hashlittle2, so fall back to checking
|
||||
// hashlittle2
|
||||
auto xxHash3 = XXH3_64bits(data, dataLen);
|
||||
xxHash3Sum.part1 = static_cast<uint32_t>((xxHash3 >> 32) & 0x00ffffff);
|
||||
xxHash3Sum.part2 = static_cast<uint32_t>(xxHash3 & 0xffffffff);
|
||||
if (xxHash3Sum == *pSumInPage) {
|
||||
TEST(true); // Read xxHash3 checksum
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try hashlittle2
|
||||
SumType hashLittle2Sum;
|
||||
hashLittle2Sum.part1 = pageNumber; // DO NOT CHANGE
|
||||
hashLittle2Sum.part2 = 0x5ca1ab1e;
|
||||
hashlittle2(pData, dataLen, &hashLittle2Sum.part1, &hashLittle2Sum.part2);
|
||||
if (hashLittle2Sum == *pSumInPage) return true;
|
||||
if (hashLittle2Sum == *pSumInPage) {
|
||||
TEST(true); // Read HashLittle2 checksum
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
TraceEvent trEvent(SevError, "SQLitePageChecksumFailure");
|
||||
trEvent.error(checksum_failed())
|
||||
.detail("CodecPageSize", pageSize)
|
||||
.detail("CodecReserveSize", reserveSize)
|
||||
.detail("Filename", filename)
|
||||
.detail("PageNumber", pageNumber)
|
||||
.detail("PageSize", pageLen)
|
||||
.detail("ChecksumInPage", pSumInPage->toString())
|
||||
.detail("ChecksumCalculatedHL2", hashLittle2Sum.toString());
|
||||
if (pSumInPage->part1 == 0) trEvent.detail("ChecksumCalculatedCRC", sum.toString());
|
||||
.detail("CodecPageSize", pageSize)
|
||||
.detail("CodecReserveSize", reserveSize)
|
||||
.detail("Filename", filename)
|
||||
.detail("PageNumber", pageNumber)
|
||||
.detail("PageSize", pageLen)
|
||||
.detail("ChecksumInPage", pSumInPage->toString())
|
||||
.detail("ChecksumCalculatedHL2", hashLittle2Sum.toString());
|
||||
if (pSumInPage->part1 == 0) {
|
||||
trEvent.detail("ChecksumCalculatedCRC", crc32Sum.toString());
|
||||
}
|
||||
if (pSumInPage->part1 >> 24 == 0) {
|
||||
trEvent.detail("ChecksumCalculatedXXHash3", xxHash3Sum.toString());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -211,9 +237,19 @@ struct SQLiteDB : NonCopyable {
|
|||
void open(bool writable);
|
||||
void createFromScratch();
|
||||
|
||||
SQLiteDB( std::string filename, bool page_checksums, bool fragment_values): filename(filename), db(NULL), btree(NULL), table(-1), freetable(-1), haveMutex(false), page_checksums(page_checksums), fragment_values(fragment_values) {}
|
||||
SQLiteDB( std::string filename, bool page_checksums, bool fragment_values): filename(filename), db(NULL), btree(NULL), table(-1), freetable(-1), haveMutex(false), page_checksums(page_checksums), fragment_values(fragment_values) {
|
||||
TraceEvent(SevDebug, "SQLiteDBCreate")
|
||||
.detail("This", (void*)this)
|
||||
.detail("Filename", filename)
|
||||
.backtrace();
|
||||
}
|
||||
|
||||
~SQLiteDB() {
|
||||
TraceEvent(SevDebug, "SQLiteDBDestroy")
|
||||
.detail("This", (void*)this)
|
||||
.detail("Filename", filename)
|
||||
.backtrace();
|
||||
|
||||
if (db) {
|
||||
if (haveMutex) {
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
|
|
@ -239,10 +275,11 @@ struct SQLiteDB : NonCopyable {
|
|||
//if (deterministicRandom()->random01() < .001) rc = SQLITE_INTERRUPT;
|
||||
if (rc) {
|
||||
// Our exceptions don't propagate through sqlite, so we don't know for sure if the error that caused this was
|
||||
// an injected fault. Assume that if fault injection is happening, this is an injected fault.
|
||||
// an injected fault. Assume that if VFSAsyncFile caught an injected Error that it caused this error return code.
|
||||
Error err = io_error();
|
||||
if (g_network->isSimulated() && (g_simulator.getCurrentProcess()->fault_injection_p1 || g_simulator.getCurrentProcess()->machine->machineProcess->fault_injection_p1 || g_simulator.getCurrentProcess()->rebooting))
|
||||
if (g_network->isSimulated() && VFSAsyncFile::checkInjectedError()) {
|
||||
err = err.asInjectedFault();
|
||||
}
|
||||
|
||||
if (db)
|
||||
db->errCode = rc;
|
||||
|
|
@ -252,6 +289,7 @@ struct SQLiteDB : NonCopyable {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
void checkpoint( bool restart ) {
|
||||
int logSize=0, checkpointCount=0;
|
||||
//double t = timer();
|
||||
|
|
@ -282,7 +320,7 @@ struct SQLiteDB : NonCopyable {
|
|||
int tables[] = {1, table, freetable};
|
||||
TraceEvent("BTreeIntegrityCheckBegin").detail("Filename", filename);
|
||||
char* e = sqlite3BtreeIntegrityCheck(btree, tables, 3, 1000, &errors, verbose);
|
||||
if (!(g_network->isSimulated() && (g_simulator.getCurrentProcess()->fault_injection_p1 || g_simulator.getCurrentProcess()->rebooting))) {
|
||||
if (!(g_network->isSimulated() && VFSAsyncFile::checkInjectedError())) {
|
||||
TraceEvent((errors||e) ? SevError : SevInfo, "BTreeIntegrityCheckResults").detail("Filename", filename).detail("ErrorTotal", errors);
|
||||
if(e != nullptr) {
|
||||
// e is a string containing 1 or more lines. Create a separate trace event for each line.
|
||||
|
|
@ -1296,6 +1334,7 @@ int SQLiteDB::checkAllPageChecksums() {
|
|||
.detail("TotalErrors", totalErrors);
|
||||
|
||||
ASSERT(!vfsAsyncIsOpen(filename));
|
||||
ASSERT(!vfsAsyncIsOpen(filename + "-wal"));
|
||||
|
||||
return totalErrors;
|
||||
}
|
||||
|
|
@ -1346,6 +1385,22 @@ void SQLiteDB::open(bool writable) {
|
|||
if (dbFile.isError()) throw dbFile.getError(); // If we've failed to open the file, throw an exception
|
||||
if (walFile.isError()) throw walFile.getError(); // If we've failed to open the file, throw an exception
|
||||
|
||||
// Set Rate control if SERVER_KNOBS are positive
|
||||
if (SERVER_KNOBS->SQLITE_WRITE_WINDOW_LIMIT > 0 && SERVER_KNOBS->SQLITE_WRITE_WINDOW_SECONDS > 0) {
|
||||
// The writer thread is created before the readers, so it should initialize the rate controls.
|
||||
if(writable) {
|
||||
// Create a new rate control and assign it to both files.
|
||||
Reference<SpeedLimit> rc(
|
||||
new SpeedLimit(SERVER_KNOBS->SQLITE_WRITE_WINDOW_LIMIT, SERVER_KNOBS->SQLITE_WRITE_WINDOW_SECONDS));
|
||||
dbFile.get()->setRateControl(rc);
|
||||
walFile.get()->setRateControl(rc);
|
||||
} else {
|
||||
// When a reader thread is opened, the rate controls should already be equal and not null
|
||||
ASSERT(dbFile.get()->getRateControl() == walFile.get()->getRateControl());
|
||||
ASSERT(dbFile.get()->getRateControl());
|
||||
}
|
||||
}
|
||||
|
||||
//TraceEvent("KVThreadInitStage").detail("Stage",2).detail("Filename", filename).detail("Writable", writable);
|
||||
|
||||
// Now that the file itself is open and locked, let sqlite open the database
|
||||
|
|
@ -1493,6 +1548,7 @@ private:
|
|||
volatile int64_t freeListPages;
|
||||
|
||||
vector< Reference<ReadCursor> > readCursors;
|
||||
Reference<IAsyncFile> dbFile, walFile;
|
||||
|
||||
struct Reader : IThreadPoolReceiver {
|
||||
SQLiteDB conn;
|
||||
|
|
@ -1574,6 +1630,7 @@ private:
|
|||
};
|
||||
|
||||
struct Writer : IThreadPoolReceiver {
|
||||
KeyValueStoreSQLite *kvs;
|
||||
SQLiteDB conn;
|
||||
Cursor* cursor;
|
||||
int commits;
|
||||
|
|
@ -1588,8 +1645,9 @@ private:
|
|||
bool checkAllChecksumsOnOpen;
|
||||
bool checkIntegrityOnOpen;
|
||||
|
||||
explicit Writer( std::string const& filename, bool isBtreeV2, bool checkAllChecksumsOnOpen, bool checkIntegrityOnOpen, volatile int64_t& writesComplete, volatile SpringCleaningStats& springCleaningStats, volatile int64_t& diskBytesUsed, volatile int64_t& freeListPages, UID dbgid, vector<Reference<ReadCursor>>* pReadThreads )
|
||||
: conn( filename, isBtreeV2, isBtreeV2 ),
|
||||
explicit Writer( KeyValueStoreSQLite *kvs, bool isBtreeV2, bool checkAllChecksumsOnOpen, bool checkIntegrityOnOpen, volatile int64_t& writesComplete, volatile SpringCleaningStats& springCleaningStats, volatile int64_t& diskBytesUsed, volatile int64_t& freeListPages, UID dbgid, vector<Reference<ReadCursor>>* pReadThreads )
|
||||
: kvs(kvs),
|
||||
conn( kvs->filename, isBtreeV2, isBtreeV2 ),
|
||||
commits(), setsThisCommit(),
|
||||
freeTableEmpty(false),
|
||||
writesComplete(writesComplete),
|
||||
|
|
@ -1619,6 +1677,8 @@ private:
|
|||
}
|
||||
}
|
||||
conn.open(true);
|
||||
kvs->dbFile = conn.dbFile;
|
||||
kvs->walFile = conn.walFile;
|
||||
|
||||
//If a wal file fails during the commit process before finishing a checkpoint, then it is possible that our wal file will be non-empty
|
||||
//when we reload it. We execute a checkpoint here to remedy that situation. This call must come before before creating a cursor because
|
||||
|
|
@ -1630,7 +1690,7 @@ private:
|
|||
if (checkIntegrityOnOpen || EXPENSIVE_VALIDATION) {
|
||||
if(conn.check(false) != 0) {
|
||||
// A corrupt btree structure must not be used.
|
||||
if (g_network->isSimulated() && (g_simulator.getCurrentProcess()->fault_injection_p1 || g_simulator.getCurrentProcess()->machine->machineProcess->fault_injection_p1 || g_simulator.getCurrentProcess()->rebooting)) {
|
||||
if (g_network->isSimulated() && VFSAsyncFile::checkInjectedError()) {
|
||||
throw file_corrupt().asInjectedFault();
|
||||
} else {
|
||||
throw file_corrupt();
|
||||
|
|
@ -1855,6 +1915,24 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void disableRateControl() {
|
||||
if(dbFile && dbFile->getRateControl()) {
|
||||
TraceEvent(SevDebug, "KeyValueStoreSQLiteShutdownRateControl").detail("Filename", dbFile->getFilename());
|
||||
Reference<IRateControl> rc = dbFile->getRateControl();
|
||||
dbFile->setRateControl({});
|
||||
rc->wakeWaiters();
|
||||
}
|
||||
dbFile.clear();
|
||||
|
||||
if(walFile && walFile->getRateControl()) {
|
||||
TraceEvent(SevDebug, "KeyValueStoreSQLiteShutdownRateControl").detail("Filename", walFile->getFilename());
|
||||
Reference<IRateControl> rc = walFile->getRateControl();
|
||||
walFile->setRateControl({});
|
||||
rc->wakeWaiters();
|
||||
}
|
||||
walFile.clear();
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> stopOnError( KeyValueStoreSQLite* self ) {
|
||||
try {
|
||||
wait( self->readThreads->getError() || self->writeThread->getError() );
|
||||
|
|
@ -1863,8 +1941,9 @@ private:
|
|||
if (e.code() == error_code_actor_cancelled)
|
||||
throw;
|
||||
|
||||
self->readThreads->stop(e);
|
||||
self->writeThread->stop(e);
|
||||
self->disableRateControl();
|
||||
self->readThreads->stop(e).isReady();
|
||||
self->writeThread->stop(e).isReady();
|
||||
}
|
||||
|
||||
return Void();
|
||||
|
|
@ -1872,8 +1951,13 @@ private:
|
|||
|
||||
ACTOR static void doClose( KeyValueStoreSQLite* self, bool deleteOnClose ) {
|
||||
state Error error = success();
|
||||
|
||||
self->disableRateControl();
|
||||
|
||||
try {
|
||||
TraceEvent("KVClose", self->logID).detail("Del", deleteOnClose);
|
||||
TraceEvent("KVClose", self->logID)
|
||||
.detail("Filename", self->filename)
|
||||
.detail("Del", deleteOnClose);
|
||||
self->starting.cancel();
|
||||
self->cleaning.cancel();
|
||||
self->logging.cancel();
|
||||
|
|
@ -1884,12 +1968,14 @@ private:
|
|||
}
|
||||
} catch (Error& e) {
|
||||
TraceEvent(SevError, "KVDoCloseError", self->logID)
|
||||
.error(e,true)
|
||||
.detail("Filename", self->filename)
|
||||
.error(e, true)
|
||||
.detail("Reason", e.code() == error_code_platform_error ? "could not delete database" : "unknown");
|
||||
error = e;
|
||||
}
|
||||
|
||||
TraceEvent("KVClosed", self->logID);
|
||||
TraceEvent("KVClosed", self->logID)
|
||||
.detail("Filename", self->filename);
|
||||
if( error.code() != error_code_actor_cancelled ) {
|
||||
self->stopped.send(Void());
|
||||
delete self;
|
||||
|
|
@ -1937,6 +2023,9 @@ KeyValueStoreSQLite::KeyValueStoreSQLite(std::string const& filename, UID id, Ke
|
|||
writeThread(CoroThreadPool::createThreadPool()),
|
||||
readsRequested(0), writesRequested(0), writesComplete(0), diskBytesUsed(0), freeListPages(0)
|
||||
{
|
||||
TraceEvent(SevDebug, "KeyValueStoreSQLiteCreate")
|
||||
.detail("Filename", filename);
|
||||
|
||||
stopOnErr = stopOnError(this);
|
||||
|
||||
#if SQLITE_THREADSAFE == 0
|
||||
|
|
@ -1949,13 +2038,14 @@ KeyValueStoreSQLite::KeyValueStoreSQLite(std::string const& filename, UID id, Ke
|
|||
|
||||
//The DB file should not already be open
|
||||
ASSERT(!vfsAsyncIsOpen(filename));
|
||||
ASSERT(!vfsAsyncIsOpen(filename + "-wal"));
|
||||
|
||||
readCursors.resize(64); //< number of read threads
|
||||
readCursors.resize(SERVER_KNOBS->SQLITE_READER_THREADS); //< number of read threads
|
||||
|
||||
sqlite3_soft_heap_limit64( SERVER_KNOBS->SOFT_HEAP_LIMIT ); // SOMEDAY: Is this a performance issue? Should we drop the cache sizes for individual threads?
|
||||
TaskPriority taskId = g_network->getCurrentTask();
|
||||
g_network->setCurrentTask(TaskPriority::DiskWrite);
|
||||
writeThread->addThread( new Writer(filename, type==KeyValueStoreType::SSD_BTREE_V2, checkChecksums, checkIntegrity, writesComplete, springCleaningStats, diskBytesUsed, freeListPages, id, &readCursors) );
|
||||
writeThread->addThread(new Writer(this, type==KeyValueStoreType::SSD_BTREE_V2, checkChecksums, checkIntegrity, writesComplete, springCleaningStats, diskBytesUsed, freeListPages, id, &readCursors));
|
||||
g_network->setCurrentTask(taskId);
|
||||
auto p = new Writer::InitAction();
|
||||
auto f = p->result.getFuture();
|
||||
|
|
|
|||
|
|
@ -98,9 +98,10 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( PEEK_STATS_SLOW_RATIO, 0.5 );
|
||||
init( PUSH_RESET_INTERVAL, 300.0 ); if ( randomize && BUGGIFY ) PUSH_RESET_INTERVAL = 20.0;
|
||||
init( PUSH_MAX_LATENCY, 0.5 ); if ( randomize && BUGGIFY ) PUSH_MAX_LATENCY = 0.0;
|
||||
init( PUSH_STATS_INTERVAL, 10.0 );
|
||||
init( PUSH_STATS_INTERVAL, 10.0 );
|
||||
init( PUSH_STATS_SLOW_AMOUNT, 2 );
|
||||
init( PUSH_STATS_SLOW_RATIO, 0.5 );
|
||||
init( TLOG_POP_BATCH_SIZE, 1000 ); if ( randomize && BUGGIFY ) TLOG_POP_BATCH_SIZE = 10;
|
||||
|
||||
// disk snapshot max timeout, to be put in TLog, storage and coordinator nodes
|
||||
init( SNAP_CREATE_MAX_TIMEOUT, 300.0 );
|
||||
|
|
@ -246,6 +247,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( DD_SS_STUCK_TIME_LIMIT, 300.0 ); if( randomize && BUGGIFY ) { DD_SS_STUCK_TIME_LIMIT = 200.0 + deterministicRandom()->random01() * 100.0; }
|
||||
init( DD_TEAMS_INFO_PRINT_INTERVAL, 60 ); if( randomize && BUGGIFY ) DD_TEAMS_INFO_PRINT_INTERVAL = 10;
|
||||
init( DD_TEAMS_INFO_PRINT_YIELD_COUNT, 100 ); if( randomize && BUGGIFY ) DD_TEAMS_INFO_PRINT_YIELD_COUNT = deterministicRandom()->random01() * 1000 + 1;
|
||||
init( DD_TEAM_ZERO_SERVER_LEFT_LOG_DELAY, 120 ); if( randomize && BUGGIFY ) DD_TEAM_ZERO_SERVER_LEFT_LOG_DELAY = 5;
|
||||
|
||||
// TeamRemover
|
||||
init( TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER, false ); if( randomize && BUGGIFY ) TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER = deterministicRandom()->random01() < 0.1 ? true : false; // false by default. disable the consistency check when it's true
|
||||
|
|
@ -276,6 +278,17 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( SQLITE_BTREE_PAGE_USABLE, 4096 - 8); // pageSize - reserveSize for page checksum
|
||||
init( SQLITE_CHUNK_SIZE_PAGES, 25600 ); // 100MB
|
||||
init( SQLITE_CHUNK_SIZE_PAGES_SIM, 1024 ); // 4MB
|
||||
init( SQLITE_READER_THREADS, 64 ); // number of read threads
|
||||
init( SQLITE_WRITE_WINDOW_SECONDS, -1 );
|
||||
init( SQLITE_WRITE_WINDOW_LIMIT, -1 );
|
||||
if( randomize && BUGGIFY ) {
|
||||
// Choose an window between .01 and 1.01 seconds.
|
||||
SQLITE_WRITE_WINDOW_SECONDS = 0.01 + deterministicRandom()->random01();
|
||||
// Choose random operations per second
|
||||
int opsPerSecond = deterministicRandom()->randomInt(1000, 5000);
|
||||
// Set window limit to opsPerSecond scaled down to window size
|
||||
SQLITE_WRITE_WINDOW_LIMIT = opsPerSecond * SQLITE_WRITE_WINDOW_SECONDS;
|
||||
}
|
||||
|
||||
// Maximum and minimum cell payload bytes allowed on primary page as calculated in SQLite.
|
||||
// These formulas are copied from SQLite, using its hardcoded constants, so if you are
|
||||
|
|
@ -317,7 +330,12 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
|
||||
// KeyValueStoreRocksDB
|
||||
init( ROCKSDB_BACKGROUND_PARALLELISM, 0 );
|
||||
init( ROCKSDB_READ_PARALLELISM, 4 );
|
||||
init( ROCKSDB_MEMTABLE_BYTES, 512 * 1024 * 1024 );
|
||||
init( ROCKSDB_UNSAFE_AUTO_FSYNC, false );
|
||||
init( ROCKSDB_PERIODIC_COMPACTION_SECONDS, 0 );
|
||||
init( ROCKSDB_PREFIX_LEN, 0 );
|
||||
init( ROCKSDB_BLOCK_CACHE_SIZE, 0 );
|
||||
|
||||
// Leader election
|
||||
bool longLeaderElection = randomize && BUGGIFY;
|
||||
|
|
@ -376,6 +394,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( PROXY_COMPUTE_BUCKETS, 20000 );
|
||||
init( PROXY_COMPUTE_GROWTH_RATE, 0.01 );
|
||||
init( TXN_STATE_SEND_AMOUNT, 4 );
|
||||
init( PROXY_REJECT_BATCH_QUEUED_TOO_LONG, true );
|
||||
|
||||
init( RESET_MASTER_BATCHES, 200 );
|
||||
init( RESET_RESOLVER_BATCHES, 200 );
|
||||
|
|
@ -486,7 +505,11 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( SPRING_BYTES_STORAGE_SERVER_BATCH, 100e6 ); if( smallStorageTarget ) SPRING_BYTES_STORAGE_SERVER_BATCH = 150e3;
|
||||
init( STORAGE_HARD_LIMIT_BYTES, 1500e6 ); if( smallStorageTarget ) STORAGE_HARD_LIMIT_BYTES = 4500e3;
|
||||
init( STORAGE_DURABILITY_LAG_HARD_MAX, 2000e6 ); if( smallStorageTarget ) STORAGE_DURABILITY_LAG_HARD_MAX = 100e6;
|
||||
init( STORAGE_DURABILITY_LAG_SOFT_MAX, 200e6 ); if( smallStorageTarget ) STORAGE_DURABILITY_LAG_SOFT_MAX = 10e6;
|
||||
init( STORAGE_DURABILITY_LAG_SOFT_MAX, 250e6 ); if( smallStorageTarget ) STORAGE_DURABILITY_LAG_SOFT_MAX = 10e6;
|
||||
|
||||
//FIXME: Low priority reads are disabled by assigning very high knob values, reduce knobs for 7.0
|
||||
init( LOW_PRIORITY_STORAGE_QUEUE_BYTES, 775e8 ); if( smallStorageTarget ) LOW_PRIORITY_STORAGE_QUEUE_BYTES = 1750e3;
|
||||
init( LOW_PRIORITY_DURABILITY_LAG, 200e6 ); if( smallStorageTarget ) LOW_PRIORITY_DURABILITY_LAG = 15e6;
|
||||
|
||||
bool smallTlogTarget = randomize && BUGGIFY;
|
||||
init( TARGET_BYTES_PER_TLOG, 2400e6 ); if( smallTlogTarget ) TARGET_BYTES_PER_TLOG = 2000e3;
|
||||
|
|
@ -513,7 +536,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( NEEDED_TPS_HISTORY_SAMPLES, 200 );
|
||||
init( TARGET_DURABILITY_LAG_VERSIONS, 350e6 ); // Should be larger than STORAGE_DURABILITY_LAG_SOFT_MAX
|
||||
init( AUTO_TAG_THROTTLE_DURABILITY_LAG_VERSIONS, 250e6 );
|
||||
init( TARGET_DURABILITY_LAG_VERSIONS_BATCH, 250e6 ); // Should be larger than STORAGE_DURABILITY_LAG_SOFT_MAX
|
||||
init( TARGET_DURABILITY_LAG_VERSIONS_BATCH, 150e6 ); // Should be larger than STORAGE_DURABILITY_LAG_SOFT_MAX
|
||||
init( DURABILITY_LAG_UNLIMITED_THRESHOLD, 50e6 );
|
||||
init( INITIAL_DURABILITY_LAG_MULTIPLIER, 1.02 );
|
||||
init( DURABILITY_LAG_REDUCTION_RATE, 0.9999 );
|
||||
|
|
@ -575,6 +598,10 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( READ_TAG_MEASUREMENT_INTERVAL, 30.0 ); if( randomize && BUGGIFY ) READ_TAG_MEASUREMENT_INTERVAL = 1.0;
|
||||
init( OPERATION_COST_BYTE_FACTOR, 16384 ); if( randomize && BUGGIFY ) OPERATION_COST_BYTE_FACTOR = 4096;
|
||||
init( PREFIX_COMPRESS_KVS_MEM_SNAPSHOTS, true ); if( randomize && BUGGIFY ) PREFIX_COMPRESS_KVS_MEM_SNAPSHOTS = false;
|
||||
init( REPORT_DD_METRICS, true );
|
||||
init( DD_METRICS_REPORT_INTERVAL, 30.0 );
|
||||
init( FETCH_KEYS_TOO_LONG_TIME_CRITERIA, 300.0 );
|
||||
init( MAX_STORAGE_COMMIT_TIME, 120.0 ); //The max fsync stall time on the storage server and tlog before marking a disk as failed
|
||||
|
||||
//Wait Failure
|
||||
init( MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS, 250 ); if( randomize && BUGGIFY ) MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS = 2;
|
||||
|
|
@ -607,6 +634,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi
|
|||
init( MAX_STATUS_REQUESTS_PER_SECOND, 256.0 );
|
||||
init( CONFIGURATION_ROWS_TO_FETCH, 20000 );
|
||||
init( DISABLE_DUPLICATE_LOG_WARNING, false );
|
||||
init( HISTOGRAM_REPORT_INTERVAL, 300.0 );
|
||||
|
||||
// IPager
|
||||
init( PAGER_RESERVED_PAGES, 1 );
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ public:
|
|||
double PUSH_STATS_INTERVAL;
|
||||
double PUSH_STATS_SLOW_AMOUNT;
|
||||
double PUSH_STATS_SLOW_RATIO;
|
||||
int TLOG_POP_BATCH_SIZE;
|
||||
|
||||
// Data distribution queue
|
||||
double HEALTH_POLL_TIME;
|
||||
|
|
@ -193,6 +194,7 @@ public:
|
|||
double DD_SS_STUCK_TIME_LIMIT; // If a storage server is not getting new versions for this amount of time, then it becomes undesired.
|
||||
int DD_TEAMS_INFO_PRINT_INTERVAL;
|
||||
int DD_TEAMS_INFO_PRINT_YIELD_COUNT;
|
||||
int DD_TEAM_ZERO_SERVER_LEFT_LOG_DELAY;
|
||||
|
||||
// TeamRemover to remove redundant teams
|
||||
bool TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER; // disable the machineTeamRemover actor
|
||||
|
|
@ -233,6 +235,9 @@ public:
|
|||
double SQLITE_FRAGMENT_MIN_SAVINGS;
|
||||
int SQLITE_CHUNK_SIZE_PAGES;
|
||||
int SQLITE_CHUNK_SIZE_PAGES_SIM;
|
||||
int SQLITE_READER_THREADS;
|
||||
int SQLITE_WRITE_WINDOW_LIMIT;
|
||||
double SQLITE_WRITE_WINDOW_SECONDS;
|
||||
|
||||
// KeyValueStoreSqlite spring cleaning
|
||||
double SPRING_CLEANING_NO_ACTION_INTERVAL;
|
||||
|
|
@ -252,7 +257,12 @@ public:
|
|||
|
||||
// KeyValueStoreRocksDB
|
||||
int ROCKSDB_BACKGROUND_PARALLELISM;
|
||||
int ROCKSDB_READ_PARALLELISM;
|
||||
int64_t ROCKSDB_MEMTABLE_BYTES;
|
||||
bool ROCKSDB_UNSAFE_AUTO_FSYNC;
|
||||
int64_t ROCKSDB_PERIODIC_COMPACTION_SECONDS;
|
||||
int ROCKSDB_PREFIX_LEN;
|
||||
int64_t ROCKSDB_BLOCK_CACHE_SIZE;
|
||||
|
||||
// Leader election
|
||||
int MAX_NOTIFICATIONS;
|
||||
|
|
@ -306,6 +316,7 @@ public:
|
|||
int PROXY_COMPUTE_BUCKETS;
|
||||
double PROXY_COMPUTE_GROWTH_RATE;
|
||||
int TXN_STATE_SEND_AMOUNT;
|
||||
bool PROXY_REJECT_BATCH_QUEUED_TOO_LONG;
|
||||
|
||||
int RESET_MASTER_BATCHES;
|
||||
int RESET_RESOLVER_BATCHES;
|
||||
|
|
@ -409,6 +420,12 @@ public:
|
|||
int64_t AUTO_TAG_THROTTLE_STORAGE_QUEUE_BYTES;
|
||||
int64_t TARGET_BYTES_PER_STORAGE_SERVER_BATCH;
|
||||
int64_t SPRING_BYTES_STORAGE_SERVER_BATCH;
|
||||
int64_t STORAGE_HARD_LIMIT_BYTES;
|
||||
int64_t STORAGE_DURABILITY_LAG_HARD_MAX;
|
||||
int64_t STORAGE_DURABILITY_LAG_SOFT_MAX;
|
||||
|
||||
int64_t LOW_PRIORITY_STORAGE_QUEUE_BYTES;
|
||||
int64_t LOW_PRIORITY_DURABILITY_LAG;
|
||||
|
||||
int64_t TARGET_BYTES_PER_TLOG;
|
||||
int64_t SPRING_BYTES_TLOG;
|
||||
|
|
@ -479,9 +496,6 @@ public:
|
|||
int FETCH_KEYS_PARALLELISM_BYTES;
|
||||
int FETCH_KEYS_LOWER_PRIORITY;
|
||||
int BUGGIFY_BLOCK_BYTES;
|
||||
int64_t STORAGE_HARD_LIMIT_BYTES;
|
||||
int64_t STORAGE_DURABILITY_LAG_HARD_MAX;
|
||||
int64_t STORAGE_DURABILITY_LAG_SOFT_MAX;
|
||||
double STORAGE_DURABILITY_LAG_REJECT_THRESHOLD;
|
||||
double STORAGE_DURABILITY_LAG_MIN_RATE;
|
||||
int STORAGE_COMMIT_BYTES;
|
||||
|
|
@ -504,6 +518,10 @@ public:
|
|||
double READ_TAG_MEASUREMENT_INTERVAL;
|
||||
int64_t OPERATION_COST_BYTE_FACTOR;
|
||||
bool PREFIX_COMPRESS_KVS_MEM_SNAPSHOTS;
|
||||
bool REPORT_DD_METRICS;
|
||||
double DD_METRICS_REPORT_INTERVAL;
|
||||
double FETCH_KEYS_TOO_LONG_TIME_CRITERIA;
|
||||
double MAX_STORAGE_COMMIT_TIME;
|
||||
|
||||
//Wait Failure
|
||||
int MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS;
|
||||
|
|
@ -536,6 +554,7 @@ public:
|
|||
double MAX_STATUS_REQUESTS_PER_SECOND;
|
||||
int CONFIGURATION_ROWS_TO_FETCH;
|
||||
bool DISABLE_DUPLICATE_LOG_WARNING;
|
||||
double HISTOGRAM_REPORT_INTERVAL;
|
||||
|
||||
// IPager
|
||||
int PAGER_RESERVED_PAGES;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include "fdbserver/ApplyMetadataMutation.h"
|
||||
#include "fdbserver/RecoveryState.h"
|
||||
#include "fdbclient/Atomic.h"
|
||||
#include "flow/Arena.h"
|
||||
#include "flow/Histogram.h"
|
||||
#include "flow/TDMetric.actor.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
|
|
@ -75,20 +77,26 @@ struct LogRouterData {
|
|||
|
||||
const UID dbgid;
|
||||
Reference<AsyncVar<Reference<ILogSystem>>> logSystem;
|
||||
NotifiedVersion version;
|
||||
NotifiedVersion minPopped;
|
||||
Optional<UID> primaryPeekLocation;
|
||||
NotifiedVersion version; // The largest version at which the log router has peeked mutations
|
||||
// from satellite tLog or primary tLogs.
|
||||
NotifiedVersion minPopped; // The minimum version among all tags that has been popped by remote tLogs.
|
||||
const Version startVersion;
|
||||
Version minKnownCommittedVersion;
|
||||
Version minKnownCommittedVersion; // The minimum durable version among all LRs.
|
||||
// A LR's durable version is the maximum version of mutations that have been
|
||||
// popped by remote tLog.
|
||||
Version poppedVersion;
|
||||
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
||||
Tag routerTag;
|
||||
bool allowPops;
|
||||
LogSet logSet;
|
||||
bool foundEpochEnd;
|
||||
double waitForVersionTime = 0;
|
||||
double maxWaitForVersionTime = 0;
|
||||
double getMoreTime = 0;
|
||||
double maxGetMoreTime = 0;
|
||||
bool foundEpochEnd; // Cluster is not fully recovered yet. LR has to handle recovery
|
||||
double waitForVersionTime = 0; // The total amount of time LR waits for remote tLog to peek and pop its data.
|
||||
double maxWaitForVersionTime = 0; // The max one-instance wait time when LR must wait for remote tLog to pop data.
|
||||
double getMoreTime = 0; // The total amount of time LR waits for satellite tLog's data to become available.
|
||||
double maxGetMoreTime = 0; // The max wait time LR spent in a pull-data-request to satellite tLog.
|
||||
int64_t generation = -1;
|
||||
Reference<Histogram> peekLatencyDist;
|
||||
|
||||
struct PeekTrackerData {
|
||||
std::map<int, Promise<std::pair<Version, bool>>> sequence_version;
|
||||
|
|
@ -98,7 +106,9 @@ struct LogRouterData {
|
|||
std::map<UID, PeekTrackerData> peekTracker;
|
||||
|
||||
CounterCollection cc;
|
||||
Counter getMoreCount, getMoreBlockedCount;
|
||||
Counter getMoreCount; // Increase by 1 when LR tries to pull data from satellite tLog.
|
||||
Counter
|
||||
getMoreBlockedCount; // Increase by 1 if data is not available when LR tries to pull data from satellite tLog.
|
||||
Future<Void> logger;
|
||||
Reference<EventCacheHolder> eventCacheHolder;
|
||||
|
||||
|
|
@ -119,9 +129,14 @@ struct LogRouterData {
|
|||
return newTagData;
|
||||
}
|
||||
|
||||
LogRouterData(UID dbgid, const InitializeLogRouterRequest& req) : dbgid(dbgid), routerTag(req.routerTag), logSystem(new AsyncVar<Reference<ILogSystem>>()),
|
||||
version(req.startVersion-1), minPopped(0), startVersion(req.startVersion), allowPops(false), minKnownCommittedVersion(0), poppedVersion(0), foundEpochEnd(false),
|
||||
cc("LogRouter", dbgid.toString()), getMoreCount("GetMoreCount", cc), getMoreBlockedCount("GetMoreBlockedCount", cc) {
|
||||
LogRouterData(UID dbgid, const InitializeLogRouterRequest& req)
|
||||
: dbgid(dbgid), routerTag(req.routerTag), logSystem(new AsyncVar<Reference<ILogSystem>>()),
|
||||
version(req.startVersion - 1), minPopped(0), generation(req.recoveryCount), startVersion(req.startVersion),
|
||||
allowPops(false), minKnownCommittedVersion(0), poppedVersion(0), foundEpochEnd(false),
|
||||
cc("LogRouter", dbgid.toString()), getMoreCount("GetMoreCount", cc),
|
||||
getMoreBlockedCount("GetMoreBlockedCount", cc),
|
||||
peekLatencyDist(Histogram::getHistogram(LiteralStringRef("LogRouter"), LiteralStringRef("PeekTLogLatency"),
|
||||
Histogram::Unit::microseconds)) {
|
||||
//setup just enough of a logSet to be able to call getPushLocations
|
||||
logSet.logServers.resize(req.tLogLocalities.size());
|
||||
logSet.tLogPolicy = req.tLogPolicy;
|
||||
|
|
@ -138,8 +153,10 @@ struct LogRouterData {
|
|||
|
||||
eventCacheHolder = Reference<EventCacheHolder>( new EventCacheHolder(dbgid.shortString() + ".PeekLocation") );
|
||||
|
||||
specialCounter(cc, "Version", [this](){ return this->version.get(); });
|
||||
// FetchedVersions: How many version of mutations buffered at LR and have not been popped by remote tLogs
|
||||
specialCounter(cc, "Version", [this]() { return this->version.get(); });
|
||||
specialCounter(cc, "MinPopped", [this](){ return this->minPopped.get(); });
|
||||
// TODO: Add minPopped locality and minPoppedId, similar as tLog Metrics
|
||||
specialCounter(cc, "FetchedVersions", [this](){ return std::max<Version>(0, std::min<Version>(SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS, this->version.get() - this->minPopped.get())); });
|
||||
specialCounter(cc, "MinKnownCommittedVersion", [this](){ return this->minKnownCommittedVersion; });
|
||||
specialCounter(cc, "PoppedVersion", [this](){ return this->poppedVersion; });
|
||||
|
|
@ -148,7 +165,12 @@ struct LogRouterData {
|
|||
specialCounter(cc, "WaitForVersionMaxMS", [this](){ double val = this->maxWaitForVersionTime; this->maxWaitForVersionTime = 0; return 1000*val; });
|
||||
specialCounter(cc, "GetMoreMS", [this](){ double val = this->getMoreTime; this->getMoreTime = 0; return 1000*val; });
|
||||
specialCounter(cc, "GetMoreMaxMS", [this](){ double val = this->maxGetMoreTime; this->maxGetMoreTime = 0; return 1000*val; });
|
||||
logger = traceCounters("LogRouterMetrics", dbgid, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "LogRouterMetrics");
|
||||
specialCounter(cc, "Generation", [this]() { return this->generation; });
|
||||
logger = traceCounters("LogRouterMetrics", dbgid, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc,
|
||||
"LogRouterMetrics", [this](TraceEvent& te) {
|
||||
te.detail("PrimaryPeekLocation", this->primaryPeekLocation);
|
||||
te.detail("RouterTag", this->routerTag.toString());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -207,8 +229,15 @@ ACTOR Future<Void> waitForVersion( LogRouterData *self, Version ver ) {
|
|||
// Since one set of log routers is created per generation of transaction logs, the gap caused by epoch end will be within MAX_VERSIONS_IN_FLIGHT of the log routers start version.
|
||||
state double startTime = now();
|
||||
if(self->version.get() < self->startVersion) {
|
||||
// Log router needs to wait for remote tLogs to process data, whose version is less than self->startVersion,
|
||||
// before the log router can pull more data (i.e., data after self->startVersion) from satellite tLog;
|
||||
// This prevents LR from getting OOM due to it pulls too much data from satellite tLog at once;
|
||||
// Note: each commit writes data to both primary tLog and satellite tLog. Satellite tLog can be viewed as
|
||||
// a part of primary tLogs.
|
||||
if(ver > self->startVersion) {
|
||||
self->version.set(self->startVersion);
|
||||
// Wait for remote tLog to peek and pop from LR,
|
||||
// so that LR's minPopped version can increase to self->startVersion
|
||||
wait(self->minPopped.whenAtLeast(self->version.get()));
|
||||
}
|
||||
self->waitForVersionTime += now() - startTime;
|
||||
|
|
@ -216,6 +245,9 @@ ACTOR Future<Void> waitForVersion( LogRouterData *self, Version ver ) {
|
|||
return Void();
|
||||
}
|
||||
if(!self->foundEpochEnd) {
|
||||
// Similar to proxy that does not keep more than MAX_READ_TRANSACTION_LIFE_VERSIONS transactions oustanding;
|
||||
// Log router does not keep more than MAX_READ_TRANSACTION_LIFE_VERSIONS transactions outstanding because
|
||||
// remote SS cannot roll back to more than MAX_READ_TRANSACTION_LIFE_VERSIONS ago.
|
||||
wait(self->minPopped.whenAtLeast(std::min(self->version.get(), ver - SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS)));
|
||||
} else {
|
||||
while(self->minPopped.get() + SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS < ver) {
|
||||
|
|
@ -235,6 +267,8 @@ ACTOR Future<Void> waitForVersion( LogRouterData *self, Version ver ) {
|
|||
return Void();
|
||||
}
|
||||
|
||||
// Log router (LR) asynchronously pull data from satellite tLogs (preferred) or primary tLogs at tag (self->routerTag)
|
||||
// for the version range from the LR's current version (exclusive) to its epoch's end version or recovery version.
|
||||
ACTOR Future<Void> pullAsyncData( LogRouterData *self ) {
|
||||
state Future<Void> dbInfoChange = Void();
|
||||
state Reference<ILogSystem::IPeekCursor> r;
|
||||
|
|
@ -255,13 +289,16 @@ ACTOR Future<Void> pullAsyncData( LogRouterData *self ) {
|
|||
state double startTime = now();
|
||||
choose {
|
||||
when(wait( getMoreF ) ) {
|
||||
self->getMoreTime += now() - startTime;
|
||||
self->maxGetMoreTime = std::max(self->maxGetMoreTime, now() - startTime);
|
||||
double peekTime = now() - startTime;
|
||||
self->peekLatencyDist->sampleSeconds(peekTime);
|
||||
self->getMoreTime += peekTime;
|
||||
self->maxGetMoreTime = std::max(self->maxGetMoreTime, peekTime);
|
||||
break;
|
||||
}
|
||||
when( wait( dbInfoChange ) ) { //FIXME: does this actually happen?
|
||||
if( self->logSystem->get() ) {
|
||||
r = self->logSystem->get()->peekLogRouter( self->dbgid, tagAt, self->routerTag );
|
||||
self->primaryPeekLocation = r->getPrimaryPeekLocation();
|
||||
TraceEvent("LogRouterPeekLocation", self->dbgid).detail("LogID", r->getPrimaryPeekLocation()).trackLatest(self->eventCacheHolder->trackingKey);
|
||||
} else {
|
||||
r = Reference<ILogSystem::IPeekCursor>();
|
||||
|
|
@ -563,6 +600,7 @@ ACTOR Future<Void> logRouterCore(
|
|||
addActor.send( logRouterPeekMessages( &logRouterData, req ) );
|
||||
}
|
||||
when( TLogPopRequest req = waitNext( interf.popMessages.getFuture() ) ) {
|
||||
// Request from remote tLog to pop data from LR
|
||||
addActor.send( logRouterPop( &logRouterData, req ) );
|
||||
}
|
||||
when (wait(error)) {}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public:
|
|||
Reference<LocalitySet> logServerSet;
|
||||
std::vector<int> logIndexArray;
|
||||
std::vector<LocalityEntry> logEntryArray;
|
||||
bool isLocal;
|
||||
bool isLocal; // true if the LogSet is in primary DC or primary DC's satellite
|
||||
int8_t locality;
|
||||
Version startVersion;
|
||||
std::vector<Future<TLogLockResult>> replies;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ struct serializable_traits<OptionalInterface<Interface>> : std::true_type {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
// Contains a generation of tLogs for an individual DC.
|
||||
struct TLogSet {
|
||||
constexpr static FileIdentifier file_identifier = 6302317;
|
||||
std::vector<OptionalInterface<TLogInterface>> tLogs;
|
||||
|
|
|
|||
|
|
@ -139,8 +139,20 @@ ACTOR Future<Void> resetChecker( ILogSystem::ServerPeekCursor* self, NetworkAddr
|
|||
self->unknownReplies = 0;
|
||||
self->fastReplies = 0;
|
||||
wait(delay(SERVER_KNOBS->PEEK_STATS_INTERVAL));
|
||||
TraceEvent("SlowPeekStats").detail("PeerAddress", addr).detail("SlowReplies", self->slowReplies).detail("FastReplies", self->fastReplies).detail("UnknownReplies", self->unknownReplies);
|
||||
if(self->slowReplies >= SERVER_KNOBS->PEEK_STATS_SLOW_AMOUNT && self->slowReplies/double(self->slowReplies+self->fastReplies) >= SERVER_KNOBS->PEEK_STATS_SLOW_RATIO) {
|
||||
TraceEvent("SlowPeekStats", self->randomID)
|
||||
.detail("PeerAddress", addr)
|
||||
.detail("SlowReplies", self->slowReplies)
|
||||
.detail("FastReplies", self->fastReplies)
|
||||
.detail("UnknownReplies", self->unknownReplies);
|
||||
|
||||
if (self->slowReplies >= SERVER_KNOBS->PEEK_STATS_SLOW_AMOUNT &&
|
||||
self->slowReplies / double(self->slowReplies + self->fastReplies) >= SERVER_KNOBS->PEEK_STATS_SLOW_RATIO) {
|
||||
|
||||
TraceEvent("ConnectionResetSlowPeek", self->randomID)
|
||||
.detail("PeerAddress", addr)
|
||||
.detail("SlowReplies", self->slowReplies)
|
||||
.detail("FastReplies", self->fastReplies)
|
||||
.detail("UnknownReplies", self->unknownReplies);
|
||||
FlowTransport::transport().resetConnection(addr);
|
||||
self->lastReset = now();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ struct ProxyStats {
|
|||
Counter txnDefaultPriorityStartIn, txnDefaultPriorityStartOut;
|
||||
Counter txnCommitIn, txnCommitVersionAssigned, txnCommitResolving, txnCommitResolved, txnCommitOut, txnCommitOutSuccess, txnCommitErrors;
|
||||
Counter txnConflicts;
|
||||
Counter txnRejectedForQueuedTooLong;
|
||||
Counter txnThrottled;
|
||||
Counter commitBatchIn, commitBatchOut;
|
||||
Counter mutationBytes;
|
||||
|
|
@ -87,6 +88,14 @@ struct ProxyStats {
|
|||
Counter conflictRanges;
|
||||
Counter keyServerLocationIn, keyServerLocationOut, keyServerLocationErrors;
|
||||
Version lastCommitVersionAssigned;
|
||||
double transactionRateAllowed, batchTransactionRateAllowed;
|
||||
double transactionLimit, batchTransactionLimit;
|
||||
// how much of the GRV requests queue was processed in one attempt to hand out read version.
|
||||
double percentageOfDefaultGRVQueueProcessed;
|
||||
double percentageOfBatchGRVQueueProcessed;
|
||||
|
||||
LatencySample defaultTxnGRVTimeInQueue;
|
||||
LatencySample batchTxnGRVTimeInQueue;
|
||||
|
||||
LatencySample commitLatencySample;
|
||||
LatencySample grvLatencySample;
|
||||
|
|
@ -94,6 +103,8 @@ struct ProxyStats {
|
|||
LatencyBands commitLatencyBands;
|
||||
LatencyBands grvLatencyBands;
|
||||
|
||||
LatencySample commitBatchingWindowSize;
|
||||
|
||||
Future<Void> logger;
|
||||
|
||||
int recentRequests;
|
||||
|
|
@ -136,36 +147,56 @@ struct ProxyStats {
|
|||
return r;
|
||||
}
|
||||
|
||||
explicit ProxyStats(UID id, Version* pVersion, NotifiedVersion* pCommittedVersion, int64_t *commitBatchesMemBytesCountPtr)
|
||||
: cc("ProxyStats", id.toString()), recentRequests(0), lastBucketBegin(now()),
|
||||
maxComputeNS(0), minComputeNS(1e12),
|
||||
bucketInterval(FLOW_KNOBS->BASIC_LOAD_BALANCE_UPDATE_RATE/FLOW_KNOBS->BASIC_LOAD_BALANCE_BUCKETS),
|
||||
txnRequestIn("TxnRequestIn", cc), txnRequestOut("TxnRequestOut", cc),
|
||||
txnRequestErrors("TxnRequestErrors", cc), txnStartIn("TxnStartIn", cc), txnStartOut("TxnStartOut", cc),
|
||||
txnStartBatch("TxnStartBatch", cc), txnSystemPriorityStartIn("TxnSystemPriorityStartIn", cc),
|
||||
txnSystemPriorityStartOut("TxnSystemPriorityStartOut", cc),
|
||||
txnBatchPriorityStartIn("TxnBatchPriorityStartIn", cc),
|
||||
txnBatchPriorityStartOut("TxnBatchPriorityStartOut", cc),
|
||||
txnDefaultPriorityStartIn("TxnDefaultPriorityStartIn", cc),
|
||||
txnDefaultPriorityStartOut("TxnDefaultPriorityStartOut", cc), txnCommitIn("TxnCommitIn", cc),
|
||||
txnCommitVersionAssigned("TxnCommitVersionAssigned", cc), txnCommitResolving("TxnCommitResolving", cc),
|
||||
txnCommitResolved("TxnCommitResolved", cc), txnCommitOut("TxnCommitOut", cc),
|
||||
txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnCommitErrors("TxnCommitErrors", cc),
|
||||
txnConflicts("TxnConflicts", cc), txnThrottled("TxnThrottled", cc), commitBatchIn("CommitBatchIn", cc),
|
||||
commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc),
|
||||
conflictRanges("ConflictRanges", cc), keyServerLocationIn("KeyServerLocationIn", cc),
|
||||
keyServerLocationOut("KeyServerLocationOut", cc), keyServerLocationErrors("KeyServerLocationErrors", cc),
|
||||
lastCommitVersionAssigned(0),
|
||||
commitLatencySample("CommitLatencyMetrics", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
grvLatencySample("GRVLatencyMetrics", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
commitLatencyBands("CommitLatencyBands", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY),
|
||||
grvLatencyBands("GRVLatencyBands", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY) {
|
||||
explicit ProxyStats(UID id, Version* pVersion, NotifiedVersion* pCommittedVersion,
|
||||
int64_t* commitBatchesMemBytesCountPtr)
|
||||
: cc("ProxyStats", id.toString()), recentRequests(0), lastBucketBegin(now()), maxComputeNS(0), minComputeNS(1e12),
|
||||
bucketInterval(FLOW_KNOBS->BASIC_LOAD_BALANCE_UPDATE_RATE / FLOW_KNOBS->BASIC_LOAD_BALANCE_BUCKETS),
|
||||
txnRequestIn("TxnRequestIn", cc), txnRequestOut("TxnRequestOut", cc), txnRequestErrors("TxnRequestErrors", cc),
|
||||
txnStartIn("TxnStartIn", cc), txnStartOut("TxnStartOut", cc), txnStartBatch("TxnStartBatch", cc),
|
||||
txnSystemPriorityStartIn("TxnSystemPriorityStartIn", cc),
|
||||
txnSystemPriorityStartOut("TxnSystemPriorityStartOut", cc),
|
||||
txnBatchPriorityStartIn("TxnBatchPriorityStartIn", cc),
|
||||
txnBatchPriorityStartOut("TxnBatchPriorityStartOut", cc),
|
||||
txnDefaultPriorityStartIn("TxnDefaultPriorityStartIn", cc),
|
||||
txnDefaultPriorityStartOut("TxnDefaultPriorityStartOut", cc), txnCommitIn("TxnCommitIn", cc),
|
||||
txnCommitVersionAssigned("TxnCommitVersionAssigned", cc), txnCommitResolving("TxnCommitResolving", cc),
|
||||
txnCommitResolved("TxnCommitResolved", cc), txnCommitOut("TxnCommitOut", cc),
|
||||
txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnCommitErrors("TxnCommitErrors", cc),
|
||||
txnConflicts("TxnConflicts", cc), txnThrottled("TxnThrottled", cc), commitBatchIn("CommitBatchIn", cc),
|
||||
txnRejectedForQueuedTooLong("TxnRejectedForQueuedTooLong", cc),
|
||||
commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc),
|
||||
conflictRanges("ConflictRanges", cc), keyServerLocationIn("KeyServerLocationIn", cc),
|
||||
keyServerLocationOut("KeyServerLocationOut", cc), keyServerLocationErrors("KeyServerLocationErrors", cc),
|
||||
lastCommitVersionAssigned(0),
|
||||
commitLatencySample("CommitLatencyMetrics", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
|
||||
SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
grvLatencySample("GRVLatencyMetrics", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
|
||||
SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
commitBatchingWindowSize("CommitBatchingWindowSize", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
|
||||
SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
commitLatencyBands("CommitLatencyBands", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY),
|
||||
grvLatencyBands("GRVLatencyBands", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY),
|
||||
defaultTxnGRVTimeInQueue("DefaultTxnGRVTimeInQueue", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
|
||||
SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
batchTxnGRVTimeInQueue("BatchTxnGRVTimeInQueue", id, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
|
||||
SERVER_KNOBS->LATENCY_SAMPLE_SIZE),
|
||||
transactionRateAllowed(0), batchTransactionRateAllowed(0), transactionLimit(0), batchTransactionLimit(0),
|
||||
percentageOfDefaultGRVQueueProcessed(0), percentageOfBatchGRVQueueProcessed(0) {
|
||||
specialCounter(cc, "LastAssignedCommitVersion", [this](){return this->lastCommitVersionAssigned;});
|
||||
specialCounter(cc, "Version", [pVersion](){return *pVersion; });
|
||||
specialCounter(cc, "CommittedVersion", [pCommittedVersion](){ return pCommittedVersion->get(); });
|
||||
specialCounter(cc, "CommitBatchesMemBytesCount", [commitBatchesMemBytesCountPtr]() { return *commitBatchesMemBytesCountPtr; });
|
||||
specialCounter(cc, "MaxCompute", [this](){ return this->getAndResetMaxCompute(); });
|
||||
specialCounter(cc, "MinCompute", [this](){ return this->getAndResetMinCompute(); });
|
||||
// The rate at which the limit(budget) is allowed to grow.
|
||||
specialCounter(cc, "SystemAndDefaultTxnRateAllowed", [this]() { return this->transactionRateAllowed; });
|
||||
specialCounter(cc, "BatchTransactionRateAllowed", [this]() { return this->batchTransactionRateAllowed; });
|
||||
specialCounter(cc, "SystemAndDefaultTxnLimit", [this]() { return this->transactionLimit; });
|
||||
specialCounter(cc, "BatchTransactionLimit", [this]() { return this->batchTransactionLimit; });
|
||||
specialCounter(cc, "PercentageOfDefaultGRVQueueProcessed",
|
||||
[this]() { return this->percentageOfDefaultGRVQueueProcessed; });
|
||||
specialCounter(cc, "PercentageOfBatchGRVQueueProcessed",
|
||||
[this]() { return this->percentageOfBatchGRVQueueProcessed; });
|
||||
logger = traceCounters("ProxyMetrics", id, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "ProxyMetrics");
|
||||
for(int i = 0; i < FLOW_KNOBS->BASIC_LOAD_BALANCE_BUCKETS; i++) {
|
||||
requestBuckets.push_back(0);
|
||||
|
|
@ -246,10 +277,12 @@ struct TransactionRateInfo {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
ACTOR Future<Void> getRate(UID myID, Reference<AsyncVar<ServerDBInfo>> db, int64_t* inTransactionCount, int64_t* inBatchTransactionCount, TransactionRateInfo *transactionRateInfo,
|
||||
TransactionRateInfo *batchTransactionRateInfo, GetHealthMetricsReply* healthMetricsReply, GetHealthMetricsReply* detailedHealthMetricsReply,
|
||||
TransactionTagMap<uint64_t>* transactionTagCounter, PrioritizedTransactionTagMap<ClientTagThrottleLimits>* throttledTags) {
|
||||
ACTOR Future<Void> getRate(UID myID, Reference<AsyncVar<ServerDBInfo>> db, int64_t* inTransactionCount,
|
||||
int64_t* inBatchTransactionCount, TransactionRateInfo* transactionRateInfo,
|
||||
TransactionRateInfo* batchTransactionRateInfo, GetHealthMetricsReply* healthMetricsReply,
|
||||
GetHealthMetricsReply* detailedHealthMetricsReply,
|
||||
TransactionTagMap<uint64_t>* transactionTagCounter,
|
||||
PrioritizedTransactionTagMap<ClientTagThrottleLimits>* throttledTags, ProxyStats* stats) {
|
||||
state Future<Void> nextRequestTimer = Never();
|
||||
state Future<Void> leaseTimeout = Never();
|
||||
state Future<GetRateInfoReply> reply = Never();
|
||||
|
|
@ -290,6 +323,15 @@ ACTOR Future<Void> getRate(UID myID, Reference<AsyncVar<ServerDBInfo>> db, int64
|
|||
transactionRateInfo->setRate(rep.transactionRate);
|
||||
batchTransactionRateInfo->setRate(rep.batchTransactionRate);
|
||||
//TraceEvent("MasterProxyRate", myID).detail("Rate", rep.transactionRate).detail("BatchRate", rep.batchTransactionRate).detail("Lease", rep.leaseDuration).detail("ReleasedTransactions", *inTransactionCount - lastTC);
|
||||
|
||||
stats->transactionRateAllowed = rep.transactionRate;
|
||||
stats->batchTransactionRateAllowed = rep.batchTransactionRate;
|
||||
// TraceEvent("MasterProxyTxRate", myID)
|
||||
// .detail("RKID", db->get().ratekeeper.get().id())
|
||||
// .detail("RateAllowed", rep.transactionRate)
|
||||
// .detail("BatchRateAllowed", rep.batchTransactionRate)
|
||||
// .detail("Lease", rep.leaseDuration)
|
||||
// .detail("ReleasedTransactions", *inTransactionCount - lastTC);
|
||||
lastTC = *inTransactionCount;
|
||||
leaseTimeout = delay(rep.leaseDuration);
|
||||
nextRequestTimer = delayJittered(rep.leaseDuration / 2);
|
||||
|
|
@ -420,7 +462,7 @@ struct ProxyCommitData {
|
|||
uint64_t commitVersionRequestNumber;
|
||||
uint64_t mostRecentProcessedRequestNumber;
|
||||
KeyRangeMap<Deque<std::pair<Version,int>>> keyResolvers;
|
||||
KeyRangeMap<ServerCacheInfo> keyInfo;
|
||||
KeyRangeMap<ServerCacheInfo> keyInfo; // keyrange -> all storage servers in all DCs for the keyrange
|
||||
KeyRangeMap<bool> cacheInfo;
|
||||
std::map<Key, applyMutationsData> uid_applyMutationsData;
|
||||
bool firstProxy;
|
||||
|
|
@ -818,6 +860,20 @@ ACTOR Future<Void> releaseResolvingAfter(ProxyCommitData* self, Future<Void> rel
|
|||
return Void();
|
||||
}
|
||||
|
||||
// Try to identify recovery transaction and backup's apply mutations (blind writes).
|
||||
// Both cannot be rejected and are approximated by looking at first mutation
|
||||
// starting with 0xff.
|
||||
bool canReject(const std::vector<CommitTransactionRequest>& trs) {
|
||||
for (const auto& tr : trs) {
|
||||
if (tr.transaction.mutations.empty()) continue;
|
||||
if (tr.transaction.mutations[0].param1.startsWith(LiteralStringRef("\xff")) ||
|
||||
tr.transaction.read_conflict_ranges.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Commit one batch of transactions trs
|
||||
ACTOR Future<Void> commitBatch(
|
||||
ProxyCommitData* self,
|
||||
|
|
@ -862,9 +918,13 @@ ACTOR Future<Void> commitBatch(
|
|||
|
||||
if (debugID.present())
|
||||
g_traceBatch.addEvent("CommitDebug", debugID.get().first(), "MasterProxyServer.commitBatch.Before");
|
||||
state double timeStart = now();
|
||||
|
||||
if(localBatchNumber-self->latestLocalCommitBatchResolving.get()>SERVER_KNOBS->RESET_MASTER_BATCHES && now()-self->lastMasterReset>SERVER_KNOBS->RESET_MASTER_DELAY) {
|
||||
TraceEvent(SevWarnAlways, "ResetMasterNetwork").detail("CurrentBatch", localBatchNumber).detail("InProcessBatch", self->latestLocalCommitBatchResolving.get());
|
||||
TraceEvent(SevWarnAlways, "ConnectionResetMaster", self->dbgid)
|
||||
.detail("PeerAddress", self->master.address())
|
||||
.detail("CurrentBatch", localBatchNumber)
|
||||
.detail("InProcessBatch", self->latestLocalCommitBatchResolving.get());
|
||||
FlowTransport::transport().resetConnection(self->master.address());
|
||||
self->lastMasterReset=now();
|
||||
}
|
||||
|
|
@ -874,6 +934,32 @@ ACTOR Future<Void> commitBatch(
|
|||
// Queuing pre-resolution commit processing
|
||||
TEST(self->latestLocalCommitBatchResolving.get() < localBatchNumber - 1);
|
||||
wait(self->latestLocalCommitBatchResolving.whenAtLeast(localBatchNumber-1));
|
||||
double queuingDelay = g_network->now() - timeStart;
|
||||
if ((queuingDelay > (double)SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS / SERVER_KNOBS->VERSIONS_PER_SECOND ||
|
||||
(g_network->isSimulated() && BUGGIFY_WITH_PROB(0.01))) &&
|
||||
SERVER_KNOBS->PROXY_REJECT_BATCH_QUEUED_TOO_LONG && canReject(trs)) {
|
||||
// Disabled for the recovery transaction. otherwise, recovery can't finish and keeps doing more recoveries.
|
||||
TEST(true); // Reject transactions in the batch
|
||||
TraceEvent(SevWarnAlways, "ProxyReject", self->dbgid)
|
||||
.suppressFor(0.1)
|
||||
.detail("QDelay", queuingDelay)
|
||||
.detail("Transactions", trs.size())
|
||||
.detail("BatchNumber", localBatchNumber);
|
||||
ASSERT(self->latestLocalCommitBatchResolving.get() == localBatchNumber - 1);
|
||||
self->latestLocalCommitBatchResolving.set(localBatchNumber);
|
||||
|
||||
wait(self->latestLocalCommitBatchLogging.whenAtLeast(localBatchNumber-1));
|
||||
ASSERT(self->latestLocalCommitBatchLogging.get() == localBatchNumber - 1);
|
||||
self->latestLocalCommitBatchLogging.set(localBatchNumber);
|
||||
for (const auto& tr : trs) {
|
||||
tr.reply.sendError(transaction_too_old());
|
||||
}
|
||||
++self->stats.commitBatchOut;
|
||||
self->stats.txnCommitOut += trs.size();
|
||||
self->stats.txnRejectedForQueuedTooLong += trs.size();
|
||||
return Void();
|
||||
}
|
||||
|
||||
state Future<Void> releaseDelay = delay(std::min(SERVER_KNOBS->MAX_PROXY_COMPUTE, batchOperations*self->commitComputePerOperation[latencyBucket]), TaskPriority::ProxyMasterVersionReply);
|
||||
|
||||
if (debugID.present())
|
||||
|
|
@ -929,9 +1015,14 @@ ACTOR Future<Void> commitBatch(
|
|||
std::move(requests.txReadConflictRangeIndexMap); // used to report conflicting keys
|
||||
state Future<Void> releaseFuture = releaseResolvingAfter(self, releaseDelay, localBatchNumber);
|
||||
|
||||
if(localBatchNumber-self->latestLocalCommitBatchLogging.get()>SERVER_KNOBS->RESET_RESOLVER_BATCHES && now()-self->lastResolverReset>SERVER_KNOBS->RESET_RESOLVER_DELAY) {
|
||||
TraceEvent(SevWarnAlways, "ResetResolverNetwork").detail("CurrentBatch", localBatchNumber).detail("InProcessBatch", self->latestLocalCommitBatchLogging.get());
|
||||
if (localBatchNumber - self->latestLocalCommitBatchLogging.get() > SERVER_KNOBS->RESET_RESOLVER_BATCHES &&
|
||||
now() - self->lastResolverReset > SERVER_KNOBS->RESET_RESOLVER_DELAY) {
|
||||
|
||||
for (int r = 0; r<self->resolvers.size(); r++) {
|
||||
TraceEvent(SevWarnAlways, "ConnectionResetResolver", self->dbgid)
|
||||
.detail("PeerAddr", self->resolvers[r].address())
|
||||
.detail("CurrentBatch", localBatchNumber)
|
||||
.detail("InProcessBatch", self->latestLocalCommitBatchLogging.get());
|
||||
FlowTransport::transport().resetConnection(self->resolvers[r].address());
|
||||
}
|
||||
self->lastResolverReset=now();
|
||||
|
|
@ -1426,6 +1517,8 @@ ACTOR Future<Void> commitBatch(
|
|||
target_latency * SERVER_KNOBS->COMMIT_TRANSACTION_BATCH_INTERVAL_SMOOTHER_ALPHA +
|
||||
self->commitBatchInterval * (1 - SERVER_KNOBS->COMMIT_TRANSACTION_BATCH_INTERVAL_SMOOTHER_ALPHA)));
|
||||
|
||||
self->stats.commitBatchingWindowSize.addMeasurement(self->commitBatchInterval);
|
||||
|
||||
self->commitBatchesMemBytesCount -= currentBatchMemBytesCount;
|
||||
ASSERT_ABORT(self->commitBatchesMemBytesCount >= 0);
|
||||
wait(releaseFuture);
|
||||
|
|
@ -1548,13 +1641,10 @@ ACTOR Future<Void> sendGrvReplies(Future<GetReadVersionReply> replyFuture, std::
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR static Future<Void> transactionStarter(
|
||||
MasterProxyInterface proxy,
|
||||
Reference<AsyncVar<ServerDBInfo>> db,
|
||||
PromiseStream<Future<Void>> addActor,
|
||||
ProxyCommitData* commitData, GetHealthMetricsReply* healthMetricsReply,
|
||||
GetHealthMetricsReply* detailedHealthMetricsReply)
|
||||
{
|
||||
ACTOR static Future<Void> transactionStarter(MasterProxyInterface proxy, Reference<AsyncVar<ServerDBInfo>> db,
|
||||
PromiseStream<Future<Void>> addActor, ProxyCommitData* commitData,
|
||||
GetHealthMetricsReply* healthMetricsReply,
|
||||
GetHealthMetricsReply* detailedHealthMetricsReply, ProxyStats* stats) {
|
||||
state double lastGRVTime = 0;
|
||||
state PromiseStream<Void> GRVTimer;
|
||||
state double GRVBatchTime = SERVER_KNOBS->START_TRANSACTION_BATCH_INTERVAL_MIN;
|
||||
|
|
@ -1573,8 +1663,9 @@ ACTOR static Future<Void> transactionStarter(
|
|||
state PrioritizedTransactionTagMap<ClientTagThrottleLimits> throttledTags;
|
||||
|
||||
state PromiseStream<double> replyTimes;
|
||||
|
||||
addActor.send(getRate(proxy.id(), db, &transactionCount, &batchTransactionCount, &normalRateInfo, &batchRateInfo, healthMetricsReply, detailedHealthMetricsReply, &transactionTagCounter, &throttledTags));
|
||||
addActor.send(getRate(proxy.id(), db, &transactionCount, &batchTransactionCount, &normalRateInfo, &batchRateInfo,
|
||||
healthMetricsReply, detailedHealthMetricsReply, &transactionTagCounter, &throttledTags,
|
||||
stats));
|
||||
addActor.send(queueTransactionStartRequests(db, &systemQueue, &defaultQueue, &batchQueue, proxy.getConsistentReadVersion.getFuture(),
|
||||
GRVTimer, &lastGRVTime, &GRVBatchTime, replyTimes.getFuture(), &commitData->stats, &batchRateInfo,
|
||||
&transactionTagCounter));
|
||||
|
|
@ -1603,6 +1694,9 @@ ACTOR static Future<Void> transactionStarter(
|
|||
normalRateInfo.reset();
|
||||
batchRateInfo.reset();
|
||||
|
||||
stats->transactionLimit = normalRateInfo.limit;
|
||||
stats->batchTransactionLimit = batchRateInfo.limit;
|
||||
|
||||
int transactionsStarted[2] = {0,0};
|
||||
int systemTransactionsStarted[2] = {0,0};
|
||||
int defaultPriTransactionsStarted[2] = { 0, 0 };
|
||||
|
|
@ -1613,6 +1707,8 @@ ACTOR static Future<Void> transactionStarter(
|
|||
|
||||
int requestsToStart = 0;
|
||||
|
||||
uint32_t defaultQueueSize = defaultQueue.size();
|
||||
uint32_t batchQueueSize = batchQueue.size();
|
||||
while (requestsToStart < SERVER_KNOBS->START_TRANSACTION_MAX_REQUESTS_TO_START) {
|
||||
Deque<GetReadVersionRequest>* transactionQueue;
|
||||
if(!systemQueue.empty()) {
|
||||
|
|
@ -1641,12 +1737,16 @@ ACTOR static Future<Void> transactionStarter(
|
|||
}
|
||||
|
||||
transactionsStarted[req.flags&1] += tc;
|
||||
if (req.priority >= TransactionPriority::IMMEDIATE)
|
||||
double currentTime = g_network->timer();
|
||||
if (req.priority >= TransactionPriority::IMMEDIATE) {
|
||||
systemTransactionsStarted[req.flags & 1] += tc;
|
||||
else if (req.priority >= TransactionPriority::DEFAULT)
|
||||
} else if (req.priority >= TransactionPriority::DEFAULT) {
|
||||
defaultPriTransactionsStarted[req.flags & 1] += tc;
|
||||
else
|
||||
stats->defaultTxnGRVTimeInQueue.addMeasurement(currentTime - req.requestTime());
|
||||
} else {
|
||||
batchPriTransactionsStarted[req.flags & 1] += tc;
|
||||
stats->batchTxnGRVTimeInQueue.addMeasurement(currentTime - req.requestTime());
|
||||
}
|
||||
|
||||
start[req.flags & 1].push_back(std::move(req)); static_assert(GetReadVersionRequest::FLAG_CAUSAL_READ_RISKY == 1, "Implementation dependent on flag value");
|
||||
transactionQueue->pop_front();
|
||||
|
|
@ -1683,6 +1783,8 @@ ACTOR static Future<Void> transactionStarter(
|
|||
g_traceBatch.addEvent("TransactionDebug", debugID.get().first(), "MasterProxyServer.masterProxyServerCore.Broadcast");
|
||||
}
|
||||
|
||||
int defaultGRVProcessed = 0;
|
||||
int batchGRVProcessed = 0;
|
||||
for (int i = 0; i < start.size(); i++) {
|
||||
if (start[i].size()) {
|
||||
Future<GetReadVersionReply> readVersionReply = getLiveCommittedVersion(commitData, i, &otherProxies, debugID, transactionsStarted[i], systemTransactionsStarted[i], defaultPriTransactionsStarted[i], batchPriTransactionsStarted[i]);
|
||||
|
|
@ -1693,8 +1795,12 @@ ACTOR static Future<Void> transactionStarter(
|
|||
if (i == 0) {
|
||||
addActor.send(timeReply(readVersionReply, replyTimes));
|
||||
}
|
||||
defaultGRVProcessed += defaultPriTransactionsStarted[i];
|
||||
batchGRVProcessed += batchPriTransactionsStarted[i];
|
||||
}
|
||||
}
|
||||
stats->percentageOfDefaultGRVQueueProcessed = (double)defaultGRVProcessed / defaultQueueSize;
|
||||
stats->percentageOfBatchGRVQueueProcessed = (double)batchGRVProcessed / batchQueueSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2105,7 +2211,8 @@ ACTOR Future<Void> masterProxyServerCore(
|
|||
TraceEvent(SevInfo, "CommitBatchesMemoryLimit").detail("BytesLimit", commitBatchesMemoryLimit);
|
||||
|
||||
addActor.send(monitorRemoteCommitted(&commitData));
|
||||
addActor.send(transactionStarter(proxy, commitData.db, addActor, &commitData, &healthMetricsReply, &detailedHealthMetricsReply));
|
||||
addActor.send(transactionStarter(proxy, commitData.db, addActor, &commitData, &healthMetricsReply,
|
||||
&detailedHealthMetricsReply, &commitData.stats));
|
||||
addActor.send(readRequestServer(proxy, addActor, &commitData));
|
||||
addActor.send(rejoinServer(proxy, &commitData));
|
||||
addActor.send(healthMetricsRequestServer(proxy, &healthMetricsReply, &detailedHealthMetricsReply));
|
||||
|
|
|
|||
|
|
@ -1307,19 +1307,6 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> watchDegraded(TLogData* self) {
|
||||
if(g_network->isSimulated() && g_simulator.speedUpSimulation) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
wait(lowPriorityDelay(SERVER_KNOBS->TLOG_DEGRADED_DURATION));
|
||||
|
||||
TraceEvent(SevWarnAlways, "TLogDegraded", self->dbgid);
|
||||
TEST(true); //6.0 TLog degraded
|
||||
self->degraded->set(true);
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> doQueueCommit( TLogData* self, Reference<LogData> logData, std::vector<Reference<LogData>> missingFinalCommit ) {
|
||||
state Version ver = logData->version.get();
|
||||
state Version commitNumber = self->queueCommitBegin+1;
|
||||
|
|
@ -1332,12 +1319,11 @@ ACTOR Future<Void> doQueueCommit( TLogData* self, Reference<LogData> logData, st
|
|||
self->diskQueueCommitBytes = 0;
|
||||
self->largeDiskQueueCommitBytes.set(false);
|
||||
|
||||
state Future<Void> degraded = watchDegraded(self);
|
||||
wait(c);
|
||||
wait(ioDegradedOrTimeoutError(c, SERVER_KNOBS->MAX_STORAGE_COMMIT_TIME, self->degraded,
|
||||
SERVER_KNOBS->TLOG_DEGRADED_DURATION));
|
||||
if(g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) {
|
||||
wait(delay(6.0));
|
||||
}
|
||||
degraded.cancel();
|
||||
wait(self->queueCommitEnd.whenAtLeast(commitNumber-1));
|
||||
|
||||
//Calling check_yield instead of yield to avoid a destruction ordering problem in simulation
|
||||
|
|
|
|||
|
|
@ -36,14 +36,6 @@
|
|||
|
||||
// Split RestoreConfigFR defined in FileBackupAgent.actor.cpp to declaration in Restore.actor.h and implementation in
|
||||
// RestoreCommon.actor.cpp
|
||||
template <>
|
||||
inline Tuple Codec<ERestoreState>::pack(ERestoreState const& val) {
|
||||
return Tuple().append(val);
|
||||
}
|
||||
template <>
|
||||
inline ERestoreState Codec<ERestoreState>::unpack(Tuple const& val) {
|
||||
return (ERestoreState)val.getInt(0);
|
||||
}
|
||||
|
||||
KeyBackedProperty<ERestoreState> RestoreConfigFR::stateEnum() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
|
|
|
|||
|
|
@ -202,6 +202,10 @@ ACTOR Future<ISimulator::KillType> simulatedFDBDRebooter(Reference<ClusterConnec
|
|||
if(g_network->isSimulated() && e.code() != error_code_io_timeout && (bool)g_network->global(INetwork::enASIOTimedOut))
|
||||
TraceEvent(SevError, "IOTimeoutErrorSuppressed").detail("ErrorCode", e.code()).detail("RandomId", randomId).backtrace();
|
||||
|
||||
if (e.code() == error_code_io_timeout && !onShutdown.isReady()) {
|
||||
onShutdown = ISimulator::RebootProcess;
|
||||
}
|
||||
|
||||
if (onShutdown.isReady() && onShutdown.isError()) throw onShutdown.getError();
|
||||
if(e.code() != error_code_actor_cancelled)
|
||||
printf("SimulatedFDBDTerminated: %s\n", e.what());
|
||||
|
|
|
|||
|
|
@ -453,6 +453,11 @@ struct RolesInfo {
|
|||
obj.setKeyRawNumber("query_queue_max", storageMetrics.getValue("QueryQueueMax"));
|
||||
obj["total_queries"] = StatusCounter(storageMetrics.getValue("QueryQueue")).getStatus();
|
||||
obj["finished_queries"] = StatusCounter(storageMetrics.getValue("FinishedQueries")).getStatus();
|
||||
try { //FIXME: This field was added in a patch release, the try-catch can be removed for the 7.0 release
|
||||
obj["low_priority_queries"] = StatusCounter(storageMetrics.getValue("LowPriorityQueries")).getStatus();
|
||||
} catch(Error &e) {
|
||||
if(e.code() != error_code_attribute_not_found) throw e;
|
||||
}
|
||||
obj["bytes_queried"] = StatusCounter(storageMetrics.getValue("BytesQueried")).getStatus();
|
||||
obj["keys_queried"] = StatusCounter(storageMetrics.getValue("RowsQueried")).getStatus();
|
||||
obj["mutation_bytes"] = StatusCounter(storageMetrics.getValue("MutationBytes")).getStatus();
|
||||
|
|
@ -1607,6 +1612,7 @@ ACTOR static Future<vector<std::pair<MasterProxyInterface, EventMap>>> getProxie
|
|||
return results;
|
||||
}
|
||||
|
||||
// Returns the number of zones eligble for recruiting new tLogs after failures, to maintain the current replication factor.
|
||||
static int getExtraTLogEligibleZones(const vector<WorkerDetails>& workers, const DatabaseConfiguration& configuration) {
|
||||
std::set<StringRef> allZones;
|
||||
std::map<Key,std::set<StringRef>> dcId_zone;
|
||||
|
|
@ -1700,6 +1706,8 @@ ACTOR static Future<JsonBuilderObject> workloadStatusFetcher(Reference<AsyncVar<
|
|||
StatusCounter mutations;
|
||||
StatusCounter mutationBytes;
|
||||
StatusCounter txnConflicts;
|
||||
StatusCounter txnRejectedForQueuedTooLong;
|
||||
|
||||
StatusCounter txnStartOut;
|
||||
StatusCounter txnSystemPriorityStartOut;
|
||||
StatusCounter txnDefaultPriorityStartOut;
|
||||
|
|
@ -1712,6 +1720,7 @@ ACTOR static Future<JsonBuilderObject> workloadStatusFetcher(Reference<AsyncVar<
|
|||
mutations.updateValues( StatusCounter(ps.getValue("Mutations")) );
|
||||
mutationBytes.updateValues( StatusCounter(ps.getValue("MutationBytes")) );
|
||||
txnConflicts.updateValues( StatusCounter(ps.getValue("TxnConflicts")) );
|
||||
txnRejectedForQueuedTooLong.updateValues(StatusCounter(ps.getValue("TxnRejectedForQueuedTooLong")));
|
||||
txnStartOut.updateValues( StatusCounter(ps.getValue("TxnStartOut")) );
|
||||
txnSystemPriorityStartOut.updateValues(StatusCounter(ps.getValue("TxnSystemPriorityStartOut")));
|
||||
txnDefaultPriorityStartOut.updateValues(StatusCounter(ps.getValue("TxnDefaultPriorityStartOut")));
|
||||
|
|
@ -1731,6 +1740,7 @@ ACTOR static Future<JsonBuilderObject> workloadStatusFetcher(Reference<AsyncVar<
|
|||
JsonBuilderObject transactions;
|
||||
transactions["conflicted"] = txnConflicts.getStatus();
|
||||
transactions["started"] = txnStartOut.getStatus();
|
||||
transactions["rejected_for_queued_too_long"] = txnRejectedForQueuedTooLong.getStatus();
|
||||
transactions["started_immediate_priority"] = txnSystemPriorityStartOut.getStatus();
|
||||
transactions["started_default_priority"] = txnDefaultPriorityStartOut.getStatus();
|
||||
transactions["started_batch_priority"] = txnBatchPriorityStartOut.getStatus();
|
||||
|
|
@ -1819,6 +1829,7 @@ ACTOR static Future<JsonBuilderObject> workloadStatusFetcher(Reference<AsyncVar<
|
|||
StatusCounter reads;
|
||||
StatusCounter readKeys;
|
||||
StatusCounter readBytes;
|
||||
StatusCounter lowPriorityReads;
|
||||
|
||||
for(auto &ss : storageServers.get()) {
|
||||
TraceEventFields const& storageMetrics = ss.second.at("StorageMetrics");
|
||||
|
|
@ -1836,6 +1847,19 @@ ACTOR static Future<JsonBuilderObject> workloadStatusFetcher(Reference<AsyncVar<
|
|||
keysObj["read"] = readKeys.getStatus();
|
||||
bytesObj["read"] = readBytes.getStatus();
|
||||
|
||||
try {
|
||||
for(auto &ss : storageServers.get()) {
|
||||
TraceEventFields const& storageMetrics = ss.second.at("StorageMetrics");
|
||||
|
||||
if (storageMetrics.size() > 0) {
|
||||
//FIXME: This field was added in a patch release, for the 7.0 release move this to above loop
|
||||
lowPriorityReads.updateValues(StatusCounter(storageMetrics.getValue("LowPriorityQueries")));
|
||||
}
|
||||
}
|
||||
operationsObj["low_priority_reads"] = lowPriorityReads.getStatus();
|
||||
} catch(Error &e) {
|
||||
if(e.code() != error_code_attribute_not_found) throw e;
|
||||
}
|
||||
}
|
||||
catch (Error& e) {
|
||||
if (e.code() == error_code_actor_cancelled)
|
||||
|
|
@ -1944,7 +1968,14 @@ static JsonBuilderObject tlogFetcher(int* logFaultTolerance, const std::vector<T
|
|||
if(currentFaultTolerance >= 0) {
|
||||
localSetsWithNonNegativeFaultTolerance++;
|
||||
}
|
||||
minFaultTolerance = std::min(minFaultTolerance, currentFaultTolerance);
|
||||
|
||||
if (tLogs[i].locality == tagLocalitySatellite) {
|
||||
// FIXME: This hack to bump satellite fault tolerance, is to make it consistent
|
||||
// with 6.2.
|
||||
minFaultTolerance = std::min(minFaultTolerance, currentFaultTolerance + 1);
|
||||
} else {
|
||||
minFaultTolerance = std::min(minFaultTolerance, currentFaultTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
if (tLogs[i].isLocal && tLogs[i].locality == tagLocalitySatellite) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@
|
|||
#include "fdbserver/Knobs.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
const StringRef STORAGESERVER_HISTOGRAM_GROUP = LiteralStringRef("StorageServer");
|
||||
const StringRef FETCH_KEYS_LATENCY_HISTOGRAM = LiteralStringRef("FetchKeysLatency");
|
||||
const StringRef FETCH_KEYS_BYTES_HISTOGRAM = LiteralStringRef("FetchKeysSize");
|
||||
const StringRef FETCH_KEYS_BYTES_PER_SECOND_HISTOGRAM = LiteralStringRef("FetchKeysBandwidth");
|
||||
|
||||
struct StorageMetricSample {
|
||||
IndexedSet<Key, int64_t> sample;
|
||||
int64_t metricUnitsPerSample;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include "fdbserver/WaitFailure.h"
|
||||
#include "fdbserver/RecoveryState.h"
|
||||
#include "fdbserver/FDBExecHelper.actor.h"
|
||||
#include "flow/Histogram.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
using std::pair;
|
||||
|
|
@ -332,6 +333,7 @@ struct TLogData : NonCopyable {
|
|||
FlowLock concurrentLogRouterReads;
|
||||
FlowLock persistentDataCommitLock;
|
||||
|
||||
// Beginning of fields used by snapshot based backup and restore
|
||||
bool ignorePopRequest; // ignore pop request from storage servers
|
||||
double ignorePopDeadline; // time until which the ignorePopRequest will be
|
||||
// honored
|
||||
|
|
@ -343,19 +345,26 @@ struct TLogData : NonCopyable {
|
|||
std::map<Tag, Version> toBePopped; // map of Tag->Version for all the pops
|
||||
// that came when ignorePopRequest was set
|
||||
Reference<AsyncVar<bool>> degraded;
|
||||
// End of fields used by snapshot based backup and restore
|
||||
|
||||
std::vector<TagsAndMessage> tempTagMessages;
|
||||
|
||||
TLogData(UID dbgid, UID workerID, IKeyValueStore* persistentData, IDiskQueue * persistentQueue, Reference<AsyncVar<ServerDBInfo>> dbInfo, Reference<AsyncVar<bool>> degraded, std::string folder)
|
||||
: dbgid(dbgid), workerID(workerID), instanceID(deterministicRandom()->randomUniqueID().first()),
|
||||
persistentData(persistentData), rawPersistentQueue(persistentQueue), persistentQueue(new TLogQueue(persistentQueue, dbgid)),
|
||||
dbInfo(dbInfo), degraded(degraded), queueCommitBegin(0), queueCommitEnd(0),
|
||||
diskQueueCommitBytes(0), largeDiskQueueCommitBytes(false), bytesInput(0), bytesDurable(0), targetVolatileBytes(SERVER_KNOBS->TLOG_SPILL_THRESHOLD), overheadBytesInput(0), overheadBytesDurable(0),
|
||||
peekMemoryLimiter(SERVER_KNOBS->TLOG_SPILL_REFERENCE_MAX_PEEK_MEMORY_BYTES),
|
||||
concurrentLogRouterReads(SERVER_KNOBS->CONCURRENT_LOG_ROUTER_READS),
|
||||
ignorePopRequest(false), ignorePopDeadline(), ignorePopUid(), dataFolder(folder), toBePopped()
|
||||
{
|
||||
cx = openDBOnServer(dbInfo, TaskPriority::DefaultEndpoint, true, true);
|
||||
}
|
||||
Reference<Histogram> commitLatencyDist;
|
||||
|
||||
TLogData(UID dbgid, UID workerID, IKeyValueStore* persistentData, IDiskQueue* persistentQueue,
|
||||
Reference<AsyncVar<ServerDBInfo>> dbInfo, Reference<AsyncVar<bool>> degraded, std::string folder)
|
||||
: dbgid(dbgid), workerID(workerID), instanceID(deterministicRandom()->randomUniqueID().first()),
|
||||
persistentData(persistentData), rawPersistentQueue(persistentQueue),
|
||||
persistentQueue(new TLogQueue(persistentQueue, dbgid)), dbInfo(dbInfo), degraded(degraded), queueCommitBegin(0),
|
||||
queueCommitEnd(0), diskQueueCommitBytes(0), largeDiskQueueCommitBytes(false), bytesInput(0), bytesDurable(0),
|
||||
targetVolatileBytes(SERVER_KNOBS->TLOG_SPILL_THRESHOLD), overheadBytesInput(0), overheadBytesDurable(0),
|
||||
peekMemoryLimiter(SERVER_KNOBS->TLOG_SPILL_REFERENCE_MAX_PEEK_MEMORY_BYTES),
|
||||
concurrentLogRouterReads(SERVER_KNOBS->CONCURRENT_LOG_ROUTER_READS), ignorePopRequest(false),
|
||||
ignorePopDeadline(), ignorePopUid(), dataFolder(folder), toBePopped(),
|
||||
commitLatencyDist(Histogram::getHistogram(LiteralStringRef("tLog"), LiteralStringRef("commit"),
|
||||
Histogram::Unit::microseconds)) {
|
||||
cx = openDBOnServer(dbInfo, TaskPriority::DefaultEndpoint, true, true);
|
||||
}
|
||||
};
|
||||
|
||||
struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
||||
|
|
@ -441,13 +450,19 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
|||
bool stopped, initialized;
|
||||
DBRecoveryCount recoveryCount;
|
||||
|
||||
// If persistentDataVersion != persistentDurableDataVersion,
|
||||
// then spilling is happening from persistentDurableDataVersion to persistentDataVersion.
|
||||
// Data less than persistentDataDurableVersion is spilled on disk (or fully popped from the TLog);
|
||||
VersionMetricHandle persistentDataVersion, persistentDataDurableVersion; // The last version number in the portion of the log (written|durable) to persistentData
|
||||
NotifiedVersion version, queueCommittedVersion;
|
||||
NotifiedVersion version;
|
||||
NotifiedVersion queueCommittedVersion; // The disk queue has committed up until the queueCommittedVersion version.
|
||||
Version queueCommittingVersion;
|
||||
Version knownCommittedVersion, durableKnownCommittedVersion, minKnownCommittedVersion;
|
||||
Version queuePoppedVersion;
|
||||
Version knownCommittedVersion; // The maximum version that a proxy has told us that is committed (all TLogs have
|
||||
// ack'd a commit for this version).
|
||||
Version durableKnownCommittedVersion, minKnownCommittedVersion;
|
||||
Version queuePoppedVersion; // The disk queue has been popped up until the location which represents this version.
|
||||
Version minPoppedTagVersion;
|
||||
Tag minPoppedTag;
|
||||
Tag minPoppedTag; // The tag that makes tLog hold its data and cause tLog's disk queue increasing.
|
||||
|
||||
Deque<std::pair<Version, Standalone<VectorRef<uint8_t>>>> messageBlocks;
|
||||
std::vector<std::vector<Reference<TagData>>> tag_data; //tag.locality | tag.id
|
||||
|
|
@ -490,7 +505,8 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
|||
Version unrecoveredBefore, recoveredAt;
|
||||
|
||||
struct PeekTrackerData {
|
||||
std::map<int, Promise<std::pair<Version, bool>>> sequence_version;
|
||||
std::map<int, Promise<std::pair<Version, bool>>>
|
||||
sequence_version; // second: Version is peeked begin version. bool is onlySpilled
|
||||
double lastUpdate;
|
||||
|
||||
Tag tag;
|
||||
|
|
@ -565,12 +581,15 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
|||
queueCommittedVersion.initMetric(LiteralStringRef("TLog.QueueCommittedVersion"), cc.id);
|
||||
|
||||
specialCounter(cc, "Version", [this](){ return this->version.get(); });
|
||||
specialCounter(cc, "QueueCommittedVersion", [this](){ return this->queueCommittedVersion.get(); });
|
||||
specialCounter(cc, "QueueCommittedVersion", [this]() { return this->queueCommittedVersion.get(); });
|
||||
specialCounter(cc, "PersistentDataVersion", [this](){ return this->persistentDataVersion; });
|
||||
specialCounter(cc, "PersistentDataDurableVersion", [this](){ return this->persistentDataDurableVersion; });
|
||||
specialCounter(cc, "KnownCommittedVersion", [this](){ return this->knownCommittedVersion; });
|
||||
specialCounter(cc, "QueuePoppedVersion", [this](){ return this->queuePoppedVersion; });
|
||||
specialCounter(cc, "MinPoppedTagVersion", [this](){ return this->minPoppedTagVersion; });
|
||||
specialCounter(cc, "MinPoppedTagVersion", [this]() { return this->minPoppedTagVersion; });
|
||||
// The locality and id of the tag that is responsible for making the TLog hold onto its oldest piece of data.
|
||||
// If disk queues are growing and no one is sure why, then you shall look at this to find the tag responsible
|
||||
// for why the TLog thinks it can't throw away data.
|
||||
specialCounter(cc, "MinPoppedTagLocality", [this](){ return this->minPoppedTag.locality; });
|
||||
specialCounter(cc, "MinPoppedTagId", [this](){ return this->minPoppedTag.id; });
|
||||
specialCounter(cc, "SharedBytesInput", [tLogData](){ return tLogData->bytesInput; });
|
||||
|
|
@ -587,6 +606,7 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
|
|||
specialCounter(cc, "QueueDiskBytesTotal", [tLogData](){ return tLogData->rawPersistentQueue->getStorageBytes().total; });
|
||||
specialCounter(cc, "PeekMemoryReserved", [tLogData]() { return tLogData->peekMemoryLimiter.activePermits(); });
|
||||
specialCounter(cc, "PeekMemoryRequestsStalled", [tLogData]() { return tLogData->peekMemoryLimiter.waiters(); });
|
||||
specialCounter(cc, "Generation", [this]() { return this->recoveryCount; });
|
||||
}
|
||||
|
||||
~LogData() {
|
||||
|
|
@ -791,6 +811,9 @@ ACTOR Future<Void> updatePoppedLocation( TLogData* self, Reference<LogData> logD
|
|||
return Void();
|
||||
}
|
||||
|
||||
// It runs against the oldest TLog instance, calculates the first location in the disk queue that contains un-popped
|
||||
// data, and then issues a pop to the disk queue at that location so that anything earlier can be
|
||||
// removed/forgotten/overwritten. In effect, it applies the effect of TLogPop RPCs to disk.
|
||||
ACTOR Future<Void> popDiskQueue( TLogData* self, Reference<LogData> logData ) {
|
||||
if (!logData->initialized) return Void();
|
||||
|
||||
|
|
@ -1005,20 +1028,6 @@ ACTOR Future<Void> updatePersistentData( TLogData* self, Reference<LogData> logD
|
|||
}
|
||||
|
||||
ACTOR Future<Void> tLogPopCore( TLogData* self, Tag inputTag, Version to, Reference<LogData> logData ) {
|
||||
if (self->ignorePopRequest) {
|
||||
TraceEvent(SevDebug, "IgnoringPopRequest").detail("IgnorePopDeadline", self->ignorePopDeadline);
|
||||
|
||||
if (self->toBePopped.find(inputTag) == self->toBePopped.end()
|
||||
|| to > self->toBePopped[inputTag]) {
|
||||
self->toBePopped[inputTag] = to;
|
||||
}
|
||||
// add the pop to the toBePopped map
|
||||
TraceEvent(SevDebug, "IgnoringPopRequest")
|
||||
.detail("IgnorePopDeadline", self->ignorePopDeadline)
|
||||
.detail("Tag", inputTag.toString())
|
||||
.detail("Version", to);
|
||||
return Void();
|
||||
}
|
||||
state Version upTo = to;
|
||||
int8_t tagLocality = inputTag.locality;
|
||||
if (isPseudoLocality(tagLocality)) {
|
||||
|
|
@ -1054,38 +1063,55 @@ ACTOR Future<Void> tLogPopCore( TLogData* self, Tag inputTag, Version to, Refere
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> tLogPop( TLogData* self, TLogPopRequest req, Reference<LogData> logData ) {
|
||||
// timeout check for ignorePopRequest
|
||||
if (self->ignorePopRequest && (g_network->now() > self->ignorePopDeadline)) {
|
||||
|
||||
TraceEvent("EnableTLogPlayAllIgnoredPops");
|
||||
// use toBePopped and issue all the pops
|
||||
std::map<Tag, Version>::iterator it;
|
||||
vector<Future<Void>> ignoredPops;
|
||||
self->ignorePopRequest = false;
|
||||
self->ignorePopUid = "";
|
||||
self->ignorePopDeadline = 0.0;
|
||||
for (it = self->toBePopped.begin(); it != self->toBePopped.end(); it++) {
|
||||
TraceEvent("PlayIgnoredPop")
|
||||
.detail("Tag", it->first.toString())
|
||||
.detail("Version", it->second);
|
||||
ignoredPops.push_back(tLogPopCore(self, it->first, it->second, logData));
|
||||
ACTOR Future<Void> processPopRequests(TLogData* self, Reference<LogData> logData) {
|
||||
state std::vector<Future<Void>> ignoredPops;
|
||||
state std::map<Tag, Version>::const_iterator it;
|
||||
state int ignoredPopsPlayed = 0;
|
||||
state std::map<Tag, Version> toBePopped;
|
||||
toBePopped = std::move(self->toBePopped);
|
||||
self->toBePopped.clear();
|
||||
self->ignorePopRequest = false;
|
||||
self->ignorePopDeadline = 0.0;
|
||||
self->ignorePopUid = "";
|
||||
for (it = toBePopped.cbegin(); it != toBePopped.cend(); ++it) {
|
||||
const auto& [tag, version] = *it;
|
||||
TraceEvent("PlayIgnoredPop").detail("Tag", tag.toString()).detail("Version", version);
|
||||
ignoredPops.push_back(tLogPopCore(self, tag, version, logData));
|
||||
if (++ignoredPopsPlayed % SERVER_KNOBS->TLOG_POP_BATCH_SIZE == 0) {
|
||||
TEST(true); // Yielding while processing pop requests
|
||||
wait(yield());
|
||||
}
|
||||
self->toBePopped.clear();
|
||||
wait(waitForAll(ignoredPops));
|
||||
TraceEvent("ResetIgnorePopRequest")
|
||||
.detail("Now", g_network->now())
|
||||
.detail("IgnorePopRequest", self->ignorePopRequest)
|
||||
.detail("IgnorePopDeadline", self->ignorePopDeadline);
|
||||
}
|
||||
wait(tLogPopCore(self, req.tag, req.to, logData));
|
||||
wait(waitForAll(ignoredPops));
|
||||
TraceEvent("ResetIgnorePopRequest")
|
||||
.detail("IgnorePopRequest", self->ignorePopRequest)
|
||||
.detail("IgnorePopDeadline", self->ignorePopDeadline);
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> tLogPop( TLogData* self, TLogPopRequest req, Reference<LogData> logData ) {
|
||||
if (self->ignorePopRequest) {
|
||||
TraceEvent(SevDebug, "IgnoringPopRequest").detail("IgnorePopDeadline", self->ignorePopDeadline);
|
||||
|
||||
auto& v = self->toBePopped[req.tag];
|
||||
v = std::max(v, req.to);
|
||||
|
||||
TraceEvent(SevDebug, "IgnoringPopRequest")
|
||||
.detail("IgnorePopDeadline", self->ignorePopDeadline)
|
||||
.detail("Tag", req.tag.toString())
|
||||
.detail("Version", req.to);
|
||||
} else {
|
||||
wait(tLogPopCore(self, req.tag, req.to, logData));
|
||||
}
|
||||
req.reply.send(Void());
|
||||
return Void();
|
||||
}
|
||||
|
||||
// This function (and updatePersistentData, which is called by this function) run at a low priority and can soak up all CPU resources.
|
||||
// For this reason, they employ aggressive use of yields to avoid causing slow tasks that could introduce latencies for more important
|
||||
// work (e.g. commits).
|
||||
// This function (and updatePersistentData, which is called by this function) run at a low priority and can soak up all
|
||||
// CPU resources. For this reason, they employ aggressive use of yields to avoid causing slow tasks that could introduce
|
||||
// latencies for more important work (e.g. commits).
|
||||
// This actor is just a loop that calls updatePersistentData and popDiskQueue whenever
|
||||
// (a) there's data to be spilled or (b) we should update metadata after some commits have been fully popped.
|
||||
ACTOR Future<Void> updateStorage( TLogData* self ) {
|
||||
while(self->spillOrder.size() && !self->id_data.count(self->spillOrder.front())) {
|
||||
self->spillOrder.pop_front();
|
||||
|
|
@ -1719,19 +1745,6 @@ ACTOR Future<Void> tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> watchDegraded(TLogData* self) {
|
||||
if(g_network->isSimulated() && g_simulator.speedUpSimulation) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
wait(lowPriorityDelay(SERVER_KNOBS->TLOG_DEGRADED_DURATION));
|
||||
|
||||
TraceEvent(SevWarnAlways, "TLogDegraded", self->dbgid);
|
||||
TEST(true); //TLog degraded
|
||||
self->degraded->set(true);
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> doQueueCommit( TLogData* self, Reference<LogData> logData, std::vector<Reference<LogData>> missingFinalCommit ) {
|
||||
state Version ver = logData->version.get();
|
||||
state Version commitNumber = self->queueCommitBegin+1;
|
||||
|
|
@ -1744,12 +1757,11 @@ ACTOR Future<Void> doQueueCommit( TLogData* self, Reference<LogData> logData, st
|
|||
self->diskQueueCommitBytes = 0;
|
||||
self->largeDiskQueueCommitBytes.set(false);
|
||||
|
||||
state Future<Void> degraded = watchDegraded(self);
|
||||
wait(c);
|
||||
wait(ioDegradedOrTimeoutError(c, SERVER_KNOBS->MAX_STORAGE_COMMIT_TIME, self->degraded,
|
||||
SERVER_KNOBS->TLOG_DEGRADED_DURATION));
|
||||
if(g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) {
|
||||
wait(delay(6.0));
|
||||
}
|
||||
degraded.cancel();
|
||||
wait(self->queueCommitEnd.whenAtLeast(commitNumber-1));
|
||||
|
||||
//Calling check_yield instead of yield to avoid a destruction ordering problem in simulation
|
||||
|
|
@ -1867,7 +1879,11 @@ ACTOR Future<Void> tLogCommit(
|
|||
return Void();
|
||||
}
|
||||
|
||||
if (logData->version.get() == req.prevVersion) { // Not a duplicate (check relies on critical section between here self->version.set() below!)
|
||||
state double beforeCommitT = now();
|
||||
|
||||
// Not a duplicate (check relies on critical section between here self->version.set() below!)
|
||||
state bool isNotDuplicate = (logData->version.get() == req.prevVersion);
|
||||
if (isNotDuplicate) {
|
||||
if(req.debugID.present())
|
||||
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.Before");
|
||||
|
||||
|
|
@ -1905,6 +1921,10 @@ ACTOR Future<Void> tLogCommit(
|
|||
return Void();
|
||||
}
|
||||
|
||||
if (isNotDuplicate) {
|
||||
self->commitLatencyDist->sampleSeconds(now() - beforeCommitT);
|
||||
}
|
||||
|
||||
if(req.debugID.present())
|
||||
g_traceBatch.addEvent("CommitDebug", tlogDebugID.get().first(), "TLog.tLogCommit.After");
|
||||
|
||||
|
|
@ -2130,30 +2150,16 @@ tLogEnablePopReq(TLogEnablePopRequest enablePopReq, TLogData* self, Reference<Lo
|
|||
enablePopReq.reply.sendError(operation_failed());
|
||||
return Void();
|
||||
}
|
||||
TraceEvent("EnableTLogPlayAllIgnoredPops2");
|
||||
// use toBePopped and issue all the pops
|
||||
std::map<Tag, Version>::iterator it;
|
||||
state vector<Future<Void>> ignoredPops;
|
||||
self->ignorePopRequest = false;
|
||||
self->ignorePopDeadline = 0.0;
|
||||
self->ignorePopUid = "";
|
||||
for (it = self->toBePopped.begin(); it != self->toBePopped.end(); it++) {
|
||||
TraceEvent("PlayIgnoredPop")
|
||||
.detail("Tag", it->first.toString())
|
||||
.detail("Version", it->second);
|
||||
ignoredPops.push_back(tLogPopCore(self, it->first, it->second, logData));
|
||||
}
|
||||
TraceEvent("TLogExecCmdPopEnable")
|
||||
.detail("UidStr", enablePopReq.snapUID.toString())
|
||||
.detail("IgnorePopUid", self->ignorePopUid)
|
||||
.detail("IgnporePopRequest", self->ignorePopRequest)
|
||||
.detail("IgnporePopDeadline", self->ignorePopDeadline)
|
||||
.detail("PersistentDataVersion", logData->persistentDataVersion)
|
||||
.detail("PersistentDatadurableVersion", logData->persistentDataDurableVersion)
|
||||
.detail("QueueCommittedVersion", logData->queueCommittedVersion.get())
|
||||
.detail("Version", logData->version.get());
|
||||
wait(waitForAll(ignoredPops));
|
||||
self->toBePopped.clear();
|
||||
TraceEvent("EnableTLogPlayAllIgnoredPops2")
|
||||
.detail("UidStr", enablePopReq.snapUID.toString())
|
||||
.detail("IgnorePopUid", self->ignorePopUid)
|
||||
.detail("IgnorePopRequest", self->ignorePopRequest)
|
||||
.detail("IgnorePopDeadline", self->ignorePopDeadline)
|
||||
.detail("PersistentDataVersion", logData->persistentDataVersion)
|
||||
.detail("PersistentDataDurableVersion", logData->persistentDataDurableVersion)
|
||||
.detail("QueueCommittedVersion", logData->queueCommittedVersion.get())
|
||||
.detail("Version", logData->version.get());
|
||||
wait(processPopRequests(self, logData));
|
||||
enablePopReq.reply.send(Void());
|
||||
return Void();
|
||||
}
|
||||
|
|
@ -2243,6 +2249,11 @@ ACTOR Future<Void> serveTLogInterface( TLogData* self, TLogInterface tli, Refere
|
|||
when( TLogSnapRequest snapReq = waitNext( tli.snapRequest.getFuture() ) ) {
|
||||
logData->addActor.send( tLogSnapCreate( snapReq, self, logData) );
|
||||
}
|
||||
when(wait(self->ignorePopRequest ? delayUntil(self->ignorePopDeadline) : Never())) {
|
||||
TEST(true); // Hit ignorePopDeadline
|
||||
TraceEvent("EnableTLogPlayAllIgnoredPops").detail("IgnoredPopDeadline", self->ignorePopDeadline);
|
||||
logData->addActor.send(processPopRequests(self, logData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2265,6 +2276,7 @@ void removeLog( TLogData* self, Reference<LogData> logData ) {
|
|||
}
|
||||
}
|
||||
|
||||
// remote tLog pull data from log routers
|
||||
ACTOR Future<Void> pullAsyncData( TLogData* self, Reference<LogData> logData, std::vector<Tag> tags, Version beginVersion, Optional<Version> endVersion, bool poppedIsKnownCommitted ) {
|
||||
state Future<Void> dbInfoChange = Void();
|
||||
state Reference<ILogSystem::IPeekCursor> r;
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ OldTLogCoreData::OldTLogCoreData(const OldLogData& oldData)
|
|||
struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogSystem> {
|
||||
const UID dbgid;
|
||||
LogSystemType logSystemType;
|
||||
std::vector<Reference<LogSet>> tLogs; // LogSets in different locations: primary, remote or satellite
|
||||
std::vector<Reference<LogSet>> tLogs; // LogSets in different locations: primary, satellite, or remote
|
||||
int expectedLogSets;
|
||||
int logRouterTags;
|
||||
int txsTags;
|
||||
|
|
@ -195,7 +195,14 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
Version knownCommittedVersion;
|
||||
Version backupStartVersion = invalidVersion; // max(tLogs[0].startVersion, previous epochEnd).
|
||||
LocalityData locality;
|
||||
std::map< std::pair<UID, Tag>, std::pair<Version, Version> > outstandingPops; // For each currently running popFromLog actor, (log server #, tag)->popped version
|
||||
// For each currently running popFromLog actor, outstandingPops is
|
||||
// (logID, tag)->(max popped version, durableKnownCommittedVersion).
|
||||
// Why do we need durableKnownCommittedVersion? knownCommittedVersion gives the lower bound of what data
|
||||
// will need to be copied into the next generation to restore the replication factor.
|
||||
// Guess: It probably serves as a minimum version of what data should be on a TLog in the next generation and
|
||||
// sending a pop for anything less than durableKnownCommittedVersion for the TLog will be absurd.
|
||||
std::map<std::pair<UID, Tag>, std::pair<Version, Version>> outstandingPops;
|
||||
|
||||
Optional<PromiseStream<Future<Void>>> addActor;
|
||||
ActorCollection popActors;
|
||||
std::vector<OldLogData> oldLogData; // each element has the log info. in one old epoch.
|
||||
|
|
@ -270,6 +277,9 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
Version& localityVersion = pseudoLocalityPopVersion[tag];
|
||||
localityVersion = std::max(localityVersion, upTo);
|
||||
Version minVersion = localityVersion;
|
||||
// Why do we need to use the minimum popped version among all tags? Reason: for example,
|
||||
// 2 pseudo tags pop 100 or 150, respectively. It's only safe to pop min(100, 150),
|
||||
// because [101,150) is needed by another pseudo tag.
|
||||
for (const int8_t locality : pseudoLocalities) {
|
||||
minVersion = std::min(minVersion, pseudoLocalityPopVersion[Tag(locality, tag.id)]);
|
||||
}
|
||||
|
|
@ -340,6 +350,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
return logSystem;
|
||||
}
|
||||
|
||||
// Convert TagPartitionedLogSystem to DBCoreState and override input newState as return value
|
||||
void toCoreState(DBCoreState& newState) final {
|
||||
if( recoveryComplete.isValid() && recoveryComplete.isError() )
|
||||
throw recoveryComplete.getError();
|
||||
|
|
@ -948,6 +959,10 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
}
|
||||
}
|
||||
|
||||
// LogRouter or BackupWorker use this function to obtain a cursor for peeking tlogs of a generation (i.e., epoch).
|
||||
// Specifically, the epoch is determined by looking up "dbgid" in tlog sets of generations.
|
||||
// The returned cursor can peek data at the "tag" from the given "begin" version to that epoch's end version or
|
||||
// the recovery version for the latest old epoch. For the current epoch, the cursor has no end version.
|
||||
Reference<IPeekCursor> peekLogRouter(UID dbgid, Version begin, Tag tag) final {
|
||||
bool found = false;
|
||||
for (const auto& log : tLogs) {
|
||||
|
|
@ -1038,7 +1053,12 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
bestSet = bestSatelliteSet;
|
||||
}
|
||||
|
||||
TraceEvent("TLogPeekLogRouterOldSets", dbgid).detail("Tag", tag.toString()).detail("Begin", begin).detail("OldEpoch", old.epochEnd).detail("RecoveredAt", recoveredAt.present() ? recoveredAt.get() : -1).detail("FirstOld", firstOld);
|
||||
TraceEvent("TLogPeekLogRouterOldSets", dbgid)
|
||||
.detail("Tag", tag.toString())
|
||||
.detail("Begin", begin)
|
||||
.detail("OldEpoch", old.epochEnd)
|
||||
.detail("RecoveredAt", recoveredAt.present() ? recoveredAt.get() : -1)
|
||||
.detail("FirstOld", firstOld);
|
||||
//FIXME: do this merge on one of the logs in the other data center to avoid sending multiple copies across the WAN
|
||||
return Reference<ILogSystem::SetPeekCursor>( new ILogSystem::SetPeekCursor( localSets, bestSet, localSets[bestSet]->bestLocationFor( tag ), tag, begin, firstOld && recoveredAt.present() ? recoveredAt.get() + 1 : old.epochEnd, true ) );
|
||||
}
|
||||
|
|
@ -1109,6 +1129,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
}
|
||||
}
|
||||
|
||||
// pop 'tag.locality' type data up to the 'upTo' version
|
||||
void pop(Version upTo, Tag tag, Version durableKnownCommittedVersion, int8_t popLocality) final {
|
||||
if (upTo <= 0) return;
|
||||
if (tag.locality == tagLocalityRemoteLog) {
|
||||
|
|
@ -1134,6 +1155,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
}
|
||||
}
|
||||
|
||||
// pop tag from log up to the version defined in self->outstandingPops[].first
|
||||
ACTOR static Future<Void> popFromLog(TagPartitionedLogSystem* self,
|
||||
Reference<AsyncVar<OptionalInterface<TLogInterface>>> log, Tag tag,
|
||||
double time) {
|
||||
|
|
@ -1141,6 +1163,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted<TagPartitionedLogS
|
|||
loop {
|
||||
wait( delay(time, TaskPriority::TLogPop) );
|
||||
|
||||
// to: first is upto version, second is durableKnownComittedVersion
|
||||
state std::pair<Version,Version> to = self->outstandingPops[ std::make_pair(log->get().id(),tag) ];
|
||||
|
||||
if (to.first <= last) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "fdbserver/VFSAsync.h"
|
||||
|
||||
/*
|
||||
** The maximum pathname length supported by this VFS.
|
||||
*/
|
||||
|
|
@ -58,43 +60,26 @@
|
|||
#define EXCLUSIVE_LOCK 4
|
||||
const uint32_t RESERVED_COUNT = 1U<<29;
|
||||
|
||||
/*
|
||||
** When using this VFS, the sqlite3_file* handles that SQLite uses are
|
||||
** actually pointers to instances of type VFSAsyncFile.
|
||||
*/
|
||||
typedef struct VFSAsyncFile VFSAsyncFile;
|
||||
struct VFSAsyncFile {
|
||||
sqlite3_file base; /* Base class. Must be first. */
|
||||
int flags;
|
||||
std::string filename;
|
||||
Reference<IAsyncFile> file;
|
||||
VFSAsyncFile::VFSAsyncFile(std::string const& filename, int flags)
|
||||
: filename(filename), flags(flags), pLockCount(&filename_lockCount_openCount[filename].first), debug_zcrefs(0), debug_zcreads(0), debug_reads(0), chunkSize(0) {
|
||||
filename_lockCount_openCount[filename].second++;
|
||||
|
||||
uint32_t * const pLockCount; // +1 for each SHARED_LOCK, or 1+X_COUNT for lock level X
|
||||
int lockLevel; // NO_LOCK, SHARED_LOCK, RESERVED_LOCK, PENDING_LOCK, or EXCLUSIVE_LOCK
|
||||
TraceEvent(SevDebug, "VFSAsyncFileConstruct")
|
||||
.detail("Filename", filename)
|
||||
.detail("OpenCount", filename_lockCount_openCount[filename].second)
|
||||
.detail("LockCount", filename_lockCount_openCount[filename].first)
|
||||
.backtrace();
|
||||
}
|
||||
|
||||
struct SharedMemoryInfo *sharedMemory;
|
||||
int sharedMemorySharedLocks;
|
||||
int sharedMemoryExclusiveLocks;
|
||||
|
||||
int debug_zcrefs, debug_zcreads, debug_reads;
|
||||
|
||||
int chunkSize;
|
||||
|
||||
VFSAsyncFile(std::string const& filename, int flags) : filename(filename), flags(flags), pLockCount(&filename_lockCount_openCount[filename].first), debug_zcrefs(0), debug_zcreads(0), debug_reads(0), chunkSize(0) {
|
||||
filename_lockCount_openCount[filename].second++;
|
||||
}
|
||||
~VFSAsyncFile();
|
||||
|
||||
static std::map<std::string, std::pair<uint32_t,int>> filename_lockCount_openCount;
|
||||
};
|
||||
std::map<std::string, std::pair<uint32_t,int>> VFSAsyncFile::filename_lockCount_openCount;
|
||||
|
||||
static int asyncClose(sqlite3_file *pFile){
|
||||
VFSAsyncFile *p = (VFSAsyncFile*)pFile;
|
||||
|
||||
/*TraceEvent("VFSAsyncClose").detail("Fd", p->file->debugFD())
|
||||
.detail("Filename", p->filename).detail("ZCRefs", p->debug_zcrefs)
|
||||
.detail("ZCReads", p->debug_zcreads).detail("NormalReads", p->debug_reads).backtrace();*/
|
||||
TraceEvent(SevDebug, "VFSAsyncFileDestroy")
|
||||
.detail("Filename", p->filename)
|
||||
.backtrace();
|
||||
|
||||
//printf("Closing %s: %d zcrefs, %d/%d reads zc\n", filename.c_str(), debug_zcrefs, debug_zcreads, debug_zcreads+debug_reads);
|
||||
ASSERT( !p->debug_zcrefs );
|
||||
|
||||
|
|
@ -112,7 +97,10 @@ static int asyncRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOf
|
|||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
} catch (Error& ) {
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_READ);
|
||||
}
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
}
|
||||
|
|
@ -123,7 +111,10 @@ static int asyncReleaseZeroCopy(sqlite3_file* pFile, void* data, int iAmt, sqlit
|
|||
try{
|
||||
--p->debug_zcrefs;
|
||||
p->file->releaseZeroCopy( data, iAmt, iOfst );
|
||||
} catch (Error& ) {
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR);
|
||||
}
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
|
|
@ -145,7 +136,10 @@ static int asyncReadZeroCopy(sqlite3_file *pFile, void **data, int iAmt, sqlite_
|
|||
}
|
||||
++p->debug_zcreads;
|
||||
return SQLITE_OK;
|
||||
} catch (Error& ) {
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_READ);
|
||||
}
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
}
|
||||
|
|
@ -162,7 +156,10 @@ static int asyncReadZeroCopy(sqlite3_file *pFile, void **data, int iAmt, sqlite_
|
|||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
} catch (Error& ) {
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_READ);
|
||||
}
|
||||
return SQLITE_IOERR_READ;
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +175,10 @@ static int asyncWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_in
|
|||
try {
|
||||
waitFor( p->file->write( zBuf, iAmt, iOfst ) );
|
||||
return SQLITE_OK;
|
||||
} catch(Error& ) {
|
||||
} catch(Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_WRITE);
|
||||
}
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
}
|
||||
|
|
@ -194,7 +194,10 @@ static int asyncTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|||
try {
|
||||
waitFor( p->file->truncate( size ) );
|
||||
return SQLITE_OK;
|
||||
} catch(Error& ) {
|
||||
} catch(Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_TRUNCATE);
|
||||
}
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
}
|
||||
|
|
@ -204,8 +207,12 @@ static int asyncSync(sqlite3_file *pFile, int flags){
|
|||
try {
|
||||
waitFor( p->file->sync() );
|
||||
return SQLITE_OK;
|
||||
} catch (Error& e) {
|
||||
TraceEvent("VFSSyncError")
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_FSYNC);
|
||||
}
|
||||
|
||||
TraceEvent("VFSAsyncFileSyncError")
|
||||
.error(e)
|
||||
.detail("Filename", p->filename)
|
||||
.detail("Sqlite3File", (int64_t)pFile)
|
||||
|
|
@ -223,7 +230,10 @@ static int VFSAsyncFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|||
try {
|
||||
*pSize = waitForAndGet( p->file->size() );
|
||||
return SQLITE_OK;
|
||||
} catch (Error& ) {
|
||||
} catch (Error &e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR_FSTAT);
|
||||
}
|
||||
return SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
}
|
||||
|
|
@ -464,10 +474,20 @@ static int asyncDeviceCharacteristics(sqlite3_file *pFile){ return 0; }
|
|||
}
|
||||
|
||||
VFSAsyncFile::~VFSAsyncFile() {
|
||||
//TraceEvent("VFSAsyncFileDel").detail("Filename", filename);
|
||||
|
||||
TraceEvent(SevDebug, "VFSAsyncFileDestroyStart")
|
||||
.detail("Filename", filename)
|
||||
.detail("OpenCount", filename_lockCount_openCount[filename].second)
|
||||
.detail("LockCount", filename_lockCount_openCount[filename].first)
|
||||
.backtrace();
|
||||
|
||||
if (!--filename_lockCount_openCount[filename].second) {
|
||||
filename_lockCount_openCount.erase(filename);
|
||||
|
||||
TraceEvent(SevDebug, "VFSAsyncFileDestroy")
|
||||
.detail("Filename", filename)
|
||||
.backtrace();
|
||||
|
||||
//Always delete the shared memory when the last copy of the file is deleted. In simulation, this is helpful because "killing" a file without properly closing
|
||||
//it can result in a shared memory state that causes corruption when reopening the killed file. The only expected penalty from doing this
|
||||
//is a potentially slower open operation on a database, but that should happen infrequently.
|
||||
|
|
@ -537,14 +557,15 @@ static int asyncOpen(
|
|||
// Note that SQLiteDB::open also opens the db file, so its flags and modes are important, too
|
||||
p->file = waitForAndGet( IAsyncFileSystem::filesystem()->open( p->filename, oflags, 0600 ) );
|
||||
|
||||
/*TraceEvent("VFSOpened")
|
||||
TraceEvent(SevDebug, "VFSAsyncFileOpened")
|
||||
.detail("Filename", p->filename)
|
||||
.detail("Fd", DEBUG_DETERMINISM ? 0 : p->file->debugFD())
|
||||
.detail("Flags", flags)
|
||||
.detail("Sqlite3File", DEBUG_DETERMINISM ? 0 : (int64_t)pFile)
|
||||
.detail("IAsyncFile", DEBUG_DETERMINISM ? 0 : (int64_t)p->file.getPtr());*/
|
||||
.backtrace();
|
||||
|
||||
} catch (Error& e) {
|
||||
TraceEvent("SQLiteOpenFail").error(e).detail("Filename", p->filename);
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_CANTOPEN);
|
||||
}
|
||||
TraceEvent("VFSAsyncFileOpenError").error(e).detail("Filename", p->filename);
|
||||
p->~VFSAsyncFile();
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
|
|
@ -648,6 +669,9 @@ static int asyncFullPathname(
|
|||
memcpy(zPathOut, s.c_str(), s.size()+1);
|
||||
return SQLITE_OK;
|
||||
} catch (Error& e) {
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_IOERR);
|
||||
}
|
||||
TraceEvent(SevError,"VFSAsyncFullPathnameError").error(e).detail("PathIn", (std::string)zPath);
|
||||
return SQLITE_IOERR;
|
||||
} catch(...) {
|
||||
|
|
@ -716,7 +740,10 @@ static int asyncSleep(sqlite3_vfs *pVfs, int microseconds){
|
|||
waitFor( g_network->delay( microseconds*1e-6, TaskPriority::DefaultDelay ) || simCancel );
|
||||
return microseconds;
|
||||
} catch( Error &e ) {
|
||||
TraceEvent(SevError, "AsyncSleepError").error(e,true);
|
||||
if(e.isInjectedFault()) {
|
||||
VFSAsyncFile::setInjectedError(SQLITE_ERROR);
|
||||
}
|
||||
TraceEvent(SevError, "VFSAsyncSleepError").error(e,true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* VFSAsync.h
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* 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 "sqlite/sqlite3.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "fdbrpc/IAsyncFile.h"
|
||||
|
||||
/*
|
||||
** When using this VFS, the sqlite3_file* handles that SQLite uses are
|
||||
** actually pointers to instances of type VFSAsyncFile.
|
||||
*/
|
||||
typedef struct VFSAsyncFile VFSAsyncFile;
|
||||
struct VFSAsyncFile {
|
||||
sqlite3_file base; /* Base class. Must be first. */
|
||||
int flags;
|
||||
std::string filename;
|
||||
Reference<IAsyncFile> file;
|
||||
|
||||
// The functions setInjectedError() and checkInjectedError() use an INetwork global to store the last
|
||||
// return code from VFSAsyncFile method resulting from catching an injected Error exception. This allows
|
||||
// callers of the SQLite API to determine if an error code returned appears to be due to an injected
|
||||
// error in simulation.
|
||||
//
|
||||
// This scheme is not perfect, as it is possible for non-injected errors to occur after injected errors
|
||||
// and be incorrectly recognized as injected. This problem already existed, however, as the previous scheme
|
||||
// assumed that any SQLite error that occurred after any injected error in the simulated process was
|
||||
// itself injected.
|
||||
//
|
||||
// Unfortunately, there is no easy or reliable way to plumb the injectedness of an error though the return
|
||||
// path of VFSAsyncFile -> SQLite -> SQLite API calls made by KeyValueStoreSQLite.
|
||||
//
|
||||
// An attempt was made to store injected errors in VFSAsyncFile instances and expose a SQLiteDB's file
|
||||
// instances via the SQLite API. This failed, however, because sometimes files are opened, encounter an
|
||||
// error, and are closed within the lifetime of one SQLite API call so the caller never has an opportunity
|
||||
// to access the VFSAsyncFile object or its injected error state.
|
||||
//
|
||||
// An attempt was also made to match SQLite API return codes to VFSAsyncFile injected error return codes on
|
||||
// a 1:1 basis, only flagging a code as rejected if it matches the last injected error code and only once.
|
||||
// This would have been more accurate (though coincidences could occur). This scheme also failed, however,
|
||||
// because sometimes errors from SQLite APIs are temporarily ignored, relying on a subsequent API call to error,
|
||||
// however this error could be different and would not have been produced directly by VFSAsyncFile.
|
||||
//
|
||||
static void setInjectedError(int64_t rc) {
|
||||
g_network->setGlobal(INetwork::enSQLiteInjectedError, (flowGlobalType)rc);
|
||||
TraceEvent("VFSSetInjectedError").detail("ErrorCode", rc).detail("NetworkPtr", (void *)g_network).backtrace();
|
||||
}
|
||||
|
||||
static bool checkInjectedError() {
|
||||
// Error code is only checked for non-zero because the SQLite API error code after an injected error
|
||||
// may not match the error code returned by VFSAsyncFile when the inject error occurred.
|
||||
bool e = g_network->global(INetwork::enSQLiteInjectedError) != (flowGlobalType)0;
|
||||
TraceEvent("VFSCheckInjectedError")
|
||||
.detail("Found", e)
|
||||
.detail("ErrorCode", (int64_t)g_network->global(INetwork::enSQLiteInjectedError))
|
||||
.backtrace();
|
||||
return e;
|
||||
}
|
||||
|
||||
uint32_t * const pLockCount; // +1 for each SHARED_LOCK, or 1+X_COUNT for lock level X
|
||||
int lockLevel; // NO_LOCK, SHARED_LOCK, RESERVED_LOCK, PENDING_LOCK, or EXCLUSIVE_LOCK
|
||||
|
||||
struct SharedMemoryInfo *sharedMemory;
|
||||
int sharedMemorySharedLocks;
|
||||
int sharedMemoryExclusiveLocks;
|
||||
|
||||
int debug_zcrefs, debug_zcreads, debug_reads;
|
||||
|
||||
int chunkSize;
|
||||
|
||||
VFSAsyncFile(std::string const& filename, int flags);
|
||||
~VFSAsyncFile();
|
||||
|
||||
static std::map<std::string, std::pair<uint32_t,int>> filename_lockCount_openCount;
|
||||
};
|
||||
|
|
@ -1649,11 +1649,10 @@ public:
|
|||
|
||||
if (!cacheEntry.initialized()) {
|
||||
debug_printf("DWALPager(%s) issuing actual read of %s\n", filename.c_str(), toString(pageID).c_str());
|
||||
cacheEntry.readFuture = readPhysicalPage(this, (PhysicalPageID)pageID);
|
||||
cacheEntry.readFuture = forwardError(readPhysicalPage(this, (PhysicalPageID)pageID), errorPromise);
|
||||
cacheEntry.writeFuture = Void();
|
||||
}
|
||||
|
||||
cacheEntry.readFuture = forwardError(cacheEntry.readFuture, errorPromise);
|
||||
return cacheEntry.readFuture;
|
||||
}
|
||||
|
||||
|
|
@ -1837,6 +1836,7 @@ public:
|
|||
state Version minStopVersion = cutoff.version - (BUGGIFY ? deterministicRandom()->randomInt(0, 10) : (self->remapCleanupWindow * SERVER_KNOBS->REDWOOD_REMAP_CLEANUP_LAG));
|
||||
self->remapDestinationsSimOnly.clear();
|
||||
|
||||
state int sinceYield = 0;
|
||||
loop {
|
||||
state Optional<RemappedPage> p = wait(self->remapQueue.pop(cutoff));
|
||||
debug_printf("DWALPager(%s) remapCleanup popped %s\n", self->filename.c_str(), ::toString(p).c_str());
|
||||
|
|
@ -1855,6 +1855,11 @@ public:
|
|||
if (self->remapCleanupStop && p.get().version >= minStopVersion) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(++sinceYield >= 100) {
|
||||
sinceYield = 0;
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
debug_printf("DWALPager(%s) remapCleanup stopped (stop=%d)\n", self->filename.c_str(), self->remapCleanupStop);
|
||||
|
|
|
|||
|
|
@ -210,8 +210,10 @@ struct RecruitFromConfigurationReply {
|
|||
std::vector<WorkerInterface> proxies;
|
||||
std::vector<WorkerInterface> resolvers;
|
||||
std::vector<WorkerInterface> storageServers;
|
||||
std::vector<WorkerInterface> oldLogRouters;
|
||||
Optional<Key> dcId;
|
||||
std::vector<WorkerInterface> oldLogRouters; // During recovery, log routers for older generations will be recruited.
|
||||
Optional<Key> dcId; // dcId is where master is recruited. It prefers to be in configuration.primaryDcId, but
|
||||
// it can be recruited from configuration.secondaryDc: The dcId will be the secondaryDcId and
|
||||
// this generation's primaryDC in memory is different from configuration.primaryDcId.
|
||||
bool satelliteFallback;
|
||||
|
||||
RecruitFromConfigurationReply() : satelliteFallback(false) {}
|
||||
|
|
@ -385,7 +387,7 @@ struct InitializeBackupReply {
|
|||
LogEpoch backupEpoch;
|
||||
|
||||
InitializeBackupReply() = default;
|
||||
InitializeBackupReply(BackupInterface interface, LogEpoch e) : interf(interface), backupEpoch(e) {}
|
||||
InitializeBackupReply(BackupInterface bi, LogEpoch e) : interf(bi), backupEpoch(e) {}
|
||||
|
||||
template <class Ar>
|
||||
void serialize(Ar& ar) {
|
||||
|
|
|
|||
|
|
@ -366,6 +366,14 @@ void failAfter( Future<Void> trigger, Endpoint e ) {
|
|||
failAfter( trigger, g_simulator.getProcess( e ) );
|
||||
}
|
||||
|
||||
ACTOR Future<Void> histogramReport() {
|
||||
loop {
|
||||
wait(delay(SERVER_KNOBS->HISTOGRAM_REPORT_INTERVAL));
|
||||
|
||||
GetHistogramRegistry().logReport();
|
||||
}
|
||||
}
|
||||
|
||||
void testSerializationSpeed() {
|
||||
double tstart;
|
||||
double build = 0, serialize = 0, deserialize = 0, copy = 0, deallocate = 0;
|
||||
|
|
@ -485,8 +493,10 @@ ACTOR Future<Void> dumpDatabase( Database cx, std::string outputFilename, KeyRan
|
|||
void memoryTest();
|
||||
void skipListTest();
|
||||
|
||||
Future<Void> startSystemMonitor(std::string dataFolder, Optional<Standalone<StringRef>> zoneId, Optional<Standalone<StringRef>> machineId) {
|
||||
initializeSystemMonitorMachineState(SystemMonitorMachineState(dataFolder, zoneId, machineId, g_network->getLocalAddress().ip));
|
||||
Future<Void> startSystemMonitor(std::string dataFolder, Optional<Standalone<StringRef>> dcId,
|
||||
Optional<Standalone<StringRef>> zoneId, Optional<Standalone<StringRef>> machineId) {
|
||||
initializeSystemMonitorMachineState(
|
||||
SystemMonitorMachineState(dataFolder, dcId, zoneId, machineId, g_network->getLocalAddress().ip));
|
||||
|
||||
systemMonitor();
|
||||
return recurring( &systemMonitor, 5.0, TaskPriority::FlushTrace );
|
||||
|
|
@ -1554,6 +1564,9 @@ int main(int argc, char* argv[]) {
|
|||
delete FLOW_KNOBS;
|
||||
delete SERVER_KNOBS;
|
||||
delete CLIENT_KNOBS;
|
||||
FLOW_KNOBS = nullptr;
|
||||
SERVER_KNOBS = nullptr;
|
||||
CLIENT_KNOBS = nullptr;
|
||||
FlowKnobs* flowKnobs = new FlowKnobs;
|
||||
ClientKnobs* clientKnobs = new ClientKnobs;
|
||||
ServerKnobs* serverKnobs = new ServerKnobs;
|
||||
|
|
@ -1728,6 +1741,8 @@ int main(int argc, char* argv[]) {
|
|||
if (role == Simulation) {
|
||||
TraceEvent("Simulation").detail("TestFile", opts.testFile);
|
||||
|
||||
auto histogramReportActor = histogramReport();
|
||||
|
||||
clientKnobs->trace();
|
||||
flowKnobs->trace();
|
||||
serverKnobs->trace();
|
||||
|
|
@ -1885,6 +1900,7 @@ int main(int argc, char* argv[]) {
|
|||
actors.push_back(fdbd(opts.connectionFile, opts.localities, opts.processClass, dataFolder, dataFolder,
|
||||
opts.storageMemLimit, opts.metricsConnFile, opts.metricsPrefix, opts.rsssize,
|
||||
opts.whitelistBinPaths));
|
||||
actors.push_back(histogramReport());
|
||||
// actors.push_back( recurring( []{}, .001 ) ); // for ASIO latency measurement
|
||||
|
||||
f = stopAfter(waitForAll(actors));
|
||||
|
|
@ -1898,14 +1914,14 @@ int main(int argc, char* argv[]) {
|
|||
g_network->run();
|
||||
} else if (role == Test) {
|
||||
setupRunLoopProfiler();
|
||||
auto m = startSystemMonitor(opts.dataFolder, opts.zoneId, opts.zoneId);
|
||||
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
||||
f = stopAfter(runTests(opts.connectionFile, TEST_TYPE_FROM_FILE, TEST_HERE, 1, opts.testFile, StringRef(),
|
||||
opts.localities));
|
||||
g_network->run();
|
||||
} else if (role == ConsistencyCheck) {
|
||||
setupRunLoopProfiler();
|
||||
|
||||
auto m = startSystemMonitor(opts.dataFolder, opts.zoneId, opts.zoneId);
|
||||
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
||||
f = stopAfter(runTests(opts.connectionFile, TEST_TYPE_CONSISTENCY_CHECK, TEST_HERE, 1, opts.testFile,
|
||||
StringRef(), opts.localities));
|
||||
g_network->run();
|
||||
|
|
|
|||
|
|
@ -610,13 +610,16 @@ ACTOR Future<vector<Standalone<CommitTransactionRef>>> recruitEverything( Refere
|
|||
self->backupWorkers.swap(recruits.backupWorkers);
|
||||
|
||||
TraceEvent("MasterRecoveryState", self->dbgid)
|
||||
.detail("StatusCode", RecoveryStatus::initializing_transaction_servers)
|
||||
.detail("Status", RecoveryStatus::names[RecoveryStatus::initializing_transaction_servers])
|
||||
.detail("Proxies", recruits.proxies.size())
|
||||
.detail("TLogs", recruits.tLogs.size())
|
||||
.detail("Resolvers", recruits.resolvers.size())
|
||||
.detail("BackupWorkers", self->backupWorkers.size())
|
||||
.trackLatest("MasterRecoveryState");
|
||||
.detail("StatusCode", RecoveryStatus::initializing_transaction_servers)
|
||||
.detail("Status", RecoveryStatus::names[RecoveryStatus::initializing_transaction_servers])
|
||||
.detail("Proxies", recruits.proxies.size())
|
||||
.detail("TLogs", recruits.tLogs.size())
|
||||
.detail("Resolvers", recruits.resolvers.size())
|
||||
.detail("SatelliteTLogs", recruits.satelliteTLogs.size())
|
||||
.detail("OldLogRouters", recruits.oldLogRouters.size())
|
||||
.detail("StorageServers", recruits.storageServers.size())
|
||||
.detail("BackupWorkers", self->backupWorkers.size())
|
||||
.trackLatest("MasterRecoveryState");
|
||||
|
||||
// Actually, newSeedServers does both the recruiting and initialization of the seed servers; so if this is a brand new database we are sort of lying that we are
|
||||
// past the recruitment phase. In a perfect world we would split that up so that the recruitment part happens above (in parallel with recruiting the transaction servers?).
|
||||
|
|
|
|||
|
|
@ -19,11 +19,16 @@
|
|||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "fdbrpc/fdbrpc.h"
|
||||
#include "fdbrpc/LoadBalance.h"
|
||||
#include "flow/IndexedSet.h"
|
||||
#include "flow/Hash3.h"
|
||||
#include "flow/ActorCollection.h"
|
||||
#include "flow/Hash3.h"
|
||||
#include "flow/Histogram.h"
|
||||
#include "flow/IndexedSet.h"
|
||||
#include "flow/SystemMonitor.h"
|
||||
#include "flow/Util.h"
|
||||
#include "fdbclient/Atomic.h"
|
||||
|
|
@ -52,11 +57,8 @@
|
|||
#include "fdbrpc/Smoother.h"
|
||||
#include "fdbrpc/Stats.h"
|
||||
#include "flow/TDMetric.actor.h"
|
||||
#include <type_traits>
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
using std::pair;
|
||||
using std::make_pair;
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
#ifndef __INTEL_COMPILER
|
||||
#pragma region Data Structures
|
||||
|
|
@ -224,13 +226,13 @@ struct UpdateEagerReadInfo {
|
|||
void finishKeyBegin() {
|
||||
std::sort(keyBegin.begin(), keyBegin.end());
|
||||
keyBegin.resize( std::unique(keyBegin.begin(), keyBegin.end()) - keyBegin.begin() );
|
||||
std::sort(keys.begin(), keys.end(), [](const pair<KeyRef, int>& lhs, const pair<KeyRef, int>& rhs) { return (lhs.first < rhs.first) || (lhs.first == rhs.first && lhs.second > rhs.second); } );
|
||||
keys.resize(std::unique(keys.begin(), keys.end(), [](const pair<KeyRef, int>& lhs, const pair<KeyRef, int>& rhs) { return lhs.first == rhs.first; } ) - keys.begin());
|
||||
std::sort(keys.begin(), keys.end(), [](const std::pair<KeyRef, int>& lhs, const std::pair<KeyRef, int>& rhs) { return (lhs.first < rhs.first) || (lhs.first == rhs.first && lhs.second > rhs.second); } );
|
||||
keys.resize(std::unique(keys.begin(), keys.end(), [](const std::pair<KeyRef, int>& lhs, const std::pair<KeyRef, int>& rhs) { return lhs.first == rhs.first; } ) - keys.begin());
|
||||
//value gets populated in doEagerReads
|
||||
}
|
||||
|
||||
Optional<Value>& getValue(KeyRef key) {
|
||||
int i = std::lower_bound(keys.begin(), keys.end(), pair<KeyRef, int>(key, 0), [](const pair<KeyRef, int>& lhs, const pair<KeyRef, int>& rhs) { return lhs.first < rhs.first; } ) - keys.begin();
|
||||
int i = std::lower_bound(keys.begin(), keys.end(),std::pair<KeyRef, int>(key, 0), [](const std::pair<KeyRef, int>& lhs, const std::pair<KeyRef, int>& rhs) { return lhs.first < rhs.first; } ) - keys.begin();
|
||||
ASSERT( i < keys.size() && keys[i].first == key );
|
||||
return value[i];
|
||||
}
|
||||
|
|
@ -284,9 +286,63 @@ private:
|
|||
std::map<Version, Standalone<VerUpdateRef>> mutationLog; // versions (durableVersion, version]
|
||||
|
||||
public:
|
||||
public:
|
||||
// Histograms
|
||||
struct FetchKeysHistograms {
|
||||
const Reference<Histogram> latency;
|
||||
const Reference<Histogram> bytes;
|
||||
const Reference<Histogram> bandwidth;
|
||||
|
||||
FetchKeysHistograms()
|
||||
: latency(Histogram::getHistogram(STORAGESERVER_HISTOGRAM_GROUP, FETCH_KEYS_LATENCY_HISTOGRAM,
|
||||
Histogram::Unit::microseconds)),
|
||||
bytes(Histogram::getHistogram(STORAGESERVER_HISTOGRAM_GROUP, FETCH_KEYS_BYTES_HISTOGRAM,
|
||||
Histogram::Unit::bytes)),
|
||||
bandwidth(Histogram::getHistogram(STORAGESERVER_HISTOGRAM_GROUP, FETCH_KEYS_BYTES_PER_SECOND_HISTOGRAM,
|
||||
Histogram::Unit::bytes_per_second)) {}
|
||||
} fetchKeysHistograms;
|
||||
|
||||
class CurrentRunningFetchKeys {
|
||||
std::unordered_map<UID, double> startTimeMap;
|
||||
std::unordered_map<UID, KeyRange> keyRangeMap;
|
||||
|
||||
static const StringRef emptyString;
|
||||
static const KeyRangeRef emptyKeyRange;
|
||||
public:
|
||||
void recordStart(const UID id, const KeyRange& keyRange) {
|
||||
startTimeMap[id] = now();
|
||||
keyRangeMap[id] = keyRange;
|
||||
}
|
||||
|
||||
void recordFinish(const UID id) {
|
||||
startTimeMap.erase(id);
|
||||
keyRangeMap.erase(id);
|
||||
}
|
||||
|
||||
std::pair<double, KeyRange> longestTime() const {
|
||||
if (numRunning() == 0) {
|
||||
return {-1, emptyKeyRange};
|
||||
}
|
||||
|
||||
const double currentTime = now();
|
||||
double longest = 0;
|
||||
UID UIDofLongest;
|
||||
for (const auto& kv: startTimeMap) {
|
||||
const double currentRunningTime = currentTime - kv.second;
|
||||
if (longest < currentRunningTime) {
|
||||
longest = currentRunningTime;
|
||||
UIDofLongest = kv.first;
|
||||
}
|
||||
}
|
||||
return {longest, keyRangeMap.at(UIDofLongest)};
|
||||
}
|
||||
|
||||
int numRunning() const { return startTimeMap.size(); }
|
||||
} currentRunningFetchKeys;
|
||||
|
||||
Tag tag;
|
||||
vector<pair<Version,Tag>> history;
|
||||
vector<pair<Version,Tag>> allHistory;
|
||||
vector<std::pair<Version,Tag>> history;
|
||||
vector<std::pair<Version,Tag>> allHistory;
|
||||
Version poppedAllAfter;
|
||||
std::map<Version, Arena> freeable; // for each version, an Arena that must be held until that version is < oldestVersion
|
||||
Arena lastArena;
|
||||
|
|
@ -333,8 +389,8 @@ public:
|
|||
poppedAllAfter = std::numeric_limits<Version>::max();
|
||||
}
|
||||
|
||||
vector<pair<Version,Tag>>* hist = &history;
|
||||
vector<pair<Version,Tag>> allHistoryCopy;
|
||||
vector<std::pair<Version,Tag>>* hist = &history;
|
||||
vector<std::pair<Version,Tag>> allHistoryCopy;
|
||||
if(popAllTags) {
|
||||
allHistoryCopy = allHistory;
|
||||
hist = &allHistoryCopy;
|
||||
|
|
@ -528,7 +584,7 @@ public:
|
|||
|
||||
struct Counters {
|
||||
CounterCollection cc;
|
||||
Counter allQueries, getKeyQueries, getValueQueries, getRangeQueries, finishedQueries, rowsQueried, bytesQueried, watchQueries, emptyQueries;
|
||||
Counter allQueries, getKeyQueries, getValueQueries, getRangeQueries, finishedQueries, lowPriorityQueries, rowsQueried, bytesQueried, watchQueries, emptyQueries;
|
||||
Counter bytesInput, bytesDurable, bytesFetched,
|
||||
mutationBytes; // Like bytesInput but without MVCC accounting
|
||||
Counter sampledBytesCleared;
|
||||
|
|
@ -548,6 +604,7 @@ public:
|
|||
getRangeQueries("GetRangeQueries", cc),
|
||||
allQueries("QueryQueue", cc),
|
||||
finishedQueries("FinishedQueries", cc),
|
||||
lowPriorityQueries("LowPriorityQueries", cc),
|
||||
rowsQueried("RowsQueried", cc),
|
||||
bytesQueried("BytesQueried", cc),
|
||||
watchQueries("WatchQueries", cc),
|
||||
|
|
@ -601,22 +658,18 @@ public:
|
|||
}
|
||||
} counters;
|
||||
|
||||
StorageServer(IKeyValueStore* storage, Reference<AsyncVar<ServerDBInfo>> const& db, StorageServerInterface const& ssi)
|
||||
: instanceID(deterministicRandom()->randomUniqueID().first()),
|
||||
storage(this, storage), db(db), actors(false),
|
||||
lastTLogVersion(0), lastVersionWithData(0), restoredVersion(0),
|
||||
rebootAfterDurableVersion(std::numeric_limits<Version>::max()),
|
||||
durableInProgress(Void()),
|
||||
versionLag(0), primaryLocality(tagLocalityInvalid),
|
||||
updateEagerReads(0),
|
||||
shardChangeCounter(0),
|
||||
fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_BYTES),
|
||||
shuttingDown(false), debug_inApplyUpdate(false), debug_lastValidateTime(0), watchBytes(0), numWatches(0),
|
||||
logProtocol(0), counters(this), tag(invalidTag), maxQueryQueue(0), thisServerID(ssi.id()),
|
||||
readQueueSizeMetric(LiteralStringRef("StorageServer.ReadQueueSize")),
|
||||
behind(false), versionBehind(false), byteSampleClears(false, LiteralStringRef("\xff\xff\xff")), noRecentUpdates(false),
|
||||
lastUpdate(now()), poppedAllAfter(std::numeric_limits<Version>::max()), cpuUsage(0.0), diskUsage(0.0)
|
||||
{
|
||||
StorageServer(IKeyValueStore* storage, Reference<AsyncVar<ServerDBInfo>> const& db,
|
||||
StorageServerInterface const& ssi)
|
||||
: fetchKeysHistograms(), instanceID(deterministicRandom()->randomUniqueID().first()), storage(this, storage),
|
||||
db(db), actors(false), lastTLogVersion(0), lastVersionWithData(0), restoredVersion(0),
|
||||
rebootAfterDurableVersion(std::numeric_limits<Version>::max()), durableInProgress(Void()), versionLag(0),
|
||||
primaryLocality(tagLocalityInvalid), updateEagerReads(0), shardChangeCounter(0),
|
||||
fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_BYTES), shuttingDown(false),
|
||||
debug_inApplyUpdate(false), debug_lastValidateTime(0), watchBytes(0), numWatches(0), logProtocol(0),
|
||||
counters(this), tag(invalidTag), maxQueryQueue(0), thisServerID(ssi.id()),
|
||||
readQueueSizeMetric(LiteralStringRef("StorageServer.ReadQueueSize")), behind(false), versionBehind(false),
|
||||
byteSampleClears(false, LiteralStringRef("\xff\xff\xff")), noRecentUpdates(false), lastUpdate(now()),
|
||||
poppedAllAfter(std::numeric_limits<Version>::max()), cpuUsage(0.0), diskUsage(0.0) {
|
||||
version.initMetric(LiteralStringRef("StorageServer.Version"), counters.cc.id);
|
||||
oldestVersion.initMetric(LiteralStringRef("StorageServer.OldestVersion"), counters.cc.id);
|
||||
durableVersion.initMetric(LiteralStringRef("StorageServer.DurableVersion"), counters.cc.id);
|
||||
|
|
@ -691,6 +744,7 @@ public:
|
|||
return counters.bytesInput.getValue() - counters.bytesDurable.getValue();
|
||||
}
|
||||
|
||||
// penalty used by loadBalance() to balance requests among SSes. We prefer SS with less write queue size.
|
||||
double getPenalty() {
|
||||
return std::max(std::max(1.0, (queueSize() - (SERVER_KNOBS->TARGET_BYTES_PER_STORAGE_SERVER -
|
||||
2.0 * SERVER_KNOBS->SPRING_BYTES_STORAGE_SERVER)) /
|
||||
|
|
@ -698,6 +752,18 @@ public:
|
|||
(currentRate() < 1e-6 ? 1e6 : 1.0 / currentRate()));
|
||||
}
|
||||
|
||||
// Normally the storage server prefers to serve read requests over making mutations
|
||||
// durable to disk. However, when the storage server falls to far behind on
|
||||
// making mutations durable, this function will change the priority to prefer writes.
|
||||
Future<Void> getQueryDelay() {
|
||||
if ((version.get() - durableVersion.get() > SERVER_KNOBS->LOW_PRIORITY_DURABILITY_LAG) ||
|
||||
(queueSize() > SERVER_KNOBS->LOW_PRIORITY_STORAGE_QUEUE_BYTES)) {
|
||||
++counters.lowPriorityQueries;
|
||||
return delay(0, TaskPriority::LowPriorityRead);
|
||||
}
|
||||
return delay(0, TaskPriority::DefaultEndpoint);
|
||||
}
|
||||
|
||||
template<class Reply>
|
||||
using isLoadBalancedReply = std::is_base_of<LoadBalancedReply, Reply>;
|
||||
|
||||
|
|
@ -729,6 +795,9 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
const StringRef StorageServer::CurrentRunningFetchKeys::emptyString = LiteralStringRef("");
|
||||
const KeyRangeRef StorageServer::CurrentRunningFetchKeys::emptyKeyRange = KeyRangeRef(StorageServer::CurrentRunningFetchKeys::emptyString, StorageServer::CurrentRunningFetchKeys::emptyString);
|
||||
|
||||
// If and only if key:=value is in (storage+versionedData), // NOT ACTUALLY: and key < allKeys.end,
|
||||
// and H(key) < |key+value|/bytesPerSample,
|
||||
// let sampledSize = max(|key+value|,bytesPerSample)
|
||||
|
|
@ -923,7 +992,7 @@ ACTOR Future<Void> getValueQ( StorageServer* data, GetValueRequest req ) {
|
|||
|
||||
// Active load balancing runs at a very high priority (to obtain accurate queue lengths)
|
||||
// so we need to downgrade here
|
||||
wait( delay(0, TaskPriority::DefaultEndpoint) );
|
||||
wait( data->getQueryDelay() );
|
||||
|
||||
if( req.debugID.present() )
|
||||
g_traceBatch.addEvent("GetValueDebug", req.debugID.get().first(), "getValueQ.DoRead"); //.detail("TaskID", g_network->getCurrentTask());
|
||||
|
|
@ -1485,13 +1554,10 @@ ACTOR Future<Void> getKeyValuesQ( StorageServer* data, GetKeyValuesRequest req )
|
|||
// so we need to downgrade here
|
||||
if (SERVER_KNOBS->FETCH_KEYS_LOWER_PRIORITY && req.isFetchKeys) {
|
||||
wait( delay(0, TaskPriority::FetchKeys) );
|
||||
// } else if (false) {
|
||||
// // Placeholder for up-prioritizing fetches for important requests
|
||||
// taskType = TaskPriority::DefaultDelay;
|
||||
} else {
|
||||
wait( delay(0, TaskPriority::DefaultEndpoint) );
|
||||
wait( data->getQueryDelay() );
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if( req.debugID.present() )
|
||||
g_traceBatch.addEvent("TransactionDebug", req.debugID.get().first(), "storageserver.getKeyValues.Before");
|
||||
|
|
@ -1622,7 +1688,7 @@ ACTOR Future<Void> getKeyQ( StorageServer* data, GetKeyRequest req ) {
|
|||
|
||||
// Active load balancing runs at a very high priority (to obtain accurate queue lengths)
|
||||
// so we need to downgrade here
|
||||
wait( delay(0, TaskPriority::DefaultEndpoint) );
|
||||
wait( data->getQueryDelay() );
|
||||
|
||||
try {
|
||||
state Version version = wait( waitForVersion( data, req.version ) );
|
||||
|
|
@ -1791,7 +1857,7 @@ bool changeDurableVersion( StorageServer* data, Version desiredDurableVersion )
|
|||
setDataDurableVersion(data->thisServerID, data->durableVersion.get());
|
||||
if (checkFatalError.isReady()) checkFatalError.get();
|
||||
|
||||
//TraceEvent("ForgotVersionsBefore", data->thisServerID).detail("Version", nextDurableVersion);
|
||||
// TraceEvent("ForgotVersionsBefore", data->thisServerID).detail("Version", nextDurableVersion);
|
||||
validate(data);
|
||||
|
||||
return nextDurableVersion == desiredDurableVersion;
|
||||
|
|
@ -2107,16 +2173,56 @@ ACTOR Future<Void> logFetchKeysWarning(AddingShard* shard) {
|
|||
loop {
|
||||
state double waitSeconds = BUGGIFY ? 5.0 : 600.0;
|
||||
wait(delay(waitSeconds));
|
||||
TraceEvent(waitSeconds > 300.0 ? SevWarnAlways : SevInfo, "FetchKeysTooLong").detail("Duration", now() - startTime).detail("Phase", shard->phase).detail("Begin", shard->keys.begin.printable()).detail("End", shard->keys.end.printable());
|
||||
|
||||
const auto traceEventLevel = waitSeconds > SERVER_KNOBS->FETCH_KEYS_TOO_LONG_TIME_CRITERIA ? SevWarnAlways : SevInfo;
|
||||
TraceEvent(traceEventLevel, "FetchKeysTooLong")
|
||||
.detail("Duration", now() - startTime)
|
||||
.detail("Phase", shard->phase)
|
||||
.detail("Begin", shard->keys.begin.printable())
|
||||
.detail("End", shard->keys.end.printable());
|
||||
}
|
||||
}
|
||||
|
||||
class FetchKeysMetricReporter {
|
||||
const UID uid;
|
||||
const double startTime;
|
||||
int fetchedBytes;
|
||||
StorageServer::FetchKeysHistograms& histograms;
|
||||
StorageServer::CurrentRunningFetchKeys& currentRunning;
|
||||
|
||||
public:
|
||||
FetchKeysMetricReporter(const UID& uid_, const double startTime_, const KeyRange& keyRange, StorageServer::FetchKeysHistograms& histograms_, StorageServer::CurrentRunningFetchKeys& currentRunning_)
|
||||
: uid(uid_), startTime(startTime_), fetchedBytes(0), histograms(histograms_), currentRunning(currentRunning_) {
|
||||
|
||||
currentRunning.recordStart(uid, keyRange);
|
||||
}
|
||||
|
||||
void addFetchedBytes(const int bytes) { fetchedBytes += bytes; }
|
||||
|
||||
~FetchKeysMetricReporter() {
|
||||
double latency = now() - startTime;
|
||||
|
||||
// If fetchKeys is *NOT* run, i.e. returning immediately, still report a record.
|
||||
if (latency == 0) latency = 1e6;
|
||||
|
||||
const uint32_t bandwidth = fetchedBytes / latency;
|
||||
|
||||
histograms.latency->sampleSeconds(latency);
|
||||
histograms.bytes->sample(fetchedBytes);
|
||||
histograms.bandwidth->sample(bandwidth);
|
||||
|
||||
currentRunning.recordFinish(uid);
|
||||
}
|
||||
};
|
||||
|
||||
ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
||||
state const UID fetchKeysID = deterministicRandom()->randomUniqueID();
|
||||
state TraceInterval interval("FetchKeys");
|
||||
state KeyRange keys = shard->keys;
|
||||
state Future<Void> warningLogger = logFetchKeysWarning(shard);
|
||||
state double startt = now();
|
||||
state const double startTime = now();
|
||||
state int fetchBlockBytes = BUGGIFY ? SERVER_KNOBS->BUGGIFY_BLOCK_BYTES : SERVER_KNOBS->FETCH_BLOCK_BYTES;
|
||||
state FetchKeysMetricReporter metricReporter(fetchKeysID, startTime, keys, data->fetchKeysHistograms, data->currentRunningFetchKeys);
|
||||
|
||||
// delay(0) to force a return to the run loop before the work of fetchKeys is started.
|
||||
// This allows adding->start() to be called inline with CSK.
|
||||
|
|
@ -2154,7 +2260,7 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
|||
|
||||
state double executeStart = now();
|
||||
++data->counters.fetchWaitingCount;
|
||||
data->counters.fetchWaitingMS += 1000*(executeStart - startt);
|
||||
data->counters.fetchWaitingMS += 1000 * (executeStart - startTime);
|
||||
|
||||
// Fetch keys gets called while the update actor is processing mutations. data->version will not be updated until all mutations for a version
|
||||
// have been processed. We need to take the durableVersionLock to ensure data->version is greater than the version of the mutation which caused
|
||||
|
|
@ -2194,6 +2300,7 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
|||
debugKeyRange("fetchRange", fetchVersion, keys);
|
||||
for(auto k = this_block.begin(); k != this_block.end(); ++k) debugMutation("fetch", fetchVersion, MutationRef(MutationRef::SetValue, k->key, k->value));
|
||||
|
||||
metricReporter.addFetchedBytes(expectedSize);
|
||||
data->counters.bytesFetched += expectedSize;
|
||||
if( fetchBlockBytes > expectedSize ) {
|
||||
holdingFKPL.release( fetchBlockBytes - expectedSize );
|
||||
|
|
@ -2261,8 +2368,9 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
|||
while (!shard->updates.empty() && shard->updates[0].version <= fetchVersion) shard->updates.pop_front();
|
||||
|
||||
//FIXME: remove when we no longer support upgrades from 5.X
|
||||
if(debug_getRangeRetries >= 100) {
|
||||
if (debug_getRangeRetries >= 100) {
|
||||
data->cx->enableLocalityLoadBalance = false;
|
||||
TraceEvent(SevWarnAlways, "FKDisableLB").detail("FKID", fetchKeysID);
|
||||
}
|
||||
|
||||
debug_getRangeRetries++;
|
||||
|
|
@ -2281,6 +2389,7 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
|||
|
||||
//FIXME: remove when we no longer support upgrades from 5.X
|
||||
data->cx->enableLocalityLoadBalance = true;
|
||||
TraceEvent(SevWarnAlways, "FKReenableLB").detail("FKID", fetchKeysID);
|
||||
|
||||
// We have completed the fetch and write of the data, now we wait for MVCC window to pass.
|
||||
// As we have finished this work, we will allow more work to start...
|
||||
|
|
@ -2379,7 +2488,7 @@ ACTOR Future<Void> fetchKeys( StorageServer *data, AddingShard* shard ) {
|
|||
|
||||
TraceEvent(SevError, "FetchKeysError", data->thisServerID)
|
||||
.error(e)
|
||||
.detail("Elapsed", now()-startt)
|
||||
.detail("Elapsed", now() - startTime)
|
||||
.detail("KeyBegin", keys.begin)
|
||||
.detail("KeyEnd",keys.end);
|
||||
if (e.code() != error_code_actor_cancelled)
|
||||
|
|
@ -3081,7 +3190,7 @@ ACTOR Future<Void> updateStorage(StorageServer* data) {
|
|||
durableDelay = delay(SERVER_KNOBS->STORAGE_COMMIT_INTERVAL, TaskPriority::UpdateStorage);
|
||||
}
|
||||
|
||||
wait( durable );
|
||||
wait( ioTimeoutError(durable, SERVER_KNOBS->MAX_STORAGE_COMMIT_TIME));
|
||||
|
||||
debug_advanceMinCommittedVersion( data->thisServerID, newOldestVersion );
|
||||
|
||||
|
|
@ -3226,7 +3335,9 @@ bool StorageServerDisk::makeVersionMutationsDurable( Version& prevStorageVersion
|
|||
void StorageServerDisk::makeVersionDurable( Version version ) {
|
||||
storage->set( KeyValueRef(persistVersion, BinaryWriter::toValue(version, Unversioned())) );
|
||||
|
||||
//TraceEvent("MakeDurable", data->thisServerID).detail("FromVersion", prevStorageVersion).detail("ToVersion", version);
|
||||
// TraceEvent("MakeDurable", data->thisServerID)
|
||||
// .detail("FromVersion", prevStorageVersion)
|
||||
// .detail("ToVersion", version);
|
||||
}
|
||||
|
||||
void StorageServerDisk::changeLogProtocol(Version version, ProtocolVersion protocol) {
|
||||
|
|
@ -3621,7 +3732,10 @@ ACTOR Future<Void> metricsCore( StorageServer* self, StorageServerInterface ssi
|
|||
|
||||
wait( self->byteSampleRecovery );
|
||||
|
||||
self->actors.add(traceCounters("StorageMetrics", self->thisServerID, SERVER_KNOBS->STORAGE_LOGGING_DELAY, &self->counters.cc, self->thisServerID.toString() + "/StorageMetrics"));
|
||||
Tag tag = self->tag;
|
||||
self->actors.add(traceCounters("StorageMetrics", self->thisServerID, SERVER_KNOBS->STORAGE_LOGGING_DELAY,
|
||||
&self->counters.cc, self->thisServerID.toString() + "/StorageMetrics",
|
||||
[tag](TraceEvent& te) { te.detail("Tag", tag.toString()); }));
|
||||
|
||||
loop {
|
||||
choose {
|
||||
|
|
@ -3733,6 +3847,35 @@ ACTOR Future<Void> serveWatchValueRequests( StorageServer* self, FutureStream<Wa
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> reportStorageServerState(StorageServer* self) {
|
||||
if (!SERVER_KNOBS->REPORT_DD_METRICS) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
loop {
|
||||
wait(delay(SERVER_KNOBS->DD_METRICS_REPORT_INTERVAL));
|
||||
|
||||
const auto numRunningFetchKeys = self->currentRunningFetchKeys.numRunning();
|
||||
if (numRunningFetchKeys == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto longestRunningFetchKeys = self->currentRunningFetchKeys.longestTime();
|
||||
|
||||
auto level = SevInfo;
|
||||
if (longestRunningFetchKeys.first >= SERVER_KNOBS->FETCH_KEYS_TOO_LONG_TIME_CRITERIA) {
|
||||
level = SevWarnAlways;
|
||||
}
|
||||
|
||||
TraceEvent(level, "FetchKeyCurrentStatus")
|
||||
.detail("Timestamp", now())
|
||||
.detail("LongestRunningTime", longestRunningFetchKeys.first)
|
||||
.detail("StartKey", longestRunningFetchKeys.second.begin.printable())
|
||||
.detail("EndKey", longestRunningFetchKeys.second.end.printable())
|
||||
.detail("NumRunning", numRunningFetchKeys);
|
||||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> storageServerCore( StorageServer* self, StorageServerInterface ssi )
|
||||
{
|
||||
state Future<Void> doUpdate = Void();
|
||||
|
|
@ -3753,6 +3896,7 @@ ACTOR Future<Void> storageServerCore( StorageServer* self, StorageServerInterfac
|
|||
self->actors.add(serveGetKeyRequests(self, ssi.getKey.getFuture()));
|
||||
self->actors.add(serveWatchValueRequests(self, ssi.watchValue.getFuture()));
|
||||
self->actors.add(traceRole(Role::STORAGE_SERVER, ssi.id()));
|
||||
self->actors.add(reportStorageServerState(self));
|
||||
|
||||
self->transactionTagCounter.startNewInterval(self->thisServerID);
|
||||
self->actors.add(recurring([&](){ self->transactionTagCounter.startNewInterval(self->thisServerID); }, SERVER_KNOBS->READ_TAG_MEASUREMENT_INTERVAL));
|
||||
|
|
|
|||
|
|
@ -212,8 +212,7 @@ ACTOR Future<Void> workerHandleErrors(FutureStream<ErrorInfo> errors) {
|
|||
|
||||
endRole(err.role, err.id, "Error", ok, err.error);
|
||||
|
||||
|
||||
if (err.error.code() == error_code_please_reboot || err.error.code() == error_code_io_timeout || (err.role == Role::SHARED_TRANSACTION_LOG && err.error.code() == error_code_io_error )) throw err.error;
|
||||
if (err.error.code() == error_code_please_reboot || (err.role == Role::SHARED_TRANSACTION_LOG && (err.error.code() == error_code_io_error || err.error.code() == error_code_io_timeout))) throw err.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -978,7 +977,8 @@ ACTOR Future<Void> workerServer(
|
|||
|
||||
filesClosed.add(stopping.getFuture());
|
||||
|
||||
initializeSystemMonitorMachineState(SystemMonitorMachineState(folder, locality.zoneId(), locality.machineId(), g_network->getLocalAddress().ip));
|
||||
initializeSystemMonitorMachineState(SystemMonitorMachineState(
|
||||
folder, locality.dcId(), locality.zoneId(), locality.machineId(), g_network->getLocalAddress().ip));
|
||||
|
||||
{
|
||||
auto recruited = interf; //ghetto! don't we all love a good #define
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@
|
|||
extern IKeyValueStore *makeDummyKeyValueStore();
|
||||
|
||||
template <class T>
|
||||
class Histogram {
|
||||
class TestHistogram {
|
||||
public:
|
||||
Histogram(int minSamples = 100) : minSamples(minSamples) { reset(); }
|
||||
TestHistogram(int minSamples = 100) : minSamples(minSamples) { reset(); }
|
||||
|
||||
void reset() {
|
||||
N = 0;
|
||||
|
|
@ -145,7 +145,7 @@ struct KVTest {
|
|||
}
|
||||
};
|
||||
|
||||
ACTOR Future<Void> testKVRead( KVTest* test, Key key, Histogram<float>* latency, PerfIntCounter* count ) {
|
||||
ACTOR Future<Void> testKVRead(KVTest* test, Key key, TestHistogram<float>* latency, PerfIntCounter* count) {
|
||||
// state Version s1 = test->lastCommit;
|
||||
state Version s2 = test->lastDurable;
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ ACTOR Future<Void> testKVRead( KVTest* test, Key key, Histogram<float>* latency,
|
|||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Void> testKVReadSaturation( KVTest* test, Histogram<float>* latency, PerfIntCounter* count ) {
|
||||
ACTOR Future<Void> testKVReadSaturation(KVTest* test, TestHistogram<float>* latency, PerfIntCounter* count) {
|
||||
while (true) {
|
||||
state double begin = timer();
|
||||
Optional<Value> val = wait(test->store->readValue(test->randomKey()));
|
||||
|
|
@ -173,7 +173,7 @@ ACTOR Future<Void> testKVReadSaturation( KVTest* test, Histogram<float>* latency
|
|||
}
|
||||
}
|
||||
|
||||
ACTOR Future<Void> testKVCommit( KVTest* test, Histogram<float>* latency, PerfIntCounter* count ) {
|
||||
ACTOR Future<Void> testKVCommit(KVTest* test, TestHistogram<float>* latency, PerfIntCounter* count) {
|
||||
state Version v = test->lastSet;
|
||||
test->lastCommit = v;
|
||||
state double begin = timer();
|
||||
|
|
@ -194,7 +194,7 @@ struct KVStoreTestWorkload : TestWorkload {
|
|||
bool doSetup, doClear, doCount;
|
||||
std::string filename;
|
||||
PerfIntCounter reads, sets, commits;
|
||||
Histogram<float> readLatency, commitLatency;
|
||||
TestHistogram<float> readLatency, commitLatency;
|
||||
double setupTook;
|
||||
std::string storeType;
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ struct KVStoreTestWorkload : TestWorkload {
|
|||
return Void();
|
||||
}
|
||||
virtual Future<bool> check(Database const& cx) { return true; }
|
||||
void metricsFromHistogram(vector<PerfMetric>& m, std::string name, Histogram<float>& h) {
|
||||
void metricsFromHistogram(vector<PerfMetric>& m, std::string name, TestHistogram<float>& h) {
|
||||
m.push_back(PerfMetric("Min " + name, 1000.0 * h.min(), true));
|
||||
m.push_back(PerfMetric("Average " + name, 1000.0 * h.mean(), true));
|
||||
m.push_back(PerfMetric("Median " + name, 1000.0 * h.medianEstimate(), true));
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ struct LowLatencyWorkload : TestWorkload {
|
|||
virtual std::string description() { return "LowLatency"; }
|
||||
|
||||
virtual Future<Void> setup( Database const& cx ) {
|
||||
if (g_network->isSimulated()) {
|
||||
ASSERT(const_cast<ServerKnobs*>(SERVER_KNOBS)->setKnob("min_delay_cc_worst_fit_candidacy_seconds", "5"));
|
||||
ASSERT(const_cast<ServerKnobs*>(SERVER_KNOBS)->setKnob("max_delay_cc_worst_fit_candidacy_seconds", "10"));
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -270,13 +270,15 @@ struct MachineAttritionWorkload : TestWorkload {
|
|||
|
||||
ISimulator::KillType kt = ISimulator::Reboot;
|
||||
if( !self->reboot ) {
|
||||
int killType = deterministicRandom()->randomInt(0,3);
|
||||
int killType = deterministicRandom()->randomInt(0,3); //FIXME: enable disk stalls
|
||||
if( killType == 0 )
|
||||
kt = ISimulator::KillInstantly;
|
||||
else if( killType == 1 )
|
||||
kt = ISimulator::InjectFaults;
|
||||
else
|
||||
else if( killType == 2 )
|
||||
kt = ISimulator::RebootAndDelete;
|
||||
else
|
||||
kt = ISimulator::FailDisk;
|
||||
}
|
||||
TraceEvent("Assassination").detail("TargetDatacenter", target).detail("Reboot", self->reboot).detail("KillType", kt);
|
||||
|
||||
|
|
@ -336,7 +338,20 @@ struct MachineAttritionWorkload : TestWorkload {
|
|||
TraceEvent("RebootAndDelete").detail("TargetMachine", targetMachine.toString());
|
||||
g_simulator.killZone( targetMachine.zoneId(), ISimulator::RebootAndDelete );
|
||||
} else {
|
||||
auto kt = (deterministicRandom()->random01() < 0.5 || !self->allowFaultInjection) ? ISimulator::KillInstantly : ISimulator::InjectFaults;
|
||||
auto kt = ISimulator::KillInstantly;
|
||||
if( self->allowFaultInjection ) {
|
||||
if( randomDouble < 0.50 ) {
|
||||
kt = ISimulator::InjectFaults;
|
||||
}
|
||||
//FIXME: enable disk stalls
|
||||
/*
|
||||
if( randomDouble < 0.56 ) {
|
||||
kt = ISimulator::InjectFaults;
|
||||
} else if( randomDouble < 0.66 ) {
|
||||
kt = ISimulator::FailDisk;
|
||||
}
|
||||
*/
|
||||
}
|
||||
g_simulator.killZone( targetMachine.zoneId(), kt );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,6 +408,9 @@ struct RemoveServersSafelyWorkload : TestWorkload {
|
|||
if (coordinators.size() > 2) {
|
||||
auto randomCoordinator = deterministicRandom()->randomChoice(coordinators);
|
||||
coordExcl = AddressExclusion(randomCoordinator.ip, randomCoordinator.port);
|
||||
TraceEvent("RemoveAndKill", functionId)
|
||||
.detail("Step", "ChooseCoordinator")
|
||||
.detail("Coordinator", describe(coordExcl));
|
||||
}
|
||||
}
|
||||
std::copy(toKill.begin(), toKill.end(), std::back_inserter(toKillArray));
|
||||
|
|
@ -417,11 +420,12 @@ struct RemoveServersSafelyWorkload : TestWorkload {
|
|||
state bool safe = false;
|
||||
state std::set<AddressExclusion> failSet =
|
||||
random_subset(toKillArray, deterministicRandom()->randomInt(0, toKillArray.size() + 1));
|
||||
if (coordExcl.isValid()) {
|
||||
failSet.insert(coordExcl);
|
||||
}
|
||||
toKillMarkFailedArray.resize(failSet.size());
|
||||
std::copy(failSet.begin(), failSet.end(), toKillMarkFailedArray.begin());
|
||||
std::sort(toKillMarkFailedArray.begin(), toKillMarkFailedArray.end());
|
||||
if (coordExcl.isValid()) {
|
||||
toKillMarkFailedArray.push_back(coordExcl);
|
||||
}
|
||||
TraceEvent("RemoveAndKill", functionId)
|
||||
.detail("Step", "SafetyCheck")
|
||||
.detail("Exclusions", describe(toKillMarkFailedArray));
|
||||
|
|
@ -460,6 +464,7 @@ struct RemoveServersSafelyWorkload : TestWorkload {
|
|||
toKillMarkFailedArray.erase(removeServer);
|
||||
}
|
||||
ASSERT(toKillMarkFailedArray.size() <= toKillArray.size());
|
||||
std::sort(toKillArray.begin(), toKillArray.end());
|
||||
auto removeServer = toKill.begin();
|
||||
TraceEvent("RemoveAndKill", functionId)
|
||||
.detail("Step", "ReplaceNonFailedKillSet")
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public: // variables
|
|||
public: // ctor & dtor
|
||||
SnapTestWorkload(WorkloadContext const& wcx)
|
||||
: TestWorkload(wcx), numSnaps(0), maxSnapDelay(0.0), testID(0), snapUID() {
|
||||
TraceEvent("SnapTestWorkload Constructor");
|
||||
TraceEvent("SnapTestWorkloadConstructor");
|
||||
std::string workloadName = "SnapTest";
|
||||
maxRetryCntToRetrieveMessage = 10;
|
||||
|
||||
|
|
@ -219,6 +219,7 @@ public: // workload functions
|
|||
snapFailed = true;
|
||||
break;
|
||||
}
|
||||
wait(delay(5.0));
|
||||
}
|
||||
}
|
||||
CSimpleIni ini;
|
||||
|
|
|
|||
|
|
@ -365,6 +365,20 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
|
|||
ASSERT(e.code() == error_code_key_outside_legal_range);
|
||||
tx->reset();
|
||||
}
|
||||
// test case when registered range is the same as the underlying module
|
||||
try {
|
||||
state Standalone<RangeResultRef> result = wait(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"),
|
||||
LiteralStringRef("\xff\xff/worker_interfaces0")),
|
||||
CLIENT_KNOBS->TOO_MANY));
|
||||
// We should have at least 1 process in the cluster
|
||||
ASSERT(result.size());
|
||||
state KeyValueRef entry = deterministicRandom()->randomChoice(result);
|
||||
Optional<Value> singleRes = wait(tx->get(entry.key));
|
||||
ASSERT(singleRes.present() && singleRes.get() == entry.value);
|
||||
tx->reset();
|
||||
} catch (Error& e) {
|
||||
wait(tx->onError(e));
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -411,11 +411,11 @@ protected:
|
|||
char confFileDirectory[2048];
|
||||
char *fileNameStart;
|
||||
if( !GetFullPathName( confFile.c_str(), 2048, confFileDirectory, &fileNameStart ) ) {
|
||||
errorExit( format( "get path of conf file (%s)", confFile ).c_str() );
|
||||
errorExit( format( "get path of conf file (%s)", confFile.c_str() ).c_str() );
|
||||
}
|
||||
|
||||
if( !fileNameStart ) {
|
||||
errorExit( format( "file name not present (%s)", confFile ).c_str() );
|
||||
errorExit( format( "file name not present (%s)", confFile.c_str() ).c_str() );
|
||||
}
|
||||
|
||||
// Test file existence
|
||||
|
|
@ -838,7 +838,7 @@ void print_usage(const char *name) {
|
|||
" -h, --help Display this help and exit.\n", name);
|
||||
}
|
||||
|
||||
int main(DWORD argc, LPCSTR *argv) {
|
||||
int main(int argc, LPCSTR *argv) {
|
||||
_set_FMA3_enable(0); // Workaround for VS 2013 code generation bug. See https://connect.microsoft.com/VisualStudio/feedback/details/811093/visual-studio-2013-rtm-c-x64-code-generation-bug-for-avx2-instructions
|
||||
|
||||
int status = 0;
|
||||
|
|
@ -866,14 +866,14 @@ int main(DWORD argc, LPCSTR *argv) {
|
|||
}
|
||||
|
||||
// Check, if logging is enabled
|
||||
else if ((!strnicmp("-l", argv[loop], 2)) ||
|
||||
else if ((!_strnicmp("-l", argv[loop], 2)) ||
|
||||
(!_strnicmp("--l", argv[loop], 3)))
|
||||
{
|
||||
logging = true;
|
||||
}
|
||||
|
||||
// Check, if help is requested
|
||||
else if ((!strnicmp("-h", argv[loop], 2)) ||
|
||||
else if ((!_strnicmp("-h", argv[loop], 2)) ||
|
||||
(!_strnicmp("--h", argv[loop], 3)))
|
||||
{
|
||||
print_usage(argv[0]);
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ CServiceBase::CServiceBase(char *serviceName,
|
|||
bool fCanPauseContinue)
|
||||
{
|
||||
// Service name must be a valid string and cannot be NULL.
|
||||
m_name = (serviceName == NULL) ? "" : serviceName;
|
||||
m_name = (serviceName == NULL) ? const_cast<char*>("") : serviceName;
|
||||
|
||||
m_statusHandle = NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -679,7 +679,8 @@ inline bool operator==(const StringRef& lhs, const StringRef& rhs) {
|
|||
if (lhs.size() == 0 && rhs.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
return lhs.size() == rhs.size() && memcmp(lhs.begin(), rhs.begin(), lhs.size()) == 0;
|
||||
ASSERT(lhs.size() >= 0);
|
||||
return lhs.size() == rhs.size() && memcmp(lhs.begin(), rhs.begin(), static_cast<unsigned int>(lhs.size())) == 0;
|
||||
}
|
||||
inline bool operator<(const StringRef& lhs, const StringRef& rhs) {
|
||||
if (std::min(lhs.size(), rhs.size()) > 0) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ set(FLOW_SRCS
|
|||
FileTraceLogWriter.h
|
||||
Hash3.c
|
||||
Hash3.h
|
||||
Histogram.cpp
|
||||
Histogram.h
|
||||
IDispatched.h
|
||||
IRandom.h
|
||||
IThreadPool.cpp
|
||||
|
|
@ -81,7 +83,9 @@ set(FLOW_SRCS
|
|||
serialize.h
|
||||
stacktrace.amalgamation.cpp
|
||||
stacktrace.h
|
||||
version.cpp)
|
||||
version.cpp
|
||||
xxhash.c
|
||||
xxhash.h)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SourceVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/SourceVersion.h)
|
||||
|
||||
|
|
|
|||
|
|
@ -91,13 +91,25 @@ Error internal_error_impl(const char* a_nm, long long a, const char * op_nm, con
|
|||
return Error(error_code_internal_error);
|
||||
}
|
||||
|
||||
Error::Error(int error_code)
|
||||
: error_code(error_code), flags(0)
|
||||
{
|
||||
Error::Error(int error_code) : error_code(error_code), flags(0) {
|
||||
if (TRACE_SAMPLE()) TraceEvent(SevSample, "ErrorCreated").detail("ErrorCode", error_code);
|
||||
//std::cout << "Error: " << error_code << std::endl;
|
||||
// std::cout << "Error: " << error_code << std::endl;
|
||||
if (error_code >= 3000 && error_code < 6000) {
|
||||
TraceEvent(SevError, "SystemError").error(*this).backtrace();
|
||||
{
|
||||
TraceEvent te(SevError, "SystemError");
|
||||
te.error(*this).backtrace();
|
||||
if (error_code == error_code_unknown_error) {
|
||||
auto exception = std::current_exception();
|
||||
if (exception) {
|
||||
try {
|
||||
std::rethrow_exception(exception);
|
||||
} catch (std::exception& e) {
|
||||
te.detail("StdException", e.what());
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g_crashOnError) {
|
||||
flushOutputStreams();
|
||||
flushTraceFileVoid();
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@
|
|||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
//#ifdef WIN32
|
||||
//#include <windows.h>
|
||||
//#undef min
|
||||
//#undef max
|
||||
//#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@
|
|||
#define TRACEFILE_FLAGS O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC
|
||||
#define TRACEFILE_MODE 0664
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#undef max
|
||||
#undef min
|
||||
//#include <windows.h>
|
||||
//#undef max
|
||||
//#undef min
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue