Merge remote-tracking branch 'apple/main' into vgasiunas-retry-dboperations

This commit is contained in:
Vaidas Gasiunas 2022-10-07 09:14:36 +02:00
commit 0e09978bde
119 changed files with 3186 additions and 1519 deletions

View File

@ -30,13 +30,13 @@ endif()
add_custom_command(OUTPUT ${asm_file} ${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h add_custom_command(OUTPUT ${asm_file} ${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h
COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${os} ${cpu} COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${os} ${cpu}
${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp ${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp
${asm_file} ${asm_file}
${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h ${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp
COMMENT "Generate C bindings") COMMENT "Generate C bindings")
add_custom_target(fdb_c_generated DEPENDS ${asm_file} add_custom_target(fdb_c_generated DEPENDS ${asm_file}
${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h) ${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h)
vexillographer_compile(TARGET fdb_c_options LANG c OUT ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/fdb_c_options.g.h vexillographer_compile(TARGET fdb_c_options LANG c OUT ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/fdb_c_options.g.h
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/fdb_c_options.g.h) OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/fdb_c_options.g.h)
@ -66,9 +66,9 @@ if(APPLE)
set(symbols ${CMAKE_CURRENT_BINARY_DIR}/fdb_c.symbols) set(symbols ${CMAKE_CURRENT_BINARY_DIR}/fdb_c.symbols)
add_custom_command(OUTPUT ${symbols} add_custom_command(OUTPUT ${symbols}
COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/symbolify.py COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/symbolify.py
${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c.h ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c.h
${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c_internal.h ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c_internal.h
${symbols} ${symbols}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/symbolify.py ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c.h ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c_internal.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/symbolify.py ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c.h ${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c_internal.h
COMMENT "Generate exported_symbols_list") COMMENT "Generate exported_symbols_list")
add_custom_target(exported_symbols_list DEPENDS ${symbols}) add_custom_target(exported_symbols_list DEPENDS ${symbols})
@ -76,7 +76,7 @@ if(APPLE)
target_link_options(fdb_c PRIVATE "LINKER:-no_weak_exports,-exported_symbols_list,${symbols}") target_link_options(fdb_c PRIVATE "LINKER:-no_weak_exports,-exported_symbols_list,${symbols}")
elseif(WIN32) elseif(WIN32)
else() else()
if (NOT USE_UBSAN) if(NOT USE_UBSAN)
# For ubsan we need to export type information for the vptr check to work. # For ubsan we need to export type information for the vptr check to work.
# Otherwise we only want to export fdb symbols in the fdb c api. # Otherwise we only want to export fdb symbols in the fdb c api.
target_link_options(fdb_c PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.map") target_link_options(fdb_c PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.map")
@ -127,7 +127,7 @@ if(NOT WIN32)
test/unit/fdb_api.hpp) test/unit/fdb_api.hpp)
add_library(fdb_cpp INTERFACE test/fdb_api.hpp) add_library(fdb_cpp INTERFACE test/fdb_api.hpp)
target_sources(fdb_cpp INTERFACE ) target_sources(fdb_cpp INTERFACE)
target_include_directories(fdb_cpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/test) target_include_directories(fdb_cpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/test)
target_link_libraries(fdb_cpp INTERFACE fdb_c fmt::fmt) target_link_libraries(fdb_cpp INTERFACE fdb_c fmt::fmt)
@ -157,7 +157,7 @@ if(NOT WIN32)
test/apitester/TesterWatchAndWaitWorkload.cpp test/apitester/TesterWatchAndWaitWorkload.cpp
test/apitester/TesterWorkload.cpp test/apitester/TesterWorkload.cpp
test/apitester/TesterWorkload.h test/apitester/TesterWorkload.h
) )
add_library(fdb_c_unit_tests_impl OBJECT ${UNIT_TEST_SRCS}) add_library(fdb_c_unit_tests_impl OBJECT ${UNIT_TEST_SRCS})
add_library(fdb_c_api_tester_impl OBJECT ${API_TESTER_SRCS}) add_library(fdb_c_api_tester_impl OBJECT ${API_TESTER_SRCS})
@ -199,8 +199,8 @@ if(NOT WIN32)
target_include_directories(fdb_c_api_tester_impl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/ ${CMAKE_SOURCE_DIR}/flow/include ${CMAKE_BINARY_DIR}/flow/include) target_include_directories(fdb_c_api_tester_impl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/ ${CMAKE_SOURCE_DIR}/flow/include ${CMAKE_BINARY_DIR}/flow/include)
target_link_libraries(fdb_c_api_tester_impl PRIVATE fdb_cpp toml11_target Threads::Threads fmt::fmt boost_target) target_link_libraries(fdb_c_api_tester_impl PRIVATE fdb_cpp toml11_target Threads::Threads fmt::fmt boost_target)
if (NOT APPLE) if(NOT APPLE)
target_link_libraries(fdb_c_api_tester_impl PRIVATE stdc++fs) target_link_libraries(fdb_c_api_tester_impl PRIVATE stdc++fs)
endif() endif()
target_link_libraries(fdb_c_api_tester_impl PRIVATE SimpleOpt) target_link_libraries(fdb_c_api_tester_impl PRIVATE SimpleOpt)
@ -228,222 +228,222 @@ if(NOT WIN32)
set(FDB_C_TARGET $<TARGET_OBJECTS:fdb_c>) set(FDB_C_TARGET $<TARGET_OBJECTS:fdb_c>)
else() else()
set(FDB_C_TARGET $<TARGET_FILE:fdb_c>) set(FDB_C_TARGET $<TARGET_FILE:fdb_c>)
endif() add_custom_command(
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so COMMAND ${CMAKE_COMMAND} -E copy ${FDB_C_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
COMMAND ${CMAKE_COMMAND} -E copy ${FDB_C_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so DEPENDS fdb_c
DEPENDS fdb_c COMMENT "Copy libfdb_c to use as external client for test")
COMMENT "Copy libfdb_c to use as external client for test") add_custom_target(external_client DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so)
add_custom_target(external_client DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so) add_dependencies(fdb_c_unit_tests_impl external_client)
add_dependencies(fdb_c_unit_tests_impl external_client) add_dependencies(disconnected_timeout_unit_tests external_client)
add_dependencies(disconnected_timeout_unit_tests external_client) add_dependencies(fdb_c_api_tester_impl external_client)
add_dependencies(fdb_c_api_tester_impl external_client)
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_setup_tests NAME fdb_c_setup_tests
COMMAND $<TARGET_FILE:fdb_c_setup_tests>) COMMAND $<TARGET_FILE:fdb_c_setup_tests>)
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_unit_tests NAME fdb_c_unit_tests
COMMAND $<TARGET_FILE:fdb_c_unit_tests> COMMAND $<TARGET_FILE:fdb_c_unit_tests>
@CLUSTER_FILE@ @CLUSTER_FILE@
fdb) fdb)
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_unit_tests_version_510 NAME fdb_c_unit_tests_version_510
COMMAND $<TARGET_FILE:fdb_c_unit_tests_version_510> COMMAND $<TARGET_FILE:fdb_c_unit_tests_version_510>
@CLUSTER_FILE@ @CLUSTER_FILE@
fdb) fdb)
add_fdbclient_test( add_fdbclient_test(
NAME trace_partial_file_suffix_test NAME trace_partial_file_suffix_test
COMMAND $<TARGET_FILE:trace_partial_file_suffix_test> COMMAND $<TARGET_FILE:trace_partial_file_suffix_test>
@CLUSTER_FILE@ @CLUSTER_FILE@
fdb) fdb)
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_external_client_unit_tests NAME fdb_c_external_client_unit_tests
COMMAND $<TARGET_FILE:fdb_c_unit_tests> COMMAND $<TARGET_FILE:fdb_c_unit_tests>
@CLUSTER_FILE@ @CLUSTER_FILE@
fdb fdb
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
) )
add_unavailable_fdbclient_test( add_unavailable_fdbclient_test(
NAME disconnected_timeout_unit_tests NAME disconnected_timeout_unit_tests
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests> COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
@CLUSTER_FILE@ @CLUSTER_FILE@
) )
add_unavailable_fdbclient_test( add_unavailable_fdbclient_test(
NAME disconnected_timeout_external_client_unit_tests NAME disconnected_timeout_external_client_unit_tests
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests> COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
@CLUSTER_FILE@ @CLUSTER_FILE@
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
) )
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_api_tests NAME fdb_c_api_tests
DISABLE_LOG_DUMP DISABLE_LOG_DUMP
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
--cluster-file --cluster-file
@CLUSTER_FILE@ @CLUSTER_FILE@
--tester-binary --tester-binary
$<TARGET_FILE:fdb_c_api_tester> $<TARGET_FILE:fdb_c_api_tester>
--external-client-library --external-client-library
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
--test-dir --test-dir
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
--tmp-dir --tmp-dir
@TMP_DIR@ @TMP_DIR@
--log-dir --log-dir
@LOG_DIR@ @LOG_DIR@
) )
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_api_tests_local_only NAME fdb_c_api_tests_local_only
DISABLE_LOG_DUMP DISABLE_LOG_DUMP
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
--cluster-file --cluster-file
@CLUSTER_FILE@ @CLUSTER_FILE@
--tester-binary --tester-binary
$<TARGET_FILE:fdb_c_api_tester> $<TARGET_FILE:fdb_c_api_tester>
--test-dir --test-dir
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/local_tests ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/local_tests
--tmp-dir --tmp-dir
@TMP_DIR@ @TMP_DIR@
--log-dir --log-dir
@LOG_DIR@ @LOG_DIR@
) )
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_api_tests_blob_granule NAME fdb_c_api_tests_blob_granule
DISABLE_LOG_DUMP DISABLE_LOG_DUMP
API_TEST_BLOB_GRANULES_ENABLED API_TEST_BLOB_GRANULES_ENABLED
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
--cluster-file --cluster-file
@CLUSTER_FILE@ @CLUSTER_FILE@
--tester-binary --tester-binary
$<TARGET_FILE:fdb_c_api_tester> $<TARGET_FILE:fdb_c_api_tester>
--external-client-library --external-client-library
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
--test-dir --test-dir
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/blobgranuletests ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/blobgranuletests
--blob-granule-local-file-path --blob-granule-local-file-path
@DATA_DIR@/fdbblob/ @DATA_DIR@/fdbblob/
--tmp-dir --tmp-dir
@TMP_DIR@ @TMP_DIR@
--log-dir --log-dir
@LOG_DIR@ @LOG_DIR@
) )
add_fdbclient_test( add_fdbclient_test(
NAME fdb_c_api_tests_with_tls NAME fdb_c_api_tests_with_tls
DISABLE_LOG_DUMP DISABLE_LOG_DUMP
TLS_ENABLED TLS_ENABLED
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
--cluster-file --cluster-file
@CLUSTER_FILE@ @CLUSTER_FILE@
--tester-binary --tester-binary
$<TARGET_FILE:fdb_c_api_tester> $<TARGET_FILE:fdb_c_api_tester>
--external-client-library --external-client-library
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so ${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
--test-dir --test-dir
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
--tmp-dir --tmp-dir
@TMP_DIR@ @TMP_DIR@
--log-dir --log-dir
@LOG_DIR@ @LOG_DIR@
--tls-cert-file --tls-cert-file
@CLIENT_CERT_FILE@ @CLIENT_CERT_FILE@
--tls-key-file --tls-key-file
@CLIENT_KEY_FILE@ @CLIENT_KEY_FILE@
--tls-ca-file --tls-ca-file
@SERVER_CA_FILE@ @SERVER_CA_FILE@
) )
add_test(NAME fdb_c_upgrade_to_future_version add_test(NAME fdb_c_upgrade_to_future_version
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.2.0" "7.3.0" "7.2.0"
--process-number 3
)
set_tests_properties("fdb_c_upgrade_to_future_version" PROPERTIES ENVIRONMENT "${SANITIZER_OPTIONS}")
add_test(NAME fdb_c_upgrade_to_future_version_blob_granules
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/ApiBlobGranulesCorrectness.toml
--upgrade-path "7.2.0" "7.3.0" "7.2.0"
--blob-granules-enabled
--process-number 3
)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT USE_SANITIZER)
add_test(NAME fdb_c_upgrade_single_threaded_630api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadSingleThr.toml
--upgrade-path "6.3.23" "7.0.0" "7.1.9" "7.2.0"
--process-number 1
)
add_test(NAME fdb_c_upgrade_single_threaded_700api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadSingleThr.toml
--upgrade-path "7.0.0" "7.1.9" "7.2.0"
--process-number 1
)
add_test(NAME fdb_c_upgrade_multi_threaded_630api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.2.0" "7.3.0" "7.2.0" --upgrade-path "6.3.23" "7.0.0" "7.1.9" "7.2.0" "7.1.9"
--process-number 3 --process-number 3
) )
set_tests_properties("fdb_c_upgrade_to_future_version" PROPERTIES ENVIRONMENT "${SANITIZER_OPTIONS}")
add_test(NAME fdb_c_upgrade_to_future_version_blob_granules add_test(NAME fdb_c_upgrade_multi_threaded_700api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/ApiBlobGranulesCorrectness.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.2.0" "7.3.0" "7.2.0" --upgrade-path "7.0.0" "7.1.9" "7.2.0" "7.1.9"
--blob-granules-enabled
--process-number 3 --process-number 3
) )
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT USE_SANITIZER) add_test(NAME fdb_c_upgrade_multi_threaded_710api
add_test(NAME fdb_c_upgrade_single_threaded_630api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadSingleThr.toml
--upgrade-path "6.3.23" "7.0.0" "7.1.9" "7.2.0"
--process-number 1
)
add_test(NAME fdb_c_upgrade_single_threaded_700api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadSingleThr.toml
--upgrade-path "7.0.0" "7.1.9" "7.2.0"
--process-number 1
)
add_test(NAME fdb_c_upgrade_multi_threaded_630api
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "6.3.23" "7.0.0" "7.1.9" "7.2.0" "7.1.9" --upgrade-path "7.1.9" "7.2.0" "7.1.9"
--process-number 3 --process-number 3
) )
add_test(NAME fdb_c_upgrade_multi_threaded_700api add_test(NAME fdb_c_cluster_wiggle
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.0.0" "7.1.9" "7.2.0" "7.1.9" --upgrade-path "7.2.0" "wiggle"
--process-number 3 --disable-log-dump
) --process-number 3
--redundancy double
)
add_test(NAME fdb_c_upgrade_multi_threaded_710api add_test(NAME fdb_c_wiggle_and_upgrade_latest
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.1.9" "7.2.0" "7.1.9" --upgrade-path "7.1.9" "wiggle" "7.2.0"
--process-number 3 --disable-log-dump
) --process-number 3
--redundancy double
add_test(NAME fdb_c_cluster_wiggle
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.2.0" "wiggle"
--disable-log-dump
--process-number 3
--redundancy double
)
add_test(NAME fdb_c_wiggle_and_upgrade_latest
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "7.1.9" "wiggle" "7.2.0"
--disable-log-dump
--process-number 3
--redundancy double
) )
add_test(NAME fdb_c_wiggle_and_upgrade_63 add_test(NAME fdb_c_wiggle_and_upgrade_63
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml --test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
--upgrade-path "6.3.24" "wiggle" "7.0.0" --upgrade-path "6.3.24" "wiggle" "7.0.0"
--disable-log-dump --disable-log-dump
--process-number 3 --process-number 3
--redundancy double --redundancy double
) )
endif()
endif() endif()
endif() endif()
@ -462,12 +462,12 @@ set_target_properties(c_workloads PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/share/foundationdb") LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/share/foundationdb")
target_link_libraries(c_workloads PUBLIC fdb_c) target_link_libraries(c_workloads PUBLIC fdb_c)
if (NOT WIN32 AND NOT APPLE AND NOT OPEN_FOR_IDE) if(NOT WIN32 AND NOT APPLE AND NOT OPEN_FOR_IDE)
target_link_options(c_workloads PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/external_workload.map,-z,nodelete") target_link_options(c_workloads PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/external_workload.map,-z,nodelete")
endif() endif()
# Generate shim library in Linux builds # Generate shim library in Linux builds
if (OPEN_FOR_IDE) if(OPEN_FOR_IDE)
add_library(fdb_c_shim OBJECT foundationdb/fdb_c_shim.h fdb_c_shim.cpp) add_library(fdb_c_shim OBJECT foundationdb/fdb_c_shim.h fdb_c_shim.cpp)
target_link_libraries(fdb_c_shim PUBLIC dl) target_link_libraries(fdb_c_shim PUBLIC dl)
@ -499,10 +499,10 @@ elseif(NOT WIN32 AND NOT APPLE AND NOT USE_SANITIZER) # Linux Only, non-santizer
add_custom_command(OUTPUT ${SHIM_LIB_GEN_SRC} add_custom_command(OUTPUT ${SHIM_LIB_GEN_SRC}
COMMAND $<TARGET_FILE:Python3::Interpreter> ${IMPLIBSO_SRC_DIR}/implib-gen.py COMMAND $<TARGET_FILE:Python3::Interpreter> ${IMPLIBSO_SRC_DIR}/implib-gen.py
--target ${CMAKE_SYSTEM_PROCESSOR} --target ${CMAKE_SYSTEM_PROCESSOR}
--outdir ${SHIM_LIB_OUTPUT_DIR} --outdir ${SHIM_LIB_OUTPUT_DIR}
--dlopen-callback=fdb_shim_dlopen_callback --dlopen-callback=fdb_shim_dlopen_callback
$<TARGET_FILE:fdb_c> $<TARGET_FILE:fdb_c>
DEPENDS ${IMPLIBSO_SRC} fdb_c DEPENDS ${IMPLIBSO_SRC} fdb_c
COMMENT "Generating source code for C shim library") COMMENT "Generating source code for C shim library")
@ -526,12 +526,12 @@ elseif(NOT WIN32 AND NOT APPLE AND NOT USE_SANITIZER) # Linux Only, non-santizer
add_test(NAME fdb_c_shim_library_tests add_test(NAME fdb_c_shim_library_tests
COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/test/fdb_c_shim_tests.py COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/test/fdb_c_shim_tests.py
--build-dir ${CMAKE_BINARY_DIR} --build-dir ${CMAKE_BINARY_DIR}
--unit-tests-bin $<TARGET_FILE:fdb_c_shim_unit_tests> --unit-tests-bin $<TARGET_FILE:fdb_c_shim_unit_tests>
--api-tester-bin $<TARGET_FILE:fdb_c_shim_api_tester> --api-tester-bin $<TARGET_FILE:fdb_c_shim_api_tester>
--shim-lib-tester-bin $<TARGET_FILE:fdb_c_shim_lib_tester> --shim-lib-tester-bin $<TARGET_FILE:fdb_c_shim_lib_tester>
--api-test-dir ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests --api-test-dir ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
) )
endif() # End Linux only, non-sanitizer only endif() # End Linux only, non-sanitizer only
@ -580,16 +580,16 @@ fdb_install(
if(NOT WIN32 AND NOT APPLE AND NOT USE_SANITIZER) # Linux Only, non-sanitizer only if(NOT WIN32 AND NOT APPLE AND NOT USE_SANITIZER) # Linux Only, non-sanitizer only
fdb_install( fdb_install(
FILES foundationdb/fdb_c_shim.h FILES foundationdb/fdb_c_shim.h
DESTINATION include DESTINATION include
DESTINATION_SUFFIX /foundationdb DESTINATION_SUFFIX /foundationdb
COMPONENT clients) COMPONENT clients)
fdb_install( fdb_install(
TARGETS fdb_c_shim TARGETS fdb_c_shim
EXPORT ${targets_export_name} EXPORT ${targets_export_name}
DESTINATION lib DESTINATION lib
COMPONENT clients) COMPONENT clients)
endif() # End Linux only, non-ubsan only endif() # End Linux only, non-ubsan only

View File

@ -1001,7 +1001,7 @@ GetMappedRangeResult getMappedIndexEntries(int beginId,
TEST_CASE("versionstamp_unit_test") { TEST_CASE("versionstamp_unit_test") {
// a random 12 bytes long StringRef as a versionstamp // a random 12 bytes long StringRef as a versionstamp
StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12"_sr; StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12"_sr;
Versionstamp vs(str), vs2(str); TupleVersionstamp vs(str), vs2(str);
ASSERT(vs == vs2); ASSERT(vs == vs2);
ASSERT(vs.begin() != vs2.begin()); ASSERT(vs.begin() != vs2.begin());
@ -1031,7 +1031,7 @@ TEST_CASE("versionstamp_unit_test") {
TEST_CASE("tuple_support_versionstamp") { TEST_CASE("tuple_support_versionstamp") {
// a random 12 bytes long StringRef as a versionstamp // a random 12 bytes long StringRef as a versionstamp
StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12"_sr; StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12"_sr;
Versionstamp vs(str); TupleVersionstamp vs(str);
const Tuple t = Tuple::makeTuple(prefix, RECORD, vs, "{K[3]}"_sr, "{...}"_sr); const Tuple t = Tuple::makeTuple(prefix, RECORD, vs, "{K[3]}"_sr, "{...}"_sr);
ASSERT(t.getVersionstamp(2) == vs); ASSERT(t.getVersionstamp(2) == vs);
@ -1047,7 +1047,7 @@ TEST_CASE("tuple_fail_to_append_truncated_versionstamp") {
// a truncated 11 bytes long StringRef as a versionstamp // a truncated 11 bytes long StringRef as a versionstamp
StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11"_sr; StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11"_sr;
try { try {
Versionstamp truncatedVersionstamp(str); TupleVersionstamp truncatedVersionstamp(str);
} catch (Error& e) { } catch (Error& e) {
return; return;
} }
@ -1058,7 +1058,7 @@ TEST_CASE("tuple_fail_to_append_longer_versionstamp") {
// a longer than expected 13 bytes long StringRef as a versionstamp // a longer than expected 13 bytes long StringRef as a versionstamp
StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11"_sr; StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11"_sr;
try { try {
Versionstamp longerVersionstamp(str); TupleVersionstamp longerVersionstamp(str);
} catch (Error& e) { } catch (Error& e) {
return; return;
} }

View File

@ -24,7 +24,9 @@
#pragma once #pragma once
#include "bindings/flow/fdb_flow.h" #include "bindings/flow/fdb_flow.h"
#include "fdbclient/Versionstamp.h" #include "fdbclient/TupleVersionstamp.h"
typedef TupleVersionstamp Versionstamp;
namespace FDB { namespace FDB {
struct Uuid { struct Uuid {

View File

@ -4,20 +4,24 @@ function(compile_zstd)
include(FetchContent) include(FetchContent)
set(ZSTD_SOURCE_DIR ${CMAKE_BINARY_DIR}/zstd) FetchContent_Declare(ZSTD
FetchContent_Declare(
ZSTD
GIT_REPOSITORY https://github.com/facebook/zstd.git GIT_REPOSITORY https://github.com/facebook/zstd.git
GIT_TAG v1.5.2 GIT_TAG v1.5.2
SOURCE_DIR ${ZSTD_SOURCE_DIR} SOURCE_SUBDIR "build/cmake"
BINARY_DIR ${ZSTD_SOURCE_DIR}
SOURCE_SUBDIR "build/cmake"
) )
FetchContent_MakeAvailable(ZSTD) FetchContent_GetProperties(ZSTD)
if (NOT zstd_POPULATED)
FetchContent_Populate(ZSTD)
add_library(ZSTD::ZSTD STATIC IMPORTED) add_subdirectory(${zstd_SOURCE_DIR}/build/cmake ${zstd_BINARY_DIR})
set_target_properties(ZSTD::ZSTD PROPERTIES IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/libzstd.a")
target_include_directories(ZSTD::ZSTD PUBLIC ${ZSTD_INCLUDE_DIRS}) if (CLANG)
target_compile_options(zstd PRIVATE -Wno-array-bounds -Wno-tautological-compare)
target_compile_options(libzstd_static PRIVATE -Wno-array-bounds -Wno-tautological-compare)
target_compile_options(zstd-frugal PRIVATE -Wno-array-bounds -Wno-tautological-compare)
endif()
endif()
set(ZSTD_LIB_INCLUDE_DIR ${zstd_SOURCE_DIR}/lib PARENT_SCOPE)
endfunction(compile_zstd) endfunction(compile_zstd)

149
contrib/boost_zstd/zstd.cpp Normal file
View File

@ -0,0 +1,149 @@
// (C) Copyright Reimar Döffinger 2018.
// Based on zstd.cpp by:
// (C) Copyright Milan Svoboda 2008.
// (C) Copyright Jonathan Turkanis 2003.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
// See http://www.boost.org/libs/iostreams for documentation.
// Define BOOST_IOSTREAMS_SOURCE so that <boost/iostreams/detail/config.hpp>
// knows that we are building the library (possibly exporting code), rather
// than using it (possibly importing code).
#define BOOST_IOSTREAMS_SOURCE
#include <zstd.h>
#include <boost/throw_exception.hpp>
#include <boost/iostreams/detail/config/dyn_link.hpp>
#include <boost/iostreams/filter/zstd.hpp>
namespace boost {
namespace iostreams {
namespace zstd {
// Compression levels
const uint32_t best_speed = 1;
const uint32_t best_compression = 19;
const uint32_t default_compression = 3;
// Status codes
const int okay = 0;
const int stream_end = 1;
// Flush codes
const int finish = 0;
const int flush = 1;
const int run = 2;
} // End namespace zstd.
//------------------Implementation of zstd_error------------------------------//
zstd_error::zstd_error(size_t error) : BOOST_IOSTREAMS_FAILURE(ZSTD_getErrorName(error)), error_(error) {}
void zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(size_t error) {
if (ZSTD_isError(error))
boost::throw_exception(zstd_error(error));
}
//------------------Implementation of zstd_base-------------------------------//
namespace detail {
zstd_base::zstd_base()
: cstream_(ZSTD_createCStream()), dstream_(ZSTD_createDStream()), in_(new ZSTD_inBuffer), out_(new ZSTD_outBuffer),
eof_(0) {}
zstd_base::~zstd_base() {
ZSTD_freeCStream(static_cast<ZSTD_CStream*>(cstream_));
ZSTD_freeDStream(static_cast<ZSTD_DStream*>(dstream_));
delete static_cast<ZSTD_inBuffer*>(in_);
delete static_cast<ZSTD_outBuffer*>(out_);
}
void zstd_base::before(const char*& src_begin, const char* src_end, char*& dest_begin, char* dest_end) {
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
in->src = src_begin;
in->size = static_cast<size_t>(src_end - src_begin);
in->pos = 0;
out->dst = dest_begin;
out->size = static_cast<size_t>(dest_end - dest_begin);
out->pos = 0;
}
void zstd_base::after(const char*& src_begin, char*& dest_begin, bool) {
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
src_begin = reinterpret_cast<const char*>(in->src) + in->pos;
dest_begin = reinterpret_cast<char*>(out->dst) + out->pos;
}
int zstd_base::deflate(int action) {
ZSTD_CStream* s = static_cast<ZSTD_CStream*>(cstream_);
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
// Ignore spurious extra calls.
// Note size > 0 will trigger an error in this case.
if (eof_ && in->size == 0)
return zstd::stream_end;
size_t result = ZSTD_compressStream(s, out, in);
zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result);
if (action != zstd::run) {
result = action == zstd::finish ? ZSTD_endStream(s, out) : ZSTD_flushStream(s, out);
zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result);
eof_ = action == zstd::finish && result == 0;
return result == 0 ? zstd::stream_end : zstd::okay;
}
return zstd::okay;
}
int zstd_base::inflate(int action) {
ZSTD_DStream* s = static_cast<ZSTD_DStream*>(dstream_);
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
// need loop since iostream code cannot handle short reads
do {
size_t result = ZSTD_decompressStream(s, out, in);
zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result);
} while (in->pos < in->size && out->pos < out->size);
return action == zstd::finish && in->size == 0 && out->pos == 0 ? zstd::stream_end : zstd::okay;
}
void zstd_base::reset(bool compress, bool realloc) {
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
if (realloc) {
memset(in, 0, sizeof(*in));
memset(out, 0, sizeof(*out));
eof_ = 0;
zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(
compress ? ZSTD_initCStream(static_cast<ZSTD_CStream*>(cstream_), level)
: ZSTD_initDStream(static_cast<ZSTD_DStream*>(dstream_)));
}
}
void zstd_base::do_init(const zstd_params& p, bool compress, zstd::alloc_func, zstd::free_func, void*) {
ZSTD_inBuffer* in = static_cast<ZSTD_inBuffer*>(in_);
ZSTD_outBuffer* out = static_cast<ZSTD_outBuffer*>(out_);
memset(in, 0, sizeof(*in));
memset(out, 0, sizeof(*out));
eof_ = 0;
level = p.level;
zstd_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(
compress ? ZSTD_initCStream(static_cast<ZSTD_CStream*>(cstream_), level)
: ZSTD_initDStream(static_cast<ZSTD_DStream*>(dstream_)));
}
} // End namespace detail.
//----------------------------------------------------------------------------//
} // namespace iostreams
} // namespace boost

View File

@ -439,7 +439,7 @@ The ``tenant`` command is used to view and manage the tenants in a cluster. The
create create
^^^^^^ ^^^^^^
``tenant create <NAME> [tenant_group=<TENANT_GROUP>]`` ``tenant create <NAME> [tenant_group=<TENANT_GROUP>] [assigned_cluster=<CLUSTER_NAME>]``
Creates a new tenant in the cluster. Creates a new tenant in the cluster.
@ -447,6 +447,8 @@ Creates a new tenant in the cluster.
``TENANT_GROUP`` - The tenant group the tenant will be placed in. ``TENANT_GROUP`` - The tenant group the tenant will be placed in.
``CLUSTER_NAME`` - The cluster the tenant will be placed in (metacluster only). If unspecified, the metacluster will choose the cluster.
delete delete
^^^^^^ ^^^^^^

View File

@ -174,7 +174,7 @@ ACTOR Future<bool> blobKeyCommandActor(Database localDb,
version = v; version = v;
} }
if (key >= LiteralStringRef("\xff")) { if (key >= "\xff"_sr) {
fmt::print("No blob history for system keyspace\n", key.printable()); fmt::print("No blob history for system keyspace\n", key.printable());
return false; return false;
} else { } else {

View File

@ -26,7 +26,7 @@
#include "flow/Arena.h" #include "flow/Arena.h"
#include "flow/FastRef.h" #include "flow/FastRef.h"
#include "flow/ThreadHelper.actor.h" #include "flow/ThreadHelper.actor.h"
#include "fdbclient/ConsistencyScanInterface.h" #include "fdbclient/ConsistencyScanInterface.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
namespace fdb_cli { namespace fdb_cli {

View File

@ -291,13 +291,7 @@ ACTOR Future<bool> metaclusterStatusCommand(Reference<IDatabase> db, std::vector
std::map<ClusterName, DataClusterMetadata> clusters = std::map<ClusterName, DataClusterMetadata> clusters =
wait(MetaclusterAPI::listClusters(db, ""_sr, "\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS)); wait(MetaclusterAPI::listClusters(db, ""_sr, "\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS));
ClusterUsage totalCapacity; auto capacityNumbers = MetaclusterAPI::metaclusterCapacity(clusters);
ClusterUsage totalAllocated;
for (auto cluster : clusters) {
totalCapacity.numTenantGroups +=
std::max(cluster.second.entry.capacity.numTenantGroups, cluster.second.entry.allocated.numTenantGroups);
totalAllocated.numTenantGroups += cluster.second.entry.allocated.numTenantGroups;
}
if (useJson) { if (useJson) {
json_spirit::mObject obj; json_spirit::mObject obj;
@ -305,15 +299,15 @@ ACTOR Future<bool> metaclusterStatusCommand(Reference<IDatabase> db, std::vector
json_spirit::mObject metaclusterObj; json_spirit::mObject metaclusterObj;
metaclusterObj["data_clusters"] = (int)clusters.size(); metaclusterObj["data_clusters"] = (int)clusters.size();
metaclusterObj["capacity"] = totalCapacity.toJson(); metaclusterObj["capacity"] = capacityNumbers.first.toJson();
metaclusterObj["allocated"] = totalAllocated.toJson(); metaclusterObj["allocated"] = capacityNumbers.second.toJson();
obj["metacluster"] = metaclusterObj; obj["metacluster"] = metaclusterObj;
fmt::print("{}\n", json_spirit::write_string(json_spirit::mValue(obj), json_spirit::pretty_print).c_str()); fmt::print("{}\n", json_spirit::write_string(json_spirit::mValue(obj), json_spirit::pretty_print).c_str());
} else { } else {
fmt::print(" number of data clusters: {}\n", clusters.size()); fmt::print(" number of data clusters: {}\n", clusters.size());
fmt::print(" tenant group capacity: {}\n", totalCapacity.numTenantGroups); fmt::print(" tenant group capacity: {}\n", capacityNumbers.first.numTenantGroups);
fmt::print(" allocated tenant groups: {}\n", totalAllocated.numTenantGroups); fmt::print(" allocated tenant groups: {}\n", capacityNumbers.second.numTenantGroups);
} }
return true; return true;

View File

@ -605,8 +605,10 @@ void tenantGenerator(const char* text,
std::vector<const char*> tenantHintGenerator(std::vector<StringRef> const& tokens, bool inArgument) { std::vector<const char*> tenantHintGenerator(std::vector<StringRef> const& tokens, bool inArgument) {
if (tokens.size() == 1) { if (tokens.size() == 1) {
return { "<create|delete|list|get|configure|rename>", "[ARGS]" }; return { "<create|delete|list|get|configure|rename>", "[ARGS]" };
} else if (tokencmp(tokens[1], "create") && tokens.size() < 4) { } else if (tokencmp(tokens[1], "create") && tokens.size() < 5) {
static std::vector<const char*> opts = { "<NAME> [tenant_group=<TENANT_GROUP>]" }; static std::vector<const char*> opts = { "<NAME>",
"[tenant_group=<TENANT_GROUP>]",
"[assigned_cluster=<CLUSTER_NAME>]" };
return std::vector<const char*>(opts.begin() + tokens.size() - 2, opts.end()); return std::vector<const char*>(opts.begin() + tokens.size() - 2, opts.end());
} else if (tokencmp(tokens[1], "delete") && tokens.size() < 3) { } else if (tokencmp(tokens[1], "delete") && tokens.size() < 3) {
static std::vector<const char*> opts = { "<NAME>" }; static std::vector<const char*> opts = { "<NAME>" };

View File

@ -1672,23 +1672,13 @@ TEST_CASE("/blobgranule/files/applyDelta") {
printf("Testing blob granule delta applying\n"); printf("Testing blob granule delta applying\n");
Arena a; Arena a;
// do this 2 phase arena creation of string refs instead of LiteralStringRef because there is no char* StringRef StringRef k_a = StringRef(a, "A"_sr);
// constructor, and valgrind might complain if the stringref data isn't in the arena StringRef k_ab = StringRef(a, "AB"_sr);
std::string sk_a = "A"; StringRef k_b = StringRef(a, "B"_sr);
std::string sk_ab = "AB"; StringRef k_c = StringRef(a, "C"_sr);
std::string sk_b = "B"; StringRef k_z = StringRef(a, "Z"_sr);
std::string sk_c = "C"; StringRef val1 = StringRef(a, "1"_sr);
std::string sk_z = "Z"; StringRef val2 = StringRef(a, "2"_sr);
std::string sval1 = "1";
std::string sval2 = "2";
StringRef k_a = StringRef(a, sk_a);
StringRef k_ab = StringRef(a, sk_ab);
StringRef k_b = StringRef(a, sk_b);
StringRef k_c = StringRef(a, sk_c);
StringRef k_z = StringRef(a, sk_z);
StringRef val1 = StringRef(a, sval1);
StringRef val2 = StringRef(a, sval2);
std::map<KeyRef, ValueRef> data; std::map<KeyRef, ValueRef> data;
data.insert({ k_a, val1 }); data.insert({ k_a, val1 });

View File

@ -79,9 +79,9 @@ public:
DRConfig(Reference<Task> task) DRConfig(Reference<Task> task)
: DRConfig(BinaryReader::fromStringRef<UID>(task->params[BackupAgentBase::keyConfigLogUid], Unversioned())) {} : DRConfig(BinaryReader::fromStringRef<UID>(task->params[BackupAgentBase::keyConfigLogUid], Unversioned())) {}
KeyBackedBinaryValue<int64_t> rangeBytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> rangeBytesWritten() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedBinaryValue<int64_t> logBytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> logBytesWritten() { return configSpace.pack(__FUNCTION__sr); }
void clear(Reference<ReadYourWritesTransaction> tr) { tr->clear(configSpace.range()); } void clear(Reference<ReadYourWritesTransaction> tr) { tr->clear(configSpace.range()); }
@ -136,7 +136,7 @@ struct BackupRangeTaskFunc : TaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
} Params; } Params;
static const Key keyAddBackupRangeTasks; static const Key keyAddBackupRangeTasks;
@ -704,7 +704,7 @@ struct CopyLogRangeTaskFunc : TaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
} Params; } Params;
static const Key keyNextBeginVersion; static const Key keyNextBeginVersion;
@ -1455,7 +1455,7 @@ struct OldCopyLogRangeTaskFunc : TaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
} Params; } Params;
static const Key keyNextBeginVersion; static const Key keyNextBeginVersion;

View File

@ -65,7 +65,7 @@ FDB_DEFINE_BOOLEAN_PARAM(IncrementalBackupOnly);
FDB_DEFINE_BOOLEAN_PARAM(OnlyApplyMutationLogs); FDB_DEFINE_BOOLEAN_PARAM(OnlyApplyMutationLogs);
#define SevFRTestInfo SevVerbose #define SevFRTestInfo SevVerbose
//#define SevFRTestInfo SevInfo // #define SevFRTestInfo SevInfo
static std::string boolToYesOrNo(bool val) { static std::string boolToYesOrNo(bool val) {
return val ? std::string("Yes") : std::string("No"); return val ? std::string("Yes") : std::string("No");
@ -157,41 +157,37 @@ public:
RestoreConfig(UID uid = UID()) : KeyBackedConfig(fileRestorePrefixRange.begin, uid) {} RestoreConfig(UID uid = UID()) : KeyBackedConfig(fileRestorePrefixRange.begin, uid) {}
RestoreConfig(Reference<Task> task) : KeyBackedConfig(fileRestorePrefixRange.begin, task) {} RestoreConfig(Reference<Task> task) : KeyBackedConfig(fileRestorePrefixRange.begin, task) {}
KeyBackedProperty<ERestoreState> stateEnum() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<ERestoreState> stateEnum() { return configSpace.pack(__FUNCTION__sr); }
Future<StringRef> stateText(Reference<ReadYourWritesTransaction> tr) { Future<StringRef> stateText(Reference<ReadYourWritesTransaction> tr) {
return map(stateEnum().getD(tr), return map(stateEnum().getD(tr),
[](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); }); [](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); });
} }
KeyBackedProperty<Key> addPrefix() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> addPrefix() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Key> removePrefix() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> removePrefix() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<bool> onlyApplyMutationLogs() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> onlyApplyMutationLogs() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<bool> inconsistentSnapshotOnly() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> inconsistentSnapshotOnly() { return configSpace.pack(__FUNCTION__sr); }
// XXX: Remove restoreRange() once it is safe to remove. It has been changed to restoreRanges // XXX: Remove restoreRange() once it is safe to remove. It has been changed to restoreRanges
KeyBackedProperty<KeyRange> restoreRange() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<KeyRange> restoreRange() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<std::vector<KeyRange>> restoreRanges() { KeyBackedProperty<std::vector<KeyRange>> restoreRanges() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__)); KeyBackedProperty<Key> batchFuture() { return configSpace.pack(__FUNCTION__sr); }
} KeyBackedProperty<Version> beginVersion() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Key> batchFuture() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> restoreVersion() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Version> beginVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> firstConsistentVersion() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Version> restoreVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
KeyBackedProperty<Version> firstConsistentVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
KeyBackedProperty<Reference<IBackupContainer>> sourceContainer() { KeyBackedProperty<Reference<IBackupContainer>> sourceContainer() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
// Get the source container as a bare URL, without creating a container instance // Get the source container as a bare URL, without creating a container instance
KeyBackedProperty<Value> sourceContainerURL() { return configSpace.pack("sourceContainer"_sr); } KeyBackedProperty<Value> sourceContainerURL() { return configSpace.pack("sourceContainer"_sr); }
// Total bytes written by all log and range restore tasks. // Total bytes written by all log and range restore tasks.
KeyBackedBinaryValue<int64_t> bytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> bytesWritten() { return configSpace.pack(__FUNCTION__sr); }
// File blocks that have had tasks created for them by the Dispatch task // File blocks that have had tasks created for them by the Dispatch task
KeyBackedBinaryValue<int64_t> filesBlocksDispatched() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> filesBlocksDispatched() { return configSpace.pack(__FUNCTION__sr); }
// File blocks whose tasks have finished // File blocks whose tasks have finished
KeyBackedBinaryValue<int64_t> fileBlocksFinished() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> fileBlocksFinished() { return configSpace.pack(__FUNCTION__sr); }
// Total number of files in the fileMap // Total number of files in the fileMap
KeyBackedBinaryValue<int64_t> fileCount() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> fileCount() { return configSpace.pack(__FUNCTION__sr); }
// Total number of file blocks in the fileMap // Total number of file blocks in the fileMap
KeyBackedBinaryValue<int64_t> fileBlockCount() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> fileBlockCount() { return configSpace.pack(__FUNCTION__sr); }
Future<std::vector<KeyRange>> getRestoreRangesOrDefault(Reference<ReadYourWritesTransaction> tr) { Future<std::vector<KeyRange>> getRestoreRangesOrDefault(Reference<ReadYourWritesTransaction> tr) {
return getRestoreRangesOrDefault_impl(this, tr); return getRestoreRangesOrDefault_impl(this, tr);
@ -234,7 +230,7 @@ public:
}; };
typedef KeyBackedSet<RestoreFile> FileSetT; typedef KeyBackedSet<RestoreFile> FileSetT;
FileSetT fileSet() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } FileSetT fileSet() { return configSpace.pack(__FUNCTION__sr); }
Future<bool> isRunnable(Reference<ReadYourWritesTransaction> tr) { Future<bool> isRunnable(Reference<ReadYourWritesTransaction> tr) {
return map(stateEnum().getD(tr), [](ERestoreState s) -> bool { return map(stateEnum().getD(tr), [](ERestoreState s) -> bool {
@ -1533,9 +1529,9 @@ struct BackupRangeTaskFunc : BackupTaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<Key> beginKey() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Key> beginKey() { return __FUNCTION__sr; }
static TaskParam<Key> endKey() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Key> endKey() { return __FUNCTION__sr; }
static TaskParam<bool> addBackupRangeTasks() { return LiteralStringRef(__FUNCTION__); } static TaskParam<bool> addBackupRangeTasks() { return __FUNCTION__sr; }
} Params; } Params;
std::string toString(Reference<Task> task) const override { std::string toString(Reference<Task> task) const override {
@ -1899,11 +1895,11 @@ struct BackupSnapshotDispatchTask : BackupTaskFuncBase {
static struct { static struct {
// Set by Execute, used by Finish // Set by Execute, used by Finish
static TaskParam<int64_t> shardsBehind() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> shardsBehind() { return __FUNCTION__sr; }
// Set by Execute, used by Finish // Set by Execute, used by Finish
static TaskParam<bool> snapshotFinished() { return LiteralStringRef(__FUNCTION__); } static TaskParam<bool> snapshotFinished() { return __FUNCTION__sr; }
// Set by Execute, used by Finish // Set by Execute, used by Finish
static TaskParam<Version> nextDispatchVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> nextDispatchVersion() { return __FUNCTION__sr; }
} Params; } Params;
StringRef getName() const override { return name; }; StringRef getName() const override { return name; };
@ -2471,10 +2467,10 @@ struct BackupLogRangeTaskFunc : BackupTaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<bool> addBackupLogRangeTasks() { return LiteralStringRef(__FUNCTION__); } static TaskParam<bool> addBackupLogRangeTasks() { return __FUNCTION__sr; }
static TaskParam<int64_t> fileSize() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> fileSize() { return __FUNCTION__sr; }
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
} Params; } Params;
StringRef getName() const override { return name; }; StringRef getName() const override { return name; };
@ -2563,7 +2559,7 @@ struct BackupLogRangeTaskFunc : BackupTaskFuncBase {
readCommitted(cx, results, lock, range, Terminator::False, AccessSystemKeys::True, LockAware::True)); readCommitted(cx, results, lock, range, Terminator::False, AccessSystemKeys::True, LockAware::True));
} }
state Future<Void> sendEOS = map(errorOr(waitForAll(rc)), [=](ErrorOr<Void> const& result) { state Future<Void> sendEOS = map(errorOr(waitForAll(rc)), [=](ErrorOr<Void> const& result) mutable {
if (result.isError()) if (result.isError())
results.sendError(result.getError()); results.sendError(result.getError());
else else
@ -2712,9 +2708,9 @@ struct EraseLogRangeTaskFunc : BackupTaskFuncBase {
StringRef getName() const override { return name; }; StringRef getName() const override { return name; };
static struct { static struct {
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
static TaskParam<Key> destUidValue() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Key> destUidValue() { return __FUNCTION__sr; }
} Params; } Params;
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr, ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr,
@ -2786,8 +2782,8 @@ struct BackupLogsDispatchTask : BackupTaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<Version> prevBeginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> prevBeginVersion() { return __FUNCTION__sr; }
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
} Params; } Params;
ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr, ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -3015,7 +3011,7 @@ struct BackupSnapshotManifest : BackupTaskFuncBase {
static StringRef name; static StringRef name;
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
} Params; } Params;
ACTOR static Future<Void> _execute(Database cx, ACTOR static Future<Void> _execute(Database cx,
@ -3214,7 +3210,7 @@ struct StartFullBackupTaskFunc : BackupTaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
} Params; } Params;
ACTOR static Future<Void> _execute(Database cx, ACTOR static Future<Void> _execute(Database cx,
@ -3457,9 +3453,9 @@ REGISTER_TASKFUNC(RestoreCompleteTaskFunc);
struct RestoreFileTaskFuncBase : RestoreTaskFuncBase { struct RestoreFileTaskFuncBase : RestoreTaskFuncBase {
struct InputParams { struct InputParams {
static TaskParam<RestoreFile> inputFile() { return LiteralStringRef(__FUNCTION__); } static TaskParam<RestoreFile> inputFile() { return __FUNCTION__sr; }
static TaskParam<int64_t> readOffset() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> readOffset() { return __FUNCTION__sr; }
static TaskParam<int64_t> readLen() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> readLen() { return __FUNCTION__sr; }
} Params; } Params;
std::string toString(Reference<Task> task) const override { std::string toString(Reference<Task> task) const override {
@ -3474,8 +3470,8 @@ struct RestoreRangeTaskFunc : RestoreFileTaskFuncBase {
static struct : InputParams { static struct : InputParams {
// The range of data that the (possibly empty) data represented, which is set if it intersects the target // The range of data that the (possibly empty) data represented, which is set if it intersects the target
// restore range // restore range
static TaskParam<KeyRange> originalFileRange() { return LiteralStringRef(__FUNCTION__); } static TaskParam<KeyRange> originalFileRange() { return __FUNCTION__sr; }
static TaskParam<std::vector<KeyRange>> originalFileRanges() { return LiteralStringRef(__FUNCTION__); } static TaskParam<std::vector<KeyRange>> originalFileRanges() { return __FUNCTION__sr; }
static std::vector<KeyRange> getOriginalFileRanges(Reference<Task> task) { static std::vector<KeyRange> getOriginalFileRanges(Reference<Task> task) {
if (originalFileRanges().exists(task)) { if (originalFileRanges().exists(task)) {
@ -4071,11 +4067,11 @@ struct RestoreDispatchTaskFunc : RestoreTaskFuncBase {
StringRef getName() const override { return name; }; StringRef getName() const override { return name; };
static struct { static struct {
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
static TaskParam<std::string> beginFile() { return LiteralStringRef(__FUNCTION__); } static TaskParam<std::string> beginFile() { return __FUNCTION__sr; }
static TaskParam<int64_t> beginBlock() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> beginBlock() { return __FUNCTION__sr; }
static TaskParam<int64_t> batchSize() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> batchSize() { return __FUNCTION__sr; }
static TaskParam<int64_t> remainingInBatch() { return LiteralStringRef(__FUNCTION__); } static TaskParam<int64_t> remainingInBatch() { return __FUNCTION__sr; }
} Params; } Params;
ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr, ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -4544,7 +4540,7 @@ struct StartFullRestoreTaskFunc : RestoreTaskFuncBase {
static constexpr uint32_t version = 1; static constexpr uint32_t version = 1;
static struct { static struct {
static TaskParam<Version> firstVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> firstVersion() { return __FUNCTION__sr; }
} Params; } Params;
// Find all files needed for the restore and save them in the RestoreConfig for the task. // Find all files needed for the restore and save them in the RestoreConfig for the task.

View File

@ -874,12 +874,12 @@ ACTOR Future<Optional<ClusterConnectionString>> getClusterConnectionStringFromSt
ACTOR Future<Void> verifyConfigurationDatabaseAlive(Database cx) { ACTOR Future<Void> verifyConfigurationDatabaseAlive(Database cx) {
state Backoff backoff; state Backoff backoff;
state Reference<ISingleThreadTransaction> configTr;
loop { loop {
try { try {
// Attempt to read a random value from the configuration // Attempt to read a random value from the configuration
// database to make sure it is online. // database to make sure it is online.
state Reference<ISingleThreadTransaction> configTr = configTr = ISingleThreadTransaction::create(ISingleThreadTransaction::Type::PAXOS_CONFIG, cx);
ISingleThreadTransaction::create(ISingleThreadTransaction::Type::PAXOS_CONFIG, cx);
Tuple tuple; Tuple tuple;
tuple.appendNull(); // config class tuple.appendNull(); // config class
tuple << "test"_sr; tuple << "test"_sr;

View File

@ -24,6 +24,19 @@
FDB_DEFINE_BOOLEAN_PARAM(AddNewTenants); FDB_DEFINE_BOOLEAN_PARAM(AddNewTenants);
FDB_DEFINE_BOOLEAN_PARAM(RemoveMissingTenants); FDB_DEFINE_BOOLEAN_PARAM(RemoveMissingTenants);
std::string clusterTypeToString(const ClusterType& clusterType) {
switch (clusterType) {
case ClusterType::STANDALONE:
return "standalone";
case ClusterType::METACLUSTER_MANAGEMENT:
return "metacluster_management";
case ClusterType::METACLUSTER_DATA:
return "metacluster_data";
default:
return "unknown";
}
}
std::string DataClusterEntry::clusterStateToString(DataClusterState clusterState) { std::string DataClusterEntry::clusterStateToString(DataClusterState clusterState) {
switch (clusterState) { switch (clusterState) {
case DataClusterState::READY: case DataClusterState::READY:

View File

@ -27,6 +27,17 @@
namespace MetaclusterAPI { namespace MetaclusterAPI {
std::pair<ClusterUsage, ClusterUsage> metaclusterCapacity(std::map<ClusterName, DataClusterMetadata> const& clusters) {
ClusterUsage tenantGroupCapacity;
ClusterUsage tenantGroupsAllocated;
for (auto cluster : clusters) {
tenantGroupCapacity.numTenantGroups +=
std::max(cluster.second.entry.capacity.numTenantGroups, cluster.second.entry.allocated.numTenantGroups);
tenantGroupsAllocated.numTenantGroups += cluster.second.entry.allocated.numTenantGroups;
}
return { tenantGroupCapacity, tenantGroupsAllocated };
}
ACTOR Future<Reference<IDatabase>> openDatabase(ClusterConnectionString connectionString) { ACTOR Future<Reference<IDatabase>> openDatabase(ClusterConnectionString connectionString) {
if (g_network->isSimulated()) { if (g_network->isSimulated()) {
Reference<IClusterConnectionRecord> clusterFile = Reference<IClusterConnectionRecord> clusterFile =

View File

@ -376,7 +376,7 @@ TEST_CASE("/fdbclient/MonitorLeader/parseConnectionString/fuzz") {
output += "#"; output += "#";
int charCount = deterministicRandom()->randomInt(0, 20); int charCount = deterministicRandom()->randomInt(0, 20);
for (int i = 0; i < charCount; i++) { for (int i = 0; i < charCount; i++) {
output += deterministicRandom()->randomChoice(LiteralStringRef("asdfzxcv123345:!@#$#$&()<\"\' \t")); output += deterministicRandom()->randomChoice("asdfzxcv123345:!@#$#$&()<\"\' \t"_sr);
} }
output += deterministicRandom()->randomChoice("\n\r"_sr); output += deterministicRandom()->randomChoice("\n\r"_sr);
} }

View File

@ -32,6 +32,7 @@
#include <vector> #include <vector>
#include "boost/algorithm/string.hpp" #include "boost/algorithm/string.hpp"
#include "flow/CodeProbe.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fdbclient/FDBOptions.g.h" #include "fdbclient/FDBOptions.g.h"
@ -222,6 +223,52 @@ void DatabaseContext::addSSIdTagMapping(const UID& uid, const Tag& tag) {
ssidTagMapping[uid] = tag; ssidTagMapping[uid] = tag;
} }
void DatabaseContext::getLatestCommitVersionForSSID(const UID& ssid, Tag& tag, Version& commitVersion) {
// initialization
tag = invalidTag;
commitVersion = invalidVersion;
auto iter = ssidTagMapping.find(ssid);
if (iter != ssidTagMapping.end()) {
tag = iter->second;
if (ssVersionVectorCache.hasVersion(tag)) {
commitVersion = ssVersionVectorCache.getVersion(tag);
}
}
}
void DatabaseContext::getLatestCommitVersion(const StorageServerInterface& ssi,
Version readVersion,
VersionVector& latestCommitVersion) {
latestCommitVersion.clear();
if (ssVersionVectorCache.getMaxVersion() == invalidVersion) {
return;
}
// Error checking (based on the assumption that the read version was not obtained
// from the client's grv cache).
if (readVersion > ssVersionVectorCache.getMaxVersion()) {
TraceEvent(SevError, "ReadVersionExceedsVersionVectorMax")
.detail("ReadVersion", readVersion)
.detail("VersionVector", ssVersionVectorCache.toString());
if (g_network->isSimulated()) {
ASSERT(false);
} else {
return; // Do not return a stale commit version in production.
}
}
Tag tag = invalidTag;
Version commitVersion = invalidVersion;
getLatestCommitVersionForSSID(ssi.id(), tag, commitVersion);
if (tag != invalidTag && commitVersion != invalidVersion && commitVersion < readVersion) {
latestCommitVersion.setVersion(tag, commitVersion);
}
}
void DatabaseContext::getLatestCommitVersions(const Reference<LocationInfo>& locationInfo, void DatabaseContext::getLatestCommitVersions(const Reference<LocationInfo>& locationInfo,
Version readVersion, Version readVersion,
Reference<TransactionState> info, Reference<TransactionState> info,
@ -254,24 +301,20 @@ void DatabaseContext::getLatestCommitVersions(const Reference<LocationInfo>& loc
std::map<Version, std::set<Tag>> versionMap; // order the versions to be returned std::map<Version, std::set<Tag>> versionMap; // order the versions to be returned
for (int i = 0; i < locationInfo->locations()->size(); i++) { for (int i = 0; i < locationInfo->locations()->size(); i++) {
bool updatedVersionMap = false;
Version commitVersion = invalidVersion;
Tag tag = invalidTag; Tag tag = invalidTag;
auto iter = ssidTagMapping.find(locationInfo->locations()->getId(i)); Version commitVersion = invalidVersion; // latest commit version
if (iter != ssidTagMapping.end()) { getLatestCommitVersionForSSID(locationInfo->locations()->getId(i), tag, commitVersion);
tag = iter->second;
if (ssVersionVectorCache.hasVersion(tag)) { bool updatedVersionMap = false;
commitVersion = ssVersionVectorCache.getVersion(tag); // latest commit version if (tag != invalidTag && commitVersion != invalidVersion && commitVersion < readVersion) {
if (commitVersion < readVersion) { updatedVersionMap = true;
updatedVersionMap = true; versionMap[commitVersion].insert(tag);
versionMap[commitVersion].insert(tag);
}
}
} }
// Do not log if commitVersion >= readVersion. // Do not log if commitVersion >= readVersion.
if (!updatedVersionMap && commitVersion == invalidVersion) { if (!updatedVersionMap && commitVersion == invalidVersion) {
TraceEvent(SevDebug, "CommitVersionNotFoundForSS") TraceEvent(SevDebug, "CommitVersionNotFoundForSS")
.detail("InSSIDMap", iter != ssidTagMapping.end() ? 1 : 0) .detail("InSSIDMap", tag != invalidTag ? 1 : 0)
.detail("Tag", tag) .detail("Tag", tag)
.detail("CommitVersion", commitVersion) .detail("CommitVersion", commitVersion)
.detail("ReadVersion", readVersion) .detail("ReadVersion", readVersion)
@ -3632,6 +3675,9 @@ ACTOR Future<Version> watchValue(Database cx, Reference<const WatchParameters> p
parameters->useProvisionalProxies, parameters->useProvisionalProxies,
Reverse::False, Reverse::False,
parameters->version)); parameters->version));
if (parameters->tenant.tenantId != locationInfo.tenantEntry.id) {
throw tenant_not_found();
}
try { try {
state Optional<UID> watchValueID = Optional<UID>(); state Optional<UID> watchValueID = Optional<UID>();
@ -5305,6 +5351,44 @@ Future<TenantInfo> populateAndGetTenant(Reference<TransactionState> trState, Key
} }
} }
// Restarts a watch after a database switch
ACTOR Future<Void> restartWatch(Database cx,
TenantInfo tenantInfo,
Key key,
Optional<Value> value,
TagSet tags,
SpanContext spanContext,
TaskPriority taskID,
Optional<UID> debugID,
UseProvisionalProxies useProvisionalProxies) {
// The ID of the tenant may be different on the cluster that we switched to, so obtain the new ID
if (tenantInfo.name.present()) {
state KeyRangeLocationInfo locationInfo = wait(getKeyLocation(cx,
tenantInfo,
key,
&StorageServerInterface::watchValue,
spanContext,
debugID,
useProvisionalProxies,
Reverse::False,
latestVersion));
tenantInfo.tenantId = locationInfo.tenantEntry.id;
}
wait(watchValueMap(cx->minAcceptableReadVersion,
tenantInfo,
key,
value,
cx,
tags,
spanContext,
taskID,
debugID,
useProvisionalProxies));
return Void();
}
// FIXME: This seems pretty horrible. Now a Database can't die until all of its watches do... // FIXME: This seems pretty horrible. Now a Database can't die until all of its watches do...
ACTOR Future<Void> watch(Reference<Watch> watch, ACTOR Future<Void> watch(Reference<Watch> watch,
Database cx, Database cx,
@ -5332,16 +5416,15 @@ ACTOR Future<Void> watch(Reference<Watch> watch,
when(wait(cx->connectionFileChanged())) { when(wait(cx->connectionFileChanged())) {
CODE_PROBE(true, "Recreated a watch after switch"); CODE_PROBE(true, "Recreated a watch after switch");
cx->clearWatchMetadata(); cx->clearWatchMetadata();
watch->watchFuture = watchValueMap(cx->minAcceptableReadVersion, watch->watchFuture = restartWatch(cx,
tenantInfo, tenantInfo,
watch->key, watch->key,
watch->value, watch->value,
cx, tags,
tags, spanContext,
spanContext, taskID,
taskID, debugID,
debugID, useProvisionalProxies);
useProvisionalProxies);
} }
} }
} }
@ -7928,6 +8011,23 @@ ACTOR Future<Standalone<VectorRef<BlobGranuleChunkRef>>> readBlobGranulesActor(
granuleEndKey = keyRange.end; granuleEndKey = keyRange.end;
} }
if (g_network->isSimulated() && !g_simulator->speedUpSimulation && BUGGIFY_WITH_PROB(0.01)) {
// simulate as if we read a stale mapping and a different worker owns the granule
ASSERT(!self->trState->cx->blobWorker_interf.empty());
CODE_PROBE(true, "Randomizing blob worker id for request");
TraceEvent ev("RandomizingBlobWorkerForReq");
ev.detail("OriginalWorker", workerId);
int randomIdx = deterministicRandom()->randomInt(0, self->trState->cx->blobWorker_interf.size());
for (auto& it : self->trState->cx->blobWorker_interf) {
if (randomIdx == 0) {
workerId = it.first;
break;
}
randomIdx--;
}
ev.detail("NewWorker", workerId);
}
state BlobGranuleFileRequest req; state BlobGranuleFileRequest req;
req.keyRange = KeyRangeRef(StringRef(req.arena, granuleStartKey), StringRef(req.arena, granuleEndKey)); req.keyRange = KeyRangeRef(StringRef(req.arena, granuleStartKey), StringRef(req.arena, granuleEndKey));
req.beginVersion = begin; req.beginVersion = begin;

View File

@ -459,7 +459,7 @@ public:
if (!it.is_unreadable() && !it.is_unknown_range() && key.offset > 1) { if (!it.is_unreadable() && !it.is_unknown_range() && key.offset > 1) {
*readThroughEnd = true; *readThroughEnd = true;
key.setKey(maxKey); // maxKey is a KeyRef, but points to a LiteralStringRef. TODO: how can we ASSERT this? key.setKey(maxKey); // maxKey is a KeyRef, but points to a literal. TODO: how can we ASSERT this?
key.offset = 1; key.offset = 1;
return; return;
} }

View File

@ -98,7 +98,7 @@ S3BlobStoreEndpoint::BlobKnobs::BlobKnobs() {
bool S3BlobStoreEndpoint::BlobKnobs::set(StringRef name, int value) { bool S3BlobStoreEndpoint::BlobKnobs::set(StringRef name, int value) {
#define TRY_PARAM(n, sn) \ #define TRY_PARAM(n, sn) \
if (name == LiteralStringRef(#n) || name == LiteralStringRef(#sn)) { \ if (name == #n || name == #sn) { \
n = value; \ n = value; \
return true; \ return true; \
} }
@ -476,7 +476,7 @@ ACTOR Future<Void> deleteRecursively_impl(Reference<S3BlobStoreEndpoint> b,
state Future<Void> done = b->listObjectsStream(bucket, resultStream, prefix, '/', std::numeric_limits<int>::max()); state Future<Void> done = b->listObjectsStream(bucket, resultStream, prefix, '/', std::numeric_limits<int>::max());
// Wrap done in an actor which will send end_of_stream since listObjectsStream() does not (so that many calls can // Wrap done in an actor which will send end_of_stream since listObjectsStream() does not (so that many calls can
// write to the same stream) // write to the same stream)
done = map(done, [=](Void) { done = map(done, [=](Void) mutable {
resultStream.sendError(end_of_stream()); resultStream.sendError(end_of_stream());
return Void(); return Void();
}); });
@ -1193,7 +1193,7 @@ ACTOR Future<S3BlobStoreEndpoint::ListResult> listObjects_impl(Reference<S3BlobS
bstore->listObjectsStream(bucket, resultStream, prefix, delimiter, maxDepth, recurseFilter); bstore->listObjectsStream(bucket, resultStream, prefix, delimiter, maxDepth, recurseFilter);
// Wrap done in an actor which sends end_of_stream because list does not so that many lists can write to the same // Wrap done in an actor which sends end_of_stream because list does not so that many lists can write to the same
// stream // stream
done = map(done, [=](Void) { done = map(done, [=](Void) mutable {
resultStream.sendError(end_of_stream()); resultStream.sendError(end_of_stream());
return Void(); return Void();
}); });
@ -1759,7 +1759,7 @@ Future<Void> S3BlobStoreEndpoint::finishMultiPartUpload(std::string const& bucke
} }
TEST_CASE("/backup/s3/v4headers") { TEST_CASE("/backup/s3/v4headers") {
S3BlobStoreEndpoint::Credentials creds{ "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "" } S3BlobStoreEndpoint::Credentials creds{ "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "" };
// GET without query parameters // GET without query parameters
{ {
S3BlobStoreEndpoint s3("s3.amazonaws.com", "443", "amazonaws", "proxy", "port", creds); S3BlobStoreEndpoint s3("s3.amazonaws.com", "443", "amazonaws", "proxy", "port", creds);

View File

@ -21,7 +21,7 @@
#include "fdbclient/Schemas.h" #include "fdbclient/Schemas.h"
// NOTE: also change mr-status-json-schemas.rst.inc // NOTE: also change mr-status-json-schemas.rst.inc
const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( const KeyRef JSONSchemas::statusSchema = R"statusSchema(
{ {
"cluster":{ "cluster":{
"storage_wiggler": { "storage_wiggler": {
@ -602,7 +602,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
} }
], ],
)statusSchema" )statusSchema"
R"statusSchema( R"statusSchema(
"recovery_state":{ "recovery_state":{
"seconds_since_last_recovered":1, "seconds_since_last_recovered":1,
"required_resolvers":1, "required_resolvers":1,
@ -964,6 +964,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
}, },
"tenants":{ "tenants":{
"num_tenants":0 "num_tenants":0
},
"metacluster" : {
"cluster_type" : "standalone"
} }
}, },
"client":{ "client":{
@ -1005,9 +1008,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
"up_to_date":true "up_to_date":true
} }
} }
})statusSchema"); })statusSchema"_sr;
const KeyRef JSONSchemas::clusterConfigurationSchema = LiteralStringRef(R"configSchema( const KeyRef JSONSchemas::clusterConfigurationSchema = R"configSchema(
{ {
"create":{ "create":{
"$enum":[ "$enum":[
@ -1077,9 +1080,9 @@ const KeyRef JSONSchemas::clusterConfigurationSchema = LiteralStringRef(R"config
"auto_logs":3, "auto_logs":3,
"commit_proxies":5, "commit_proxies":5,
"grv_proxies":1 "grv_proxies":1
})configSchema"); })configSchema"_sr;
const KeyRef JSONSchemas::latencyBandConfigurationSchema = LiteralStringRef(R"configSchema( const KeyRef JSONSchemas::latencyBandConfigurationSchema = R"configSchema(
{ {
"get_read_version":{ "get_read_version":{
"bands":[ "bands":[
@ -1099,30 +1102,30 @@ const KeyRef JSONSchemas::latencyBandConfigurationSchema = LiteralStringRef(R"co
], ],
"max_commit_bytes":0 "max_commit_bytes":0
} }
})configSchema"); })configSchema"_sr;
const KeyRef JSONSchemas::dataDistributionStatsSchema = LiteralStringRef(R"""( const KeyRef JSONSchemas::dataDistributionStatsSchema = R"""(
{ {
"shard_bytes": 1947000 "shard_bytes": 1947000
} }
)"""); )"""_sr;
const KeyRef JSONSchemas::logHealthSchema = LiteralStringRef(R"""( const KeyRef JSONSchemas::logHealthSchema = R"""(
{ {
"log_queue": 156 "log_queue": 156
} }
)"""); )"""_sr;
const KeyRef JSONSchemas::storageHealthSchema = LiteralStringRef(R"""( const KeyRef JSONSchemas::storageHealthSchema = R"""(
{ {
"cpu_usage": 3.28629447047675, "cpu_usage": 3.28629447047675,
"disk_usage": 0.19997897369207954, "disk_usage": 0.19997897369207954,
"storage_durability_lag": 5050809, "storage_durability_lag": 5050809,
"storage_queue": 2030 "storage_queue": 2030
} }
)"""); )"""_sr;
const KeyRef JSONSchemas::aggregateHealthSchema = LiteralStringRef(R"""( const KeyRef JSONSchemas::aggregateHealthSchema = R"""(
{ {
"batch_limited": false, "batch_limited": false,
"limiting_storage_durability_lag": 5050809, "limiting_storage_durability_lag": 5050809,
@ -1132,12 +1135,12 @@ const KeyRef JSONSchemas::aggregateHealthSchema = LiteralStringRef(R"""(
"worst_storage_queue": 2030, "worst_storage_queue": 2030,
"worst_log_queue": 156 "worst_log_queue": 156
} }
)"""); )"""_sr;
const KeyRef JSONSchemas::managementApiErrorSchema = LiteralStringRef(R"""( const KeyRef JSONSchemas::managementApiErrorSchema = R"""(
{ {
"retriable": false, "retriable": false,
"command": "exclude", "command": "exclude",
"message": "The reason of the error" "message": "The reason of the error"
} }
)"""); )"""_sr;

View File

@ -383,6 +383,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
// Use a smaller memtable in simulation to avoid OOMs. // Use a smaller memtable in simulation to avoid OOMs.
int64_t memtableBytes = isSimulated ? 32 * 1024 : 512 * 1024 * 1024; int64_t memtableBytes = isSimulated ? 32 * 1024 : 512 * 1024 * 1024;
init( ROCKSDB_MEMTABLE_BYTES, memtableBytes ); init( ROCKSDB_MEMTABLE_BYTES, memtableBytes );
init( ROCKSDB_LEVEL_STYLE_COMPACTION, true );
init( ROCKSDB_UNSAFE_AUTO_FSYNC, false ); init( ROCKSDB_UNSAFE_AUTO_FSYNC, false );
init( ROCKSDB_PERIODIC_COMPACTION_SECONDS, 0 ); init( ROCKSDB_PERIODIC_COMPACTION_SECONDS, 0 );
init( ROCKSDB_PREFIX_LEN, 0 ); init( ROCKSDB_PREFIX_LEN, 0 );
@ -406,6 +407,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
// If true, enables dynamic adjustment of ROCKSDB_WRITE_RATE_LIMITER_BYTES according to the recent demand of background IO. // If true, enables dynamic adjustment of ROCKSDB_WRITE_RATE_LIMITER_BYTES according to the recent demand of background IO.
init( ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE, true ); init( ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE, true );
init( DEFAULT_FDB_ROCKSDB_COLUMN_FAMILY, "fdb"); init( DEFAULT_FDB_ROCKSDB_COLUMN_FAMILY, "fdb");
init( ROCKSDB_DISABLE_AUTO_COMPACTIONS, false ); // RocksDB default
init( ROCKSDB_PERFCONTEXT_ENABLE, false ); if( randomize && BUGGIFY ) ROCKSDB_PERFCONTEXT_ENABLE = deterministicRandom()->coinflip() ? false : true; init( ROCKSDB_PERFCONTEXT_ENABLE, false ); if( randomize && BUGGIFY ) ROCKSDB_PERFCONTEXT_ENABLE = deterministicRandom()->coinflip() ? false : true;
init( ROCKSDB_PERFCONTEXT_SAMPLE_RATE, 0.0001 ); init( ROCKSDB_PERFCONTEXT_SAMPLE_RATE, 0.0001 );
@ -971,6 +973,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( BLOB_MANAGER_STATUS_EXP_BACKOFF_EXPONENT, 1.5 ); init( BLOB_MANAGER_STATUS_EXP_BACKOFF_EXPONENT, 1.5 );
init( BLOB_MANAGER_CONCURRENT_MERGE_CHECKS, 64 ); if( randomize && BUGGIFY ) BLOB_MANAGER_CONCURRENT_MERGE_CHECKS = 1 << deterministicRandom()->randomInt(0, 7); init( BLOB_MANAGER_CONCURRENT_MERGE_CHECKS, 64 ); if( randomize && BUGGIFY ) BLOB_MANAGER_CONCURRENT_MERGE_CHECKS = 1 << deterministicRandom()->randomInt(0, 7);
init( BLOB_MANIFEST_BACKUP, false ); init( BLOB_MANIFEST_BACKUP, false );
init( BLOB_MANIFEST_BACKUP_INTERVAL, isSimulated ? 5.0 : 30.0 );
init( BLOB_FULL_RESTORE_MODE, false ); init( BLOB_FULL_RESTORE_MODE, false );
init( BGCC_TIMEOUT, isSimulated ? 10.0 : 120.0 ); init( BGCC_TIMEOUT, isSimulated ? 10.0 : 120.0 );

View File

@ -993,7 +993,7 @@ ACTOR Future<bool> checkExclusion(Database db,
state int ssTotalCount = 0; state int ssTotalCount = 0;
state int ssExcludedCount = 0; state int ssExcludedCount = 0;
state std::unordered_set<std::string> diskLocalities(); state std::unordered_set<std::string> diskLocalities;
state int64_t totalKvStoreFreeBytes = 0; state int64_t totalKvStoreFreeBytes = 0;
state int64_t totalKvStoreUsedBytes = 0; state int64_t totalKvStoreUsedBytes = 0;
state int64_t totalKvStoreUsedBytesNonExcluded = 0; state int64_t totalKvStoreUsedBytesNonExcluded = 0;

View File

@ -412,7 +412,7 @@ public:
Reference<FutureBucket> futureBucket, Reference<FutureBucket> futureBucket,
Reference<Task> task) { Reference<Task> task) {
state Reference<TaskFuncBase> taskFunc; state Reference<TaskFuncBase> taskFunc;
state VerifyTask verifyTask = false; state VerifyTask verifyTask(false);
if (!task || !TaskFuncBase::isValidTask(task)) if (!task || !TaskFuncBase::isValidTask(task))
return false; return false;

View File

@ -117,7 +117,7 @@ Tuple& Tuple::append(Tuple const& tuple) {
return *this; return *this;
} }
Tuple& Tuple::append(Versionstamp const& vs) { Tuple& Tuple::append(TupleVersionstamp const& vs) {
offsets.push_back(data.size()); offsets.push_back(data.size());
data.push_back(data.arena(), VERSIONSTAMP_96_CODE); data.push_back(data.arena(), VERSIONSTAMP_96_CODE);
@ -413,7 +413,7 @@ double Tuple::getDouble(size_t index) const {
return bigEndianDouble(swap); return bigEndianDouble(swap);
} }
Versionstamp Tuple::getVersionstamp(size_t index) const { TupleVersionstamp Tuple::getVersionstamp(size_t index) const {
if (index >= offsets.size()) { if (index >= offsets.size()) {
throw invalid_tuple_index(); throw invalid_tuple_index();
} }
@ -422,7 +422,7 @@ Versionstamp Tuple::getVersionstamp(size_t index) const {
if (code != VERSIONSTAMP_96_CODE) { if (code != VERSIONSTAMP_96_CODE) {
throw invalid_tuple_data_type(); throw invalid_tuple_data_type();
} }
return Versionstamp(StringRef(data.begin() + offsets[index] + 1, VERSIONSTAMP_TUPLE_SIZE)); return TupleVersionstamp(StringRef(data.begin() + offsets[index] + 1, VERSIONSTAMP_TUPLE_SIZE));
} }
Tuple::UserTypeStr Tuple::getUserType(size_t index) const { Tuple::UserTypeStr Tuple::getUserType(size_t index) const {
@ -495,7 +495,7 @@ TEST_CASE("/fdbclient/Tuple/makeTuple") {
"byteStr"_sr, "byteStr"_sr,
Tuple::UnicodeStr("str"_sr), Tuple::UnicodeStr("str"_sr),
nullptr, nullptr,
Versionstamp("000000000000"_sr), TupleVersionstamp("000000000000"_sr),
Tuple::UserTypeStr(0x41, "12345678"_sr)); Tuple::UserTypeStr(0x41, "12345678"_sr));
Tuple t2 = Tuple() Tuple t2 = Tuple()
.append(1) .append(1)
@ -505,7 +505,7 @@ TEST_CASE("/fdbclient/Tuple/makeTuple") {
.append("byteStr"_sr) .append("byteStr"_sr)
.append(Tuple::UnicodeStr("str"_sr)) .append(Tuple::UnicodeStr("str"_sr))
.append(nullptr) .append(nullptr)
.append(Versionstamp("000000000000"_sr)) .append(TupleVersionstamp("000000000000"_sr))
.append(Tuple::UserTypeStr(0x41, "12345678"_sr)); .append(Tuple::UserTypeStr(0x41, "12345678"_sr));
ASSERT(t1.pack() == t2.pack()); ASSERT(t1.pack() == t2.pack());
@ -531,7 +531,7 @@ TEST_CASE("/fdbclient/Tuple/unpack") {
"byteStr"_sr, "byteStr"_sr,
Tuple::UnicodeStr("str"_sr), Tuple::UnicodeStr("str"_sr),
nullptr, nullptr,
Versionstamp("000000000000"_sr), TupleVersionstamp("000000000000"_sr),
Tuple::UserTypeStr(0x41, "12345678"_sr)); Tuple::UserTypeStr(0x41, "12345678"_sr));
Standalone<StringRef> packed = t1.pack(); Standalone<StringRef> packed = t1.pack();

View File

@ -1,13 +1,13 @@
#include "fdbclient/Versionstamp.h" #include "fdbclient/TupleVersionstamp.h"
Versionstamp::Versionstamp(StringRef str) { TupleVersionstamp::TupleVersionstamp(StringRef str) {
if (str.size() != VERSIONSTAMP_TUPLE_SIZE) { if (str.size() != VERSIONSTAMP_TUPLE_SIZE) {
throw invalid_versionstamp_size(); throw invalid_versionstamp_size();
} }
data = str; data = str;
} }
int16_t Versionstamp::getBatchNumber() const { int16_t TupleVersionstamp::getBatchNumber() const {
const uint8_t* begin = data.begin(); const uint8_t* begin = data.begin();
begin += 8; begin += 8;
int16_t batchNumber = *(int16_t*)(begin); int16_t batchNumber = *(int16_t*)(begin);
@ -15,7 +15,7 @@ int16_t Versionstamp::getBatchNumber() const {
return batchNumber; return batchNumber;
} }
int16_t Versionstamp::getUserVersion() const { int16_t TupleVersionstamp::getUserVersion() const {
const uint8_t* begin = data.begin(); const uint8_t* begin = data.begin();
begin += 10; begin += 10;
int16_t userVersion = *(int16_t*)(begin); int16_t userVersion = *(int16_t*)(begin);
@ -23,22 +23,22 @@ int16_t Versionstamp::getUserVersion() const {
return userVersion; return userVersion;
} }
const uint8_t* Versionstamp::begin() const { const uint8_t* TupleVersionstamp::begin() const {
return data.begin(); return data.begin();
} }
int64_t Versionstamp::getVersion() const { int64_t TupleVersionstamp::getVersion() const {
const uint8_t* begin = data.begin(); const uint8_t* begin = data.begin();
int64_t version = *(int64_t*)begin; int64_t version = *(int64_t*)begin;
version = bigEndian64(version); version = bigEndian64(version);
return version; return version;
} }
size_t Versionstamp::size() const { size_t TupleVersionstamp::size() const {
return VERSIONSTAMP_TUPLE_SIZE; return VERSIONSTAMP_TUPLE_SIZE;
} }
bool Versionstamp::operator==(const Versionstamp& other) const { bool TupleVersionstamp::operator==(const TupleVersionstamp& other) const {
return getVersion() == other.getVersion() && getBatchNumber() == other.getBatchNumber() && return getVersion() == other.getVersion() && getBatchNumber() == other.getBatchNumber() &&
getUserVersion() == other.getUserVersion(); getUserVersion() == other.getUserVersion();
} }

View File

@ -143,7 +143,7 @@ public:
futureBucket = std::move(r.futureBucket); futureBucket = std::move(r.futureBucket);
} }
KeyBackedProperty<Key> lastBackupTimestamp() { return config.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> lastBackupTimestamp() { return config.pack(__FUNCTION__sr); }
Future<Void> run(Database cx, double pollDelay, int maxConcurrentTasks) { Future<Void> run(Database cx, double pollDelay, int maxConcurrentTasks) {
return taskBucket->run(cx, futureBucket, std::make_shared<double const>(pollDelay), maxConcurrentTasks); return taskBucket->run(cx, futureBucket, std::make_shared<double const>(pollDelay), maxConcurrentTasks);
@ -633,7 +633,7 @@ static inline Future<std::vector<KeyBackedTag>> getAllBackupTags(Reference<ReadY
class KeyBackedConfig { class KeyBackedConfig {
public: public:
static struct { static struct {
static TaskParam<UID> uid() { return LiteralStringRef(__FUNCTION__); } static TaskParam<UID> uid() { return __FUNCTION__sr; }
} TaskParams; } TaskParams;
KeyBackedConfig(StringRef prefix, UID uid = UID()) KeyBackedConfig(StringRef prefix, UID uid = UID())
@ -666,7 +666,7 @@ public:
}); });
} }
KeyBackedProperty<std::string> tag() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<std::string> tag() { return configSpace.pack(__FUNCTION__sr); }
UID getUid() { return uid; } UID getUid() { return uid; }
@ -675,12 +675,10 @@ public:
void clear(Reference<ReadYourWritesTransaction> tr) { tr->clear(configSpace.range()); } void clear(Reference<ReadYourWritesTransaction> tr) { tr->clear(configSpace.range()); }
// lastError is a pair of error message and timestamp expressed as an int64_t // lastError is a pair of error message and timestamp expressed as an int64_t
KeyBackedProperty<std::pair<std::string, Version>> lastError() { KeyBackedProperty<std::pair<std::string, Version>> lastError() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
KeyBackedMap<int64_t, std::pair<std::string, Version>> lastErrorPerType() { KeyBackedMap<int64_t, std::pair<std::string, Version>> lastErrorPerType() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// Updates the error per type map and the last error property // Updates the error per type map and the last error property
@ -769,47 +767,41 @@ public:
// Map of range end boundaries to info about the backup file written for that range. // Map of range end boundaries to info about the backup file written for that range.
typedef KeyBackedMap<Key, RangeSlice> RangeFileMapT; typedef KeyBackedMap<Key, RangeSlice> RangeFileMapT;
RangeFileMapT snapshotRangeFileMap() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } RangeFileMapT snapshotRangeFileMap() { return configSpace.pack(__FUNCTION__sr); }
// Number of kv range files that were both committed to persistent storage AND inserted into // Number of kv range files that were both committed to persistent storage AND inserted into
// the snapshotRangeFileMap. Note that since insertions could replace 1 or more existing // the snapshotRangeFileMap. Note that since insertions could replace 1 or more existing
// map entries this is not necessarily the number of entries currently in the map. // map entries this is not necessarily the number of entries currently in the map.
// This value exists to help with sizing of kv range folders for BackupContainers that // This value exists to help with sizing of kv range folders for BackupContainers that
// require it. // require it.
KeyBackedBinaryValue<int64_t> snapshotRangeFileCount() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> snapshotRangeFileCount() { return configSpace.pack(__FUNCTION__sr); }
// Coalesced set of ranges already dispatched for writing. // Coalesced set of ranges already dispatched for writing.
typedef KeyBackedMap<Key, bool> RangeDispatchMapT; typedef KeyBackedMap<Key, bool> RangeDispatchMapT;
RangeDispatchMapT snapshotRangeDispatchMap() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } RangeDispatchMapT snapshotRangeDispatchMap() { return configSpace.pack(__FUNCTION__sr); }
// Interval to use for the first (initial) snapshot. // Interval to use for the first (initial) snapshot.
KeyBackedProperty<int64_t> initialSnapshotIntervalSeconds() { KeyBackedProperty<int64_t> initialSnapshotIntervalSeconds() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
// Interval to use for determining the target end version for new snapshots // Interval to use for determining the target end version for new snapshots
KeyBackedProperty<int64_t> snapshotIntervalSeconds() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<int64_t> snapshotIntervalSeconds() { return configSpace.pack(__FUNCTION__sr); }
// When the current snapshot began // When the current snapshot began
KeyBackedProperty<Version> snapshotBeginVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> snapshotBeginVersion() { return configSpace.pack(__FUNCTION__sr); }
// When the current snapshot is desired to end. // When the current snapshot is desired to end.
// This can be changed at runtime to speed up or slow down a snapshot // This can be changed at runtime to speed up or slow down a snapshot
KeyBackedProperty<Version> snapshotTargetEndVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> snapshotTargetEndVersion() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<int64_t> snapshotBatchSize() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<int64_t> snapshotBatchSize() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Key> snapshotBatchFuture() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> snapshotBatchFuture() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Key> snapshotBatchDispatchDoneKey() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> snapshotBatchDispatchDoneKey() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<int64_t> snapshotDispatchLastShardsBehind() { KeyBackedProperty<int64_t> snapshotDispatchLastShardsBehind() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
KeyBackedProperty<Version> snapshotDispatchLastVersion() { KeyBackedProperty<Version> snapshotDispatchLastVersion() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
Future<Void> initNewSnapshot(Reference<ReadYourWritesTransaction> tr, int64_t intervalSeconds = -1) { Future<Void> initNewSnapshot(Reference<ReadYourWritesTransaction> tr, int64_t intervalSeconds = -1) {
BackupConfig& copy = *this; // Capture this by value instead of this ptr BackupConfig& copy = *this; // Capture this by value instead of this ptr
@ -843,56 +835,50 @@ public:
}); });
} }
KeyBackedBinaryValue<int64_t> rangeBytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> rangeBytesWritten() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedBinaryValue<int64_t> logBytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedBinaryValue<int64_t> logBytesWritten() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<EBackupState> stateEnum() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<EBackupState> stateEnum() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Reference<IBackupContainer>> backupContainer() { KeyBackedProperty<Reference<IBackupContainer>> backupContainer() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
// Set to true when all backup workers for saving mutation logs have been started. // Set to true when all backup workers for saving mutation logs have been started.
KeyBackedProperty<bool> allWorkerStarted() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> allWorkerStarted() { return configSpace.pack(__FUNCTION__sr); }
// Each backup worker adds its (epoch, tag.id) to this property. // Each backup worker adds its (epoch, tag.id) to this property.
KeyBackedProperty<std::vector<std::pair<int64_t, int64_t>>> startedBackupWorkers() { KeyBackedProperty<std::vector<std::pair<int64_t, int64_t>>> startedBackupWorkers() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// Set to true if backup worker is enabled. // Set to true if backup worker is enabled.
KeyBackedProperty<bool> backupWorkerEnabled() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> backupWorkerEnabled() { return configSpace.pack(__FUNCTION__sr); }
// Set to true if partitioned log is enabled (only useful if backup worker is also enabled). // Set to true if partitioned log is enabled (only useful if backup worker is also enabled).
KeyBackedProperty<bool> partitionedLogEnabled() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> partitionedLogEnabled() { return configSpace.pack(__FUNCTION__sr); }
// Set to true if only requesting incremental backup without base snapshot. // Set to true if only requesting incremental backup without base snapshot.
KeyBackedProperty<bool> incrementalBackupOnly() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> incrementalBackupOnly() { return configSpace.pack(__FUNCTION__sr); }
// Latest version for which all prior versions have saved by backup workers. // Latest version for which all prior versions have saved by backup workers.
KeyBackedProperty<Version> latestBackupWorkerSavedVersion() { KeyBackedProperty<Version> latestBackupWorkerSavedVersion() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
// Stop differntial logging if already started or don't start after completing KV ranges // Stop differntial logging if already started or don't start after completing KV ranges
KeyBackedProperty<bool> stopWhenDone() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<bool> stopWhenDone() { return configSpace.pack(__FUNCTION__sr); }
// Enable snapshot backup file encryption // Enable snapshot backup file encryption
KeyBackedProperty<bool> enableSnapshotBackupEncryption() { KeyBackedProperty<bool> enableSnapshotBackupEncryption() { return configSpace.pack(__FUNCTION__sr); }
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
// Latest version for which all prior versions have had their log copy tasks completed // Latest version for which all prior versions have had their log copy tasks completed
KeyBackedProperty<Version> latestLogEndVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> latestLogEndVersion() { return configSpace.pack(__FUNCTION__sr); }
// The end version of the last complete snapshot // The end version of the last complete snapshot
KeyBackedProperty<Version> latestSnapshotEndVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> latestSnapshotEndVersion() { return configSpace.pack(__FUNCTION__sr); }
// The end version of the first complete snapshot // The end version of the first complete snapshot
KeyBackedProperty<Version> firstSnapshotEndVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Version> firstSnapshotEndVersion() { return configSpace.pack(__FUNCTION__sr); }
KeyBackedProperty<Key> destUidValue() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<Key> destUidValue() { return configSpace.pack(__FUNCTION__sr); }
Future<Optional<Version>> getLatestRestorableVersion(Reference<ReadYourWritesTransaction> tr) { Future<Optional<Version>> getLatestRestorableVersion(Reference<ReadYourWritesTransaction> tr) {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
@ -923,7 +909,7 @@ public:
}); });
} }
KeyBackedProperty<std::vector<KeyRange>> backupRanges() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } KeyBackedProperty<std::vector<KeyRange>> backupRanges() { return configSpace.pack(__FUNCTION__sr); }
void startMutationLogs(Reference<ReadYourWritesTransaction> tr, KeyRangeRef backupRange, Key destUidValue) { void startMutationLogs(Reference<ReadYourWritesTransaction> tr, KeyRangeRef backupRange, Key destUidValue) {
Key mutationLogsDestKey = destUidValue.withPrefix(backupLogKeys.begin); Key mutationLogsDestKey = destUidValue.withPrefix(backupLogKeys.begin);

View File

@ -18,8 +18,11 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef FDBCLIENT_CONSISTENCYSCANINTERFACE_H #if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_CONSISTENCYSCANINTERFACE_ACTOR_G_H)
#define FDBCLIENT_CONSISTENCYSCANINTERFACE_H #define FDBCLIENT_CONSISTENCYSCANINTERFACE_ACTOR_G_H
#include "fdbclient/ConsistencyScanInterface.actor.g.h"
#elif !defined(FDBCLIENT_CONSISTENCYSCANINTERFACE_ACTOR_H)
#define FDBCLIENT_CONSISTENCYSCANINTERFACE_ACTOR_H
#include "fdbclient/CommitProxyInterface.h" #include "fdbclient/CommitProxyInterface.h"
#include "fdbclient/DatabaseConfiguration.h" #include "fdbclient/DatabaseConfiguration.h"
@ -28,6 +31,8 @@
#include "fdbrpc/fdbrpc.h" #include "fdbrpc/fdbrpc.h"
#include "fdbrpc/Locality.h" #include "fdbrpc/Locality.h"
#include "flow/actorcompiler.h" // must be last include
struct ConsistencyScanInterface { struct ConsistencyScanInterface {
constexpr static FileIdentifier file_identifier = 4983265; constexpr static FileIdentifier file_identifier = 4983265;
RequestStream<ReplyPromise<Void>> waitFailure; RequestStream<ReplyPromise<Void>> waitFailure;
@ -155,35 +160,37 @@ struct ConsistencyScanInfo {
} }
}; };
Future<Version> getVersion(Database const& cx); ACTOR Future<Version> getVersion(Database cx);
Future<bool> getKeyServers( ACTOR Future<bool> getKeyServers(
Database const& cx, Database cx,
Promise<std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>>> const& keyServersPromise, Promise<std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>>> keyServersPromise,
KeyRangeRef const& kr, KeyRangeRef kr,
bool const& performQuiescentChecks); bool performQuiescentChecks);
Future<bool> getKeyLocations(Database const& cx, ACTOR Future<bool> getKeyLocations(Database cx,
std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>> const& shards, std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>> shards,
Promise<Standalone<VectorRef<KeyValueRef>>> const& keyLocationPromise, Promise<Standalone<VectorRef<KeyValueRef>>> keyLocationPromise,
bool const& performQuiescentChecks); bool performQuiescentChecks);
Future<bool> checkDataConsistency(Database const& cx, ACTOR Future<bool> checkDataConsistency(Database cx,
VectorRef<KeyValueRef> const& keyLocations, VectorRef<KeyValueRef> keyLocations,
DatabaseConfiguration const& configuration, DatabaseConfiguration configuration,
std::map<UID, StorageServerInterface> const& tssMapping, std::map<UID, StorageServerInterface> tssMapping,
bool const& performQuiescentChecks, bool performQuiescentChecks,
bool const& performTSSCheck, bool performTSSCheck,
bool const& firstClient, bool firstClient,
bool const& failureIsError, bool failureIsError,
int const& clientId, int clientId,
int const& clientCount, int clientCount,
bool const& distributed, bool distributed,
bool const& shuffleShards, bool shuffleShards,
int const& shardSampleFactor, int shardSampleFactor,
int64_t const& sharedRandomNumber, int64_t sharedRandomNumber,
int64_t const& repetitions, int64_t repetitions,
int64_t* const& bytesReadInPreviousRound, int64_t* bytesReadInPreviousRound,
int const& restart, int restart,
int64_t const& maxRate, int64_t maxRate,
int64_t const& targetInterval, int64_t targetInterval,
KeyRef const& progressKey); KeyRef progressKey);
#include "flow/unactorcompiler.h"
#endif // FDBCLIENT_CONSISTENCYSCANINTERFACE_H #endif // FDBCLIENT_CONSISTENCYSCANINTERFACE_H

View File

@ -655,6 +655,12 @@ public:
// Adds or updates the specified (UID, Tag) pair in the tag mapping. // Adds or updates the specified (UID, Tag) pair in the tag mapping.
void addSSIdTagMapping(const UID& uid, const Tag& tag); void addSSIdTagMapping(const UID& uid, const Tag& tag);
// Returns the latest commit version that mutated the specified storage server.
// @in ssid id of the storage server interface
// @out tag storage server's tag, if an entry exists for "ssid" in "ssidTagMapping"
// @out commitVersion latest commit version that mutated the storage server
void getLatestCommitVersionForSSID(const UID& ssid, Tag& tag, Version& commitVersion);
// Returns the latest commit versions that mutated the specified storage servers // Returns the latest commit versions that mutated the specified storage servers
/// @note returns the latest commit version for a storage server only if the latest /// @note returns the latest commit version for a storage server only if the latest
// commit version of that storage server is below the specified "readVersion". // commit version of that storage server is below the specified "readVersion".
@ -663,6 +669,14 @@ public:
Reference<TransactionState> info, Reference<TransactionState> info,
VersionVector& latestCommitVersions); VersionVector& latestCommitVersions);
// Returns the latest commit version that mutated the specified storage server.
// @note this is a lightweight version of "getLatestCommitVersions()", to be used
// when the state ("TransactionState") of the transaction that fetched the read
// version is not available.
void getLatestCommitVersion(const StorageServerInterface& ssi,
Version readVersion,
VersionVector& latestCommitVersion);
// used in template functions to create a transaction // used in template functions to create a transaction
using TransactionT = ReadYourWritesTransaction; using TransactionT = ReadYourWritesTransaction;
Reference<TransactionT> createTransaction(); Reference<TransactionT> createTransaction();

View File

@ -1654,4 +1654,36 @@ struct transaction_creator_traits<T, std::void_t<typename T::TransactionT>> : st
template <typename T> template <typename T>
constexpr bool is_transaction_creator = transaction_creator_traits<T>::value; constexpr bool is_transaction_creator = transaction_creator_traits<T>::value;
struct Versionstamp {
Version version = invalidVersion;
uint16_t batchNumber = 0;
bool operator==(const Versionstamp& r) const { return version == r.version && batchNumber == r.batchNumber; }
bool operator!=(const Versionstamp& r) const { return !(*this == r); }
bool operator<(const Versionstamp& r) const {
return version < r.version || (version == r.version && batchNumber < r.batchNumber);
}
bool operator>(const Versionstamp& r) const { return r < *this; }
bool operator<=(const Versionstamp& r) const { return !(*this > r); }
bool operator>=(const Versionstamp& r) const { return !(*this < r); }
template <class Ar>
void serialize(Ar& ar) {
int64_t beVersion;
int16_t beBatch;
if constexpr (!Ar::isDeserializing) {
beVersion = bigEndian64(version);
beBatch = bigEndian16(batchNumber);
}
serializer(ar, beVersion, beBatch);
if constexpr (Ar::isDeserializing) {
version = bigEndian64(version);
batchNumber = bigEndian16(beBatch);
}
}
};
#endif #endif

View File

@ -29,6 +29,7 @@
#include "fdbclient/GenericTransactionHelper.h" #include "fdbclient/GenericTransactionHelper.h"
#include "fdbclient/Subspace.h" #include "fdbclient/Subspace.h"
#include "flow/ObjectSerializer.h" #include "flow/ObjectSerializer.h"
#include "flow/Platform.h"
#include "flow/genericactors.actor.h" #include "flow/genericactors.actor.h"
#include "flow/serialize.h" #include "flow/serialize.h"
@ -305,6 +306,14 @@ public:
tr->atomicOp(key, BinaryWriter::toValue<T>(val, Unversioned()), type); tr->atomicOp(key, BinaryWriter::toValue<T>(val, Unversioned()), type);
} }
template <class Transaction>
void setVersionstamp(Transaction tr, T const& val, int offset) {
tr->atomicOp(
key,
BinaryWriter::toValue<T>(val, Unversioned()).withSuffix(StringRef(reinterpret_cast<uint8_t*>(&offset), 4)),
MutationRef::SetVersionstampedValue);
}
template <class Transaction> template <class Transaction>
void clear(Transaction tr) { void clear(Transaction tr) {
tr->clear(key); tr->clear(key);

View File

@ -53,6 +53,8 @@ struct Traceable<ClusterUsage> : std::true_type {
} }
}; };
std::string clusterTypeToString(const ClusterType& clusterType);
// Represents the various states that a data cluster could be in. // Represents the various states that a data cluster could be in.
// //
// READY - the data cluster is active // READY - the data cluster is active
@ -98,6 +100,15 @@ struct DataClusterEntry {
} }
}; };
struct MetaclusterMetrics {
int numTenants = 0;
int numDataClusters = 0;
int tenantGroupCapacity = 0;
int tenantGroupsAllocated = 0;
MetaclusterMetrics() = default;
};
struct MetaclusterRegistrationEntry { struct MetaclusterRegistrationEntry {
constexpr static FileIdentifier file_identifier = 13448589; constexpr static FileIdentifier file_identifier = 13448589;

View File

@ -115,6 +115,9 @@ struct ManagementClusterMetadata {
static KeyBackedSet<Tuple> clusterTenantGroupIndex; static KeyBackedSet<Tuple> clusterTenantGroupIndex;
}; };
// Helper function to compute metacluster capacity by passing the result of MetaclusterAPI::listClusters
std::pair<ClusterUsage, ClusterUsage> metaclusterCapacity(std::map<ClusterName, DataClusterMetadata> const& clusters);
ACTOR Future<Reference<IDatabase>> openDatabase(ClusterConnectionString connectionString); ACTOR Future<Reference<IDatabase>> openDatabase(ClusterConnectionString connectionString);
ACTOR template <class Transaction> ACTOR template <class Transaction>

View File

@ -308,6 +308,7 @@ public:
int ROCKSDB_BACKGROUND_PARALLELISM; int ROCKSDB_BACKGROUND_PARALLELISM;
int ROCKSDB_READ_PARALLELISM; int ROCKSDB_READ_PARALLELISM;
int64_t ROCKSDB_MEMTABLE_BYTES; int64_t ROCKSDB_MEMTABLE_BYTES;
bool ROCKSDB_LEVEL_STYLE_COMPACTION;
bool ROCKSDB_UNSAFE_AUTO_FSYNC; bool ROCKSDB_UNSAFE_AUTO_FSYNC;
int64_t ROCKSDB_PERIODIC_COMPACTION_SECONDS; int64_t ROCKSDB_PERIODIC_COMPACTION_SECONDS;
int ROCKSDB_PREFIX_LEN; int ROCKSDB_PREFIX_LEN;
@ -329,6 +330,7 @@ public:
int64_t ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC; int64_t ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC;
bool ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE; bool ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE;
std::string DEFAULT_FDB_ROCKSDB_COLUMN_FAMILY; std::string DEFAULT_FDB_ROCKSDB_COLUMN_FAMILY;
bool ROCKSDB_DISABLE_AUTO_COMPACTIONS;
bool ROCKSDB_PERFCONTEXT_ENABLE; // Enable rocks perf context metrics. May cause performance overhead bool ROCKSDB_PERFCONTEXT_ENABLE; // Enable rocks perf context metrics. May cause performance overhead
double ROCKSDB_PERFCONTEXT_SAMPLE_RATE; double ROCKSDB_PERFCONTEXT_SAMPLE_RATE;
double ROCKSDB_METRICS_SAMPLE_INTERVAL; double ROCKSDB_METRICS_SAMPLE_INTERVAL;
@ -950,6 +952,7 @@ public:
double BGCC_TIMEOUT; double BGCC_TIMEOUT;
double BGCC_MIN_INTERVAL; double BGCC_MIN_INTERVAL;
bool BLOB_MANIFEST_BACKUP; bool BLOB_MANIFEST_BACKUP;
double BLOB_MANIFEST_BACKUP_INTERVAL;
bool BLOB_FULL_RESTORE_MODE; bool BLOB_FULL_RESTORE_MODE;
// Blob metadata // Blob metadata

View File

@ -583,7 +583,7 @@ Future<Void> enableAuto(Reference<DB> db, bool enabled) {
tr->get(tagThrottleAutoEnabledKey); tr->get(tagThrottleAutoEnabledKey);
Optional<Value> value = wait(safeThreadFutureToFuture(valueF)); Optional<Value> value = wait(safeThreadFutureToFuture(valueF));
if (!value.present() || (enabled && value.get() != "1"_sr) || (!enabled && value.get() != "0"_sr)) { if (!value.present() || (enabled && value.get() != "1"_sr) || (!enabled && value.get() != "0"_sr)) {
tr->set(tagThrottleAutoEnabledKey, LiteralStringRef(enabled ? "1" : "0")); tr->set(tagThrottleAutoEnabledKey, enabled ? "1"_sr : "0"_sr);
signalThrottleChange<typename DB::TransactionT>(tr); signalThrottleChange<typename DB::TransactionT>(tr);
wait(safeThreadFutureToFuture(tr->commit())); wait(safeThreadFutureToFuture(tr->commit()));

View File

@ -115,7 +115,7 @@ public:
}; };
struct ReservedTaskParams { struct ReservedTaskParams {
static TaskParam<Version> scheduledVersion() { return LiteralStringRef(__FUNCTION__); } static TaskParam<Version> scheduledVersion() { return __FUNCTION__sr; }
}; };
class FutureBucket; class FutureBucket;
@ -480,7 +480,8 @@ struct TaskFuncBase : IDispatched<TaskFuncBase, Standalone<StringRef>, std::func
}; };
#define REGISTER_TASKFUNC(TaskFunc) REGISTER_FACTORY(TaskFuncBase, TaskFunc, name) #define REGISTER_TASKFUNC(TaskFunc) REGISTER_FACTORY(TaskFuncBase, TaskFunc, name)
#define REGISTER_TASKFUNC_ALIAS(TaskFunc, Alias) \ #define REGISTER_TASKFUNC_ALIAS(TaskFunc, Alias) \
REGISTER_DISPATCHED_ALIAS(TaskFunc, Alias, TaskFunc::name, LiteralStringRef(#Alias)) REGISTER_DISPATCHED_ALIAS( \
TaskFunc, Alias, TaskFunc::name, StringRef(reinterpret_cast<const uint8_t*>(#Alias), sizeof(#Alias) - 1))
struct TaskCompletionKey { struct TaskCompletionKey {
Future<Key> get(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket); Future<Key> get(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket);

View File

@ -25,7 +25,7 @@
#include "flow/flow.h" #include "flow/flow.h"
#include "fdbclient/FDBTypes.h" #include "fdbclient/FDBTypes.h"
#include "fdbclient/Versionstamp.h" #include "fdbclient/TupleVersionstamp.h"
struct Tuple { struct Tuple {
struct UnicodeStr { struct UnicodeStr {
@ -63,7 +63,7 @@ struct Tuple {
Tuple& append(double); Tuple& append(double);
Tuple& append(std::nullptr_t); Tuple& append(std::nullptr_t);
Tuple& appendNull(); Tuple& appendNull();
Tuple& append(Versionstamp const&); Tuple& append(TupleVersionstamp const&);
Tuple& append(UserTypeStr const&); Tuple& append(UserTypeStr const&);
Standalone<StringRef> pack() const { Standalone<StringRef> pack() const {
@ -92,7 +92,7 @@ struct Tuple {
StringRef subTupleRawString(size_t index) const; StringRef subTupleRawString(size_t index) const;
ElementType getType(size_t index) const; ElementType getType(size_t index) const;
Standalone<StringRef> getString(size_t index) const; Standalone<StringRef> getString(size_t index) const;
Versionstamp getVersionstamp(size_t index) const; TupleVersionstamp getVersionstamp(size_t index) const;
int64_t getInt(size_t index, bool allow_incomplete = false) const; int64_t getInt(size_t index, bool allow_incomplete = false) const;
bool getBool(size_t index) const; bool getBool(size_t index) const;
float getFloat(size_t index) const; float getFloat(size_t index) const;

View File

@ -1,5 +1,5 @@
/* /*
* Versionstamp.h * TupleVersionstamp.h
* *
* This source file is part of the FoundationDB open source project * This source file is part of the FoundationDB open source project
* *
@ -27,15 +27,15 @@
const size_t VERSIONSTAMP_TUPLE_SIZE = 12; const size_t VERSIONSTAMP_TUPLE_SIZE = 12;
struct Versionstamp { struct TupleVersionstamp {
Versionstamp(StringRef); TupleVersionstamp(StringRef);
int64_t getVersion() const; int64_t getVersion() const;
int16_t getBatchNumber() const; int16_t getBatchNumber() const;
int16_t getUserVersion() const; int16_t getUserVersion() const;
size_t size() const; size_t size() const;
const uint8_t* begin() const; const uint8_t* begin() const;
bool operator==(const Versionstamp&) const; bool operator==(const TupleVersionstamp&) const;
private: private:
Standalone<StringRef> data; Standalone<StringRef> data;

View File

@ -48,7 +48,7 @@ TEST_CASE("/flow/actorcompiler/lineNumbers") {
} }
break; break;
} }
ASSERT(LiteralStringRef(__FILE__).endsWith("FlowTests.actor.cpp"_sr)); ASSERT(__FILE__sr.endsWith("FlowTests.actor.cpp"_sr));
return Void(); return Void();
} }
@ -144,6 +144,14 @@ ACTOR static Future<Void> cheeseWaitActor() {
return Void(); return Void();
} }
size_t cheeseWaitActorSize() {
#ifndef OPEN_FOR_IDE
return sizeof(CheeseWaitActorActor);
#else
return 0ul;
#endif
}
ACTOR static void trivialVoidActor(int* result) { ACTOR static void trivialVoidActor(int* result) {
*result = 1; *result = 1;
} }
@ -353,7 +361,7 @@ TEST_CASE("/flow/flow/cancel2") {
return Void(); return Void();
} }
namespace { namespace flow_tests_details {
// Simple message for flatbuffers unittests // Simple message for flatbuffers unittests
struct Int { struct Int {
constexpr static FileIdentifier file_identifier = 12345; constexpr static FileIdentifier file_identifier = 12345;
@ -365,7 +373,7 @@ struct Int {
serializer(ar, value); serializer(ar, value);
} }
}; };
} // namespace } // namespace flow_tests_details
TEST_CASE("/flow/flow/nonserializable futures") { TEST_CASE("/flow/flow/nonserializable futures") {
// Types no longer need to be statically serializable to make futures, promises, actors // Types no longer need to be statically serializable to make futures, promises, actors
@ -381,16 +389,16 @@ TEST_CASE("/flow/flow/nonserializable futures") {
// ReplyPromise can be used like a normal promise // ReplyPromise can be used like a normal promise
{ {
ReplyPromise<Int> rpInt; ReplyPromise<flow_tests_details::Int> rpInt;
Future<Int> f = rpInt.getFuture(); Future<flow_tests_details::Int> f = rpInt.getFuture();
ASSERT(!f.isReady()); ASSERT(!f.isReady());
rpInt.send(123); rpInt.send(123);
ASSERT(f.get().value == 123); ASSERT(f.get().value == 123);
} }
{ {
RequestStream<Int> rsInt; RequestStream<flow_tests_details::Int> rsInt;
FutureStream<Int> f = rsInt.getFuture(); FutureStream<flow_tests_details::Int> f = rsInt.getFuture();
rsInt.send(1); rsInt.send(1);
rsInt.send(2); rsInt.send(2);
ASSERT(f.pop().value == 1); ASSERT(f.pop().value == 1);
@ -403,7 +411,7 @@ TEST_CASE("/flow/flow/nonserializable futures") {
TEST_CASE("/flow/flow/networked futures") { TEST_CASE("/flow/flow/networked futures") {
// RequestStream can be serialized // RequestStream can be serialized
{ {
RequestStream<Int> locInt; RequestStream<flow_tests_details::Int> locInt;
BinaryWriter wr(IncludeVersion()); BinaryWriter wr(IncludeVersion());
wr << locInt; wr << locInt;
@ -411,7 +419,7 @@ TEST_CASE("/flow/flow/networked futures") {
locInt.getEndpoint().getPrimaryAddress() == FlowTransport::transport().getLocalAddress()); locInt.getEndpoint().getPrimaryAddress() == FlowTransport::transport().getLocalAddress());
BinaryReader rd(wr.toValue(), IncludeVersion()); BinaryReader rd(wr.toValue(), IncludeVersion());
RequestStream<Int> remoteInt; RequestStream<flow_tests_details::Int> remoteInt;
rd >> remoteInt; rd >> remoteInt;
ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint()); ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint());
@ -420,14 +428,14 @@ TEST_CASE("/flow/flow/networked futures") {
// ReplyPromise can be serialized // ReplyPromise can be serialized
// TODO: This needs to fiddle with g_currentDeliveryPeerAddress // TODO: This needs to fiddle with g_currentDeliveryPeerAddress
if (0) { if (0) {
ReplyPromise<Int> locInt; ReplyPromise<flow_tests_details::Int> locInt;
BinaryWriter wr(IncludeVersion()); BinaryWriter wr(IncludeVersion());
wr << locInt; wr << locInt;
ASSERT(locInt.getEndpoint().isValid() && locInt.getEndpoint().isLocal()); ASSERT(locInt.getEndpoint().isValid() && locInt.getEndpoint().isLocal());
BinaryReader rd(wr.toValue(), IncludeVersion()); BinaryReader rd(wr.toValue(), IncludeVersion());
ReplyPromise<Int> remoteInt; ReplyPromise<flow_tests_details::Int> remoteInt;
rd >> remoteInt; rd >> remoteInt;
ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint()); ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint());
@ -1084,7 +1092,7 @@ TEST_CASE("#flow/flow/perf/actor patterns") {
ASSERT(out2[i].isReady()); ASSERT(out2[i].isReady());
} }
printf("2xcheeseActor(chooseTwoActor(cheeseActor(fifo), never)): %0.2f M/sec\n", N / 1e6 / (timer() - start)); printf("2xcheeseActor(chooseTwoActor(cheeseActor(fifo), never)): %0.2f M/sec\n", N / 1e6 / (timer() - start));
printf("sizeof(CheeseWaitActorActor) == %zu\n", sizeof(CheeseWaitActorActor)); printf("sizeof(CheeseWaitActorActor) == %zu\n", cheeseWaitActorSize());
} }
{ {

View File

@ -217,8 +217,8 @@ TEST_CASE("fdbrpc/SimExternalClient") {
// Wait until server is ready // Wait until server is ready
threadSleep(0.01); threadSleep(0.01);
} }
state Standalone<StringRef> data = state Standalone<StringRef> data(
deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(0, maxDataLength + 1)); deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(0, maxDataLength + 1)));
PacketWriter packetWriter(packetQueue.getWriteBuffer(data.size()), nullptr, Unversioned()); PacketWriter packetWriter(packetQueue.getWriteBuffer(data.size()), nullptr, Unversioned());
packetWriter.serializeBytes(data); packetWriter.serializeBytes(data);
wait(externalConn->onWritable()); wait(externalConn->onWritable());

View File

@ -25,11 +25,16 @@
inline void throw_operation_failed() { inline void throw_operation_failed() {
throw operation_failed(); throw operation_failed();
} }
#ifdef OPEN_FOR_IDE
bool testFuzzActor(Future<int> (*actor)(FutureStream<int>, PromiseStream<int>, Future<Void>),
const char* desc,
std::vector<int> const& expectedOutput);
#else
// This is in dsltest.actor.cpp: // This is in dsltest.actor.cpp:
bool testFuzzActor(Future<int> (*actor)(FutureStream<int> const&, PromiseStream<int> const&, Future<Void> const&), bool testFuzzActor(Future<int> (*actor)(FutureStream<int> const&, PromiseStream<int> const&, Future<Void> const&),
const char* desc, const char* desc,
std::vector<int> const& expectedOutput); std::vector<int> const& expectedOutput);
#endif
// This is defined by ActorFuzz.actor.cpp (generated by actorFuzz.py) // This is defined by ActorFuzz.actor.cpp (generated by actorFuzz.py)
// Returns (tests passed, tests total) // Returns (tests passed, tests total)

View File

@ -343,7 +343,7 @@ private:
TraceEvent("AFCUnderlyingOpenEnd").detail("Filename", filename); TraceEvent("AFCUnderlyingOpenEnd").detail("Filename", filename);
int64_t l = wait(f->size()); int64_t l = wait(f->size());
TraceEvent("AFCUnderlyingSize").detail("Filename", filename).detail("Size", l); TraceEvent("AFCUnderlyingSize").detail("Filename", filename).detail("Size", l);
return new AsyncFileCached(f, filename, l, pageCache); return Reference<AsyncFileCached>(new AsyncFileCached(f, filename, l, pageCache)).castTo<IAsyncFile>();
} catch (Error& e) { } catch (Error& e) {
if (e.code() != error_code_actor_cancelled) if (e.code() != error_code_actor_cancelled)
openFiles.erase(filename); openFiles.erase(filename);

View File

@ -94,10 +94,12 @@ Future<Void> tssComparison(Req req,
// we want to record ss/tss errors to metrics // we want to record ss/tss errors to metrics
state int srcErrorCode = error_code_success; state int srcErrorCode = error_code_success;
state int tssErrorCode = error_code_success; state int tssErrorCode = error_code_success;
state ErrorOr<Resp> src;
state Optional<ErrorOr<Resp>> tss;
loop { loop {
choose { choose {
when(state ErrorOr<Resp> src = wait(fSource)) { when(wait(store(src, fSource))) {
srcEndTime = now(); srcEndTime = now();
fSource = Never(); fSource = Never();
finished++; finished++;
@ -105,7 +107,7 @@ Future<Void> tssComparison(Req req,
break; break;
} }
} }
when(state Optional<ErrorOr<Resp>> tss = wait(fTssWithTimeout)) { when(wait(store(tss, fTssWithTimeout))) {
tssEndTime = now(); tssEndTime = now();
fTssWithTimeout = Never(); fTssWithTimeout = Never();
finished++; finished++;

View File

@ -503,6 +503,8 @@ public:
bool allowStorageMigrationTypeChange = false; bool allowStorageMigrationTypeChange = false;
double injectTargetedSSRestartTime = std::numeric_limits<double>::max(); double injectTargetedSSRestartTime = std::numeric_limits<double>::max();
double injectSSDelayTime = std::numeric_limits<double>::max(); double injectSSDelayTime = std::numeric_limits<double>::max();
double injectTargetedBMRestartTime = std::numeric_limits<double>::max();
double injectTargetedBWRestartTime = std::numeric_limits<double>::max();
std::unordered_map<Standalone<StringRef>, PrivateKey> authKeys; std::unordered_map<Standalone<StringRef>, PrivateKey> authKeys;

View File

@ -19,6 +19,7 @@
*/ */
#include "fdbserver/BlobGranuleValidation.actor.h" #include "fdbserver/BlobGranuleValidation.actor.h"
#include "fdbserver/Knobs.h"
#include "flow/actorcompiler.h" // has to be last include #include "flow/actorcompiler.h" // has to be last include
ACTOR Future<std::pair<RangeResult, Version>> readFromFDB(Database cx, KeyRange range) { ACTOR Future<std::pair<RangeResult, Version>> readFromFDB(Database cx, KeyRange range) {
@ -332,3 +333,97 @@ ACTOR Future<Void> validateGranuleSummaries(Database cx,
throw e; throw e;
} }
} }
struct feed_cmp_f {
bool operator()(const std::pair<Key, KeyRange>& lhs, const std::pair<Key, KeyRange>& rhs) const {
if (lhs.second.begin == rhs.second.begin) {
return lhs.second.end < rhs.second.end;
}
return lhs.second.begin < rhs.second.begin;
}
};
ACTOR Future<std::vector<std::pair<Key, KeyRange>>> getActiveFeeds(Transaction* tr) {
RangeResult feedResult = wait(tr->getRange(changeFeedKeys, 10000));
ASSERT(!feedResult.more);
std::vector<std::pair<Key, KeyRange>> results;
for (auto& it : feedResult) {
Key feedKey = it.key.removePrefix(changeFeedPrefix);
KeyRange feedRange;
Version version;
ChangeFeedStatus status;
std::tie(feedRange, version, status) = decodeChangeFeedValue(it.value);
results.push_back({ feedKey, feedRange });
}
std::sort(results.begin(), results.end(), feed_cmp_f());
return results;
}
// TODO: add debug parameter
// FIXME: this check currently assumes blob granules are the only users of change feeds, and will fail if that is not
// the case
ACTOR Future<Void> checkFeedCleanup(Database cx, bool debug) {
if (SERVER_KNOBS->BLOB_WORKER_FORCE_FLUSH_CLEANUP_DELAY < 0) {
// no guarantee of feed cleanup, return
return Void();
}
// big extra timeout just because simulation can take a while to quiesce
state double checkTimeoutOnceStable = 300.0 + 2 * SERVER_KNOBS->BLOB_WORKER_FORCE_FLUSH_CLEANUP_DELAY;
state Optional<double> stableTimestamp;
state Standalone<VectorRef<KeyRangeRef>> lastGranules;
state Transaction tr(cx);
loop {
try {
// get set of current granules. if different than last set of granules
state Standalone<VectorRef<KeyRangeRef>> granules = wait(tr.getBlobGranuleRanges(normalKeys, 10000));
state std::vector<std::pair<Key, KeyRange>> activeFeeds = wait(getActiveFeeds(&tr));
// TODO REMOVE
if (debug) {
fmt::print("{0} granules and {1} active feeds found\n", granules.size(), activeFeeds.size());
}
/*fmt::print("Granules:\n");
for (auto& it : granules) {
fmt::print(" [{0} - {1})\n", it.begin.printable(), it.end.printable());
}*/
bool allPresent = granules.size() == activeFeeds.size();
for (int i = 0; allPresent && i < granules.size(); i++) {
if (granules[i] != activeFeeds[i].second) {
if (debug) {
fmt::print("Feed {0} for [{1} - {2}) still exists despite no granule!\n",
activeFeeds[i].first.printable(),
activeFeeds[i].second.begin.printable(),
activeFeeds[i].second.end.printable());
}
allPresent = false;
break;
}
}
if (allPresent) {
if (debug) {
fmt::print("Feed Cleanup Check Complete\n");
}
return Void();
}
if (granules != lastGranules) {
stableTimestamp.reset();
} else if (!stableTimestamp.present()) {
stableTimestamp = now();
}
lastGranules = granules;
// ensure this converges within a time window of granules becoming stable
if (stableTimestamp.present()) {
ASSERT(now() - stableTimestamp.get() <= checkTimeoutOnceStable);
}
wait(delay(2.0));
} catch (Error& e) {
wait(tr.onError(e));
}
}
}

View File

@ -384,6 +384,7 @@ struct BlobManagerData : NonCopyable, ReferenceCounted<BlobManagerData> {
int64_t epoch; int64_t epoch;
int64_t seqNo = 1; int64_t seqNo = 1;
int64_t manifestDumperSeqNo = 1;
Promise<Void> iAmReplaced; Promise<Void> iAmReplaced;
@ -484,6 +485,19 @@ struct BlobManagerData : NonCopyable, ReferenceCounted<BlobManagerData> {
} }
return false; return false;
} }
bool maybeInjectTargetedRestart() {
// inject a BW restart at most once per test
if (g_network->isSimulated() && !g_simulator->speedUpSimulation &&
now() > g_simulator->injectTargetedBMRestartTime) {
CODE_PROBE(true, "Injecting BM targeted restart");
TraceEvent("SimBMInjectTargetedRestart", id);
g_simulator->injectTargetedBMRestartTime = std::numeric_limits<double>::max();
iAmReplaced.send(Void());
return true;
}
return false;
}
}; };
// Helper function for alignKeys(). // Helper function for alignKeys().
@ -848,7 +862,18 @@ ACTOR Future<Void> doRangeAssignment(Reference<BlobManagerData> bmData,
if (bmData->workersById.count(workerID.get()) == 0) { if (bmData->workersById.count(workerID.get()) == 0) {
throw no_more_servers(); throw no_more_servers();
} }
wait(bmData->workersById[workerID.get()].assignBlobRangeRequest.getReply(req)); state Future<Void> assignFuture = bmData->workersById[workerID.get()].assignBlobRangeRequest.getReply(req);
if (BUGGIFY) {
// wait for request to actually send
wait(delay(0));
if (bmData->maybeInjectTargetedRestart()) {
throw blob_manager_replaced();
}
}
wait(assignFuture);
if (assignment.previousFailure.present()) { if (assignment.previousFailure.present()) {
// previous assign failed and this one succeeded // previous assign failed and this one succeeded
--bmData->stats.blockedAssignments; --bmData->stats.blockedAssignments;
@ -1216,6 +1241,10 @@ ACTOR Future<Void> writeInitialGranuleMapping(Reference<BlobManagerData> bmData,
} }
i += j; i += j;
} }
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
return Void(); return Void();
} }
@ -1654,6 +1683,11 @@ ACTOR Future<Void> reevaluateInitialSplit(Reference<BlobManagerData> bmData,
} }
} }
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
// transaction committed, send updated range assignments. Even if there is only one range still, we need to revoke // transaction committed, send updated range assignments. Even if there is only one range still, we need to revoke
// it and re-assign it to cancel the old granule and retry // it and re-assign it to cancel the old granule and retry
CODE_PROBE(true, "BM successfully changed initial split too big"); CODE_PROBE(true, "BM successfully changed initial split too big");
@ -1941,6 +1975,11 @@ ACTOR Future<Void> maybeSplitRange(Reference<BlobManagerData> bmData,
wait(tr->commit()); wait(tr->commit());
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
// Update BlobGranuleMergeBoundary in-memory state. // Update BlobGranuleMergeBoundary in-memory state.
for (auto it = splitPoints.boundaries.begin(); it != splitPoints.boundaries.end(); it++) { for (auto it = splitPoints.boundaries.begin(); it != splitPoints.boundaries.end(); it++) {
bmData->mergeBoundaries[it->first] = it->second; bmData->mergeBoundaries[it->first] = it->second;
@ -2218,6 +2257,11 @@ ACTOR Future<std::pair<UID, Version>> persistMergeGranulesStart(Reference<BlobMa
wait(tr->commit()); wait(tr->commit());
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
Version mergeVersion = tr->getCommittedVersion(); Version mergeVersion = tr->getCommittedVersion();
if (BM_DEBUG) { if (BM_DEBUG) {
fmt::print("Granule merge intent persisted [{0} - {1}): {2} @ {3}!\n", fmt::print("Granule merge intent persisted [{0} - {1}): {2} @ {3}!\n",
@ -2377,6 +2421,12 @@ ACTOR Future<bool> persistMergeGranulesDone(Reference<BlobManagerData> bmData,
tr->getCommittedVersion()); tr->getCommittedVersion());
} }
CODE_PROBE(true, "Granule merge complete"); CODE_PROBE(true, "Granule merge complete");
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
return true; return true;
} catch (Error& e) { } catch (Error& e) {
wait(tr->onError(e)); wait(tr->onError(e));
@ -3672,6 +3722,10 @@ ACTOR Future<Void> recoverBlobManager(Reference<BlobManagerData> bmData) {
} }
} }
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
throw blob_manager_replaced();
}
// Get set of workers again. Some could have died after reporting assignments // Get set of workers again. Some could have died after reporting assignments
std::unordered_set<UID> endingWorkers; std::unordered_set<UID> endingWorkers;
for (auto& it : bmData->workersById) { for (auto& it : bmData->workersById) {
@ -3760,6 +3814,10 @@ ACTOR Future<Void> recoverBlobManager(Reference<BlobManagerData> bmData) {
ASSERT(bmData->doneRecovering.canBeSet()); ASSERT(bmData->doneRecovering.canBeSet());
bmData->doneRecovering.send(Void()); bmData->doneRecovering.send(Void());
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
throw blob_manager_replaced();
}
return Void(); return Void();
} }
@ -4464,6 +4522,12 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
// instead cover whole of intersecting granules at begin/end // instead cover whole of intersecting granules at begin/end
wait(krmSetRangeCoalescing(&tr, blobGranuleForcePurgedKeys.begin, range, normalKeys, "1"_sr)); wait(krmSetRangeCoalescing(&tr, blobGranuleForcePurgedKeys.begin, range, normalKeys, "1"_sr));
wait(tr.commit()); wait(tr.commit());
if (BUGGIFY && self->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
break; break;
} catch (Error& e) { } catch (Error& e) {
wait(tr.onError(e)); wait(tr.onError(e));
@ -4681,7 +4745,7 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
if (BM_PURGE_DEBUG) { if (BM_PURGE_DEBUG) {
fmt::print("BM {0} Checking {1} parents\n", self->epoch, currHistoryNode.parentVersions.size()); fmt::print("BM {0} Checking {1} parents\n", self->epoch, currHistoryNode.parentVersions.size());
} }
Optional<UID> mergeChildID = Optional<UID> mergeChildID2 =
currHistoryNode.parentVersions.size() > 1 ? currHistoryNode.granuleID : Optional<UID>(); currHistoryNode.parentVersions.size() > 1 ? currHistoryNode.granuleID : Optional<UID>();
for (int i = 0; i < currHistoryNode.parentVersions.size(); i++) { for (int i = 0; i < currHistoryNode.parentVersions.size(); i++) {
// for (auto& parent : currHistoryNode.parentVersions.size()) { // for (auto& parent : currHistoryNode.parentVersions.size()) {
@ -4712,7 +4776,7 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
// the parent's end version is this node's startVersion, // the parent's end version is this node's startVersion,
// since this node must have started where it's parent finished // since this node must have started where it's parent finished
historyEntryQueue.push({ parentRange, parentVersion, startVersion, mergeChildID }); historyEntryQueue.push({ parentRange, parentVersion, startVersion, mergeChildID2 });
} }
} }
@ -4760,6 +4824,10 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
fmt::print("BM {0}: About to fully delete granule {1}\n", self->epoch, granuleId.toString()); fmt::print("BM {0}: About to fully delete granule {1}\n", self->epoch, granuleId.toString());
} }
wait(fullyDeleteGranule(self, granuleId, historyKey, purgeVersion, keyRange, mergeChildId, force)); wait(fullyDeleteGranule(self, granuleId, historyKey, purgeVersion, keyRange, mergeChildId, force));
if (BUGGIFY && self->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
} }
if (BM_PURGE_DEBUG) { if (BM_PURGE_DEBUG) {
@ -5072,16 +5140,12 @@ ACTOR Future<bool> hasPendingSplit(Reference<BlobManagerData> self) {
// FIXME: could eventually make this more thorough by storing some state in the DB or something // FIXME: could eventually make this more thorough by storing some state in the DB or something
// FIXME: simpler solution could be to shuffle ranges // FIXME: simpler solution could be to shuffle ranges
ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) { ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) {
state Reference<IRateControl> rateLimiter = state Reference<IRateControl> rateLimiter =
Reference<IRateControl>(new SpeedLimit(SERVER_KNOBS->BG_CONSISTENCY_CHECK_TARGET_SPEED_KB * 1024, 1)); Reference<IRateControl>(new SpeedLimit(SERVER_KNOBS->BG_CONSISTENCY_CHECK_TARGET_SPEED_KB * 1024, 1));
bmData->initBStore();
if (BM_DEBUG) { if (BM_DEBUG) {
fmt::print("BGCC starting\n"); fmt::print("BGCC starting\n");
} }
if (isFullRestoreMode())
wait(printRestoreSummary(bmData->db, bmData->bstore));
loop { loop {
if (g_network->isSimulated() && g_simulator->speedUpSimulation) { if (g_network->isSimulated() && g_simulator->speedUpSimulation) {
@ -5091,14 +5155,6 @@ ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) {
return Void(); return Void();
} }
// Only dump blob manifest when there is no pending split to ensure data consistency
if (SERVER_KNOBS->BLOB_MANIFEST_BACKUP && !isFullRestoreMode()) {
bool pendingSplit = wait(hasPendingSplit(bmData));
if (!pendingSplit) {
wait(dumpManifest(bmData->db, bmData->bstore));
}
}
if (bmData->workersById.size() >= 1) { if (bmData->workersById.size() >= 1) {
int tries = 10; int tries = 10;
state KeyRange range; state KeyRange range;
@ -5145,6 +5201,22 @@ ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) {
} }
} }
ACTOR Future<Void> backupManifest(Reference<BlobManagerData> bmData) {
if (g_network->isSimulated() && g_simulator->speedUpSimulation) {
return Void();
}
bmData->initBStore();
loop {
bool pendingSplit = wait(hasPendingSplit(bmData));
if (!pendingSplit) {
wait(dumpManifest(bmData->db, bmData->bstore, bmData->epoch, bmData->manifestDumperSeqNo));
bmData->manifestDumperSeqNo++;
}
wait(delay(SERVER_KNOBS->BLOB_MANIFEST_BACKUP_INTERVAL));
}
}
// Simulation validation that multiple blob managers aren't started with the same epoch // Simulation validation that multiple blob managers aren't started with the same epoch
static std::map<int64_t, UID> managerEpochsSeen; static std::map<int64_t, UID> managerEpochsSeen;
@ -5209,6 +5281,9 @@ ACTOR Future<Void> blobManager(BlobManagerInterface bmInterf,
if (SERVER_KNOBS->BG_ENABLE_MERGING) { if (SERVER_KNOBS->BG_ENABLE_MERGING) {
self->addActor.send(granuleMergeChecker(self)); self->addActor.send(granuleMergeChecker(self));
} }
if (SERVER_KNOBS->BLOB_MANIFEST_BACKUP && !isFullRestoreMode()) {
self->addActor.send(backupManifest(self));
}
if (BUGGIFY) { if (BUGGIFY) {
self->addActor.send(chaosRangeMover(self)); self->addActor.send(chaosRangeMover(self));

View File

@ -18,7 +18,12 @@
* limitations under the License. * limitations under the License.
*/ */
#include <algorithm>
#include <string>
#include <vector>
#include "fdbclient/BackupContainer.h" #include "fdbclient/BackupContainer.h"
#include "fdbclient/BlobGranuleCommon.h"
#include "fdbserver/Knobs.h" #include "fdbserver/Knobs.h"
#include "flow/FastRef.h" #include "flow/FastRef.h"
#include "flow/flow.h" #include "flow/flow.h"
@ -32,13 +37,13 @@
#include "fdbserver/BlobGranuleServerCommon.actor.h" #include "fdbserver/BlobGranuleServerCommon.actor.h"
#include "flow/actorcompiler.h" // has to be last include #include "flow/actorcompiler.h" // has to be last include
#include "fmt/core.h"
// //
// This module offers routines to dump or load blob manifest file, which is used for full restore from granules // This module offers routines to dump or load blob manifest file, which is used for full restore from granules
// //
static std::string MANIFEST_FILENAME = "manifest"; // Default manifest file name on external blob storage // Default manifest folder on external blob storage
#define MANIFEST_FOLDER "manifest"
#define ENABLE_DEBUG_PRINT true #define ENABLE_DEBUG_PRINT true
template <typename... T> template <typename... T>
@ -47,10 +52,53 @@ inline void dprint(fmt::format_string<T...> fmt, T&&... args) {
fmt::print(fmt, std::forward<T>(args)...); fmt::print(fmt, std::forward<T>(args)...);
} }
// Defines a manifest file. THe file name includes the epoch of blob manager and a dump sequence number.
struct BlobManifestFile {
std::string fileName;
int64_t epoch{ 0 };
int64_t seqNo{ 0 };
BlobManifestFile(const std::string& path) {
if (sscanf(path.c_str(), MANIFEST_FOLDER "/manifest.%" SCNd64 ".%" SCNd64, &epoch, &seqNo) == 2) {
fileName = path;
}
}
// Sort in descending order of {epoch, seqNo}
bool operator<(const BlobManifestFile& rhs) const {
return epoch == rhs.epoch ? seqNo > rhs.seqNo : epoch > rhs.epoch;
}
// List all blob manifest files, sorted in descending order
ACTOR static Future<std::vector<BlobManifestFile>> list(Reference<BackupContainerFileSystem> reader) {
std::function<bool(std::string const&)> filter = [=](std::string const& path) {
BlobManifestFile file(path);
return file.epoch > 0 && file.seqNo > 0;
};
BackupContainerFileSystem::FilesAndSizesT filesAndSizes = wait(reader->listFiles(MANIFEST_FOLDER, filter));
std::vector<BlobManifestFile> result;
for (auto& f : filesAndSizes) {
BlobManifestFile file(f.first);
result.push_back(file);
}
std::sort(result.begin(), result.end());
return result;
}
// Find the last manifest file
ACTOR static Future<std::string> last(Reference<BackupContainerFileSystem> reader) {
std::vector<BlobManifestFile> files = wait(list(reader));
ASSERT(!files.empty());
return files.front().fileName;
}
};
// This class dumps blob manifest to external blob storage. // This class dumps blob manifest to external blob storage.
class BlobManifestDumper : public ReferenceCounted<BlobManifestDumper> { class BlobManifestDumper : public ReferenceCounted<BlobManifestDumper> {
public: public:
BlobManifestDumper(Database& db, Reference<BlobConnectionProvider> blobConn) : db_(db), blobConn_(blobConn) {} BlobManifestDumper(Database& db, Reference<BlobConnectionProvider> blobConn, int64_t epoch, int64_t seqNo)
: db_(db), blobConn_(blobConn), epoch_(epoch), seqNo_(seqNo) {}
virtual ~BlobManifestDumper() {} virtual ~BlobManifestDumper() {}
// Execute the dumper // Execute the dumper
@ -61,6 +109,7 @@ public:
manifest.rows = rows; manifest.rows = rows;
Value data = encode(manifest); Value data = encode(manifest);
wait(writeToFile(self, data)); wait(writeToFile(self, data));
wait(cleanup(self));
} catch (Error& e) { } catch (Error& e) {
dprint("WARNING: unexpected blob manifest dumper error {}\n", e.what()); // skip error handling for now dprint("WARNING: unexpected blob manifest dumper error {}\n", e.what()); // skip error handling for now
} }
@ -100,13 +149,14 @@ private:
// Write data to blob manifest file // Write data to blob manifest file
ACTOR static Future<Void> writeToFile(Reference<BlobManifestDumper> self, Value data) { ACTOR static Future<Void> writeToFile(Reference<BlobManifestDumper> self, Value data) {
state Reference<BackupContainerFileSystem> writer; state Reference<BackupContainerFileSystem> writer;
state std::string fileName; state std::string fullPath;
std::tie(writer, fileName) = self->blobConn_->createForWrite(MANIFEST_FILENAME); std::tie(writer, fullPath) = self->blobConn_->createForWrite(MANIFEST_FOLDER);
state std::string fileName = format(MANIFEST_FOLDER "/manifest.%lld.%lld", self->epoch_, self->seqNo_);
state Reference<IBackupFile> file = wait(writer->writeFile(fileName)); state Reference<IBackupFile> file = wait(writer->writeFile(fileName));
wait(file->append(data.begin(), data.size())); wait(file->append(data.begin(), data.size()));
wait(file->finish()); wait(file->finish());
dprint("Write blob manifest file with {} bytes\n", data.size()); dprint("Write blob manifest file {} with {} bytes\n", fileName, data.size());
return Void(); return Void();
} }
@ -117,8 +167,26 @@ private:
return wr.toValue(); return wr.toValue();
} }
// Remove old manifest file
ACTOR static Future<Void> cleanup(Reference<BlobManifestDumper> self) {
state Reference<BackupContainerFileSystem> writer;
state std::string fullPath;
std::tie(writer, fullPath) = self->blobConn_->createForWrite(MANIFEST_FOLDER);
std::vector<BlobManifestFile> files = wait(BlobManifestFile::list(writer));
if (files.size() > sMaxCount_) {
for (auto iter = files.begin() + sMaxCount_; iter < files.end(); ++iter) {
writer->deleteFile(iter->fileName);
dprint("Delete manifest file {}\n", iter->fileName);
}
}
return Void();
}
Database db_; Database db_;
Reference<BlobConnectionProvider> blobConn_; Reference<BlobConnectionProvider> blobConn_;
int64_t epoch_; // blob manager epoch
int64_t seqNo_; // manifest seq number
static const int sMaxCount_{ 5 }; // max number of manifest file to keep
}; };
// Defines granule info that interests full restore // Defines granule info that interests full restore
@ -177,8 +245,9 @@ public:
private: private:
// Read data from a manifest file // Read data from a manifest file
ACTOR static Future<Value> readFromFile(Reference<BlobManifestLoader> self) { ACTOR static Future<Value> readFromFile(Reference<BlobManifestLoader> self) {
state Reference<BackupContainerFileSystem> readBstore = self->blobConn_->getForRead(MANIFEST_FILENAME); state Reference<BackupContainerFileSystem> container = self->blobConn_->getForRead(MANIFEST_FOLDER);
state Reference<IAsyncFile> reader = wait(readBstore->readFile(MANIFEST_FILENAME)); std::string fileName = wait(BlobManifestFile::last(container));
state Reference<IAsyncFile> reader = wait(container->readFile(fileName));
state int64_t fileSize = wait(reader->size()); state int64_t fileSize = wait(reader->size());
state Arena arena; state Arena arena;
state uint8_t* data = new (arena) uint8_t[fileSize]; state uint8_t* data = new (arena) uint8_t[fileSize];
@ -353,8 +422,8 @@ private:
}; };
// API to dump a manifest copy to external storage // API to dump a manifest copy to external storage
ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn) { ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn, int64_t epoch, int64_t seqNo) {
Reference<BlobManifestDumper> dumper = makeReference<BlobManifestDumper>(db, blobConn); Reference<BlobManifestDumper> dumper = makeReference<BlobManifestDumper>(db, blobConn, epoch, seqNo);
wait(BlobManifestDumper::execute(dumper)); wait(BlobManifestDumper::execute(dumper));
return Void(); return Void();
} }

View File

@ -199,6 +199,7 @@ struct BlobWorkerData : NonCopyable, ReferenceCounted<BlobWorkerData> {
Promise<Void> doGRVCheck; Promise<Void> doGRVCheck;
NotifiedVersion grvVersion; NotifiedVersion grvVersion;
Promise<Void> fatalError; Promise<Void> fatalError;
Promise<Void> simInjectFailure;
Reference<FlowLock> initialSnapshotLock; Reference<FlowLock> initialSnapshotLock;
Reference<FlowLock> resnapshotLock; Reference<FlowLock> resnapshotLock;
@ -292,6 +293,19 @@ struct BlobWorkerData : NonCopyable, ReferenceCounted<BlobWorkerData> {
return stats.estimatedMaxResidentMemory >= memoryFullThreshold; return stats.estimatedMaxResidentMemory >= memoryFullThreshold;
} }
bool maybeInjectTargetedRestart() {
// inject a BW restart at most once per test
if (g_network->isSimulated() && !g_simulator->speedUpSimulation &&
now() > g_simulator->injectTargetedBWRestartTime) {
CODE_PROBE(true, "Injecting BW targeted restart");
TraceEvent("SimBWInjectTargetedRestart", id);
g_simulator->injectTargetedBWRestartTime = std::numeric_limits<double>::max();
simInjectFailure.send(Void());
return true;
}
return false;
}
}; };
namespace { namespace {
@ -780,6 +794,11 @@ ACTOR Future<BlobFileIndex> writeDeltaFile(Reference<BlobWorkerData> bwData,
tr->getCommittedVersion()); tr->getCommittedVersion());
} }
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
if (BUGGIFY_WITH_PROB(0.01)) { if (BUGGIFY_WITH_PROB(0.01)) {
wait(delay(deterministicRandom()->random01())); wait(delay(deterministicRandom()->random01()));
} }
@ -1007,6 +1026,11 @@ ACTOR Future<BlobFileIndex> writeSnapshot(Reference<BlobWorkerData> bwData,
.detail("Compressed", compressFilter.present()); .detail("Compressed", compressFilter.present());
} }
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
// FIXME: change when we implement multiplexing // FIXME: change when we implement multiplexing
return BlobFileIndex(version, fname, 0, serializedSize, serializedSize, cipherKeysMeta); return BlobFileIndex(version, fname, 0, serializedSize, serializedSize, cipherKeysMeta);
} }
@ -1057,6 +1081,11 @@ ACTOR Future<BlobFileIndex> dumpInitialSnapshotFromFDB(Reference<BlobWorkerData>
.detail("Version", readVersion); .detail("Version", readVersion);
DEBUG_KEY_RANGE("BlobWorkerFDBSnapshot", readVersion, metadata->keyRange, bwData->id); DEBUG_KEY_RANGE("BlobWorkerFDBSnapshot", readVersion, metadata->keyRange, bwData->id);
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
// initial snapshot is committed in fdb, we can pop the change feed up to this version // initial snapshot is committed in fdb, we can pop the change feed up to this version
inFlightPops->push_back(bwData->db->popChangeFeedMutations(cfKey, readVersion + 1)); inFlightPops->push_back(bwData->db->popChangeFeedMutations(cfKey, readVersion + 1));
return snapshotWriter.get(); return snapshotWriter.get();
@ -1443,6 +1472,10 @@ ACTOR Future<Void> reevaluateInitialSplit(Reference<BlobWorkerData> bwData,
seqno); seqno);
reply.proposedSplitKey = proposedSplitKey; reply.proposedSplitKey = proposedSplitKey;
bwData->currentManagerStatusStream.get().send(reply); bwData->currentManagerStatusStream.get().send(reply);
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
// if a new manager appears, also tell it about this granule being splittable, or retry after a certain // if a new manager appears, also tell it about this granule being splittable, or retry after a certain
// amount of time of not hearing back // amount of time of not hearing back
wait(success(timeout(bwData->currentManagerStatusStream.onChange(), 10.0))); wait(success(timeout(bwData->currentManagerStatusStream.onChange(), 10.0)));
@ -4043,6 +4076,11 @@ ACTOR Future<GranuleStartState> openGranule(Reference<BlobWorkerData> bwData, As
openEv.detail("SplitParentGranuleID", info.splitParentGranule.get().second); openEv.detail("SplitParentGranuleID", info.splitParentGranule.get().second);
} }
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
wait(delay(0)); // should be cancelled
ASSERT(false);
}
return info; return info;
} catch (Error& e) { } catch (Error& e) {
if (e.code() == error_code_granule_assignment_conflict) { if (e.code() == error_code_granule_assignment_conflict) {
@ -4985,7 +5023,7 @@ ACTOR Future<Void> blobWorker(BlobWorkerInterface bwInterf,
ASSERT(false); ASSERT(false);
throw internal_error(); throw internal_error();
} }
when(wait(selfRemoved)) { when(wait(selfRemoved || self->simInjectFailure.getFuture())) {
if (BW_DEBUG) { if (BW_DEBUG) {
printf("Blob worker detected removal. Exiting...\n"); printf("Blob worker detected removal. Exiting...\n");
} }

View File

@ -44,6 +44,8 @@
#include "fdbserver/ClusterRecovery.actor.h" #include "fdbserver/ClusterRecovery.actor.h"
#include "fdbserver/DataDistributorInterface.h" #include "fdbserver/DataDistributorInterface.h"
#include "fdbserver/DBCoreState.h" #include "fdbserver/DBCoreState.h"
#include "fdbclient/Metacluster.h"
#include "fdbclient/MetaclusterManagement.actor.h"
#include "fdbserver/MoveKeys.actor.h" #include "fdbserver/MoveKeys.actor.h"
#include "fdbserver/LeaderElection.h" #include "fdbserver/LeaderElection.h"
#include "fdbserver/LogSystem.h" #include "fdbserver/LogSystem.h"
@ -53,7 +55,7 @@
#include "fdbserver/RatekeeperInterface.h" #include "fdbserver/RatekeeperInterface.h"
#include "fdbserver/BlobManagerInterface.h" #include "fdbserver/BlobManagerInterface.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbserver/LatencyBandConfig.h" #include "fdbserver/LatencyBandConfig.h"
#include "fdbclient/GlobalConfig.actor.h" #include "fdbclient/GlobalConfig.actor.h"
#include "fdbserver/RecoveryState.h" #include "fdbserver/RecoveryState.h"
@ -1498,7 +1500,9 @@ ACTOR Future<Void> statusServer(FutureStream<StatusRequest> requests,
coordinators, coordinators,
incompatibleConnections, incompatibleConnections,
self->datacenterVersionDifference, self->datacenterVersionDifference,
configBroadcaster))); configBroadcaster,
self->db.metaclusterRegistration,
self->db.metaclusterMetrics)));
if (result.isError() && result.getError().code() == error_code_actor_cancelled) if (result.isError() && result.getError().code() == error_code_actor_cancelled)
throw result.getError(); throw result.getError();
@ -2617,23 +2621,31 @@ ACTOR Future<Void> workerHealthMonitor(ClusterControllerData* self) {
self->degradationInfo = self->getDegradationInfo(); self->degradationInfo = self->getDegradationInfo();
// Compare `self->degradedServers` with `self->excludedDegradedServers` and remove those that have // Compare `self->degradationInfo` with `self->excludedDegradedServers` and remove those that have
// recovered. // recovered.
for (auto it = self->excludedDegradedServers.begin(); it != self->excludedDegradedServers.end();) { for (auto it = self->excludedDegradedServers.begin(); it != self->excludedDegradedServers.end();) {
if (self->degradationInfo.degradedServers.find(*it) == self->degradationInfo.degradedServers.end()) { if (self->degradationInfo.degradedServers.find(*it) == self->degradationInfo.degradedServers.end() &&
self->degradationInfo.disconnectedServers.find(*it) ==
self->degradationInfo.disconnectedServers.end()) {
self->excludedDegradedServers.erase(it++); self->excludedDegradedServers.erase(it++);
} else { } else {
++it; ++it;
} }
} }
if (!self->degradationInfo.degradedServers.empty() || self->degradationInfo.degradedSatellite) { if (!self->degradationInfo.degradedServers.empty() || !self->degradationInfo.disconnectedServers.empty() ||
self->degradationInfo.degradedSatellite) {
std::string degradedServerString; std::string degradedServerString;
for (const auto& server : self->degradationInfo.degradedServers) { for (const auto& server : self->degradationInfo.degradedServers) {
degradedServerString += server.toString() + " "; degradedServerString += server.toString() + " ";
} }
std::string disconnectedServerString;
for (const auto& server : self->degradationInfo.disconnectedServers) {
disconnectedServerString += server.toString() + " ";
}
TraceEvent("ClusterControllerHealthMonitor") TraceEvent("ClusterControllerHealthMonitor")
.detail("DegradedServers", degradedServerString) .detail("DegradedServers", degradedServerString)
.detail("DisconnectedServers", disconnectedServerString)
.detail("DegradedSatellite", self->degradationInfo.degradedSatellite); .detail("DegradedSatellite", self->degradationInfo.degradedSatellite);
// Check if the cluster controller should trigger a recovery to exclude any degraded servers from // Check if the cluster controller should trigger a recovery to exclude any degraded servers from
@ -2643,6 +2655,8 @@ ACTOR Future<Void> workerHealthMonitor(ClusterControllerData* self) {
if (self->recentRecoveryCountDueToHealth() < SERVER_KNOBS->CC_MAX_HEALTH_RECOVERY_COUNT) { if (self->recentRecoveryCountDueToHealth() < SERVER_KNOBS->CC_MAX_HEALTH_RECOVERY_COUNT) {
self->recentHealthTriggeredRecoveryTime.push(now()); self->recentHealthTriggeredRecoveryTime.push(now());
self->excludedDegradedServers = self->degradationInfo.degradedServers; self->excludedDegradedServers = self->degradationInfo.degradedServers;
self->excludedDegradedServers.insert(self->degradationInfo.disconnectedServers.begin(),
self->degradationInfo.disconnectedServers.end());
TraceEvent("DegradedServerDetectedAndTriggerRecovery") TraceEvent("DegradedServerDetectedAndTriggerRecovery")
.detail("RecentRecoveryCountDueToHealth", self->recentRecoveryCountDueToHealth()); .detail("RecentRecoveryCountDueToHealth", self->recentRecoveryCountDueToHealth());
self->db.forceMasterFailure.trigger(); self->db.forceMasterFailure.trigger();
@ -2682,6 +2696,56 @@ ACTOR Future<Void> workerHealthMonitor(ClusterControllerData* self) {
} }
} }
ACTOR Future<Void> metaclusterMetricsUpdater(ClusterControllerData* self) {
loop {
state Future<Void> updaterDelay =
self->db.clusterType == ClusterType::METACLUSTER_MANAGEMENT ? delay(60.0) : Never();
choose {
when(wait(self->db.serverInfo->onChange())) {}
when(wait(updaterDelay)) {
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(self->cx);
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
state std::map<ClusterName, DataClusterMetadata> clusters;
state int64_t tenantCount;
wait(store(clusters,
MetaclusterAPI::listClustersTransaction(
tr, ""_sr, "\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS)) &&
store(tenantCount,
MetaclusterAPI::ManagementClusterMetadata::tenantMetadata().tenantCount.getD(
tr, Snapshot::False, 0)));
state std::pair<ClusterUsage, ClusterUsage> capacityNumbers =
MetaclusterAPI::metaclusterCapacity(clusters);
MetaclusterMetrics metrics;
metrics.numTenants = tenantCount;
metrics.numDataClusters = clusters.size();
metrics.tenantGroupCapacity = capacityNumbers.first.numTenantGroups;
metrics.tenantGroupsAllocated = capacityNumbers.second.numTenantGroups;
self->db.metaclusterMetrics = metrics;
TraceEvent("MetaclusterCapacity")
.detail("TotalTenants", self->db.metaclusterMetrics.numTenants)
.detail("DataClusters", self->db.metaclusterMetrics.numDataClusters)
.detail("TenantGroupCapacity", self->db.metaclusterMetrics.tenantGroupCapacity)
.detail("TenantGroupsAllocated", self->db.metaclusterMetrics.tenantGroupsAllocated);
break;
} catch (Error& e) {
TraceEvent("MetaclusterUpdaterError").error(e);
// Cluster can change types during/before a metacluster transaction
// and throw an error due to timing issues.
// In such cases, go back to choose loop instead of retrying
if (e.code() == error_code_invalid_metacluster_operation) {
break;
}
wait(tr->onError(e));
}
}
}
}
}
}
ACTOR Future<Void> clusterControllerCore(ClusterControllerFullInterface interf, ACTOR Future<Void> clusterControllerCore(ClusterControllerFullInterface interf,
Future<Void> leaderFail, Future<Void> leaderFail,
ServerCoordinators coordinators, ServerCoordinators coordinators,
@ -2721,6 +2785,7 @@ ACTOR Future<Void> clusterControllerCore(ClusterControllerFullInterface interf,
self.addActor.send(monitorBlobManager(&self)); self.addActor.send(monitorBlobManager(&self));
self.addActor.send(watchBlobGranulesConfigKey(&self)); self.addActor.send(watchBlobGranulesConfigKey(&self));
self.addActor.send(monitorConsistencyScan(&self)); self.addActor.send(monitorConsistencyScan(&self));
self.addActor.send(metaclusterMetricsUpdater(&self));
self.addActor.send(dbInfoUpdater(&self)); self.addActor.send(dbInfoUpdater(&self));
self.addActor.send(traceCounters("ClusterControllerMetrics", self.addActor.send(traceCounters("ClusterControllerMetrics",
self.id, self.id,
@ -2942,6 +3007,8 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
req.address = workerAddress; req.address = workerAddress;
req.degradedPeers.push_back(badPeer1); req.degradedPeers.push_back(badPeer1);
req.degradedPeers.push_back(badPeer2); req.degradedPeers.push_back(badPeer2);
req.disconnectedPeers.push_back(badPeer1);
req.disconnectedPeers.push_back(badPeer2);
data.updateWorkerHealth(req); data.updateWorkerHealth(req);
ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end()); ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end());
auto& health = data.workerHealth[workerAddress]; auto& health = data.workerHealth[workerAddress];
@ -2950,6 +3017,11 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
ASSERT_EQ(health.degradedPeers[badPeer1].startTime, health.degradedPeers[badPeer1].lastRefreshTime); ASSERT_EQ(health.degradedPeers[badPeer1].startTime, health.degradedPeers[badPeer1].lastRefreshTime);
ASSERT(health.degradedPeers.find(badPeer2) != health.degradedPeers.end()); ASSERT(health.degradedPeers.find(badPeer2) != health.degradedPeers.end());
ASSERT_EQ(health.degradedPeers[badPeer2].startTime, health.degradedPeers[badPeer2].lastRefreshTime); ASSERT_EQ(health.degradedPeers[badPeer2].startTime, health.degradedPeers[badPeer2].lastRefreshTime);
ASSERT_EQ(health.disconnectedPeers.size(), 2);
ASSERT(health.disconnectedPeers.find(badPeer1) != health.disconnectedPeers.end());
ASSERT_EQ(health.disconnectedPeers[badPeer1].startTime, health.disconnectedPeers[badPeer1].lastRefreshTime);
ASSERT(health.disconnectedPeers.find(badPeer2) != health.disconnectedPeers.end());
ASSERT_EQ(health.disconnectedPeers[badPeer2].startTime, health.disconnectedPeers[badPeer2].lastRefreshTime);
} }
// Create a `UpdateWorkerHealthRequest` with two bad peers, one from the previous test and a new one. // Create a `UpdateWorkerHealthRequest` with two bad peers, one from the previous test and a new one.
@ -2964,6 +3036,8 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
req.address = workerAddress; req.address = workerAddress;
req.degradedPeers.push_back(badPeer1); req.degradedPeers.push_back(badPeer1);
req.degradedPeers.push_back(badPeer3); req.degradedPeers.push_back(badPeer3);
req.disconnectedPeers.push_back(badPeer1);
req.disconnectedPeers.push_back(badPeer3);
data.updateWorkerHealth(req); data.updateWorkerHealth(req);
ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end()); ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end());
auto& health = data.workerHealth[workerAddress]; auto& health = data.workerHealth[workerAddress];
@ -2975,6 +3049,15 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
ASSERT_EQ(health.degradedPeers[badPeer2].startTime, health.degradedPeers[badPeer1].startTime); ASSERT_EQ(health.degradedPeers[badPeer2].startTime, health.degradedPeers[badPeer1].startTime);
ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end()); ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end());
ASSERT_EQ(health.degradedPeers[badPeer3].startTime, health.degradedPeers[badPeer3].lastRefreshTime); ASSERT_EQ(health.degradedPeers[badPeer3].startTime, health.degradedPeers[badPeer3].lastRefreshTime);
ASSERT_EQ(health.disconnectedPeers.size(), 3);
ASSERT(health.disconnectedPeers.find(badPeer1) != health.disconnectedPeers.end());
ASSERT_LT(health.disconnectedPeers[badPeer1].startTime, health.disconnectedPeers[badPeer1].lastRefreshTime);
ASSERT(health.disconnectedPeers.find(badPeer2) != health.disconnectedPeers.end());
ASSERT_EQ(health.disconnectedPeers[badPeer2].startTime, health.disconnectedPeers[badPeer2].lastRefreshTime);
ASSERT_EQ(health.disconnectedPeers[badPeer2].startTime, health.disconnectedPeers[badPeer1].startTime);
ASSERT(health.disconnectedPeers.find(badPeer3) != health.disconnectedPeers.end());
ASSERT_EQ(health.disconnectedPeers[badPeer3].startTime, health.disconnectedPeers[badPeer3].lastRefreshTime);
previousStartTime = health.degradedPeers[badPeer3].startTime; previousStartTime = health.degradedPeers[badPeer3].startTime;
previousRefreshTime = health.degradedPeers[badPeer3].lastRefreshTime; previousRefreshTime = health.degradedPeers[badPeer3].lastRefreshTime;
} }
@ -2992,20 +3075,10 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end()); ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end());
ASSERT_EQ(health.degradedPeers[badPeer3].startTime, previousStartTime); ASSERT_EQ(health.degradedPeers[badPeer3].startTime, previousStartTime);
ASSERT_EQ(health.degradedPeers[badPeer3].lastRefreshTime, previousRefreshTime); ASSERT_EQ(health.degradedPeers[badPeer3].lastRefreshTime, previousRefreshTime);
} ASSERT_EQ(health.disconnectedPeers.size(), 3);
ASSERT(health.disconnectedPeers.find(badPeer3) != health.disconnectedPeers.end());
// Create a `UpdateWorkerHealthRequest` with disconnected peers, which should update the bad peer's lastRefreshTime. ASSERT_EQ(health.disconnectedPeers[badPeer3].startTime, previousStartTime);
{ ASSERT_EQ(health.disconnectedPeers[badPeer3].lastRefreshTime, previousRefreshTime);
wait(delay(0.001));
UpdateWorkerHealthRequest req;
req.address = workerAddress;
req.disconnectedPeers.push_back(badPeer3);
data.updateWorkerHealth(req);
ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end());
auto& health = data.workerHealth[workerAddress];
ASSERT_EQ(health.degradedPeers.size(), 3);
ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end());
ASSERT_LT(health.degradedPeers[badPeer3].startTime, health.degradedPeers[badPeer3].lastRefreshTime);
} }
return Void(); return Void();
@ -3021,11 +3094,14 @@ TEST_CASE("/fdbserver/clustercontroller/updateRecoveredWorkers") {
NetworkAddress worker2(IPAddress(0x11111111), 1); NetworkAddress worker2(IPAddress(0x11111111), 1);
NetworkAddress badPeer1(IPAddress(0x02020202), 1); NetworkAddress badPeer1(IPAddress(0x02020202), 1);
NetworkAddress badPeer2(IPAddress(0x03030303), 1); NetworkAddress badPeer2(IPAddress(0x03030303), 1);
NetworkAddress disconnectedPeer3(IPAddress(0x04040404), 1);
// Create following test scenario: // Create following test scenario:
// worker1 -> badPeer1 active // worker1 -> badPeer1 active
// worker1 -> badPeer2 recovered // worker1 -> badPeer2 recovered
// worker1 -> disconnectedPeer3 active
// worker2 -> badPeer2 recovered // worker2 -> badPeer2 recovered
// worker2 -> disconnectedPeer3 recovered
data.workerHealth[worker1].degradedPeers[badPeer1] = { data.workerHealth[worker1].degradedPeers[badPeer1] = {
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now() now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now()
}; };
@ -3033,16 +3109,25 @@ TEST_CASE("/fdbserver/clustercontroller/updateRecoveredWorkers") {
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1,
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1 now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1
}; };
data.workerHealth[worker1].degradedPeers[disconnectedPeer3] = {
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now()
};
data.workerHealth[worker2].degradedPeers[badPeer2] = { data.workerHealth[worker2].degradedPeers[badPeer2] = {
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1,
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1 now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1
}; };
data.workerHealth[worker2].degradedPeers[disconnectedPeer3] = {
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1,
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1
};
data.updateRecoveredWorkers(); data.updateRecoveredWorkers();
ASSERT_EQ(data.workerHealth.size(), 1); ASSERT_EQ(data.workerHealth.size(), 1);
ASSERT(data.workerHealth.find(worker1) != data.workerHealth.end()); ASSERT(data.workerHealth.find(worker1) != data.workerHealth.end());
ASSERT(data.workerHealth[worker1].degradedPeers.find(badPeer1) != data.workerHealth[worker1].degradedPeers.end()); ASSERT(data.workerHealth[worker1].degradedPeers.find(badPeer1) != data.workerHealth[worker1].degradedPeers.end());
ASSERT(data.workerHealth[worker1].degradedPeers.find(badPeer2) == data.workerHealth[worker1].degradedPeers.end()); ASSERT(data.workerHealth[worker1].degradedPeers.find(badPeer2) == data.workerHealth[worker1].degradedPeers.end());
ASSERT(data.workerHealth[worker1].degradedPeers.find(disconnectedPeer3) !=
data.workerHealth[worker1].degradedPeers.end());
ASSERT(data.workerHealth.find(worker2) == data.workerHealth.end()); ASSERT(data.workerHealth.find(worker2) == data.workerHealth.end());
return Void(); return Void();
@ -3064,6 +3149,7 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
// cluster controller. // cluster controller.
{ {
data.workerHealth[worker].degradedPeers[badPeer1] = { now(), now() }; data.workerHealth[worker].degradedPeers[badPeer1] = { now(), now() };
data.workerHealth[worker].disconnectedPeers[badPeer2] = { now(), now() };
ASSERT(data.getDegradationInfo().degradedServers.empty()); ASSERT(data.getDegradationInfo().degradedServers.empty());
data.workerHealth.clear(); data.workerHealth.clear();
} }
@ -3076,6 +3162,19 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
auto degradationInfo = data.getDegradationInfo(); auto degradationInfo = data.getDegradationInfo();
ASSERT(degradationInfo.degradedServers.size() == 1); ASSERT(degradationInfo.degradedServers.size() == 1);
ASSERT(degradationInfo.degradedServers.find(badPeer1) != degradationInfo.degradedServers.end()); ASSERT(degradationInfo.degradedServers.find(badPeer1) != degradationInfo.degradedServers.end());
ASSERT(degradationInfo.disconnectedServers.empty());
data.workerHealth.clear();
}
// Test that when there is only one reported disconnected link, getDegradationInfo can return correct
// degraded server.
{
data.workerHealth[worker].disconnectedPeers[badPeer1] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
auto degradationInfo = data.getDegradationInfo();
ASSERT(degradationInfo.disconnectedServers.size() == 1);
ASSERT(degradationInfo.disconnectedServers.find(badPeer1) != degradationInfo.disconnectedServers.end());
ASSERT(degradationInfo.degradedServers.empty());
data.workerHealth.clear(); data.workerHealth.clear();
} }
@ -3085,16 +3184,25 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
now() }; now() };
data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1, data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() }; now() };
data.workerHealth[worker].disconnectedPeers[badPeer2] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer2].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
auto degradationInfo = data.getDegradationInfo(); auto degradationInfo = data.getDegradationInfo();
ASSERT(degradationInfo.degradedServers.size() == 1); ASSERT(degradationInfo.degradedServers.size() == 1);
ASSERT(degradationInfo.degradedServers.find(worker) != degradationInfo.degradedServers.end() || ASSERT(degradationInfo.degradedServers.find(worker) != degradationInfo.degradedServers.end() ||
degradationInfo.degradedServers.find(badPeer1) != degradationInfo.degradedServers.end()); degradationInfo.degradedServers.find(badPeer1) != degradationInfo.degradedServers.end());
ASSERT(degradationInfo.disconnectedServers.size() == 1);
ASSERT(degradationInfo.disconnectedServers.find(worker) != degradationInfo.disconnectedServers.end() ||
degradationInfo.disconnectedServers.find(badPeer2) != degradationInfo.disconnectedServers.end());
data.workerHealth.clear(); data.workerHealth.clear();
} }
// Test that if B complains A and C complains A, A is selected as degraded server instead of B or C. // Test that if B complains A and C complains A, A is selected as degraded server instead of B or C.
{ {
ASSERT(SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE < 4); ASSERT(SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE < 4);
// test for both degraded peers and disconnected peers.
data.workerHealth[worker].degradedPeers[badPeer1] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1, data.workerHealth[worker].degradedPeers[badPeer1] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() }; now() };
data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1, data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
@ -3103,9 +3211,19 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
now() }; now() };
data.workerHealth[badPeer2].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1, data.workerHealth[badPeer2].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() }; now() };
data.workerHealth[worker].disconnectedPeers[badPeer3] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer3].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[worker].disconnectedPeers[badPeer4] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer4].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
auto degradationInfo = data.getDegradationInfo(); auto degradationInfo = data.getDegradationInfo();
ASSERT(degradationInfo.degradedServers.size() == 1); ASSERT(degradationInfo.degradedServers.size() == 1);
ASSERT(degradationInfo.degradedServers.find(worker) != degradationInfo.degradedServers.end()); ASSERT(degradationInfo.degradedServers.find(worker) != degradationInfo.degradedServers.end());
ASSERT(degradationInfo.disconnectedServers.size() == 1);
ASSERT(degradationInfo.disconnectedServers.find(worker) != degradationInfo.disconnectedServers.end());
data.workerHealth.clear(); data.workerHealth.clear();
} }
@ -3124,6 +3242,23 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
data.workerHealth.clear(); data.workerHealth.clear();
} }
// Test that CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE doesn't affect disconnectedServers calculation.
{
ASSERT(SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE < 4);
data.workerHealth[badPeer1].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer2].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer3].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
data.workerHealth[badPeer4].disconnectedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
now() };
ASSERT(data.getDegradationInfo().disconnectedServers.size() == 1);
ASSERT(data.getDegradationInfo().disconnectedServers.find(worker) !=
data.getDegradationInfo().disconnectedServers.end());
data.workerHealth.clear();
}
// Test that if the degradation is reported both ways between A and other 4 servers, no degraded server is // Test that if the degradation is reported both ways between A and other 4 servers, no degraded server is
// returned. // returned.
{ {
@ -3245,40 +3380,65 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerRecoveryDueToDegradedServer
data.degradationInfo.degradedServers.insert(master); data.degradationInfo.degradedServers.insert(master);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(master);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// Trigger recovery when primary TLog is degraded. // Trigger recovery when primary TLog is degraded.
data.degradationInfo.degradedServers.insert(tlog); data.degradationInfo.degradedServers.insert(tlog);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(tlog);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// No recovery when satellite Tlog is degraded. // No recovery when satellite Tlog is degraded.
data.degradationInfo.degradedServers.insert(satelliteTlog); data.degradationInfo.degradedServers.insert(satelliteTlog);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(satelliteTlog);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// No recovery when remote tlog is degraded. // No recovery when remote tlog is degraded.
data.degradationInfo.degradedServers.insert(remoteTlog); data.degradationInfo.degradedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// No recovery when log router is degraded. // No recovery when log router is degraded.
data.degradationInfo.degradedServers.insert(logRouter); data.degradationInfo.degradedServers.insert(logRouter);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(logRouter);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// No recovery when backup worker is degraded. // No recovery when backup worker is degraded.
data.degradationInfo.degradedServers.insert(backup); data.degradationInfo.degradedServers.insert(backup);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(backup);
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// Trigger recovery when proxy is degraded. // Trigger recovery when proxy is degraded.
data.degradationInfo.degradedServers.insert(proxy); data.degradationInfo.degradedServers.insert(proxy);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(proxy);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// Trigger recovery when resolver is degraded. // Trigger recovery when resolver is degraded.
data.degradationInfo.degradedServers.insert(resolver); data.degradationInfo.degradedServers.insert(resolver);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers()); ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(resolver);
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
return Void(); return Void();
} }
@ -3359,6 +3519,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
data.degradationInfo.degradedServers.insert(master); data.degradationInfo.degradedServers.insert(master);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers()); ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(master);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// Trigger failover when enough servers in the txn system are degraded. // Trigger failover when enough servers in the txn system are degraded.
data.degradationInfo.degradedServers.insert(master); data.degradationInfo.degradedServers.insert(master);
@ -3367,6 +3530,13 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
data.degradationInfo.degradedServers.insert(proxy2); data.degradationInfo.degradedServers.insert(proxy2);
data.degradationInfo.degradedServers.insert(resolver); data.degradationInfo.degradedServers.insert(resolver);
ASSERT(data.shouldTriggerFailoverDueToDegradedServers()); ASSERT(data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(master);
data.degradationInfo.disconnectedServers.insert(tlog);
data.degradationInfo.disconnectedServers.insert(proxy);
data.degradationInfo.disconnectedServers.insert(proxy2);
data.degradationInfo.disconnectedServers.insert(resolver);
ASSERT(data.shouldTriggerFailoverDueToDegradedServers());
// No failover when usable region is 1. // No failover when usable region is 1.
data.db.config.usableRegions = 1; data.db.config.usableRegions = 1;
@ -3377,6 +3547,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
data.degradationInfo.degradedServers.insert(remoteTlog); data.degradationInfo.degradedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers()); ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
// No failover when some are not from transaction system // No failover when some are not from transaction system
data.degradationInfo.degradedServers.insert(NetworkAddress(IPAddress(0x13131313), 1)); data.degradationInfo.degradedServers.insert(NetworkAddress(IPAddress(0x13131313), 1));
@ -3397,6 +3570,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
data.degradationInfo.degradedServers.insert(remoteTlog); data.degradationInfo.degradedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers()); ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.degradedServers.clear(); data.degradationInfo.degradedServers.clear();
data.degradationInfo.disconnectedServers.insert(remoteTlog);
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
data.degradationInfo.disconnectedServers.clear();
return Void(); return Void();
} }

View File

@ -1164,13 +1164,33 @@ ACTOR Future<Void> readTransactionSystemState(Reference<ClusterRecoveryData> sel
wait(self->txnStateStore->readValue(MetaclusterMetadata::metaclusterRegistration().key)); wait(self->txnStateStore->readValue(MetaclusterMetadata::metaclusterRegistration().key));
Optional<MetaclusterRegistrationEntry> metaclusterRegistration = Optional<MetaclusterRegistrationEntry> metaclusterRegistration =
MetaclusterRegistrationEntry::decode(metaclusterRegistrationVal); MetaclusterRegistrationEntry::decode(metaclusterRegistrationVal);
Optional<ClusterName> metaclusterName;
Optional<UID> metaclusterId;
Optional<ClusterName> clusterName;
Optional<UID> clusterId;
self->controllerData->db.metaclusterRegistration = metaclusterRegistration;
if (metaclusterRegistration.present()) { if (metaclusterRegistration.present()) {
self->controllerData->db.metaclusterName = metaclusterRegistration.get().metaclusterName; self->controllerData->db.metaclusterName = metaclusterRegistration.get().metaclusterName;
self->controllerData->db.clusterType = metaclusterRegistration.get().clusterType; self->controllerData->db.clusterType = metaclusterRegistration.get().clusterType;
metaclusterName = metaclusterRegistration.get().metaclusterName;
metaclusterId = metaclusterRegistration.get().metaclusterId;
if (metaclusterRegistration.get().clusterType == ClusterType::METACLUSTER_DATA) {
clusterName = metaclusterRegistration.get().name;
clusterId = metaclusterRegistration.get().id;
}
} else { } else {
self->controllerData->db.metaclusterName = Optional<ClusterName>();
self->controllerData->db.clusterType = ClusterType::STANDALONE; self->controllerData->db.clusterType = ClusterType::STANDALONE;
} }
TraceEvent("MetaclusterMetadata")
.detail("ClusterType", clusterTypeToString(self->controllerData->db.clusterType))
.detail("MetaclusterName", metaclusterName)
.detail("MetaclusterId", metaclusterId)
.detail("DataClusterName", clusterName)
.detail("DataClusterId", clusterId)
.trackLatest(self->metaclusterEventHolder->trackingKey);
uniquify(self->allTags); uniquify(self->allTags);
// auto kvs = self->txnStateStore->readRange( systemKeys ); // auto kvs = self->txnStateStore->readRange( systemKeys );

View File

@ -2505,9 +2505,9 @@ ACTOR Future<Void> processCompleteTransactionStateRequest(TransactionStateResolv
std::vector<UID> src, dest; std::vector<UID> src, dest;
ServerCacheInfo info; ServerCacheInfo info;
// NOTE: An ACTOR will be compiled into several classes, the this pointer is from one of them. // NOTE: An ACTOR will be compiled into several classes, the this pointer is from one of them.
auto updateTagInfo = [this](const std::vector<UID>& uids, auto updateTagInfo = [pContext = pContext](const std::vector<UID>& uids,
std::vector<Tag>& tags, std::vector<Tag>& tags,
std::vector<Reference<StorageInfo>>& storageInfoItems) { std::vector<Reference<StorageInfo>>& storageInfoItems) {
for (const auto& id : uids) { for (const auto& id : uids) {
auto storageInfo = getStorageInfo(id, &pContext->pCommitData->storageCache, pContext->pTxnStateStore); auto storageInfo = getStorageInfo(id, &pContext->pCommitData->storageCache, pContext->pTxnStateStore);
ASSERT(storageInfo->tag != invalidTag); ASSERT(storageInfo->tag != invalidTag);

View File

@ -189,6 +189,9 @@ ACTOR Future<bool> getKeyLocations(Database cx,
state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures; state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures;
for (const auto& kv : shards[i].second) { for (const auto& kv : shards[i].second) {
resetReply(req); resetReply(req);
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
cx->getLatestCommitVersion(kv, req.version, req.ssLatestCommitVersions);
}
keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
} }
@ -554,6 +557,10 @@ ACTOR Future<bool> checkDataConsistency(Database cx,
TraceEvent("ConsistencyCheck_StoringGetFutures").detail("SSISize", storageServerInterfaces.size()); TraceEvent("ConsistencyCheck_StoringGetFutures").detail("SSISize", storageServerInterfaces.size());
for (j = 0; j < storageServerInterfaces.size(); j++) { for (j = 0; j < storageServerInterfaces.size(); j++) {
resetReply(req); resetReply(req);
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
cx->getLatestCommitVersion(
storageServerInterfaces[j], req.version, req.ssLatestCommitVersions);
}
keyValueFutures.push_back( keyValueFutures.push_back(
storageServerInterfaces[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); storageServerInterfaces[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
} }
@ -1129,4 +1136,4 @@ ACTOR Future<Void> consistencyScan(ConsistencyScanInterface csInterf, Reference<
wait(self.consistencyScanEnabled.onChange()); wait(self.consistencyScanEnabled.onChange());
} }
} }
} }

View File

@ -27,7 +27,7 @@
#include "fdbserver/Knobs.h" #include "fdbserver/Knobs.h"
#include "fdbserver/OnDemandStore.h" #include "fdbserver/OnDemandStore.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "flow/ActorCollection.h" #include "flow/ActorCollection.h"
#include "flow/ProtocolVersion.h" #include "flow/ProtocolVersion.h"
#include "flow/UnitTest.h" #include "flow/UnitTest.h"
@ -489,16 +489,16 @@ ACTOR Future<Void> leaderRegister(LeaderElectionRegInterface interf, Key key) {
// Generation register values are stored without prefixing in the coordinated state, but always begin with an // Generation register values are stored without prefixing in the coordinated state, but always begin with an
// alphanumeric character (they are always derived from a ClusterConnectionString key). Forwarding values are stored in // alphanumeric character (they are always derived from a ClusterConnectionString key). Forwarding values are stored in
// this range: // this range:
const KeyRangeRef fwdKeys(LiteralStringRef("\xff" const KeyRangeRef fwdKeys("\xff"
"fwd"), "fwd"_sr,
LiteralStringRef("\xff" "\xff"
"fwe")); "fwe"_sr);
// The time when forwarding was last set is stored in this range: // The time when forwarding was last set is stored in this range:
const KeyRangeRef fwdTimeKeys(LiteralStringRef("\xff" const KeyRangeRef fwdTimeKeys("\xff"
"fwdTime"), "fwdTime"_sr,
LiteralStringRef("\xff" "\xff"
"fwdTimf")); "fwdTimf"_sr);
struct LeaderRegisterCollection { struct LeaderRegisterCollection {
// SOMEDAY: Factor this into a generic tool? Extend ActorCollection to support removal actions? What? // SOMEDAY: Factor this into a generic tool? Extend ActorCollection to support removal actions? What?
ActorCollection actors; ActorCollection actors;

View File

@ -2684,33 +2684,21 @@ public:
.detail("Primary", self->primary) .detail("Primary", self->primary)
.detail("DcId", dcId) .detail("DcId", dcId)
.detail("Replicas", self->configuration.storageTeamSize); .detail("Replicas", self->configuration.storageTeamSize);
state Transaction tr(self->cx);
loop { int oldReplicas = wait(self->db->tryUpdateReplicasKeyForDc(dcId, self->configuration.storageTeamSize));
try { if (oldReplicas == self->configuration.storageTeamSize) {
Optional<Value> val = wait(tr.get(datacenterReplicasKeyFor(dcId))); TraceEvent("DDUpdatedAlready", self->distributorId)
state int oldReplicas = val.present() ? decodeDatacenterReplicasValue(val.get()) : 0; .detail("Primary", self->primary)
if (oldReplicas == self->configuration.storageTeamSize) { .detail("DcId", dcId)
TraceEvent("DDUpdatedAlready", self->distributorId) .detail("Replicas", self->configuration.storageTeamSize);
.detail("Primary", self->primary) } else {
.detail("DcId", dcId) TraceEvent("DDUpdatedReplicas", self->distributorId)
.detail("Replicas", self->configuration.storageTeamSize); .detail("Primary", self->primary)
return Void(); .detail("DcId", dcId)
} .detail("Replicas", self->configuration.storageTeamSize)
if (oldReplicas < self->configuration.storageTeamSize) { .detail("OldReplicas", oldReplicas);
tr.set(rebootWhenDurableKey, StringRef());
}
tr.set(datacenterReplicasKeyFor(dcId), datacenterReplicasValue(self->configuration.storageTeamSize));
wait(tr.commit());
TraceEvent("DDUpdatedReplicas", self->distributorId)
.detail("Primary", self->primary)
.detail("DcId", dcId)
.detail("Replicas", self->configuration.storageTeamSize)
.detail("OldReplicas", oldReplicas);
return Void();
} catch (Error& e) {
wait(tr.onError(e));
}
} }
return Void();
} }
ACTOR static Future<Void> serverGetTeamRequests(DDTeamCollection* self, TeamCollectionInterface tci) { ACTOR static Future<Void> serverGetTeamRequests(DDTeamCollection* self, TeamCollectionInterface tci) {
@ -2737,86 +2725,60 @@ public:
} }
} }
ACTOR static Future<UID> getClusterId(DDTeamCollection* self) {
state ReadYourWritesTransaction tr(self->cx);
loop {
try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
Optional<Value> clusterId = wait(tr.get(clusterIdKey));
ASSERT(clusterId.present());
return BinaryReader::fromStringRef<UID>(clusterId.get(), Unversioned());
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
ACTOR static Future<Void> waitServerListChange(DDTeamCollection* self, ACTOR static Future<Void> waitServerListChange(DDTeamCollection* self,
FutureStream<Void> serverRemoved, FutureStream<Void> serverRemoved,
const DDEnabledState* ddEnabledState) { const DDEnabledState* ddEnabledState) {
state Future<Void> checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch); state Future<Void> checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch);
state Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> serverListAndProcessClasses = state Future<ServerWorkerInfos> serverListAndProcessClasses = Never();
Never();
state bool isFetchingResults = false; state bool isFetchingResults = false;
state Transaction tr(self->cx);
loop { loop {
try { choose {
choose { when(wait(checkSignal)) {
when(wait(checkSignal)) { checkSignal = Never();
checkSignal = Never(); isFetchingResults = true;
isFetchingResults = true; serverListAndProcessClasses = self->db->getServerListAndProcessClasses();
serverListAndProcessClasses = NativeAPI::getServerListAndProcessClasses(&tr); }
} when(ServerWorkerInfos infos = wait(serverListAndProcessClasses)) {
when(std::vector<std::pair<StorageServerInterface, ProcessClass>> results = auto& servers = infos.servers;
wait(serverListAndProcessClasses)) { serverListAndProcessClasses = Never();
serverListAndProcessClasses = Never(); isFetchingResults = false;
isFetchingResults = false;
for (int i = 0; i < results.size(); i++) { for (int i = 0; i < servers.size(); i++) {
UID serverId = results[i].first.id(); UID serverId = servers[i].first.id();
StorageServerInterface const& ssi = results[i].first; StorageServerInterface const& ssi = servers[i].first;
ProcessClass const& processClass = results[i].second; ProcessClass const& processClass = servers[i].second;
if (!self->shouldHandleServer(ssi)) { if (!self->shouldHandleServer(ssi)) {
continue; continue;
} else if (self->server_and_tss_info.count(serverId)) { } else if (self->server_and_tss_info.count(serverId)) {
auto& serverInfo = self->server_and_tss_info[serverId]; auto& serverInfo = self->server_and_tss_info[serverId];
if (ssi.getValue.getEndpoint() != if (ssi.getValue.getEndpoint() !=
serverInfo->getLastKnownInterface().getValue.getEndpoint() || serverInfo->getLastKnownInterface().getValue.getEndpoint() ||
processClass != serverInfo->getLastKnownClass().classType()) { processClass != serverInfo->getLastKnownClass().classType()) {
Promise<std::pair<StorageServerInterface, ProcessClass>> currentInterfaceChanged = Promise<std::pair<StorageServerInterface, ProcessClass>> currentInterfaceChanged =
serverInfo->interfaceChanged; serverInfo->interfaceChanged;
serverInfo->interfaceChanged = serverInfo->interfaceChanged =
Promise<std::pair<StorageServerInterface, ProcessClass>>(); Promise<std::pair<StorageServerInterface, ProcessClass>>();
serverInfo->onInterfaceChanged = serverInfo->onInterfaceChanged =
Future<std::pair<StorageServerInterface, ProcessClass>>( Future<std::pair<StorageServerInterface, ProcessClass>>(
serverInfo->interfaceChanged.getFuture()); serverInfo->interfaceChanged.getFuture());
currentInterfaceChanged.send(std::make_pair(ssi, processClass)); currentInterfaceChanged.send(std::make_pair(ssi, processClass));
}
} else if (!self->recruitingIds.count(ssi.id())) {
self->addServer(ssi,
processClass,
self->serverTrackerErrorOut,
tr.getReadVersion().get(),
*ddEnabledState);
} }
} else if (!self->recruitingIds.count(ssi.id())) {
self->addServer(ssi,
processClass,
self->serverTrackerErrorOut,
infos.readVersion.get(),
*ddEnabledState);
} }
tr = Transaction(self->cx);
checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch);
} }
when(waitNext(serverRemoved)) {
if (isFetchingResults) { checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch);
tr = Transaction(self->cx); }
serverListAndProcessClasses = NativeAPI::getServerListAndProcessClasses(&tr); when(waitNext(serverRemoved)) {
} if (isFetchingResults) {
serverListAndProcessClasses = self->db->getServerListAndProcessClasses();
} }
} }
} catch (Error& e) {
wait(tr.onError(e));
serverListAndProcessClasses = Never();
isFetchingResults = false;
checkSignal = Void();
} }
} }
} }
@ -2928,7 +2890,7 @@ public:
try { try {
wait(self->init(initData, *ddEnabledState)); wait(self->init(initData, *ddEnabledState));
initData = Reference<InitialDataDistribution>(); initData.clear(); // release reference count
self->addActor.send(self->serverGetTeamRequests(tci)); self->addActor.send(self->serverGetTeamRequests(tci));
TraceEvent("DDTeamCollectionBegin", self->distributorId).detail("Primary", self->primary); TraceEvent("DDTeamCollectionBegin", self->distributorId).detail("Primary", self->primary);
@ -2962,10 +2924,13 @@ public:
self->addActor.send(self->storageRecruiter(recruitStorage, *ddEnabledState)); self->addActor.send(self->storageRecruiter(recruitStorage, *ddEnabledState));
self->addActor.send(self->monitorStorageServerRecruitment()); self->addActor.send(self->monitorStorageServerRecruitment());
self->addActor.send(self->waitServerListChange(serverRemoved.getFuture(), *ddEnabledState)); self->addActor.send(self->waitServerListChange(serverRemoved.getFuture(), *ddEnabledState));
self->addActor.send(self->trackExcludedServers());
self->addActor.send(self->monitorHealthyTeams()); self->addActor.send(self->monitorHealthyTeams());
self->addActor.send(self->waitHealthyZoneChange());
self->addActor.send(self->monitorPerpetualStorageWiggle()); if (!self->db->isMocked()) {
self->addActor.send(self->trackExcludedServers());
self->addActor.send(self->waitHealthyZoneChange());
self->addActor.send(self->monitorPerpetualStorageWiggle());
}
// SOMEDAY: Monitor FF/serverList for (new) servers that aren't in allServers and add or remove them // SOMEDAY: Monitor FF/serverList for (new) servers that aren't in allServers and add or remove them
loop choose { loop choose {
@ -3039,215 +3004,209 @@ public:
state int traceEventsPrinted = 0; state int traceEventsPrinted = 0;
state std::vector<const UID*> serverIDs; state std::vector<const UID*> serverIDs;
state double lastPrintTime = 0; state double lastPrintTime = 0;
state ReadYourWritesTransaction tr(self->cx);
loop { loop {
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; wait(self->printDetailedTeamsInfo.onTrigger() || self->db->waitDDTeamInfoPrintSignal());
double snapshotStart = now(); if (now() - lastPrintTime < SERVER_KNOBS->DD_TEAMS_INFO_PRINT_INTERVAL) {
continue;
}
lastPrintTime = now();
configuration = self->configuration; traceEventsPrinted = 0;
server_info = self->server_info;
teams = self->teams;
// Perform deep copy so we have a consistent snapshot, even if yields are performed
for (const auto& [machineId, info] : self->machine_info) {
machine_info.emplace(machineId, info->clone());
}
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(); double snapshotStart = now();
for (auto const& key : keys) {
// Add to or update the local server_status map
server_status[key] = self->server_status.get(key);
}
TraceEvent("DDPrintSnapshotTeamsInfo", self->getDistributorId()) configuration = self->configuration;
.detail("SnapshotSpeed", now() - snapshotStart) server_info = self->server_info;
.detail("Primary", self->isPrimary()); teams = self->teams;
// Perform deep copy so we have a consistent snapshot, even if yields are performed
for (const auto& [machineId, info] : self->machine_info) {
machine_info.emplace(machineId, info->clone());
}
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;
// Print to TraceEvents auto const& keys = self->server_status.getKeys();
TraceEvent("DDConfig", self->getDistributorId()) for (auto const& key : keys) {
.detail("StorageTeamSize", configuration.storageTeamSize) // Add to or update the local server_status map
.detail("DesiredTeamsPerServer", SERVER_KNOBS->DESIRED_TEAMS_PER_SERVER) server_status[key] = self->server_status.get(key);
.detail("MaxTeamsPerServer", SERVER_KNOBS->MAX_TEAMS_PER_SERVER) }
.detail("Primary", self->isPrimary());
TraceEvent("DDPrintSnapshotTeamsInfo", self->getDistributorId())
.detail("SnapshotSpeed", now() - snapshotStart)
.detail("Primary", self->isPrimary());
// Print to TraceEvents
TraceEvent("DDConfig", self->getDistributorId())
.detail("StorageTeamSize", configuration.storageTeamSize)
.detail("DesiredTeamsPerServer", SERVER_KNOBS->DESIRED_TEAMS_PER_SERVER)
.detail("MaxTeamsPerServer", SERVER_KNOBS->MAX_TEAMS_PER_SERVER)
.detail("Primary", self->isPrimary());
TraceEvent("ServerInfo", self->getDistributorId())
.detail("Size", server_info.size())
.detail("Primary", self->isPrimary());
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->getDistributorId()) TraceEvent("ServerInfo", self->getDistributorId())
.detail("Size", server_info.size()) .detail("ServerInfoIndex", i)
.detail("ServerID", server->first.toString())
.detail("ServerTeamOwned", server->second->getTeams().size())
.detail("MachineID", server->second->machine->machineID.contents().toString())
.detail("Primary", self->isPrimary()); .detail("Primary", self->isPrimary());
state int i; server++;
state std::map<UID, Reference<TCServerInfo>>::iterator server = server_info.begin(); if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
for (i = 0; i < server_info.size(); i++) { wait(yield());
TraceEvent("ServerInfo", self->getDistributorId()) }
.detail("ServerInfoIndex", i) }
.detail("ServerID", server->first.toString())
.detail("ServerTeamOwned", server->second->getTeams().size()) server = server_info.begin();
.detail("MachineID", server->second->machine->machineID.contents().toString()) for (i = 0; i < server_info.size(); i++) {
.detail("Primary", self->isPrimary()); const UID& uid = server->first;
server++;
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) { TraceEvent e("ServerStatus", self->getDistributorId());
wait(yield()); e.detail("ServerUID", uid)
} .detail("MachineIsValid", server_info[uid]->machine.isValid())
.detail("MachineTeamSize",
server_info[uid]->machine.isValid() ? server_info[uid]->machine->machineTeams.size() : -1)
.detail("Primary", self->isPrimary());
// ServerStatus might not be known if server was very recently added and
// storageServerFailureTracker() has not yet updated self->server_status If the UID is not found, do
// not assume the server is healthy or unhealthy
auto it = server_status.find(uid);
if (it != server_status.end()) {
e.detail("Healthy", !it->second.isUnhealthy());
} }
server = server_info.begin(); server++;
for (i = 0; i < server_info.size(); i++) { if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
const UID& uid = server->first; wait(yield());
TraceEvent e("ServerStatus", self->getDistributorId());
e.detail("ServerUID", uid)
.detail("MachineIsValid", server_info[uid]->machine.isValid())
.detail("MachineTeamSize",
server_info[uid]->machine.isValid() ? server_info[uid]->machine->machineTeams.size()
: -1)
.detail("Primary", self->isPrimary());
// ServerStatus might not be known if server was very recently added and
// storageServerFailureTracker() has not yet updated self->server_status If the UID is not found, do
// not assume the server is healthy or unhealthy
auto it = server_status.find(uid);
if (it != server_status.end()) {
e.detail("Healthy", !it->second.isUnhealthy());
}
server++;
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
wait(yield());
}
} }
}
TraceEvent("ServerTeamInfo", self->getDistributorId())
.detail("Size", teams.size())
.detail("Primary", self->isPrimary());
for (i = 0; i < teams.size(); i++) {
const auto& team = teams[i];
TraceEvent("ServerTeamInfo", self->getDistributorId()) TraceEvent("ServerTeamInfo", self->getDistributorId())
.detail("Size", teams.size()) .detail("TeamIndex", i)
.detail("Primary", self->isPrimary()); .detail("Healthy", team->isHealthy())
for (i = 0; i < teams.size(); i++) { .detail("TeamSize", team->size())
const auto& team = teams[i]; .detail("MemberIDs", team->getServerIDsStr())
.detail("Primary", self->isPrimary())
TraceEvent("ServerTeamInfo", self->getDistributorId()) .detail("TeamID", team->getTeamID())
.detail("TeamIndex", i) .detail("Shards",
.detail("Healthy", team->isHealthy()) self->shardsAffectedByTeamFailure
.detail("TeamSize", team->size()) ->getShardsFor(ShardsAffectedByTeamFailure::Team(team->getServerIDs(), self->primary))
.detail("MemberIDs", team->getServerIDsStr()) .size());
.detail("Primary", self->isPrimary()) if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
.detail("TeamID", team->getTeamID()) wait(yield());
.detail(
"Shards",
self->shardsAffectedByTeamFailure
->getShardsFor(ShardsAffectedByTeamFailure::Team(team->getServerIDs(), self->primary))
.size());
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
wait(yield());
}
} }
TraceEvent("MachineInfo", self->getDistributorId())
.detail("Size", machine_info.size())
.detail("Primary", self->isPrimary());
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) {
// ServerStatus might not be known if server was very recently added and
// storageServerFailureTracker() has not yet updated self->server_status If the UID is not
// found, do not assume the server is healthy
auto it = server_status.find(server->getId());
if (it != server_status.end() && !it->second.isUnhealthy()) {
isMachineHealthy = true;
}
}
isMachineHealthy = false;
TraceEvent("MachineInfo", self->getDistributorId())
.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->isPrimary());
machine++;
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
wait(yield());
}
}
TraceEvent("MachineTeamInfo", self->getDistributorId())
.detail("Size", machineTeams.size())
.detail("Primary", self->isPrimary());
for (i = 0; i < machineTeams.size(); i++) {
const auto& team = machineTeams[i];
TraceEvent("MachineTeamInfo", self->getDistributorId())
.detail("TeamIndex", i)
.detail("MachineIDs", team->getMachineIDsStr())
.detail("ServerTeams", team->getServerTeams().size())
.detail("Primary", self->isPrimary());
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->getDistributorId())
// .detail("Size", internedLocalityRecordKeyNameStrings.size())
// .detail("Primary", self->isPrimary());
// for (i = 0; i < internedLocalityRecordKeyNameStrings.size(); i++) {
// TraceEvent("LocalityRecordKeyIndexName", self->getDistributorId())
// .detail("KeyIndex", i)
// .detail("KeyName", internedLocalityRecordKeyNameStrings[i])
// .detail("Primary", self->isPrimary());
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
// wait(yield());
// }
// }
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("Size", machineLocalityMapEntryArraySize)
// .detail("Primary", self->isPrimary());
// for (i = 0; i < serverIDs.size(); i++) {
// const auto& serverID = serverIDs[i];
// Reference<LocalityRecord> record = machineLocalityMapRecordArray[i];
// if (record.isValid()) {
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("LocalityIndex", i)
// .detail("UID", serverID->toString())
// .detail("LocalityRecord", record->toString())
// .detail("Primary", self->isPrimary());
// } else {
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("LocalityIndex", i)
// .detail("UID", serverID->toString())
// .detail("LocalityRecord", "[NotFound]")
// .detail("Primary", self->isPrimary());
// }
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
// wait(yield());
// }
// }
} catch (Error& e) {
wait(tr.onError(e));
} }
TraceEvent("MachineInfo", self->getDistributorId())
.detail("Size", machine_info.size())
.detail("Primary", self->isPrimary());
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) {
// ServerStatus might not be known if server was very recently added and
// storageServerFailureTracker() has not yet updated self->server_status If the UID is not
// found, do not assume the server is healthy
auto it = server_status.find(server->getId());
if (it != server_status.end() && !it->second.isUnhealthy()) {
isMachineHealthy = true;
}
}
isMachineHealthy = false;
TraceEvent("MachineInfo", self->getDistributorId())
.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->isPrimary());
machine++;
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
wait(yield());
}
}
TraceEvent("MachineTeamInfo", self->getDistributorId())
.detail("Size", machineTeams.size())
.detail("Primary", self->isPrimary());
for (i = 0; i < machineTeams.size(); i++) {
const auto& team = machineTeams[i];
TraceEvent("MachineTeamInfo", self->getDistributorId())
.detail("TeamIndex", i)
.detail("MachineIDs", team->getMachineIDsStr())
.detail("ServerTeams", team->getServerTeams().size())
.detail("Primary", self->isPrimary());
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->getDistributorId())
// .detail("Size", internedLocalityRecordKeyNameStrings.size())
// .detail("Primary", self->isPrimary());
// for (i = 0; i < internedLocalityRecordKeyNameStrings.size(); i++) {
// TraceEvent("LocalityRecordKeyIndexName", self->getDistributorId())
// .detail("KeyIndex", i)
// .detail("KeyName", internedLocalityRecordKeyNameStrings[i])
// .detail("Primary", self->isPrimary());
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
// wait(yield());
// }
// }
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("Size", machineLocalityMapEntryArraySize)
// .detail("Primary", self->isPrimary());
// for (i = 0; i < serverIDs.size(); i++) {
// const auto& serverID = serverIDs[i];
// Reference<LocalityRecord> record = machineLocalityMapRecordArray[i];
// if (record.isValid()) {
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("LocalityIndex", i)
// .detail("UID", serverID->toString())
// .detail("LocalityRecord", record->toString())
// .detail("Primary", self->isPrimary());
// } else {
// TraceEvent("MachineLocalityMap", self->getDistributorId())
// .detail("LocalityIndex", i)
// .detail("UID", serverID->toString())
// .detail("LocalityRecord", "[NotFound]")
// .detail("Primary", self->isPrimary());
// }
// if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
// wait(yield());
// }
// }
} }
} }
@ -3461,6 +3420,7 @@ Future<Void> DDTeamCollection::serverTeamRemover() {
} }
Future<Void> DDTeamCollection::trackExcludedServers() { Future<Void> DDTeamCollection::trackExcludedServers() {
ASSERT(!db->isMocked());
return DDTeamCollectionImpl::trackExcludedServers(this); return DDTeamCollectionImpl::trackExcludedServers(this);
} }
@ -3483,6 +3443,7 @@ Future<Void> DDTeamCollection::perpetualStorageWiggler(AsyncVar<bool>& stopSigna
} }
Future<Void> DDTeamCollection::monitorPerpetualStorageWiggle() { Future<Void> DDTeamCollection::monitorPerpetualStorageWiggle() {
ASSERT(!db->isMocked());
return DDTeamCollectionImpl::monitorPerpetualStorageWiggle(this); return DDTeamCollectionImpl::monitorPerpetualStorageWiggle(this);
} }
@ -3492,6 +3453,7 @@ Future<Void> DDTeamCollection::waitServerListChange(FutureStream<Void> serverRem
} }
Future<Void> DDTeamCollection::waitHealthyZoneChange() { Future<Void> DDTeamCollection::waitHealthyZoneChange() {
ASSERT(!db->isMocked());
return DDTeamCollectionImpl::waitHealthyZoneChange(this); return DDTeamCollectionImpl::waitHealthyZoneChange(this);
} }
@ -3525,7 +3487,7 @@ Future<Void> DDTeamCollection::monitorHealthyTeams() {
} }
Future<UID> DDTeamCollection::getClusterId() { Future<UID> DDTeamCollection::getClusterId() {
return DDTeamCollectionImpl::getClusterId(this); return db->getClusterId();
} }
Future<UID> DDTeamCollection::getNextWigglingServerID() { Future<UID> DDTeamCollection::getNextWigglingServerID() {

View File

@ -28,6 +28,20 @@
class DDTxnProcessorImpl { class DDTxnProcessorImpl {
friend class DDTxnProcessor; friend class DDTxnProcessor;
ACTOR static Future<ServerWorkerInfos> getServerListAndProcessClasses(Database cx) {
state Transaction tr(cx);
state ServerWorkerInfos res;
loop {
try {
wait(store(res.servers, NativeAPI::getServerListAndProcessClasses(&tr)));
res.readVersion = tr.getReadVersion().get();
return res;
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
// return {sourceServers, completeSources} // return {sourceServers, completeSources}
ACTOR static Future<IDDTxnProcessor::SourceServers> getSourceServersForRange(Database cx, KeyRangeRef keys) { ACTOR static Future<IDDTxnProcessor::SourceServers> getSourceServersForRange(Database cx, KeyRangeRef keys) {
state std::set<UID> servers; state std::set<UID> servers;
@ -127,6 +141,43 @@ class DDTxnProcessorImpl {
return Void(); return Void();
} }
ACTOR static Future<int> tryUpdateReplicasKeyForDc(Database cx, Optional<Key> dcId, int storageTeamSize) {
state Transaction tr(cx);
loop {
try {
Optional<Value> val = wait(tr.get(datacenterReplicasKeyFor(dcId)));
state int oldReplicas = val.present() ? decodeDatacenterReplicasValue(val.get()) : 0;
if (oldReplicas == storageTeamSize) {
return oldReplicas;
}
if (oldReplicas < storageTeamSize) {
tr.set(rebootWhenDurableKey, StringRef());
}
tr.set(datacenterReplicasKeyFor(dcId), datacenterReplicasValue(storageTeamSize));
wait(tr.commit());
return oldReplicas;
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
ACTOR static Future<UID> getClusterId(Database cx) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
Optional<Value> clusterId = wait(tr.get(clusterIdKey));
ASSERT(clusterId.present());
return BinaryReader::fromStringRef<UID>(clusterId.get(), Unversioned());
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
// Read keyservers, return unique set of teams // Read keyservers, return unique set of teams
ACTOR static Future<Reference<InitialDataDistribution>> getInitialDataDistribution( ACTOR static Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
Database cx, Database cx,
@ -465,15 +516,29 @@ class DDTxnProcessorImpl {
} }
} }
} }
ACTOR static Future<Void> waitDDTeamInfoPrintSignal(Database cx) {
state ReadYourWritesTransaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
state Future<Void> watchFuture = tr.watch(triggerDDTeamInfoPrintKey);
wait(tr.commit());
wait(watchFuture);
return Void();
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
}; };
Future<IDDTxnProcessor::SourceServers> DDTxnProcessor::getSourceServersForRange(const KeyRangeRef range) { Future<IDDTxnProcessor::SourceServers> DDTxnProcessor::getSourceServersForRange(const KeyRangeRef range) {
return DDTxnProcessorImpl::getSourceServersForRange(cx, range); return DDTxnProcessorImpl::getSourceServersForRange(cx, range);
} }
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> DDTxnProcessor::getServerListAndProcessClasses() { Future<ServerWorkerInfos> DDTxnProcessor::getServerListAndProcessClasses() {
Transaction tr(cx); return DDTxnProcessorImpl::getServerListAndProcessClasses(cx);
return NativeAPI::getServerListAndProcessClasses(&tr);
} }
Future<MoveKeysLock> DDTxnProcessor::takeMoveKeysLock(const UID& ddId) const { Future<MoveKeysLock> DDTxnProcessor::takeMoveKeysLock(const UID& ddId) const {
@ -539,12 +604,25 @@ Future<Optional<Value>> DDTxnProcessor::readRebalanceDDIgnoreKey() const {
return DDTxnProcessorImpl::readRebalanceDDIgnoreKey(cx); return DDTxnProcessorImpl::readRebalanceDDIgnoreKey(cx);
} }
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> Future<int> DDTxnProcessor::tryUpdateReplicasKeyForDc(const Optional<Key>& dcId, const int& storageTeamSize) const {
DDMockTxnProcessor::getServerListAndProcessClasses() { return DDTxnProcessorImpl::tryUpdateReplicasKeyForDc(cx, dcId, storageTeamSize);
std::vector<std::pair<StorageServerInterface, ProcessClass>> res; }
Future<UID> DDTxnProcessor::getClusterId() const {
return DDTxnProcessorImpl::getClusterId(cx);
}
Future<Void> DDTxnProcessor::waitDDTeamInfoPrintSignal() const {
return DDTxnProcessorImpl::waitDDTeamInfoPrintSignal(cx);
}
Future<ServerWorkerInfos> DDMockTxnProcessor::getServerListAndProcessClasses() {
ServerWorkerInfos res;
for (auto& [_, mss] : mgs->allServers) { for (auto& [_, mss] : mgs->allServers) {
res.emplace_back(mss.ssi, ProcessClass(ProcessClass::StorageClass, ProcessClass::DBSource)); res.servers.emplace_back(mss.ssi, ProcessClass(ProcessClass::StorageClass, ProcessClass::DBSource));
} }
// FIXME(xwang): possible generate version from time?
res.readVersion = 0;
return res; return res;
} }
@ -619,7 +697,7 @@ Future<Reference<InitialDataDistribution>> DDMockTxnProcessor::getInitialDataDis
// FIXME: now we just ignore ddEnabledState and moveKeysLock, will fix it in the future // FIXME: now we just ignore ddEnabledState and moveKeysLock, will fix it in the future
Reference<InitialDataDistribution> res = makeReference<InitialDataDistribution>(); Reference<InitialDataDistribution> res = makeReference<InitialDataDistribution>();
res->mode = 1; res->mode = 1;
res->allServers = getServerListAndProcessClasses().get(); res->allServers = getServerListAndProcessClasses().get().servers;
res->shards = getDDShardInfos(); res->shards = getDDShardInfos();
std::tie(res->primaryTeams, res->remoteTeams) = getAllTeamsInRegion(res->shards); std::tie(res->primaryTeams, res->remoteTeams) = getAllTeamsInRegion(res->shards);
return res; return res;

View File

@ -79,7 +79,11 @@ using rocksdb::BackgroundErrorReason;
class SharedRocksDBState { class SharedRocksDBState {
public: public:
SharedRocksDBState(); SharedRocksDBState(UID id);
std::shared_ptr<LatencySample> commitLatency;
std::shared_ptr<LatencySample> commitQueueLatency;
std::shared_ptr<LatencySample> dbWriteLatency;
void setClosing() { this->closing = true; } void setClosing() { this->closing = true; }
bool isClosing() const { return this->closing; } bool isClosing() const { return this->closing; }
@ -90,6 +94,7 @@ public:
rocksdb::ReadOptions& getReadOptions() { return this->readOptions; } rocksdb::ReadOptions& getReadOptions() { return this->readOptions; }
private: private:
const UID id;
rocksdb::ColumnFamilyOptions initialCfOptions(); rocksdb::ColumnFamilyOptions initialCfOptions();
rocksdb::DBOptions initialDbOptions(); rocksdb::DBOptions initialDbOptions();
rocksdb::ReadOptions initialReadOptions(); rocksdb::ReadOptions initialReadOptions();
@ -100,13 +105,34 @@ private:
rocksdb::ReadOptions readOptions; rocksdb::ReadOptions readOptions;
}; };
SharedRocksDBState::SharedRocksDBState() SharedRocksDBState::SharedRocksDBState(UID id)
: closing(false), dbOptions(initialDbOptions()), cfOptions(initialCfOptions()), readOptions(initialReadOptions()) {} : id(id), closing(false), dbOptions(initialDbOptions()), cfOptions(initialCfOptions()),
readOptions(initialReadOptions()),
commitLatency(std::make_shared<LatencySample>("RocksDBCommitLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SAMPLE_SIZE)),
commitQueueLatency(std::make_shared<LatencySample>("RocksDBCommitQueueLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SAMPLE_SIZE)),
dbWriteLatency(std::make_shared<LatencySample>("RocksDBWriteLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SAMPLE_SIZE)) {}
rocksdb::ColumnFamilyOptions SharedRocksDBState::initialCfOptions() { rocksdb::ColumnFamilyOptions SharedRocksDBState::initialCfOptions() {
rocksdb::ColumnFamilyOptions options; rocksdb::ColumnFamilyOptions options;
options.level_compaction_dynamic_level_bytes = SERVER_KNOBS->ROCKSDB_LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES; options.level_compaction_dynamic_level_bytes = SERVER_KNOBS->ROCKSDB_LEVEL_COMPACTION_DYNAMIC_LEVEL_BYTES;
options.OptimizeLevelStyleCompaction(SERVER_KNOBS->ROCKSDB_MEMTABLE_BYTES); if (SERVER_KNOBS->ROCKSDB_LEVEL_STYLE_COMPACTION) {
options.OptimizeLevelStyleCompaction(SERVER_KNOBS->ROCKSDB_MEMTABLE_BYTES);
} else {
options.OptimizeUniversalStyleCompaction(SERVER_KNOBS->ROCKSDB_MEMTABLE_BYTES);
}
if (SERVER_KNOBS->ROCKSDB_DISABLE_AUTO_COMPACTIONS) {
options.disable_auto_compactions = SERVER_KNOBS->ROCKSDB_DISABLE_AUTO_COMPACTIONS;
}
if (SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS > 0) { if (SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS > 0) {
options.periodic_compaction_seconds = SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS; options.periodic_compaction_seconds = SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS;
@ -258,7 +284,7 @@ using DB = rocksdb::DB*;
using CF = rocksdb::ColumnFamilyHandle*; using CF = rocksdb::ColumnFamilyHandle*;
#define PERSIST_PREFIX "\xff\xff" #define PERSIST_PREFIX "\xff\xff"
const KeyRef persistVersion = LiteralStringRef(PERSIST_PREFIX "Version"); const KeyRef persistVersion = PERSIST_PREFIX "Version"_sr;
const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = "RocksDBStorage"_sr; const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = "RocksDBStorage"_sr;
const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = "RocksDBCommitLatency"_sr; const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = "RocksDBCommitLatency"_sr;
const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = "RocksDBCommitAction"_sr; const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = "RocksDBCommitAction"_sr;
@ -793,14 +819,29 @@ ACTOR Future<Void> rocksDBMetricLogger(UID id,
{ "CountWalFileSyncs", rocksdb::WAL_FILE_SYNCED, 0 }, { "CountWalFileSyncs", rocksdb::WAL_FILE_SYNCED, 0 },
{ "CountWalFileBytes", rocksdb::WAL_FILE_BYTES, 0 }, { "CountWalFileBytes", rocksdb::WAL_FILE_BYTES, 0 },
{ "CompactReadBytes", rocksdb::COMPACT_READ_BYTES, 0 }, { "CompactReadBytes", rocksdb::COMPACT_READ_BYTES, 0 },
{ "CompactReadBytesMarked", rocksdb::COMPACT_READ_BYTES_MARKED, 0 },
{ "CompactReadBytesPeriodic", rocksdb::COMPACT_READ_BYTES_PERIODIC, 0 },
{ "CompactReadBytesTtl", rocksdb::COMPACT_READ_BYTES_TTL, 0 },
{ "CompactWriteBytes", rocksdb::COMPACT_WRITE_BYTES, 0 }, { "CompactWriteBytes", rocksdb::COMPACT_WRITE_BYTES, 0 },
{ "CompactWriteBytesMarked", rocksdb::COMPACT_WRITE_BYTES_MARKED, 0 },
{ "CompactWriteBytesPeriodic", rocksdb::COMPACT_WRITE_BYTES_PERIODIC, 0 },
{ "CompactWriteBytesTtl", rocksdb::COMPACT_WRITE_BYTES_TTL, 0 },
{ "FlushWriteBytes", rocksdb::FLUSH_WRITE_BYTES, 0 }, { "FlushWriteBytes", rocksdb::FLUSH_WRITE_BYTES, 0 },
{ "CountBlocksCompressed", rocksdb::NUMBER_BLOCK_COMPRESSED, 0 }, { "CountBlocksCompressed", rocksdb::NUMBER_BLOCK_COMPRESSED, 0 },
{ "CountBlocksDecompressed", rocksdb::NUMBER_BLOCK_DECOMPRESSED, 0 }, { "CountBlocksDecompressed", rocksdb::NUMBER_BLOCK_DECOMPRESSED, 0 },
{ "RowCacheHit", rocksdb::ROW_CACHE_HIT, 0 }, { "RowCacheHit", rocksdb::ROW_CACHE_HIT, 0 },
{ "RowCacheMiss", rocksdb::ROW_CACHE_MISS, 0 }, { "RowCacheMiss", rocksdb::ROW_CACHE_MISS, 0 },
{ "CountIterSkippedKeys", rocksdb::NUMBER_ITER_SKIP, 0 }, { "CountIterSkippedKeys", rocksdb::NUMBER_ITER_SKIP, 0 },
};
state std::vector<std::pair<const char*, uint32_t>> histogramStats = {
{ "CompactionTime", rocksdb::COMPACTION_TIME },
{ "CompactionCPUTime", rocksdb::COMPACTION_CPU_TIME },
{ "CompressionTimeNanos", rocksdb::COMPRESSION_TIMES_NANOS },
{ "DecompressionTimeNanos", rocksdb::DECOMPRESSION_TIMES_NANOS },
{ "HardRateLimitDelayCount", rocksdb::HARD_RATE_LIMIT_DELAY_COUNT },
{ "SoftRateLimitDelayCount", rocksdb::SOFT_RATE_LIMIT_DELAY_COUNT },
{ "WriteStall", rocksdb::WRITE_STALL },
}; };
state std::vector<std::pair<const char*, std::string>> intPropertyStats = { state std::vector<std::pair<const char*, std::string>> intPropertyStats = {
@ -857,6 +898,13 @@ ACTOR Future<Void> rocksDBMetricLogger(UID id,
cum = stat; cum = stat;
} }
for (auto& [name, histogram] : histogramStats) {
rocksdb::HistogramData histogram_data;
statistics->histogramData(histogram, &histogram_data);
e.detail(format("%s%d", name, "P95"), histogram_data.percentile95);
e.detail(format("%s%d", name, "P99"), histogram_data.percentile99);
}
for (const auto& [name, property] : intPropertyStats) { for (const auto& [name, property] : intPropertyStats) {
stat = 0; stat = 0;
// GetAggregatedIntProperty gets the aggregated int property from all column families. // GetAggregatedIntProperty gets the aggregated int property from all column families.
@ -1129,9 +1177,9 @@ struct RocksDBKeyValueStore : IKeyValueStore {
if (doPerfContextMetrics) { if (doPerfContextMetrics) {
perfContextMetrics->reset(); perfContextMetrics->reset();
} }
double commitBeginTime; double commitBeginTime = timer_monotonic();
sharedState->commitQueueLatency->addMeasurement(commitBeginTime - a.startTime);
if (a.getHistograms) { if (a.getHistograms) {
commitBeginTime = timer_monotonic();
metricPromiseStream->send( metricPromiseStream->send(
std::make_pair(ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM.toString(), commitBeginTime - a.startTime)); std::make_pair(ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM.toString(), commitBeginTime - a.startTime));
} }
@ -1156,9 +1204,11 @@ struct RocksDBKeyValueStore : IKeyValueStore {
} }
s = db->Write(options, a.batchToCommit.get()); s = db->Write(options, a.batchToCommit.get());
readIterPool->update(); readIterPool->update();
double currTime = timer_monotonic();
sharedState->dbWriteLatency->addMeasurement(currTime - writeBeginTime);
if (a.getHistograms) { if (a.getHistograms) {
metricPromiseStream->send( metricPromiseStream->send(
std::make_pair(ROCKSDB_WRITE_HISTOGRAM.toString(), timer_monotonic() - writeBeginTime)); std::make_pair(ROCKSDB_WRITE_HISTOGRAM.toString(), currTime - writeBeginTime));
} }
if (!s.ok()) { if (!s.ok()) {
@ -1167,21 +1217,23 @@ struct RocksDBKeyValueStore : IKeyValueStore {
} else { } else {
a.done.send(Void()); a.done.send(Void());
double compactRangeBeginTime = a.getHistograms ? timer_monotonic() : 0; if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
for (const auto& keyRange : deletes) { double compactRangeBeginTime = a.getHistograms ? timer_monotonic() : 0;
auto begin = toSlice(keyRange.begin); for (const auto& keyRange : deletes) {
auto end = toSlice(keyRange.end); auto begin = toSlice(keyRange.begin);
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) { auto end = toSlice(keyRange.end);
ASSERT(db->SuggestCompactRange(cf, &begin, &end).ok()); ASSERT(db->SuggestCompactRange(cf, &begin, &end).ok());
} }
} if (a.getHistograms) {
if (a.getHistograms) { metricPromiseStream->send(std::make_pair(ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM.toString(),
metricPromiseStream->send(std::make_pair(ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM.toString(), timer_monotonic() - compactRangeBeginTime));
timer_monotonic() - compactRangeBeginTime)); }
} }
} }
currTime = timer_monotonic();
sharedState->commitLatency->addMeasurement(currTime - a.startTime);
if (a.getHistograms) { if (a.getHistograms) {
double currTime = timer_monotonic();
metricPromiseStream->send( metricPromiseStream->send(
std::make_pair(ROCKSDB_COMMIT_ACTION_HISTOGRAM.toString(), currTime - commitBeginTime)); std::make_pair(ROCKSDB_COMMIT_ACTION_HISTOGRAM.toString(), currTime - commitBeginTime));
metricPromiseStream->send( metricPromiseStream->send(
@ -1593,7 +1645,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
}; };
explicit RocksDBKeyValueStore(const std::string& path, UID id) explicit RocksDBKeyValueStore(const std::string& path, UID id)
: sharedState(std::make_shared<SharedRocksDBState>()), path(path), id(id), : id(id), sharedState(std::make_shared<SharedRocksDBState>(id)), path(path),
perfContextMetrics(new PerfContextMetrics()), perfContextMetrics(new PerfContextMetrics()),
readIterPool(new ReadIteratorPool(id, db, defaultFdbCF, sharedState->getReadOptions())), readIterPool(new ReadIteratorPool(id, db, defaultFdbCF, sharedState->getReadOptions())),
readSemaphore(SERVER_KNOBS->ROCKSDB_READ_QUEUE_SOFT_MAX), readSemaphore(SERVER_KNOBS->ROCKSDB_READ_QUEUE_SOFT_MAX),
@ -2331,12 +2383,16 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/RocksDBReopen") {
// Confirm that `init()` is idempotent. // Confirm that `init()` is idempotent.
wait(kvStore->init()); wait(kvStore->init());
Optional<Value> val = wait(kvStore->readValue("foo"_sr)); {
ASSERT(Optional<Value>("bar"_sr) == val); Optional<Value> val = wait(kvStore->readValue("foo"_sr));
ASSERT(Optional<Value>("bar"_sr) == val);
}
Future<Void> closed = kvStore->onClosed(); {
kvStore->dispose(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->dispose();
wait(closed);
}
platform::eraseDirectoryRecursive(rocksDBTestDir); platform::eraseDirectoryRecursive(rocksDBTestDir);
return Void(); return Void();
@ -2374,8 +2430,10 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/CheckpointRestoreColumnFamily")
checkpoints.push_back(metaData); checkpoints.push_back(metaData);
wait(kvStoreCopy->restore(checkpoints)); wait(kvStoreCopy->restore(checkpoints));
Optional<Value> val = wait(kvStoreCopy->readValue("foo"_sr)); {
ASSERT(Optional<Value>("bar"_sr) == val); Optional<Value> val = wait(kvStoreCopy->readValue("foo"_sr));
ASSERT(Optional<Value>("bar"_sr) == val);
}
std::vector<Future<Void>> closes; std::vector<Future<Void>> closes;
closes.push_back(kvStore->onClosed()); closes.push_back(kvStore->onClosed());

View File

@ -309,7 +309,17 @@ struct SQLiteDB : NonCopyable {
db, 0, restart ? SQLITE_CHECKPOINT_RESTART : SQLITE_CHECKPOINT_FULL, &logSize, &checkpointCount); db, 0, restart ? SQLITE_CHECKPOINT_RESTART : SQLITE_CHECKPOINT_FULL, &logSize, &checkpointCount);
if (!rc) if (!rc)
break; break;
if ((sqlite3_errcode(db) & 0xff) == SQLITE_BUSY) {
// In simulation, if the process is shutting down then do not wait/retry on a busy result because the wait
// or the retry could take too long to complete such that the virtual process is forcibly destroyed first,
// leaking all outstanding actors and their related states which would include references to the SQLite
// data files which would remain open. This would cause the replacement virtual process to fail an assert
// when opening the SQLite files as they would already be in use.
if (g_network->isSimulated() && g_simulator->getCurrentProcess()->shutdownSignal.isSet()) {
VFSAsyncFile::setInjectedError(rc);
checkError("checkpoint", rc);
ASSERT(false); // should never reach this point
} else if ((sqlite3_errcode(db) & 0xff) == SQLITE_BUSY) {
// printf("#"); // printf("#");
// threadSleep(.010); // threadSleep(.010);
sqlite3_sleep(10); sqlite3_sleep(10);

View File

@ -1894,12 +1894,13 @@ struct ShardedRocksDBKeyValueStore : IKeyValueStore {
} }
a.done.send(Void()); a.done.send(Void());
for (const auto& [id, range] : deletes) { if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
auto cf = columnFamilyMap->find(id); for (const auto& [id, range] : deletes) {
ASSERT(cf != columnFamilyMap->end()); auto cf = columnFamilyMap->find(id);
auto begin = toSlice(range.begin); ASSERT(cf != columnFamilyMap->end());
auto end = toSlice(range.end); auto begin = toSlice(range.begin);
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) { auto end = toSlice(range.end);
ASSERT(a.db->SuggestCompactRange(cf->second, &begin, &end).ok()); ASSERT(a.db->SuggestCompactRange(cf->second, &begin, &end).ok());
} }
} }
@ -2597,8 +2598,10 @@ TEST_CASE("noSim/ShardedRocksDB/SingleShardRead") {
Optional<Value> val = wait(kvStore->readValue("a"_sr)); Optional<Value> val = wait(kvStore->readValue("a"_sr));
ASSERT(Optional<Value>("foo"_sr) == val); ASSERT(Optional<Value>("foo"_sr) == val);
Optional<Value> val = wait(kvStore->readValue("ac"_sr)); {
ASSERT(Optional<Value>("bar"_sr) == val); Optional<Value> val = wait(kvStore->readValue("ac"_sr));
ASSERT(Optional<Value>("bar"_sr) == val);
}
Future<Void> closed = kvStore->onClosed(); Future<Void> closed = kvStore->onClosed();
kvStore->dispose(); kvStore->dispose();
@ -2662,17 +2665,21 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
} }
// Read backward full range. // Read backward full range.
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), -1000, 10000)); {
ASSERT_EQ(result.size(), expectedRows.size()); RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), -1000, 10000));
for (int i = 0; i < expectedRows.size(); ++i) { ASSERT_EQ(result.size(), expectedRows.size());
ASSERT(result[i] == expectedRows[59 - i]); for (int i = 0; i < expectedRows.size(); ++i) {
ASSERT(result[i] == expectedRows[59 - i]);
}
} }
// Forward with row limit. // Forward with row limit.
RangeResult result = wait(kvStore->readRange(KeyRangeRef("2"_sr, "6"_sr), 10, 10000)); {
ASSERT_EQ(result.size(), 10); RangeResult result = wait(kvStore->readRange(KeyRangeRef("2"_sr, "6"_sr), 10, 10000));
for (int i = 0; i < 10; ++i) { ASSERT_EQ(result.size(), 10);
ASSERT(result[i] == expectedRows[20 + i]); for (int i = 0; i < 10; ++i) {
ASSERT(result[i] == expectedRows[20 + i]);
}
} }
// Add another range on shard-1. // Add another range on shard-1.
@ -2688,32 +2695,40 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
wait(kvStore->commit(false)); wait(kvStore->commit(false));
Future<Void> closed = kvStore->onClosed(); {
kvStore->close(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->close();
wait(closed);
}
kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
wait(kvStore->init()); wait(kvStore->init());
// Read all values. // Read all values.
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000)); {
ASSERT_EQ(result.size(), expectedRows.size()); RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000));
for (int i = 0; i < expectedRows.size(); ++i) { ASSERT_EQ(result.size(), expectedRows.size());
ASSERT(result[i] == expectedRows[i]); for (int i = 0; i < expectedRows.size(); ++i) {
ASSERT(result[i] == expectedRows[i]);
}
} }
// Read partial range with row limit // Read partial range with row limit
RangeResult result = wait(kvStore->readRange(KeyRangeRef("5"_sr, ":"_sr), 35, 10000)); {
ASSERT_EQ(result.size(), 35); RangeResult result = wait(kvStore->readRange(KeyRangeRef("5"_sr, ":"_sr), 35, 10000));
for (int i = 0; i < result.size(); ++i) { ASSERT_EQ(result.size(), 35);
ASSERT(result[i] == expectedRows[40 + i]); for (int i = 0; i < result.size(); ++i) {
ASSERT(result[i] == expectedRows[40 + i]);
}
} }
// Clear a range on a single shard. // Clear a range on a single shard.
kvStore->clear(KeyRangeRef("40"_sr, "45"_sr)); kvStore->clear(KeyRangeRef("40"_sr, "45"_sr));
wait(kvStore->commit(false)); wait(kvStore->commit(false));
RangeResult result = wait(kvStore->readRange(KeyRangeRef("4"_sr, "5"_sr), 20, 10000)); {
ASSERT_EQ(result.size(), 5); RangeResult result = wait(kvStore->readRange(KeyRangeRef("4"_sr, "5"_sr), 20, 10000));
ASSERT_EQ(result.size(), 5);
}
// Clear a single value. // Clear a single value.
kvStore->clear(KeyRangeRef("01"_sr, keyAfter("01"_sr))); kvStore->clear(KeyRangeRef("01"_sr, keyAfter("01"_sr)));
@ -2726,21 +2741,29 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
kvStore->clear(KeyRangeRef("1"_sr, "8"_sr)); kvStore->clear(KeyRangeRef("1"_sr, "8"_sr));
wait(kvStore->commit(false)); wait(kvStore->commit(false));
Future<Void> closed = kvStore->onClosed(); {
kvStore->close(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->close();
wait(closed);
}
kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
wait(kvStore->init()); wait(kvStore->init());
RangeResult result = wait(kvStore->readRange(KeyRangeRef("1"_sr, "8"_sr), 1000, 10000)); {
ASSERT_EQ(result.size(), 0); RangeResult result = wait(kvStore->readRange(KeyRangeRef("1"_sr, "8"_sr), 1000, 10000));
ASSERT_EQ(result.size(), 0);
}
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000)); {
ASSERT_EQ(result.size(), 19); RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000));
ASSERT_EQ(result.size(), 19);
}
Future<Void> closed = kvStore->onClosed(); {
kvStore->dispose(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->dispose();
wait(closed);
}
return Void(); return Void();
} }
@ -2755,17 +2778,21 @@ TEST_CASE("noSim/ShardedRocksDB/ShardOps") {
wait(kvStore->init()); wait(kvStore->init());
// Add some ranges. // Add some ranges.
std::vector<Future<Void>> addRangeFutures; {
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("a"_sr, "c"_sr), "shard-1")); std::vector<Future<Void>> addRangeFutures;
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("c"_sr, "f"_sr), "shard-2")); addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("a"_sr, "c"_sr), "shard-1"));
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("c"_sr, "f"_sr), "shard-2"));
wait(waitForAll(addRangeFutures)); wait(waitForAll(addRangeFutures));
}
std::vector<Future<Void>> addRangeFutures; {
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("x"_sr, "z"_sr), "shard-1")); std::vector<Future<Void>> addRangeFutures;
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("l"_sr, "n"_sr), "shard-3")); addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("x"_sr, "z"_sr), "shard-1"));
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("l"_sr, "n"_sr), "shard-3"));
wait(waitForAll(addRangeFutures)); wait(waitForAll(addRangeFutures));
}
// Remove single range. // Remove single range.
std::vector<std::string> shardsToCleanUp; std::vector<std::string> shardsToCleanUp;
@ -2821,30 +2848,37 @@ TEST_CASE("noSim/ShardedRocksDB/ShardOps") {
kvStore = rocksdbStore; kvStore = rocksdbStore;
wait(kvStore->init()); wait(kvStore->init());
auto dataMap = rocksdbStore->getDataMapping(); {
for (auto it = dataMap.begin(); it != dataMap.end(); ++it) { auto dataMap = rocksdbStore->getDataMapping();
std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id " for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {
<< it->second << "\n"; std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id "
<< it->second << "\n";
}
ASSERT(dataMap == mapping);
} }
ASSERT(dataMap == mapping);
// Remove all the ranges. // Remove all the ranges.
state std::vector<std::string> shardsToCleanUp = kvStore->removeRange(KeyRangeRef("a"_sr, "z"_sr)); {
ASSERT_EQ(shardsToCleanUp.size(), 3); state std::vector<std::string> shardsToCleanUp = kvStore->removeRange(KeyRangeRef("a"_sr, "z"_sr));
ASSERT_EQ(shardsToCleanUp.size(), 3);
// Add another range to shard-2. // Add another range to shard-2.
wait(kvStore->addRange(KeyRangeRef("h"_sr, "i"_sr), "shard-2")); wait(kvStore->addRange(KeyRangeRef("h"_sr, "i"_sr), "shard-2"));
// Clean up shards. Shard-2 should not be removed. // Clean up shards. Shard-2 should not be removed.
wait(kvStore->cleanUpShardsIfNeeded(shardsToCleanUp)); wait(kvStore->cleanUpShardsIfNeeded(shardsToCleanUp));
}
{
auto dataMap = rocksdbStore->getDataMapping();
ASSERT_EQ(dataMap.size(), 2);
ASSERT(dataMap[0].second == "shard-2");
}
auto dataMap = rocksdbStore->getDataMapping(); {
ASSERT_EQ(dataMap.size(), 2); Future<Void> closed = kvStore->onClosed();
ASSERT(dataMap[0].second == "shard-2"); kvStore->dispose();
wait(closed);
Future<Void> closed = kvStore->onClosed(); }
kvStore->dispose();
wait(closed);
return Void(); return Void();
} }
@ -2865,8 +2899,10 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
kvStore->set(KeyValueRef(testSpecialKey, testSpecialValue)); kvStore->set(KeyValueRef(testSpecialKey, testSpecialValue));
wait(kvStore->commit(false)); wait(kvStore->commit(false));
Optional<Value> val = wait(kvStore->readValue(testSpecialKey)); {
ASSERT(val.get() == testSpecialValue); Optional<Value> val = wait(kvStore->readValue(testSpecialKey));
ASSERT(val.get() == testSpecialValue);
}
// Add some ranges. // Add some ranges.
std::vector<Future<Void>> addRangeFutures; std::vector<Future<Void>> addRangeFutures;
@ -2893,10 +2929,14 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
} }
// Read value back. // Read value back.
Optional<Value> val = wait(kvStore->readValue("a1"_sr)); {
ASSERT(val == Optional<Value>("foo"_sr)); Optional<Value> val = wait(kvStore->readValue("a1"_sr));
Optional<Value> val = wait(kvStore->readValue("d1"_sr)); ASSERT(val == Optional<Value>("foo"_sr));
ASSERT(val == Optional<Value>("bar"_sr)); }
{
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
ASSERT(val == Optional<Value>("bar"_sr));
}
// Remove range containing a1. // Remove range containing a1.
kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "b"_sr), false); kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "b"_sr), false);
@ -2904,22 +2944,30 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
wait(kvStore->commit(false)); wait(kvStore->commit(false));
// Read a1. // Read a1.
Optional<Value> val = wait(kvStore->readValue("a1"_sr)); {
ASSERT(!val.present()); Optional<Value> val = wait(kvStore->readValue("a1"_sr));
ASSERT(!val.present());
}
// Restart. // Restart.
Future<Void> closed = kvStore->onClosed(); {
kvStore->close(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->close();
wait(closed);
}
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
kvStore = rocksdbStore; kvStore = rocksdbStore;
wait(kvStore->init()); wait(kvStore->init());
// Read again. // Read again.
Optional<Value> val = wait(kvStore->readValue("a1"_sr)); {
ASSERT(!val.present()); Optional<Value> val = wait(kvStore->readValue("a1"_sr));
Optional<Value> val = wait(kvStore->readValue("d1"_sr)); ASSERT(!val.present());
ASSERT(val == Optional<Value>("bar"_sr)); }
{
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
ASSERT(val == Optional<Value>("bar"_sr));
}
auto mapping = rocksdbStore->getDataMapping(); auto mapping = rocksdbStore->getDataMapping();
ASSERT(mapping.size() == 3); ASSERT(mapping.size() == 3);
@ -2930,46 +2978,56 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
ASSERT(mapping.size() == 1); ASSERT(mapping.size() == 1);
// Restart. // Restart.
Future<Void> closed = kvStore->onClosed(); {
kvStore->close(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->close();
wait(closed);
}
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
kvStore = rocksdbStore; kvStore = rocksdbStore;
wait(kvStore->init()); wait(kvStore->init());
// Because range metadata was not committed, ranges should be restored. // Because range metadata was not committed, ranges should be restored.
auto mapping = rocksdbStore->getDataMapping(); {
ASSERT(mapping.size() == 3); auto mapping = rocksdbStore->getDataMapping();
ASSERT(mapping.size() == 3);
// Remove ranges again. // Remove ranges again.
kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "f"_sr), false); kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "f"_sr), false);
kvStore->removeRange(KeyRangeRef("a"_sr, "f"_sr)); kvStore->removeRange(KeyRangeRef("a"_sr, "f"_sr));
mapping = rocksdbStore->getDataMapping(); mapping = rocksdbStore->getDataMapping();
ASSERT(mapping.size() == 1); ASSERT(mapping.size() == 1);
wait(kvStore->commit(false)); wait(kvStore->commit(false));
}
// Restart. // Restart.
Future<Void> closed = kvStore->onClosed(); {
kvStore->close(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->close();
wait(closed);
}
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
kvStore = rocksdbStore; kvStore = rocksdbStore;
wait(kvStore->init()); wait(kvStore->init());
// No range available. // No range available.
auto mapping = rocksdbStore->getDataMapping(); {
for (auto it = mapping.begin(); it != mapping.end(); ++it) { auto mapping = rocksdbStore->getDataMapping();
std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id " for (auto it = mapping.begin(); it != mapping.end(); ++it) {
<< it->second << "\n"; std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id "
<< it->second << "\n";
}
ASSERT(mapping.size() == 1);
} }
ASSERT(mapping.size() == 1);
Future<Void> closed = kvStore->onClosed(); {
kvStore->dispose(); Future<Void> closed = kvStore->onClosed();
wait(closed); kvStore->dispose();
wait(closed);
}
return Void(); return Void();
} }

View File

@ -2325,6 +2325,7 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
FlowLock* cleanUpDataMoveParallelismLock, FlowLock* cleanUpDataMoveParallelismLock,
KeyRange keys, KeyRange keys,
const DDEnabledState* ddEnabledState) { const DDEnabledState* ddEnabledState) {
state KeyRange range;
TraceEvent(SevVerbose, "CleanUpDataMoveBegin", dataMoveId).detail("DataMoveID", dataMoveId).detail("Range", keys); TraceEvent(SevVerbose, "CleanUpDataMoveBegin", dataMoveId).detail("DataMoveID", dataMoveId).detail("Range", keys);
state bool complete = false; state bool complete = false;
@ -2336,7 +2337,9 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
state Transaction tr(occ); state Transaction tr(occ);
state std::unordered_map<UID, std::vector<Shard>> physicalShardMap; state std::unordered_map<UID, std::vector<Shard>> physicalShardMap;
state std::set<UID> oldDests; state std::set<UID> oldDests;
state KeyRange range; state DataMoveMetaData dataMove;
range = KeyRange();
try { try {
tr.trState->taskID = TaskPriority::MoveKeys; tr.trState->taskID = TaskPriority::MoveKeys;
@ -2346,7 +2349,7 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
Optional<Value> val = wait(tr.get(dataMoveKeyFor(dataMoveId))); Optional<Value> val = wait(tr.get(dataMoveKeyFor(dataMoveId)));
if (val.present()) { if (val.present()) {
state DataMoveMetaData dataMove = decodeDataMoveValue(val.get()); dataMove = decodeDataMoveValue(val.get());
TraceEvent(SevVerbose, "CleanUpDataMoveMetaData", dataMoveId) TraceEvent(SevVerbose, "CleanUpDataMoveMetaData", dataMoveId)
.detail("DataMoveID", dataMoveId) .detail("DataMoveID", dataMoveId)
.detail("DataMoveMetaData", dataMove.toString()); .detail("DataMoveMetaData", dataMove.toString());

View File

@ -35,7 +35,7 @@
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbclient/ManagementAPI.actor.h" #include "fdbclient/ManagementAPI.actor.h"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.

View File

@ -533,9 +533,9 @@ ACTOR Future<Void> processCompleteTransactionStateRequest(TransactionStateResolv
std::vector<UID> src, dest; std::vector<UID> src, dest;
ServerCacheInfo info; ServerCacheInfo info;
// NOTE: An ACTOR will be compiled into several classes, the this pointer is from one of them. // NOTE: An ACTOR will be compiled into several classes, the this pointer is from one of them.
auto updateTagInfo = [this](const std::vector<UID>& uids, auto updateTagInfo = [pContext = pContext](const std::vector<UID>& uids,
std::vector<Tag>& tags, std::vector<Tag>& tags,
std::vector<Reference<StorageInfo>>& storageInfoItems) { std::vector<Reference<StorageInfo>>& storageInfoItems) {
for (const auto& id : uids) { for (const auto& id : uids) {
auto storageInfo = getStorageInfo(id, &pContext->pResolverData->storageCache, pContext->pTxnStateStore); auto storageInfo = getStorageInfo(id, &pContext->pResolverData->storageCache, pContext->pTxnStateStore);
ASSERT(storageInfo->tag != invalidTag); ASSERT(storageInfo->tag != invalidTag);
@ -632,6 +632,7 @@ ACTOR Future<Void> resolverCore(ResolverInterface resolver,
state Reference<Resolver> self(new Resolver(resolver.id(), initReq.commitProxyCount, initReq.resolverCount)); state Reference<Resolver> self(new Resolver(resolver.id(), initReq.commitProxyCount, initReq.resolverCount));
state ActorCollection actors(false); state ActorCollection actors(false);
state Future<Void> doPollMetrics = self->resolverCount > 1 ? Void() : Future<Void>(Never()); state Future<Void> doPollMetrics = self->resolverCount > 1 ? Void() : Future<Void>(Never());
state PromiseStream<Future<Void>> addActor;
actors.add(waitFailureServer(resolver.waitFailure.getFuture())); actors.add(waitFailureServer(resolver.waitFailure.getFuture()));
actors.add(traceRole(Role::RESOLVER, resolver.id())); actors.add(traceRole(Role::RESOLVER, resolver.id()));
@ -647,7 +648,6 @@ ACTOR Future<Void> resolverCore(ResolverInterface resolver,
// Initialize txnStateStore // Initialize txnStateStore
self->logSystem = ILogSystem::fromServerDBInfo(resolver.id(), db->get(), false, addActor); self->logSystem = ILogSystem::fromServerDBInfo(resolver.id(), db->get(), false, addActor);
self->localTLogCount = db->get().logSystemConfig.numLogs(); self->localTLogCount = db->get().logSystemConfig.numLogs();
state PromiseStream<Future<Void>> addActor;
state Future<Void> onError = state Future<Void> onError =
transformError(actorCollection(addActor.getFuture()), broken_promise(), resolver_failed()); transformError(actorCollection(addActor.getFuture()), broken_promise(), resolver_failed());
state TransactionStateResolveContext transactionStateResolveContext; state TransactionStateResolveContext transactionStateResolveContext;

View File

@ -38,33 +38,33 @@
// RestoreCommon.actor.cpp // RestoreCommon.actor.cpp
KeyBackedProperty<ERestoreState> RestoreConfigFR::stateEnum() { KeyBackedProperty<ERestoreState> RestoreConfigFR::stateEnum() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
Future<StringRef> RestoreConfigFR::stateText(Reference<ReadYourWritesTransaction> tr) { Future<StringRef> RestoreConfigFR::stateText(Reference<ReadYourWritesTransaction> tr) {
return map(stateEnum().getD(tr), [](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); }); return map(stateEnum().getD(tr), [](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); });
} }
KeyBackedProperty<Key> RestoreConfigFR::addPrefix() { KeyBackedProperty<Key> RestoreConfigFR::addPrefix() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
KeyBackedProperty<Key> RestoreConfigFR::removePrefix() { KeyBackedProperty<Key> RestoreConfigFR::removePrefix() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// XXX: Remove restoreRange() once it is safe to remove. It has been changed to restoreRanges // XXX: Remove restoreRange() once it is safe to remove. It has been changed to restoreRanges
KeyBackedProperty<KeyRange> RestoreConfigFR::restoreRange() { KeyBackedProperty<KeyRange> RestoreConfigFR::restoreRange() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
KeyBackedProperty<std::vector<KeyRange>> RestoreConfigFR::restoreRanges() { KeyBackedProperty<std::vector<KeyRange>> RestoreConfigFR::restoreRanges() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
KeyBackedProperty<Key> RestoreConfigFR::batchFuture() { KeyBackedProperty<Key> RestoreConfigFR::batchFuture() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
KeyBackedProperty<Version> RestoreConfigFR::restoreVersion() { KeyBackedProperty<Version> RestoreConfigFR::restoreVersion() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
KeyBackedProperty<Reference<IBackupContainer>> RestoreConfigFR::sourceContainer() { KeyBackedProperty<Reference<IBackupContainer>> RestoreConfigFR::sourceContainer() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// Get the source container as a bare URL, without creating a container instance // Get the source container as a bare URL, without creating a container instance
KeyBackedProperty<Value> RestoreConfigFR::sourceContainerURL() { KeyBackedProperty<Value> RestoreConfigFR::sourceContainerURL() {
@ -73,23 +73,23 @@ KeyBackedProperty<Value> RestoreConfigFR::sourceContainerURL() {
// Total bytes written by all log and range restore tasks. // Total bytes written by all log and range restore tasks.
KeyBackedBinaryValue<int64_t> RestoreConfigFR::bytesWritten() { KeyBackedBinaryValue<int64_t> RestoreConfigFR::bytesWritten() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// File blocks that have had tasks created for them by the Dispatch task // File blocks that have had tasks created for them by the Dispatch task
KeyBackedBinaryValue<int64_t> RestoreConfigFR::filesBlocksDispatched() { KeyBackedBinaryValue<int64_t> RestoreConfigFR::filesBlocksDispatched() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// File blocks whose tasks have finished // File blocks whose tasks have finished
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlocksFinished() { KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlocksFinished() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// Total number of files in the fileMap // Total number of files in the fileMap
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileCount() { KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileCount() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
// Total number of file blocks in the fileMap // Total number of file blocks in the fileMap
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlockCount() { KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlockCount() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
Future<std::vector<KeyRange>> RestoreConfigFR::getRestoreRangesOrDefault(Reference<ReadYourWritesTransaction> tr) { Future<std::vector<KeyRange>> RestoreConfigFR::getRestoreRangesOrDefault(Reference<ReadYourWritesTransaction> tr) {
@ -108,7 +108,7 @@ ACTOR Future<std::vector<KeyRange>> RestoreConfigFR::getRestoreRangesOrDefault_i
} }
KeyBackedSet<RestoreConfigFR::RestoreFile> RestoreConfigFR::fileSet() { KeyBackedSet<RestoreConfigFR::RestoreFile> RestoreConfigFR::fileSet() {
return configSpace.pack(LiteralStringRef(__FUNCTION__)); return configSpace.pack(__FUNCTION__sr);
} }
Future<bool> RestoreConfigFR::isRunnable(Reference<ReadYourWritesTransaction> tr) { Future<bool> RestoreConfigFR::isRunnable(Reference<ReadYourWritesTransaction> tr) {

View File

@ -526,8 +526,11 @@ ACTOR Future<Void> fetchCheckpointFile(Database cx,
} }
state int attempt = 0; state int attempt = 0;
state int64_t offset = 0;
state Reference<IAsyncFile> asyncFile;
loop { loop {
try { try {
asyncFile = Reference<IAsyncFile>();
++attempt; ++attempt;
TraceEvent("FetchCheckpointFileBegin") TraceEvent("FetchCheckpointFileBegin")
.detail("RemoteFile", remoteFile) .detail("RemoteFile", remoteFile)
@ -539,8 +542,7 @@ ACTOR Future<Void> fetchCheckpointFile(Database cx,
wait(IAsyncFileSystem::filesystem()->deleteFile(localFile, true)); wait(IAsyncFileSystem::filesystem()->deleteFile(localFile, true));
const int64_t flags = IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_READWRITE | const int64_t flags = IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_READWRITE |
IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_NO_AIO; IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_NO_AIO;
state int64_t offset = 0; wait(store(asyncFile, IAsyncFileSystem::filesystem()->open(localFile, flags, 0666)));
state Reference<IAsyncFile> asyncFile = wait(IAsyncFileSystem::filesystem()->open(localFile, flags, 0666));
state ReplyPromiseStream<FetchCheckpointReply> stream = state ReplyPromiseStream<FetchCheckpointReply> stream =
ssi.fetchCheckpoint.getReplyStream(FetchCheckpointRequest(metaData->checkpointID, remoteFile)); ssi.fetchCheckpoint.getReplyStream(FetchCheckpointRequest(metaData->checkpointID, remoteFile));

View File

@ -2437,6 +2437,14 @@ ACTOR void setupAndRun(std::string dataFolder,
g_simulator->injectSSDelayTime = 60.0 + 240.0 * deterministicRandom()->random01(); g_simulator->injectSSDelayTime = 60.0 + 240.0 * deterministicRandom()->random01();
} }
if (deterministicRandom()->random01() < 0.25) {
g_simulator->injectTargetedBMRestartTime = 60.0 + 340.0 * deterministicRandom()->random01();
}
if (deterministicRandom()->random01() < 0.25) {
g_simulator->injectTargetedBWRestartTime = 60.0 + 340.0 * deterministicRandom()->random01();
}
// Build simulator allow list // Build simulator allow list
allowList.addTrustedSubnet("0.0.0.0/2"sv); allowList.addTrustedSubnet("0.0.0.0/2"sv);
allowList.addTrustedSubnet("abcd::/16"sv); allowList.addTrustedSubnet("abcd::/16"sv);

View File

@ -19,15 +19,16 @@
*/ */
#include <cinttypes> #include <cinttypes>
#include "fdbclient/BackupAgent.actor.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fdbclient/BackupAgent.actor.h"
#include "fdbclient/BlobWorkerInterface.h" #include "fdbclient/BlobWorkerInterface.h"
#include "fdbclient/KeyBackedTypes.h" #include "fdbclient/KeyBackedTypes.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "flow/ITrace.h" #include "flow/ITrace.h"
#include "flow/ProtocolVersion.h" #include "flow/ProtocolVersion.h"
#include "flow/Trace.h" #include "flow/Trace.h"
#include "fdbclient/NativeAPI.actor.h" #include "fdbclient/NativeAPI.actor.h"
#include "fdbclient/Metacluster.h"
#include "fdbclient/SystemData.h" #include "fdbclient/SystemData.h"
#include "fdbclient/ReadYourWrites.h" #include "fdbclient/ReadYourWrites.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
@ -35,7 +36,7 @@
#include "fdbserver/ClusterRecovery.actor.h" #include "fdbserver/ClusterRecovery.actor.h"
#include "fdbserver/CoordinationInterface.h" #include "fdbserver/CoordinationInterface.h"
#include "fdbserver/DataDistribution.actor.h" #include "fdbserver/DataDistribution.actor.h"
#include "fdbclient/ConsistencyScanInterface.h" #include "fdbclient/ConsistencyScanInterface.actor.h"
#include "flow/UnitTest.h" #include "flow/UnitTest.h"
#include "fdbserver/QuietDatabase.h" #include "fdbserver/QuietDatabase.h"
#include "fdbserver/RecoveryState.h" #include "fdbserver/RecoveryState.h"
@ -2924,7 +2925,9 @@ ACTOR Future<StatusReply> clusterGetStatus(
ServerCoordinators coordinators, ServerCoordinators coordinators,
std::vector<NetworkAddress> incompatibleConnections, std::vector<NetworkAddress> incompatibleConnections,
Version datacenterVersionDifference, Version datacenterVersionDifference,
ConfigBroadcaster const* configBroadcaster) { ConfigBroadcaster const* configBroadcaster,
Optional<MetaclusterRegistrationEntry> metaclusterRegistration,
MetaclusterMetrics metaclusterMetrics) {
state double tStart = timer(); state double tStart = timer();
state JsonBuilderArray messages; state JsonBuilderArray messages;
@ -3066,6 +3069,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
state JsonBuilderObject qos; state JsonBuilderObject qos;
state JsonBuilderObject dataOverlay; state JsonBuilderObject dataOverlay;
state JsonBuilderObject tenants; state JsonBuilderObject tenants;
state JsonBuilderObject metacluster;
state JsonBuilderObject storageWiggler; state JsonBuilderObject storageWiggler;
state std::unordered_set<UID> wiggleServers; state std::unordered_set<UID> wiggleServers;
@ -3248,6 +3252,25 @@ ACTOR Future<StatusReply> clusterGetStatus(
if (!qos.empty()) if (!qos.empty())
statusObj["qos"] = qos; statusObj["qos"] = qos;
// Metacluster metadata
if (metaclusterRegistration.present()) {
metacluster["cluster_type"] = clusterTypeToString(metaclusterRegistration.get().clusterType);
metacluster["metacluster_name"] = metaclusterRegistration.get().metaclusterName;
metacluster["metacluster_id"] = metaclusterRegistration.get().metaclusterId.toString();
if (metaclusterRegistration.get().clusterType == ClusterType::METACLUSTER_DATA) {
metacluster["data_cluster_name"] = metaclusterRegistration.get().name;
metacluster["data_cluster_id"] = metaclusterRegistration.get().id.toString();
} else { // clusterType == ClusterType::METACLUSTER_MANAGEMENT
metacluster["num_data_clusters"] = metaclusterMetrics.numDataClusters;
tenants["num_tenants"] = metaclusterMetrics.numTenants;
tenants["tenant_group_capacity"] = metaclusterMetrics.tenantGroupCapacity;
tenants["tenant_groups_allocated"] = metaclusterMetrics.tenantGroupsAllocated;
}
} else {
metacluster["cluster_type"] = clusterTypeToString(ClusterType::STANDALONE);
}
statusObj["metacluster"] = metacluster;
if (!tenants.empty()) if (!tenants.empty())
statusObj["tenants"] = tenants; statusObj["tenants"] = tenants;

View File

@ -72,8 +72,7 @@ class TagThrottlerImpl {
} }
self->autoThrottlingEnabled = SERVER_KNOBS->AUTO_TAG_THROTTLING_ENABLED; self->autoThrottlingEnabled = SERVER_KNOBS->AUTO_TAG_THROTTLING_ENABLED;
if (!committed) if (!committed)
tr.set(tagThrottleAutoEnabledKey, tr.set(tagThrottleAutoEnabledKey, self->autoThrottlingEnabled ? "1"_sr : "0"_sr);
LiteralStringRef(self->autoThrottlingEnabled ? "1" : "0"));
} }
RkTagThrottleCollection updatedTagThrottles; RkTagThrottleCollection updatedTagThrottles;

View File

@ -18,37 +18,40 @@
* limitations under the License. * limitations under the License.
*/ */
#include "fmt/format.h" #include "fdbclient/CommitTransaction.h"
#include "fdbclient/FDBTypes.h" #include "fdbclient/FDBTypes.h"
#include "fdbclient/Tuple.h"
#include "fdbrpc/ContinuousSample.h"
#include "fdbrpc/simulator.h"
#include "fdbserver/DeltaTree.h"
#include "fdbserver/IKeyValueStore.h"
#include "fdbserver/IPager.h"
#include "fdbserver/Knobs.h" #include "fdbserver/Knobs.h"
#include "fdbserver/WorkerInterface.actor.h"
#include "flow/ActorCollection.h"
#include "flow/Error.h" #include "flow/Error.h"
#include "flow/FastRef.h"
#include "flow/flow.h"
#include "flow/genericactors.actor.h"
#include "flow/Histogram.h"
#include "flow/IAsyncFile.h"
#include "flow/IRandom.h" #include "flow/IRandom.h"
#include "flow/Knobs.h" #include "flow/Knobs.h"
#include "flow/ObjectSerializer.h" #include "flow/ObjectSerializer.h"
#include "flow/Trace.h"
#include "flow/flow.h"
#include "flow/Histogram.h"
#include <limits>
#include <random>
#include "fdbrpc/ContinuousSample.h"
#include "fdbrpc/simulator.h"
#include "fdbserver/IPager.h"
#include "fdbclient/Tuple.h"
#include "flow/serialize.h" #include "flow/serialize.h"
#include "flow/genericactors.actor.h" #include "flow/Trace.h"
#include "flow/UnitTest.h" #include "flow/UnitTest.h"
#include "flow/IAsyncFile.h" #include "fmt/format.h"
#include "flow/ActorCollection.h"
#include <boost/intrusive/list.hpp>
#include <cinttypes>
#include <limits>
#include <map> #include <map>
#include <random>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "fdbclient/CommitTransaction.h"
#include "fdbserver/IKeyValueStore.h"
#include "fdbserver/DeltaTree.h"
#include <string.h>
#include <cinttypes>
#include <boost/intrusive/list.hpp>
#include "flow/actorcompiler.h" // must be last include #include "flow/actorcompiler.h" // must be last include
#define REDWOOD_DEBUG 0 #define REDWOOD_DEBUG 0
@ -4202,7 +4205,6 @@ std::string toString(BTreeNodeLinkRef id) {
return std::string("BTreePageID") + toString(id.begin(), id.end()); return std::string("BTreePageID") + toString(id.begin(), id.end());
} }
#define STR(x) LiteralStringRef(x)
struct RedwoodRecordRef { struct RedwoodRecordRef {
typedef uint8_t byte; typedef uint8_t byte;
@ -4769,6 +4771,8 @@ struct DecodeBoundaryVerifier {
struct DecodeBoundaries { struct DecodeBoundaries {
Key lower; Key lower;
Key upper; Key upper;
unsigned int height;
Optional<int64_t> domainId;
bool empty() const { return lower.empty() && upper.empty(); } bool empty() const { return lower.empty() && upper.empty(); }
}; };
@ -4777,6 +4781,11 @@ struct DecodeBoundaryVerifier {
std::vector<Key> boundarySamples; std::vector<Key> boundarySamples;
int boundarySampleSize = 1000; int boundarySampleSize = 1000;
int boundaryPopulation = 0; int boundaryPopulation = 0;
Reference<IPageEncryptionKeyProvider> keyProvider;
// Sample rate of pages to be scanned to verify if all entries in the page meet domain prefix requirement.
double domainPrefixScanProbability = 0.01;
uint64_t domainPrefixScanCount = 0;
static DecodeBoundaryVerifier* getVerifier(std::string name) { static DecodeBoundaryVerifier* getVerifier(std::string name) {
static std::map<std::string, DecodeBoundaryVerifier> verifiers; static std::map<std::string, DecodeBoundaryVerifier> verifiers;
@ -4787,6 +4796,8 @@ struct DecodeBoundaryVerifier {
return nullptr; return nullptr;
} }
void setKeyProvider(Reference<IPageEncryptionKeyProvider> kp) { keyProvider = kp; }
void sampleBoundary(Key b) { void sampleBoundary(Key b) {
if (boundaryPopulation <= boundarySampleSize) { if (boundaryPopulation <= boundarySampleSize) {
boundarySamples.push_back(b); boundarySamples.push_back(b);
@ -4803,21 +4814,53 @@ struct DecodeBoundaryVerifier {
return boundarySamples[deterministicRandom()->randomInt(0, boundarySamples.size())]; return boundarySamples[deterministicRandom()->randomInt(0, boundarySamples.size())];
} }
void update(BTreeNodeLinkRef id, Version v, Key lowerBound, Key upperBound) { bool update(BTreeNodeLinkRef id,
Version v,
Key lowerBound,
Key upperBound,
unsigned int height,
Optional<int64_t> domainId) {
sampleBoundary(lowerBound); sampleBoundary(lowerBound);
sampleBoundary(upperBound); sampleBoundary(upperBound);
debug_printf("decodeBoundariesUpdate %s %s '%s' to '%s'\n", debug_printf("decodeBoundariesUpdate %s %s '%s' to '%s', %u, %s\n",
::toString(id).c_str(), ::toString(id).c_str(),
::toString(v).c_str(), ::toString(v).c_str(),
lowerBound.printable().c_str(), lowerBound.printable().c_str(),
upperBound.printable().c_str()); upperBound.printable().c_str(),
height,
Traceable<decltype(domainId)>::toString(domainId).c_str());
if (domainId.present()) {
ASSERT(keyProvider && keyProvider->enableEncryptionDomain());
// Temporarily disabling the check, since if a tenant is removed, where the key provider
// would not find the domain, the data for the tenant may still be in Redwood and being read.
// TODO(yiwu): re-enable the check.
/*
if (domainId.get() != keyProvider->getDefaultEncryptionDomainId() &&
!keyProvider->keyFitsInDomain(domainId.get(), lowerBound, false)) {
fprintf(stderr,
"Page lower bound not in domain: %s %s, domain id %s, lower bound '%s'\n",
::toString(id).c_str(),
::toString(v).c_str(),
::toString(domainId).c_str(),
lowerBound.printable().c_str());
return false;
}
*/
}
auto& b = boundariesByPageID[id.front()][v]; auto& b = boundariesByPageID[id.front()][v];
ASSERT(b.empty()); ASSERT(b.empty());
b = { lowerBound, upperBound }; b = { lowerBound, upperBound, height, domainId };
return true;
} }
bool verify(LogicalPageID id, Version v, Key lowerBound, Key upperBound) { bool verify(LogicalPageID id,
Version v,
Key lowerBound,
Key upperBound,
Optional<int64_t> domainId,
BTreePage::BinaryTree::Cursor& cursor) {
auto i = boundariesByPageID.find(id); auto i = boundariesByPageID.find(id);
ASSERT(i != boundariesByPageID.end()); ASSERT(i != boundariesByPageID.end());
ASSERT(!i->second.empty()); ASSERT(!i->second.empty());
@ -4836,10 +4879,66 @@ struct DecodeBoundaryVerifier {
b->second.upper.printable().c_str()); b->second.upper.printable().c_str());
return false; return false;
} }
if (!b->second.domainId.present()) {
ASSERT(!keyProvider || !keyProvider->enableEncryptionDomain());
ASSERT(!domainId.present());
} else {
ASSERT(keyProvider->enableEncryptionDomain());
if (b->second.domainId != domainId) {
fprintf(stderr,
"Page encrypted with incorrect domain: %s %s, using %s, written %s\n",
::toString(id).c_str(),
::toString(v).c_str(),
::toString(domainId).c_str(),
::toString(b->second.domainId).c_str());
return false;
}
// Temporarily disabling the check, since if a tenant is removed, where the key provider
// would not find the domain, the data for the tenant may still be in Redwood and being read.
// TODO(yiwu): re-enable the check.
/*
ASSERT(domainId.present());
auto checkKeyFitsInDomain = [&]() -> bool {
if (!keyProvider->keyFitsInDomain(domainId.get(), cursor.get().key, b->second.height > 1)) {
fprintf(stderr,
"Encryption domain mismatch on %s, %s, domain: %s, key %s\n",
::toString(id).c_str(),
::toString(v).c_str(),
::toString(domainId).c_str(),
cursor.get().key.printable().c_str());
return false;
}
return true;
};
if (domainId.get() != keyProvider->getDefaultEncryptionDomainId()) {
cursor.moveFirst();
if (cursor.valid() && !checkKeyFitsInDomain()) {
return false;
}
cursor.moveLast();
if (cursor.valid() && !checkKeyFitsInDomain()) {
return false;
}
} else {
if (deterministicRandom()->random01() < domainPrefixScanProbability) {
cursor.moveFirst();
while (cursor.valid()) {
if (!checkKeyFitsInDomain()) {
return false;
}
cursor.moveNext();
}
domainPrefixScanCount++;
}
}
*/
}
return true; return true;
} }
void update(Version v, LogicalPageID oldID, LogicalPageID newID) { void updatePageId(Version v, LogicalPageID oldID, LogicalPageID newID) {
auto& old = boundariesByPageID[oldID]; auto& old = boundariesByPageID[oldID];
ASSERT(!old.empty()); ASSERT(!old.empty());
auto i = old.end(); auto i = old.end();
@ -5054,6 +5153,10 @@ public:
} }
m_pBoundaryVerifier = DecodeBoundaryVerifier::getVerifier(name); m_pBoundaryVerifier = DecodeBoundaryVerifier::getVerifier(name);
if (m_pBoundaryVerifier != nullptr) {
m_pBoundaryVerifier->setKeyProvider(m_keyProvider);
}
m_pDecodeCacheMemory = m_pager->getPageCachePenaltySource(); m_pDecodeCacheMemory = m_pager->getPageCachePenaltySource();
m_lazyClearActor = 0; m_lazyClearActor = 0;
m_init = init_impl(this); m_init = init_impl(this);
@ -5575,16 +5678,27 @@ private:
// Describes a range of a vector of records that should be built into a single BTreePage // Describes a range of a vector of records that should be built into a single BTreePage
struct PageToBuild { struct PageToBuild {
PageToBuild(int index, int blockSize, EncodingType t) PageToBuild(int index,
int blockSize,
EncodingType encodingType,
unsigned int height,
bool useEncryptionDomain,
bool splitByDomain,
IPageEncryptionKeyProvider* keyProvider)
: startIndex(index), count(0), pageSize(blockSize), : startIndex(index), count(0), pageSize(blockSize),
largeDeltaTree(pageSize > BTreePage::BinaryTree::SmallSizeLimit), blockSize(blockSize), blockCount(1), largeDeltaTree(pageSize > BTreePage::BinaryTree::SmallSizeLimit), blockSize(blockSize), blockCount(1),
kvBytes(0) { kvBytes(0), encodingType(encodingType), height(height), useEncryptionDomain(useEncryptionDomain),
splitByDomain(splitByDomain), keyProvider(keyProvider) {
// Subtrace Page header overhead, BTreePage overhead, and DeltaTree (BTreePage::BinaryTree) overhead. // Subtrace Page header overhead, BTreePage overhead, and DeltaTree (BTreePage::BinaryTree) overhead.
bytesLeft = ArenaPage::getUsableSize(blockSize, t) - sizeof(BTreePage) - sizeof(BTreePage::BinaryTree); bytesLeft =
ArenaPage::getUsableSize(blockSize, encodingType) - sizeof(BTreePage) - sizeof(BTreePage::BinaryTree);
} }
PageToBuild next(EncodingType t) { return PageToBuild(endIndex(), blockSize, t); } PageToBuild next() {
return PageToBuild(
endIndex(), blockSize, encodingType, height, useEncryptionDomain, splitByDomain, keyProvider);
}
int startIndex; // Index of the first record int startIndex; // Index of the first record
int count; // Number of records added to the page int count; // Number of records added to the page
@ -5596,6 +5710,16 @@ private:
int blockCount; // The number of blocks in pageSize int blockCount; // The number of blocks in pageSize
int kvBytes; // The amount of user key/value bytes added to the page int kvBytes; // The amount of user key/value bytes added to the page
EncodingType encodingType;
unsigned int height;
bool useEncryptionDomain;
bool splitByDomain;
IPageEncryptionKeyProvider* keyProvider;
Optional<int64_t> domainId = Optional<int64_t>();
size_t domainPrefixLength = 0;
bool canUseDefaultDomain = false;
// Number of bytes used by the generated/serialized BTreePage, including all headers // Number of bytes used by the generated/serialized BTreePage, including all headers
int usedBytes() const { return pageSize - bytesLeft; } int usedBytes() const { return pageSize - bytesLeft; }
@ -5615,17 +5739,18 @@ private:
int endIndex() const { return startIndex + count; } int endIndex() const { return startIndex + count; }
std::string toString() const { std::string toString() const {
return format( return format("{start=%d count=%d used %d/%d bytes (%.2f%% slack) kvBytes=%d blocks=%d blockSize=%d "
"{start=%d count=%d used %d/%d bytes (%.2f%% slack) kvBytes=%d blocks=%d blockSize=%d large=%d}", "large=%d, domain=%s}",
startIndex, startIndex,
count, count,
usedBytes(), usedBytes(),
pageSize, pageSize,
slackFraction() * 100, slackFraction() * 100,
kvBytes, kvBytes,
blockCount, blockCount,
blockSize, blockSize,
largeDeltaTree); largeDeltaTree,
::toString(domainId).c_str());
} }
// Move an item from a to b if a has 2 or more items and the item fits in b // Move an item from a to b if a has 2 or more items and the item fits in b
@ -5657,7 +5782,8 @@ private:
// Try to add a record of the given delta size to the page. // Try to add a record of the given delta size to the page.
// If force is true, the page will be expanded to make the record fit if needed. // If force is true, the page will be expanded to make the record fit if needed.
// Return value is whether or not the record was added to the page. // Return value is whether or not the record was added to the page.
bool addRecord(const RedwoodRecordRef& rec, int deltaSize, bool force) { bool addRecord(const RedwoodRecordRef& rec, const RedwoodRecordRef& nextRecord, int deltaSize, bool force) {
int nodeSize = deltaSize + BTreePage::BinaryTree::Node::headerSize(largeDeltaTree); int nodeSize = deltaSize + BTreePage::BinaryTree::Node::headerSize(largeDeltaTree);
// If the record doesn't fit and the page can't be expanded then return false // If the record doesn't fit and the page can't be expanded then return false
@ -5665,6 +5791,53 @@ private:
return false; return false;
} }
if (useEncryptionDomain) {
int64_t defaultDomainId = keyProvider->getDefaultEncryptionDomainId();
int64_t currentDomainId;
size_t prefixLength;
if (count == 0 || (splitByDomain && count > 0)) {
std::tie(currentDomainId, prefixLength) = keyProvider->getEncryptionDomain(rec.key, domainId);
}
if (count == 0) {
domainId = currentDomainId;
domainPrefixLength = prefixLength;
canUseDefaultDomain =
(height > 1 && (currentDomainId == defaultDomainId || prefixLength == rec.key.size()));
} else if (splitByDomain) {
ASSERT(domainId.present());
if (domainId == currentDomainId) {
// The new record falls in the same domain as the rest of the page.
// Since this is not the first record, the key must contain a non-prefix portion,
// so we cannot use the default domain the encrypt the page (unless domainId is the default
// domain).
if (domainId != defaultDomainId) {
ASSERT(prefixLength < rec.key.size());
canUseDefaultDomain = false;
}
} else if (canUseDefaultDomain &&
(currentDomainId == defaultDomainId ||
(prefixLength == rec.key.size() &&
(nextRecord.key.empty() ||
!nextRecord.key.startsWith(rec.key.substr(0, prefixLength)))))) {
// The new record meets one of the following conditions:
// 1. it falls in the default domain, or
// 2. its key contain only the domain prefix, and
// 2a. the following record doesn't fall in the same domain.
// In this case switch to use the default domain to encrypt the page.
// Condition 2a is needed, because if there are multiple records from the same domain,
// they need to form their own page(s).
domainId = defaultDomainId;
domainPrefixLength = 0;
} else {
// The new record doesn't fit in the same domain as the existing page.
return false;
}
} else {
ASSERT(domainPrefixLength < rec.key.size());
canUseDefaultDomain = false;
}
}
++count; ++count;
bytesLeft -= nodeSize; bytesLeft -= nodeSize;
kvBytes += rec.kvBytes(); kvBytes += rec.kvBytes();
@ -5687,6 +5860,12 @@ private:
} }
return true; return true;
} }
void finish() {
if (useEncryptionDomain && canUseDefaultDomain) {
domainId = keyProvider->getDefaultEncryptionDomainId();
}
}
}; };
// Scans a vector of records and decides on page split points, returning a vector of 1+ pages to build // Scans a vector of records and decides on page split points, returning a vector of 1+ pages to build
@ -5706,8 +5885,25 @@ private:
// Leaves can have just one record if it's large, but internal pages should have at least 4 // Leaves can have just one record if it's large, but internal pages should have at least 4
int minRecords = height == 1 ? 1 : 4; int minRecords = height == 1 ? 1 : 4;
double maxSlack = SERVER_KNOBS->REDWOOD_PAGE_REBUILD_MAX_SLACK; double maxSlack = SERVER_KNOBS->REDWOOD_PAGE_REBUILD_MAX_SLACK;
RedwoodRecordRef emptyRecord;
std::vector<PageToBuild> pages; std::vector<PageToBuild> pages;
// Whether encryption is used and we need to set encryption domain for a page.
bool useEncryptionDomain =
ArenaPage::isEncodingTypeEncrypted(m_encodingType) && m_keyProvider->enableEncryptionDomain();
// Whether we may need to split by encryption domain. It is mean to be an optimization to avoid
// unnecessary domain check and may not be exhaust all cases.
bool splitByDomain = false;
if (useEncryptionDomain && records.size() > 1) {
int64_t firstDomain = std::get<0>(m_keyProvider->getEncryptionDomain(records[0].key));
int64_t lastDomain = std::get<0>(m_keyProvider->getEncryptionDomain(records[records.size() - 1].key));
// If the two record falls in the same non-default domain, we know all the records fall in the
// same domain. Otherwise we may need to split pages by domain.
if (firstDomain != lastDomain || firstDomain == m_keyProvider->getDefaultEncryptionDomainId()) {
splitByDomain = true;
}
}
// deltaSizes contains pair-wise delta sizes for [lowerBound, records..., upperBound] // deltaSizes contains pair-wise delta sizes for [lowerBound, records..., upperBound]
std::vector<int> deltaSizes(records.size() + 1); std::vector<int> deltaSizes(records.size() + 1);
deltaSizes.front() = records.front().deltaSize(*lowerBound, prefixLen, true); deltaSizes.front() = records.front().deltaSize(*lowerBound, prefixLen, true);
@ -5716,28 +5912,34 @@ private:
deltaSizes[i] = records[i].deltaSize(records[i - 1], prefixLen, true); deltaSizes[i] = records[i].deltaSize(records[i - 1], prefixLen, true);
} }
PageToBuild p(0, m_blockSize, m_encodingType); PageToBuild p(
0, m_blockSize, m_encodingType, height, useEncryptionDomain, splitByDomain, m_keyProvider.getPtr());
for (int i = 0; i < records.size(); ++i) { for (int i = 0; i < records.size();) {
bool force = p.count < minRecords || p.slackFraction() > maxSlack; bool force = p.count < minRecords || p.slackFraction() > maxSlack;
debug_printf( if (i == 0 || p.count > 0) {
" before addRecord i=%d records=%d deltaSize=%d kvSize=%d force=%d pageToBuild=%s record=%s", debug_printf(" before addRecord i=%d records=%d deltaSize=%d kvSize=%d force=%d pageToBuild=%s "
i, "record=%s",
records.size(), i,
deltaSizes[i], records.size(),
records[i].kvBytes(), deltaSizes[i],
force, records[i].kvBytes(),
p.toString().c_str(), force,
records[i].toString(height == 1).c_str()); p.toString().c_str(),
records[i].toString(height == 1).c_str());
}
if (!p.addRecord(records[i], deltaSizes[i], force)) { if (!p.addRecord(records[i], i + 1 < records.size() ? records[i + 1] : emptyRecord, deltaSizes[i], force)) {
p.finish();
pages.push_back(p); pages.push_back(p);
p = p.next(m_encodingType); p = p.next();
p.addRecord(records[i], deltaSizes[i], true); } else {
i++;
} }
} }
if (p.count > 0) { if (p.count > 0) {
p.finish();
pages.push_back(p); pages.push_back(p);
} }
@ -5750,15 +5952,20 @@ private:
PageToBuild& a = pages[pages.size() - 2]; PageToBuild& a = pages[pages.size() - 2];
PageToBuild& b = pages.back(); PageToBuild& b = pages.back();
// While the last page page has too much slack and the second to last page // We can rebalance the two pages only if they are in the same encryption domain.
// has more than the minimum record count, shift a record from the second ASSERT(!useEncryptionDomain || (a.domainId.present() && b.domainId.present()));
// to last page to the last page. if (!useEncryptionDomain || a.domainId.get() == b.domainId.get()) {
while (b.slackFraction() > maxSlack && a.count > minRecords) {
int i = a.lastIndex(); // While the last page page has too much slack and the second to last page
if (!PageToBuild::shiftItem(a, b, deltaSizes[i], records[i].kvBytes())) { // has more than the minimum record count, shift a record from the second
break; // to last page to the last page.
while (b.slackFraction() > maxSlack && a.count > minRecords) {
int i = a.lastIndex();
if (!PageToBuild::shiftItem(a, b, deltaSizes[i], records[i].kvBytes())) {
break;
}
debug_printf(" After shifting i=%d: a=%s b=%s\n", i, a.toString().c_str(), b.toString().c_str());
} }
debug_printf(" After shifting i=%d: a=%s b=%s\n", i, a.toString().c_str(), b.toString().c_str());
} }
} }
@ -5781,8 +5988,13 @@ private:
// All records share the prefix shared by the lower and upper boundaries // All records share the prefix shared by the lower and upper boundaries
state int prefixLen = lowerBound->getCommonPrefixLen(*upperBound); state int prefixLen = lowerBound->getCommonPrefixLen(*upperBound);
// Whether encryption is used and we need to set encryption domain for a page.
state bool useEncryptionDomain =
ArenaPage::isEncodingTypeEncrypted(self->m_encodingType) && self->m_keyProvider->enableEncryptionDomain();
state std::vector<PageToBuild> pagesToBuild = state std::vector<PageToBuild> pagesToBuild =
self->splitPages(lowerBound, upperBound, prefixLen, entries, height); self->splitPages(lowerBound, upperBound, prefixLen, entries, height);
ASSERT(pagesToBuild.size() > 0);
debug_printf("splitPages returning %s\n", toString(pagesToBuild).c_str()); debug_printf("splitPages returning %s\n", toString(pagesToBuild).c_str());
// Lower bound of the page being added to // Lower bound of the page being added to
@ -5792,6 +6004,18 @@ private:
state int pageIndex; state int pageIndex;
if (useEncryptionDomain) {
ASSERT(pagesToBuild[0].domainId.present());
int64_t domainId = pagesToBuild[0].domainId.get();
// We need to make sure we use the domain prefix as the page lower bound, for the first page
// of a non-default domain on a level. That way we ensure that pages for a domain form a full subtree
// (i.e. have a single root) in the B-tree.
if (domainId != self->m_keyProvider->getDefaultEncryptionDomainId() &&
!self->m_keyProvider->keyFitsInDomain(domainId, pageLowerBound.key, false)) {
pageLowerBound = RedwoodRecordRef(entries[0].key.substr(0, pagesToBuild[0].domainPrefixLength));
}
}
for (pageIndex = 0; pageIndex < pagesToBuild.size(); ++pageIndex) { for (pageIndex = 0; pageIndex < pagesToBuild.size(); ++pageIndex) {
debug_printf("building page %d of %zu %s\n", debug_printf("building page %d of %zu %s\n",
pageIndex + 1, pageIndex + 1,
@ -5799,6 +6023,30 @@ private:
pagesToBuild[pageIndex].toString().c_str()); pagesToBuild[pageIndex].toString().c_str());
ASSERT(pagesToBuild[pageIndex].count != 0); ASSERT(pagesToBuild[pageIndex].count != 0);
// Use the next entry as the upper bound, or upperBound if there are no more entries beyond this page
int endIndex = pagesToBuild[pageIndex].endIndex();
bool lastPage = endIndex == entries.size();
pageUpperBound = lastPage ? upperBound->withoutValue() : entries[endIndex].withoutValue();
if (!lastPage) {
PageToBuild& p = pagesToBuild[pageIndex];
PageToBuild& nextPage = pagesToBuild[pageIndex + 1];
if (height == 1) {
// If this is a leaf page, and not the last one to be written, shorten the upper boundary)
int commonPrefix = pageUpperBound.getCommonPrefixLen(entries[endIndex - 1], prefixLen);
pageUpperBound.truncate(commonPrefix + 1);
}
if (useEncryptionDomain) {
ASSERT(p.domainId.present());
ASSERT(nextPage.domainId.present());
if (p.domainId.get() != nextPage.domainId.get() &&
nextPage.domainId.get() != self->m_keyProvider->getDefaultEncryptionDomainId()) {
pageUpperBound =
RedwoodRecordRef(entries[nextPage.startIndex].key.substr(0, nextPage.domainPrefixLength));
}
}
}
// For internal pages, skip first entry if child link is null. Such links only exist // For internal pages, skip first entry if child link is null. Such links only exist
// to maintain a borrow-able prefix for the previous subtree after a subtree deletion. // to maintain a borrow-able prefix for the previous subtree after a subtree deletion.
// If the null link falls on a new page post-split, then the pageLowerBound of the page // If the null link falls on a new page post-split, then the pageLowerBound of the page
@ -5812,38 +6060,34 @@ private:
--p.count; --p.count;
debug_printf("Skipping first null record, new count=%d\n", p.count); debug_printf("Skipping first null record, new count=%d\n", p.count);
// If the page is now empty then it must be the last page in pagesToBuild, otherwise there would // In case encryption or encryption domain is not enabled, if the page is now empty then it must be the
// be more than 1 item since internal pages need to have multiple children. While there is no page // last page in pagesToBuild, otherwise there would be more than 1 item since internal pages need to
// to be built here, a record must be added to the output set because the upper boundary of the last // have multiple children. In case encryption and encryption domain is enabled, however, because of the
// page split by encryption domain, it may not be the last page.
//
// Either way, a record must be added to the output set because the upper boundary of the last
// page built does not match the upper boundary of the original page that this call to writePages() is // page built does not match the upper boundary of the original page that this call to writePages() is
// replacing. Put another way, the upper boundary of the rightmost page of the page set that was just // replacing. Put another way, the upper boundary of the rightmost page of the page set that was just
// built does not match the upper boundary of the original page that the page set is replacing, so // built does not match the upper boundary of the original page that the page set is replacing, so
// adding the extra null link fixes this. // adding the extra null link fixes this.
if (p.count == 0) { if (p.count == 0) {
ASSERT(pageIndex == pagesToBuild.size() - 1); ASSERT(useEncryptionDomain || lastPage);
records.push_back_deep(records.arena(), pageUpperBound); records.push_back_deep(records.arena(), pageLowerBound);
break; pageLowerBound = pageUpperBound;
continue;
} }
} }
// Use the next entry as the upper bound, or upperBound if there are no more entries beyond this page
int endIndex = pagesToBuild[pageIndex].endIndex();
bool lastPage = endIndex == entries.size();
pageUpperBound = lastPage ? upperBound->withoutValue() : entries[endIndex].withoutValue();
// If this is a leaf page, and not the last one to be written, shorten the upper boundary
if (!lastPage && height == 1) {
int commonPrefix = pageUpperBound.getCommonPrefixLen(entries[endIndex - 1], prefixLen);
pageUpperBound.truncate(commonPrefix + 1);
}
// Create and init page here otherwise many variables must become state vars // Create and init page here otherwise many variables must become state vars
state Reference<ArenaPage> page = self->m_pager->newPageBuffer(pagesToBuild[pageIndex].blockCount); state Reference<ArenaPage> page = self->m_pager->newPageBuffer(pagesToBuild[pageIndex].blockCount);
page->init(self->m_encodingType, page->init(self->m_encodingType,
(pagesToBuild[pageIndex].blockCount == 1) ? PageType::BTreeNode : PageType::BTreeSuperNode, (pagesToBuild[pageIndex].blockCount == 1) ? PageType::BTreeNode : PageType::BTreeSuperNode,
height); height);
if (page->isEncrypted()) { if (page->isEncrypted()) {
ArenaPage::EncryptionKey k = wait(self->m_keyProvider->getLatestDefaultEncryptionKey()); ArenaPage::EncryptionKey k =
wait(useEncryptionDomain
? self->m_keyProvider->getLatestEncryptionKey(pagesToBuild[pageIndex].domainId.get())
: self->m_keyProvider->getLatestDefaultEncryptionKey());
page->encryptionKey = k; page->encryptionKey = k;
} }
@ -5936,7 +6180,8 @@ private:
} }
if (self->m_pBoundaryVerifier != nullptr) { if (self->m_pBoundaryVerifier != nullptr) {
self->m_pBoundaryVerifier->update(childPageID, v, pageLowerBound.key, pageUpperBound.key); ASSERT(self->m_pBoundaryVerifier->update(
childPageID, v, pageLowerBound.key, pageUpperBound.key, height, pagesToBuild[pageIndex].domainId));
} }
if (++sinceYield > 100) { if (++sinceYield > 100) {
@ -5985,9 +6230,26 @@ private:
// commit record, build a new root page and update records to be a link to that new page. // commit record, build a new root page and update records to be a link to that new page.
// Root pointer size is limited because the pager commit header is limited to smallestPhysicalBlock in // Root pointer size is limited because the pager commit header is limited to smallestPhysicalBlock in
// size. // size.
//
// There's another case. When encryption domain is enabled, we want to make sure the root node is encrypted
// using the default encryption domain. An indication that's not true is when the first record is not using
// dbBegin as key.
while (records.size() > 1 || while (records.size() > 1 ||
records.front().getChildPage().size() > (BUGGIFY ? 1 : BTreeCommitHeader::maxRootPointerSize)) { records.front().getChildPage().size() > (BUGGIFY ? 1 : BTreeCommitHeader::maxRootPointerSize) ||
records[0].key != dbBegin.key) {
CODE_PROBE(records.size() == 1, "Writing a new root because the current root pointer would be too large"); CODE_PROBE(records.size() == 1, "Writing a new root because the current root pointer would be too large");
if (records[0].key != dbBegin.key) {
ASSERT(self->m_keyProvider.isValid() && self->m_keyProvider->enableEncryption() &&
self->m_keyProvider->enableEncryptionDomain());
int64_t domainId;
size_t prefixLength;
std::tie(domainId, prefixLength) = self->m_keyProvider->getEncryptionDomain(records[0].key);
ASSERT(domainId != self->m_keyProvider->getDefaultEncryptionDomainId());
ASSERT(records[0].key.size() == prefixLength);
CODE_PROBE(true,
"Writing a new root because the current root is encrypted with non-default encryption "
"domain cipher key");
}
self->m_header.height = ++height; self->m_header.height = ++height;
ASSERT(height < std::numeric_limits<int8_t>::max()); ASSERT(height < std::numeric_limits<int8_t>::max());
Standalone<VectorRef<RedwoodRecordRef>> newRecords = wait( Standalone<VectorRef<RedwoodRecordRef>> newRecords = wait(
@ -6166,7 +6428,7 @@ private:
self->m_pager->updatePage(PagerEventReasons::Commit, height, newID, page); self->m_pager->updatePage(PagerEventReasons::Commit, height, newID, page);
if (self->m_pBoundaryVerifier != nullptr) { if (self->m_pBoundaryVerifier != nullptr) {
self->m_pBoundaryVerifier->update(writeVersion, oldID.front(), newID.front()); self->m_pBoundaryVerifier->updatePageId(writeVersion, oldID.front(), newID.front());
} }
self->freeBTreePage(height, oldID, writeVersion); self->freeBTreePage(height, oldID, writeVersion);
@ -6328,8 +6590,14 @@ private:
struct InternalPageModifier { struct InternalPageModifier {
InternalPageModifier() {} InternalPageModifier() {}
InternalPageModifier(Reference<const ArenaPage> p, bool alreadyCloned, bool updating, ParentInfo* parentInfo) InternalPageModifier(Reference<const ArenaPage> p,
: updating(updating), page(p), clonedPage(alreadyCloned), changesMade(false), parentInfo(parentInfo) {} bool alreadyCloned,
bool updating,
ParentInfo* parentInfo,
Reference<IPageEncryptionKeyProvider> keyProvider,
Optional<int64_t> pageDomainId)
: updating(updating), page(p), clonedPage(alreadyCloned), changesMade(false), parentInfo(parentInfo),
keyProvider(keyProvider), pageDomainId(pageDomainId) {}
// Whether updating the existing page is allowed // Whether updating the existing page is allowed
bool updating; bool updating;
@ -6344,6 +6612,9 @@ private:
bool changesMade; bool changesMade;
ParentInfo* parentInfo; ParentInfo* parentInfo;
Reference<IPageEncryptionKeyProvider> keyProvider;
Optional<int64_t> pageDomainId;
BTreePage* btPage() const { return (BTreePage*)page->mutateData(); } BTreePage* btPage() const { return (BTreePage*)page->mutateData(); }
bool empty() const { bool empty() const {
@ -6366,6 +6637,7 @@ private:
void insert(BTreePage::BinaryTree::Cursor end, const VectorRef<RedwoodRecordRef>& recs) { void insert(BTreePage::BinaryTree::Cursor end, const VectorRef<RedwoodRecordRef>& recs) {
int i = 0; int i = 0;
if (updating) { if (updating) {
cloneForUpdate();
// Update must be done in the new tree, not the original tree where the end cursor will be from // Update must be done in the new tree, not the original tree where the end cursor will be from
end.switchTree(btPage()->tree()); end.switchTree(btPage()->tree());
@ -6374,7 +6646,18 @@ private:
const RedwoodRecordRef& rec = recs[i]; const RedwoodRecordRef& rec = recs[i];
debug_printf("internal page (updating) insert: %s\n", rec.toString(false).c_str()); debug_printf("internal page (updating) insert: %s\n", rec.toString(false).c_str());
if (!end.insert(rec)) { // Fail if the inserted record does not belong to the same encryption domain as the existing page
// data.
bool canInsert = true;
if (page->isEncrypted() && keyProvider->enableEncryptionDomain()) {
ASSERT(keyProvider && pageDomainId.present());
canInsert = keyProvider->keyFitsInDomain(pageDomainId.get(), rec.key, true);
}
if (canInsert) {
canInsert = end.insert(rec);
}
if (!canInsert) {
debug_printf("internal page: failed to insert %s, switching to rebuild\n", debug_printf("internal page: failed to insert %s, switching to rebuild\n",
rec.toString(false).c_str()); rec.toString(false).c_str());
@ -6433,14 +6716,11 @@ private:
// If the children changed, replace [cBegin, cEnd) with newLinks // If the children changed, replace [cBegin, cEnd) with newLinks
if (u.childrenChanged) { if (u.childrenChanged) {
cloneForUpdate();
if (updating) { if (updating) {
auto c = u.cBegin; auto c = u.cBegin;
// must point c to the tree to erase from
if (c != u.cEnd) { c.switchTree(btPage()->tree());
cloneForUpdate();
// must point c to the tree to erase from
c.switchTree(btPage()->tree());
}
while (c != u.cEnd) { while (c != u.cEnd) {
debug_printf("applyUpdate (updating) erasing: %s\n", c.get().toString(false).c_str()); debug_printf("applyUpdate (updating) erasing: %s\n", c.get().toString(false).c_str());
@ -6490,6 +6770,55 @@ private:
} }
}; };
ACTOR static Future<Void> buildNewSubtree(VersionedBTree* self,
Version version,
LogicalPageID parentID,
unsigned int height,
MutationBuffer::const_iterator mBegin,
MutationBuffer::const_iterator mEnd,
InternalPageSliceUpdate* update) {
ASSERT(height > 1);
debug_printf(
"buildNewSubtree start version %" PRId64 ", height %u, %s\n'", version, height, update->toString().c_str());
state Standalone<VectorRef<RedwoodRecordRef>> records;
while (mBegin != mEnd && mBegin.key() < update->subtreeLowerBound.key) {
++mBegin;
}
while (mBegin != mEnd) {
if (mBegin.mutation().boundarySet()) {
RedwoodRecordRef rec(mBegin.key(), mBegin.mutation().boundaryValue.get());
records.push_back_deep(records.arena(), rec);
if (REDWOOD_DEBUG) {
debug_printf(" Added %s", rec.toString().c_str());
}
}
++mBegin;
}
if (records.empty()) {
update->cleared();
} else {
state unsigned int h = 1;
debug_printf("buildNewSubtree at level %u\n", h);
while (h < height) {
// Only the parentID at the root is known as we are building the subtree bottom-up.
// We use the parentID for all levels, since the parentID is currently used for
// debug use only.
Standalone<VectorRef<RedwoodRecordRef>> newRecords = wait(writePages(self,
&update->subtreeLowerBound,
&update->subtreeUpperBound,
records,
h,
version,
BTreeNodeLinkRef(),
parentID));
records = newRecords;
h++;
}
update->rebuilt(records);
}
return Void();
}
ACTOR static Future<Void> commitSubtree( ACTOR static Future<Void> commitSubtree(
VersionedBTree* self, VersionedBTree* self,
CommitBatch* batch, CommitBatch* batch,
@ -6546,6 +6875,12 @@ private:
// TryToUpdate indicates insert and erase operations should be tried on the existing page first // TryToUpdate indicates insert and erase operations should be tried on the existing page first
state bool tryToUpdate = btPage->tree()->numItems > 0 && update->boundariesNormal(); state bool tryToUpdate = btPage->tree()->numItems > 0 && update->boundariesNormal();
state bool useEncryptionDomain = page->isEncrypted() && self->m_keyProvider->enableEncryptionDomain();
state Optional<int64_t> pageDomainId;
if (useEncryptionDomain) {
pageDomainId = page->getEncryptionDomainId();
}
debug_printf("%s tryToUpdate=%d\n", context.c_str(), tryToUpdate); debug_printf("%s tryToUpdate=%d\n", context.c_str(), tryToUpdate);
debug_print(addPrefix(context, debug_print(addPrefix(context,
btPage->toString("commitSubtreeStart", btPage->toString("commitSubtreeStart",
@ -6563,7 +6898,9 @@ private:
ASSERT(self->m_pBoundaryVerifier->verify(rootID.front(), ASSERT(self->m_pBoundaryVerifier->verify(rootID.front(),
batch->snapshot->getVersion(), batch->snapshot->getVersion(),
update->cBegin.get().key, update->cBegin.get().key,
update->cBegin.next().getOrUpperBound().key)); update->cBegin.next().getOrUpperBound().key,
pageDomainId,
cursor));
} }
} }
@ -6662,8 +6999,16 @@ private:
// If updating, first try to add the record to the page // If updating, first try to add the record to the page
if (updatingDeltaTree) { if (updatingDeltaTree) {
copyForUpdate(); bool canInsert = true;
if (cursor.insert(rec, update->skipLen, maxHeightAllowed)) { if (useEncryptionDomain) {
ASSERT(pageDomainId.present());
canInsert = self->m_keyProvider->keyFitsInDomain(pageDomainId.get(), rec.key, false);
}
if (canInsert) {
copyForUpdate();
canInsert = cursor.insert(rec, update->skipLen, maxHeightAllowed);
}
if (canInsert) {
btPage->kvBytes += rec.kvBytes(); btPage->kvBytes += rec.kvBytes();
debug_printf("%s Inserted %s [mutation, boundary start]\n", debug_printf("%s Inserted %s [mutation, boundary start]\n",
context.c_str(), context.c_str(),
@ -6882,6 +7227,25 @@ private:
bool first = true; bool first = true;
if (useEncryptionDomain && cursor.valid() && update->subtreeLowerBound.key < cursor.get().key) {
mEnd = batch->mutations->lower_bound(cursor.get().key);
first = false;
if (mBegin != mEnd) {
slices.emplace_back(new InternalPageSliceUpdate());
InternalPageSliceUpdate& u = *slices.back();
u.cBegin = cursor;
u.cEnd = cursor;
u.subtreeLowerBound = update->subtreeLowerBound;
u.decodeLowerBound = u.subtreeLowerBound;
u.subtreeUpperBound = cursor.get();
u.decodeUpperBound = u.subtreeUpperBound;
u.expectedUpperBound = u.subtreeUpperBound;
u.skipLen = 0;
recursions.push_back(
self->buildNewSubtree(self, batch->writeVersion, parentID, height, mBegin, mEnd, &u));
}
}
while (cursor.valid()) { while (cursor.valid()) {
slices.emplace_back(new InternalPageSliceUpdate()); slices.emplace_back(new InternalPageSliceUpdate());
InternalPageSliceUpdate& u = *slices.back(); InternalPageSliceUpdate& u = *slices.back();
@ -7064,7 +7428,8 @@ private:
// which to build new page(s) if modification is not possible or not allowed. // which to build new page(s) if modification is not possible or not allowed.
// If pageCopy is already set it was initialized to page above so the modifier doesn't need // If pageCopy is already set it was initialized to page above so the modifier doesn't need
// to copy it // to copy it
state InternalPageModifier modifier(page, pageCopy.isValid(), tryToUpdate, parentInfo); state InternalPageModifier modifier(
page, pageCopy.isValid(), tryToUpdate, parentInfo, self->m_keyProvider, pageDomainId);
// Apply the possible changes for each subtree range recursed to, except the last one. // Apply the possible changes for each subtree range recursed to, except the last one.
// For each range, the expected next record, if any, is checked against the first boundary // For each range, the expected next record, if any, is checked against the first boundary
@ -7083,8 +7448,11 @@ private:
modifier.changesMade); modifier.changesMade);
debug_print(addPrefix(context, update->toString())); debug_print(addPrefix(context, update->toString()));
// TODO(yiwu): check whether we can pass decodeUpperBound as nextBoundary when the last slice
// have childenChanged=true.
modifier.applyUpdate(*slices.back(), modifier.applyUpdate(*slices.back(),
modifier.changesMade ? &update->subtreeUpperBound : &update->decodeUpperBound); modifier.changesMade || slices.back()->childrenChanged ? &update->subtreeUpperBound
: &update->decodeUpperBound);
state bool detachChildren = (parentInfo->count > 2); state bool detachChildren = (parentInfo->count > 2);
state bool forceUpdate = false; state bool forceUpdate = false;
@ -7147,7 +7515,8 @@ private:
if (newID != invalidPhysicalPageID) { if (newID != invalidPhysicalPageID) {
debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID); debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID);
if (self->m_pBoundaryVerifier != nullptr) { if (self->m_pBoundaryVerifier != nullptr) {
self->m_pBoundaryVerifier->update(batch->writeVersion, p, newID); self->m_pBoundaryVerifier->updatePageId(
batch->writeVersion, p, newID);
} }
p = newID; p = newID;
++stats.metrics.detachChild; ++stats.metrics.detachChild;
@ -7213,7 +7582,8 @@ private:
rec.setChildPage(newPages); rec.setChildPage(newPages);
debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID); debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID);
if (self->m_pBoundaryVerifier != nullptr) { if (self->m_pBoundaryVerifier != nullptr) {
self->m_pBoundaryVerifier->update(batch->writeVersion, p, newID); self->m_pBoundaryVerifier->updatePageId(
batch->writeVersion, p, newID);
} }
++stats.metrics.detachChild; ++stats.metrics.detachChild;
} }
@ -7223,7 +7593,6 @@ private:
} }
parentInfo->clear(); parentInfo->clear();
} }
Standalone<VectorRef<RedwoodRecordRef>> newChildEntries = Standalone<VectorRef<RedwoodRecordRef>> newChildEntries =
wait(writePages(self, wait(writePages(self,
&update->subtreeLowerBound, &update->subtreeLowerBound,
@ -7431,17 +7800,24 @@ public:
false, false,
!options.present() || options.get().cacheResult || path.back().btPage()->height != 2), !options.present() || options.get().cacheResult || path.back().btPage()->height != 2),
[=](Reference<const ArenaPage> p) { [=](Reference<const ArenaPage> p) {
BTreePage::BinaryTree::Cursor cursor = btree->getCursor(p.getPtr(), link);
#if REDWOOD_DEBUG #if REDWOOD_DEBUG
path.push_back({ p, btree->getCursor(p.getPtr(), link), link.get().getChildPage() }); path.push_back({ p, cursor, link.get().getChildPage() });
#else #else
path.push_back({ p, btree->getCursor(p.getPtr(), link) }); path.push_back({ p, cursor });
#endif #endif
if (btree->m_pBoundaryVerifier != nullptr) { if (btree->m_pBoundaryVerifier != nullptr) {
Optional<int64_t> domainId;
if (p->isEncrypted() && btree->m_keyProvider->enableEncryptionDomain()) {
domainId = p->getEncryptionDomainId();
}
ASSERT(btree->m_pBoundaryVerifier->verify(link.get().getChildPage().front(), ASSERT(btree->m_pBoundaryVerifier->verify(link.get().getChildPage().front(),
pager->getVersion(), pager->getVersion(),
link.get().key, link.get().key,
link.next().getOrUpperBound().key)); link.next().getOrUpperBound().key,
domainId,
cursor));
} }
return Void(); return Void();
}); });
@ -7724,8 +8100,13 @@ public:
// TODO(yiwu): When the cluster encryption config is available later, fail if the cluster is configured to // TODO(yiwu): When the cluster encryption config is available later, fail if the cluster is configured to
// enable encryption, but the Redwood instance is unencrypted. // enable encryption, but the Redwood instance is unencrypted.
if (encryptionKeyProvider && encryptionKeyProvider->enableEncryption()) { if (encryptionKeyProvider && encryptionKeyProvider->enableEncryption()) {
ASSERT(encryptionKeyProvider->expectedEncodingType() == EncodingType::AESEncryptionV1);
encodingType = EncodingType::AESEncryptionV1; encodingType = EncodingType::AESEncryptionV1;
m_keyProvider = encryptionKeyProvider; m_keyProvider = encryptionKeyProvider;
} else if (g_network->isSimulated() && logID.hash() % 2 == 0) {
// Simulation only. Deterministically enable encryption based on uid
encodingType = EncodingType::XOREncryption_TestOnly;
m_keyProvider = makeReference<XOREncryptionKeyProvider_TestOnly>(filename);
} }
IPager2* pager = new DWALPager(pageSize, IPager2* pager = new DWALPager(pageSize,
@ -9753,6 +10134,11 @@ TEST_CASE("Lredwood/correctness/btree") {
state bool serialTest = params.getInt("serialTest").orDefault(deterministicRandom()->random01() < 0.25); state bool serialTest = params.getInt("serialTest").orDefault(deterministicRandom()->random01() < 0.25);
state bool shortTest = params.getInt("shortTest").orDefault(deterministicRandom()->random01() < 0.25); state bool shortTest = params.getInt("shortTest").orDefault(deterministicRandom()->random01() < 0.25);
state int encoding =
params.getInt("encodingType").orDefault(deterministicRandom()->randomInt(0, EncodingType::MAX_ENCODING_TYPE));
state unsigned int encryptionDomainMode =
params.getInt("domainMode")
.orDefault(deterministicRandom()->randomInt(0, RandomEncryptionKeyProvider::EncryptionDomainMode::MAX));
state int pageSize = state int pageSize =
shortTest ? 250 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(250, 400)); shortTest ? 250 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(250, 400));
state int extentSize = state int extentSize =
@ -9803,24 +10189,25 @@ TEST_CASE("Lredwood/correctness/btree") {
// Max number of records in the BTree or the versioned written map to visit // Max number of records in the BTree or the versioned written map to visit
state int64_t maxRecordsRead = params.getInt("maxRecordsRead").orDefault(300e6); state int64_t maxRecordsRead = params.getInt("maxRecordsRead").orDefault(300e6);
state EncodingType encodingType = state EncodingType encodingType = static_cast<EncodingType>(encoding);
static_cast<EncodingType>(deterministicRandom()->randomInt(0, EncodingType::MAX_ENCODING_TYPE));
state Reference<IPageEncryptionKeyProvider> keyProvider; state Reference<IPageEncryptionKeyProvider> keyProvider;
if (encodingType == EncodingType::AESEncryptionV1) { if (encodingType == EncodingType::AESEncryptionV1) {
keyProvider = makeReference<RandomEncryptionKeyProvider>(); keyProvider = makeReference<RandomEncryptionKeyProvider>(
RandomEncryptionKeyProvider::EncryptionDomainMode(encryptionDomainMode));
} else if (encodingType == EncodingType::XOREncryption_TestOnly) { } else if (encodingType == EncodingType::XOREncryption_TestOnly) {
keyProvider = makeReference<XOREncryptionKeyProvider_TestOnly>(file); keyProvider = makeReference<XOREncryptionKeyProvider_TestOnly>(file);
} }
printf("\n"); printf("\n");
printf("file: %s\n", file.c_str()); printf("file: %s\n", file.c_str());
printf("encodingType: %d\n", encodingType);
printf("maxPageOps: %" PRId64 "\n", maxPageOps); printf("maxPageOps: %" PRId64 "\n", maxPageOps);
printf("maxVerificationMapEntries: %d\n", maxVerificationMapEntries); printf("maxVerificationMapEntries: %d\n", maxVerificationMapEntries);
printf("maxRecordsRead: %" PRId64 "\n", maxRecordsRead); printf("maxRecordsRead: %" PRId64 "\n", maxRecordsRead);
printf("pagerMemoryOnly: %d\n", pagerMemoryOnly); printf("pagerMemoryOnly: %d\n", pagerMemoryOnly);
printf("serialTest: %d\n", serialTest); printf("serialTest: %d\n", serialTest);
printf("shortTest: %d\n", shortTest); printf("shortTest: %d\n", shortTest);
printf("encodingType: %d\n", encodingType);
printf("domainMode: %d\n", encryptionDomainMode);
printf("pageSize: %d\n", pageSize); printf("pageSize: %d\n", pageSize);
printf("extentSize: %d\n", extentSize); printf("extentSize: %d\n", extentSize);
printf("maxKeySize: %d\n", maxKeySize); printf("maxKeySize: %d\n", maxKeySize);

View File

@ -65,7 +65,7 @@
#include "fdbserver/RestoreWorkerInterface.actor.h" #include "fdbserver/RestoreWorkerInterface.actor.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "fdbserver/SimulatedCluster.h" #include "fdbserver/SimulatedCluster.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/pubsub.h" #include "fdbserver/pubsub.h"

View File

@ -135,7 +135,7 @@ private:
Future<Void> collection; Future<Void> collection;
}; };
ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn); ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn, int64_t epoch, int64_t seqNo);
ACTOR Future<Void> loadManifest(Database db, Reference<BlobConnectionProvider> blobConn); ACTOR Future<Void> loadManifest(Database db, Reference<BlobConnectionProvider> blobConn);
ACTOR Future<Void> printRestoreSummary(Database db, Reference<BlobConnectionProvider> blobConn); ACTOR Future<Void> printRestoreSummary(Database db, Reference<BlobConnectionProvider> blobConn);
inline bool isFullRestoreMode() { inline bool isFullRestoreMode() {

View File

@ -60,6 +60,8 @@ ACTOR Future<Void> validateGranuleSummaries(Database cx,
Optional<TenantName> tenantName, Optional<TenantName> tenantName,
Promise<Void> testComplete); Promise<Void> testComplete);
ACTOR Future<Void> checkFeedCleanup(Database cx, bool debug);
#include "flow/unactorcompiler.h" #include "flow/unactorcompiler.h"
#endif #endif

View File

@ -31,6 +31,7 @@
#define FDBSERVER_CLUSTERCONTROLLER_ACTOR_H #define FDBSERVER_CLUSTERCONTROLLER_ACTOR_H
#include "fdbclient/DatabaseContext.h" #include "fdbclient/DatabaseContext.h"
#include "fdbclient/Metacluster.h"
#include "fdbrpc/Replication.h" #include "fdbrpc/Replication.h"
#include "fdbrpc/ReplicationUtils.h" #include "fdbrpc/ReplicationUtils.h"
#include "fdbserver/Knobs.h" #include "fdbserver/Knobs.h"
@ -142,6 +143,8 @@ public:
AsyncVar<bool> blobGranulesEnabled; AsyncVar<bool> blobGranulesEnabled;
ClusterType clusterType = ClusterType::STANDALONE; ClusterType clusterType = ClusterType::STANDALONE;
Optional<ClusterName> metaclusterName; Optional<ClusterName> metaclusterName;
Optional<MetaclusterRegistrationEntry> metaclusterRegistration;
MetaclusterMetrics metaclusterMetrics;
DBInfo() DBInfo()
: clientInfo(new AsyncVar<ClientDBInfo>()), serverInfo(new AsyncVar<ServerDBInfo>()), : clientInfo(new AsyncVar<ClientDBInfo>()), serverInfo(new AsyncVar<ServerDBInfo>()),
@ -2965,9 +2968,8 @@ public:
workerHealth[req.address].degradedPeers[degradedPeer] = { currentTime, currentTime }; workerHealth[req.address].degradedPeers[degradedPeer] = { currentTime, currentTime };
} }
// TODO(zhewu): add disconnected peers in worker health.
for (const auto& degradedPeer : req.disconnectedPeers) { for (const auto& degradedPeer : req.disconnectedPeers) {
workerHealth[req.address].degradedPeers[degradedPeer] = { currentTime, currentTime }; workerHealth[req.address].disconnectedPeers[degradedPeer] = { currentTime, currentTime };
} }
return; return;
@ -2977,23 +2979,24 @@ public:
auto& health = workerHealth[req.address]; auto& health = workerHealth[req.address];
auto updateDegradedPeer = [&health, currentTime](const NetworkAddress& degradedPeer) {
auto it = health.degradedPeers.find(degradedPeer);
if (it == health.degradedPeers.end()) {
health.degradedPeers[degradedPeer] = { currentTime, currentTime };
return;
}
it->second.lastRefreshTime = currentTime;
};
// Update the worker's degradedPeers. // Update the worker's degradedPeers.
for (const auto& peer : req.degradedPeers) { for (const auto& peer : req.degradedPeers) {
updateDegradedPeer(peer); auto it = health.degradedPeers.find(peer);
if (it == health.degradedPeers.end()) {
health.degradedPeers[peer] = { currentTime, currentTime };
continue;
}
it->second.lastRefreshTime = currentTime;
} }
// TODO(zhewu): add disconnected peers in worker health. // Update the worker's disconnectedPeers.
for (const auto& peer : req.disconnectedPeers) { for (const auto& peer : req.disconnectedPeers) {
updateDegradedPeer(peer); auto it = health.disconnectedPeers.find(peer);
if (it == health.disconnectedPeers.end()) {
health.disconnectedPeers[peer] = { currentTime, currentTime };
continue;
}
it->second.lastRefreshTime = currentTime;
} }
} }
@ -3009,10 +3012,18 @@ public:
++it; ++it;
} }
} }
for (auto it = health.disconnectedPeers.begin(); it != health.disconnectedPeers.end();) {
if (currentTime - it->second.lastRefreshTime > SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL) {
TraceEvent("WorkerPeerHealthRecovered").detail("Worker", workerAddress).detail("Peer", it->first);
health.disconnectedPeers.erase(it++);
} else {
++it;
}
}
} }
for (auto it = workerHealth.begin(); it != workerHealth.end();) { for (auto it = workerHealth.begin(); it != workerHealth.end();) {
if (it->second.degradedPeers.empty()) { if (it->second.degradedPeers.empty() && it->second.disconnectedPeers.empty()) {
TraceEvent("WorkerAllPeerHealthRecovered").detail("Worker", it->first); TraceEvent("WorkerAllPeerHealthRecovered").detail("Worker", it->first);
workerHealth.erase(it++); workerHealth.erase(it++);
} else { } else {
@ -3025,6 +3036,8 @@ public:
std::unordered_set<NetworkAddress> std::unordered_set<NetworkAddress>
degradedServers; // The servers that the cluster controller is considered as degraded. The servers in this degradedServers; // The servers that the cluster controller is considered as degraded. The servers in this
// list are not excluded unless they are added to `excludedDegradedServers`. // list are not excluded unless they are added to `excludedDegradedServers`.
std::unordered_set<NetworkAddress>
disconnectedServers; // Similar to the above list, but the servers experiencing connection issue.
bool degradedSatellite = false; // Indicates that the entire satellite DC is degraded. bool degradedSatellite = false; // Indicates that the entire satellite DC is degraded.
}; };
@ -3035,6 +3048,7 @@ public:
// Build a map keyed by measured degraded peer. This map gives the info that who complains a particular server. // Build a map keyed by measured degraded peer. This map gives the info that who complains a particular server.
std::unordered_map<NetworkAddress, std::unordered_set<NetworkAddress>> degradedLinkDst2Src; std::unordered_map<NetworkAddress, std::unordered_set<NetworkAddress>> degradedLinkDst2Src;
std::unordered_map<NetworkAddress, std::unordered_set<NetworkAddress>> disconnectedLinkDst2Src;
double currentTime = now(); double currentTime = now();
for (const auto& [server, health] : workerHealth) { for (const auto& [server, health] : workerHealth) {
for (const auto& [degradedPeer, times] : health.degradedPeers) { for (const auto& [degradedPeer, times] : health.degradedPeers) {
@ -3044,6 +3058,13 @@ public:
} }
degradedLinkDst2Src[degradedPeer].insert(server); degradedLinkDst2Src[degradedPeer].insert(server);
} }
for (const auto& [disconnectedPeer, times] : health.disconnectedPeers) {
if (currentTime - times.startTime < SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL) {
// This degraded link is not long enough to be considered as degraded.
continue;
}
disconnectedLinkDst2Src[disconnectedPeer].insert(server);
}
} }
// Sort degraded peers based on the number of workers complaining about it. // Sort degraded peers based on the number of workers complaining about it.
@ -3053,6 +3074,12 @@ public:
} }
std::sort(count2DegradedPeer.begin(), count2DegradedPeer.end(), std::greater<>()); std::sort(count2DegradedPeer.begin(), count2DegradedPeer.end(), std::greater<>());
std::vector<std::pair<int, NetworkAddress>> count2DisconnectedPeer;
for (const auto& [disconnectedPeer, complainers] : disconnectedLinkDst2Src) {
count2DisconnectedPeer.push_back({ complainers.size(), disconnectedPeer });
}
std::sort(count2DisconnectedPeer.begin(), count2DisconnectedPeer.end(), std::greater<>());
// Go through all reported degraded peers by decreasing order of the number of complainers. For a particular // Go through all reported degraded peers by decreasing order of the number of complainers. For a particular
// degraded peer, if a complainer has already be considered as degraded, we skip the current examine degraded // degraded peer, if a complainer has already be considered as degraded, we skip the current examine degraded
// peer since there has been one endpoint on the link between degradedPeer and complainer considered as // peer since there has been one endpoint on the link between degradedPeer and complainer considered as
@ -3081,9 +3108,25 @@ public:
} }
} }
DegradationInfo currentDegradationInfo;
for (const auto& [complainerCount, badServer] : count2DisconnectedPeer) {
for (const auto& complainer : disconnectedLinkDst2Src[badServer]) {
if (currentDegradationInfo.disconnectedServers.find(complainer) ==
currentDegradationInfo.disconnectedServers.end()) {
currentDegradationInfo.disconnectedServers.insert(badServer);
break;
}
}
if (SERVER_KNOBS->CC_ENABLE_ENTIRE_SATELLITE_MONITORING &&
addressInDbAndPrimarySatelliteDc(badServer, db.serverInfo) &&
complainerCount >= SERVER_KNOBS->CC_SATELLITE_DEGRADATION_MIN_COMPLAINER) {
++satelliteBadServerCount;
}
}
// For degraded server that are complained by more than SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE, we // For degraded server that are complained by more than SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE, we
// don't know if it is a hot server, or the network is bad. We remove from the returned degraded server list. // don't know if it is a hot server, or the network is bad. We remove from the returned degraded server list.
DegradationInfo currentDegradationInfo;
for (const auto& badServer : currentDegradedServers) { for (const auto& badServer : currentDegradedServers) {
if (degradedLinkDst2Src[badServer].size() <= SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE) { if (degradedLinkDst2Src[badServer].size() <= SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE) {
currentDegradationInfo.degradedServers.insert(badServer); currentDegradationInfo.degradedServers.insert(badServer);
@ -3101,43 +3144,48 @@ public:
// Whether the transaction system (in primary DC if in HA setting) contains degraded servers. // Whether the transaction system (in primary DC if in HA setting) contains degraded servers.
bool transactionSystemContainsDegradedServers() { bool transactionSystemContainsDegradedServers() {
const ServerDBInfo dbi = db.serverInfo->get(); const ServerDBInfo& dbi = db.serverInfo->get();
for (const auto& excludedServer : degradationInfo.degradedServers) { auto transactionWorkerInList = [&dbi](const std::unordered_set<NetworkAddress>& serverList) -> bool {
if (dbi.master.addresses().contains(excludedServer)) { for (const auto& server : serverList) {
return true; if (dbi.master.addresses().contains(server)) {
} return true;
for (auto& logSet : dbi.logSystemConfig.tLogs) {
if (!logSet.isLocal || logSet.locality == tagLocalitySatellite) {
continue;
} }
for (const auto& tlog : logSet.tLogs) {
if (tlog.present() && tlog.interf().addresses().contains(excludedServer)) { for (const auto& logSet : dbi.logSystemConfig.tLogs) {
if (!logSet.isLocal || logSet.locality == tagLocalitySatellite) {
continue;
}
for (const auto& tlog : logSet.tLogs) {
if (tlog.present() && tlog.interf().addresses().contains(server)) {
return true;
}
}
}
for (const auto& proxy : dbi.client.grvProxies) {
if (proxy.addresses().contains(server)) {
return true;
}
}
for (const auto& proxy : dbi.client.commitProxies) {
if (proxy.addresses().contains(server)) {
return true;
}
}
for (const auto& resolver : dbi.resolvers) {
if (resolver.addresses().contains(server)) {
return true; return true;
} }
} }
} }
for (auto& proxy : dbi.client.grvProxies) { return false;
if (proxy.addresses().contains(excludedServer)) { };
return true;
}
}
for (auto& proxy : dbi.client.commitProxies) { return transactionWorkerInList(degradationInfo.degradedServers) ||
if (proxy.addresses().contains(excludedServer)) { transactionWorkerInList(degradationInfo.disconnectedServers);
return true;
}
}
for (auto& resolver : dbi.resolvers) {
if (resolver.addresses().contains(excludedServer)) {
return true;
}
}
}
return false;
} }
// Whether transaction system in the remote DC, e.g. log router and tlogs in the remote DC, contains degraded // Whether transaction system in the remote DC, e.g. log router and tlogs in the remote DC, contains degraded
@ -3153,6 +3201,12 @@ public:
} }
} }
for (const auto& excludedServer : degradationInfo.disconnectedServers) {
if (addressInDbAndRemoteDc(excludedServer, db.serverInfo)) {
return true;
}
}
return false; return false;
} }
@ -3185,7 +3239,8 @@ public:
// Returns true when the cluster controller should trigger a recovery due to degraded servers used in the // Returns true when the cluster controller should trigger a recovery due to degraded servers used in the
// transaction system in the primary data center. // transaction system in the primary data center.
bool shouldTriggerRecoveryDueToDegradedServers() { bool shouldTriggerRecoveryDueToDegradedServers() {
if (degradationInfo.degradedServers.size() > SERVER_KNOBS->CC_MAX_EXCLUSION_DUE_TO_HEALTH) { if (degradationInfo.degradedServers.size() + degradationInfo.disconnectedServers.size() >
SERVER_KNOBS->CC_MAX_EXCLUSION_DUE_TO_HEALTH) {
return false; return false;
} }
@ -3224,8 +3279,9 @@ public:
return true; return true;
} }
if (degradationInfo.degradedServers.size() < SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MIN_DEGRADATION || int degradedServerSize = degradationInfo.degradedServers.size() + degradationInfo.disconnectedServers.size();
degradationInfo.degradedServers.size() > SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MAX_DEGRADATION) { if (degradedServerSize < SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MIN_DEGRADATION ||
degradedServerSize > SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MAX_DEGRADATION) {
return false; return false;
} }
@ -3316,6 +3372,7 @@ public:
double lastRefreshTime = 0; double lastRefreshTime = 0;
}; };
std::unordered_map<NetworkAddress, DegradedTimes> degradedPeers; std::unordered_map<NetworkAddress, DegradedTimes> degradedPeers;
std::unordered_map<NetworkAddress, DegradedTimes> disconnectedPeers;
// TODO(zhewu): Include disk and CPU signals. // TODO(zhewu): Include disk and CPU signals.
}; };

View File

@ -247,6 +247,7 @@ struct ClusterRecoveryData : NonCopyable, ReferenceCounted<ClusterRecoveryData>
Future<Void> logger; Future<Void> logger;
Reference<EventCacheHolder> metaclusterEventHolder;
Reference<EventCacheHolder> swVersionCheckedEventHolder; Reference<EventCacheHolder> swVersionCheckedEventHolder;
Reference<EventCacheHolder> recoveredConfigEventHolder; Reference<EventCacheHolder> recoveredConfigEventHolder;
Reference<EventCacheHolder> clusterRecoveryStateEventHolder; Reference<EventCacheHolder> clusterRecoveryStateEventHolder;
@ -277,6 +278,7 @@ struct ClusterRecoveryData : NonCopyable, ReferenceCounted<ClusterRecoveryData>
backupWorkerDoneRequests("BackupWorkerDoneRequests", cc), backupWorkerDoneRequests("BackupWorkerDoneRequests", cc),
getLiveCommittedVersionRequests("GetLiveCommittedVersionRequests", cc), getLiveCommittedVersionRequests("GetLiveCommittedVersionRequests", cc),
reportLiveCommittedVersionRequests("ReportLiveCommittedVersionRequests", cc), reportLiveCommittedVersionRequests("ReportLiveCommittedVersionRequests", cc),
metaclusterEventHolder(makeReference<EventCacheHolder>("MetaclusterMetadata")),
swVersionCheckedEventHolder(makeReference<EventCacheHolder>("SWVersionCompatibilityChecked")), swVersionCheckedEventHolder(makeReference<EventCacheHolder>("SWVersionCompatibilityChecked")),
recoveredConfigEventHolder(makeReference<EventCacheHolder>("RecoveredConfig")) { recoveredConfigEventHolder(makeReference<EventCacheHolder>("RecoveredConfig")) {
clusterRecoveryStateEventHolder = makeReference<EventCacheHolder>( clusterRecoveryStateEventHolder = makeReference<EventCacheHolder>(

View File

@ -29,6 +29,11 @@
struct InitialDataDistribution; struct InitialDataDistribution;
struct DDShardInfo; struct DDShardInfo;
struct ServerWorkerInfos {
std::vector<std::pair<StorageServerInterface, ProcessClass>> servers;
Optional<Version> readVersion; // the read version of the txn reading server lists
};
/* Testability Contract: /* Testability Contract:
* a. The DataDistributor has to use this interface to interact with data-plane (aka. run transaction / use Database), * a. The DataDistributor has to use this interface to interact with data-plane (aka. run transaction / use Database),
* because the testability benefits from a mock implementation; b. Other control-plane roles should consider providing * because the testability benefits from a mock implementation; b. Other control-plane roles should consider providing
@ -44,8 +49,8 @@ public:
// get the source server list and complete source server list for range // get the source server list and complete source server list for range
virtual Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) { return SourceServers{}; }; virtual Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) { return SourceServers{}; };
// get the storage server list and Process class // get the storage server list and Process class, only throw transaction non-retryable exceptions
virtual Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> getServerListAndProcessClasses() = 0; virtual Future<ServerWorkerInfos> getServerListAndProcessClasses() = 0;
virtual Future<Reference<InitialDataDistribution>> getInitialDataDistribution( virtual Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
const UID& distributorId, const UID& distributorId,
@ -65,6 +70,10 @@ public:
return Void(); return Void();
} }
virtual Future<int> tryUpdateReplicasKeyForDc(const Optional<Key>& dcId, const int& storageTeamSize) const {
return storageTeamSize;
}
virtual Future<Void> waitForDataDistributionEnabled(const DDEnabledState* ddEnabledState) const { return Void(); }; virtual Future<Void> waitForDataDistributionEnabled(const DDEnabledState* ddEnabledState) const { return Void(); };
virtual Future<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const { virtual Future<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const {
@ -109,6 +118,10 @@ public:
virtual Future<HealthMetrics> getHealthMetrics(bool detailed = false) const = 0; virtual Future<HealthMetrics> getHealthMetrics(bool detailed = false) const = 0;
virtual Future<Optional<Value>> readRebalanceDDIgnoreKey() const { return {}; } virtual Future<Optional<Value>> readRebalanceDDIgnoreKey() const { return {}; }
virtual Future<UID> getClusterId() const { return {}; }
virtual Future<Void> waitDDTeamInfoPrintSignal() const { return Never(); }
}; };
class DDTxnProcessorImpl; class DDTxnProcessorImpl;
@ -128,7 +141,7 @@ public:
Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) override; Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) override;
// Call NativeAPI implementation directly // Call NativeAPI implementation directly
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> getServerListAndProcessClasses() override; Future<ServerWorkerInfos> getServerListAndProcessClasses() override;
Future<Reference<InitialDataDistribution>> getInitialDataDistribution( Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
const UID& distributorId, const UID& distributorId,
@ -144,6 +157,8 @@ public:
const std::vector<Optional<Key>>& remoteIds, const std::vector<Optional<Key>>& remoteIds,
const DatabaseConfiguration& configuration) const override; const DatabaseConfiguration& configuration) const override;
Future<int> tryUpdateReplicasKeyForDc(const Optional<Key>& dcId, const int& storageTeamSize) const override;
Future<Void> waitForDataDistributionEnabled(const DDEnabledState* ddEnabledState) const override; Future<Void> waitForDataDistributionEnabled(const DDEnabledState* ddEnabledState) const override;
Future<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const override; Future<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const override;
@ -183,6 +198,10 @@ public:
Future<HealthMetrics> getHealthMetrics(bool detailed) const override; Future<HealthMetrics> getHealthMetrics(bool detailed) const override;
Future<Optional<Value>> readRebalanceDDIgnoreKey() const override; Future<Optional<Value>> readRebalanceDDIgnoreKey() const override;
Future<UID> getClusterId() const override;
Future<Void> waitDDTeamInfoPrintSignal() const override;
}; };
// A mock transaction implementation for test usage. // A mock transaction implementation for test usage.
@ -196,7 +215,7 @@ class DDMockTxnProcessor : public IDDTxnProcessor {
public: public:
explicit DDMockTxnProcessor(std::shared_ptr<MockGlobalState> mgs = nullptr) : mgs(std::move(mgs)){}; explicit DDMockTxnProcessor(std::shared_ptr<MockGlobalState> mgs = nullptr) : mgs(std::move(mgs)){};
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> getServerListAndProcessClasses() override; Future<ServerWorkerInfos> getServerListAndProcessClasses() override;
Future<Reference<InitialDataDistribution>> getInitialDataDistribution( Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
const UID& distributorId, const UID& distributorId,

View File

@ -37,6 +37,7 @@
#define XXH_INLINE_ALL #define XXH_INLINE_ALL
#include "flow/xxhash.h" #include "flow/xxhash.h"
#include <functional>
#include <tuple> #include <tuple>
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
@ -72,10 +73,12 @@ public:
virtual bool enableEncryptionDomain() const { return false; } virtual bool enableEncryptionDomain() const { return false; }
// Get an encryption key from given encoding header. // Get an encryption key from given encoding header.
virtual Future<EncryptionKey> getEncryptionKey(void* encodingHeader) { throw not_implemented(); } virtual Future<EncryptionKey> getEncryptionKey(const void* encodingHeader) { throw not_implemented(); }
// Get latest encryption key. If encryption domain is enabled, get encryption key for the default domain. // Get latest encryption key. If encryption domain is enabled, get encryption key for the default domain.
virtual Future<EncryptionKey> getLatestDefaultEncryptionKey() { throw not_implemented(); } virtual Future<EncryptionKey> getLatestDefaultEncryptionKey() {
return getLatestEncryptionKey(getDefaultEncryptionDomainId());
}
// Get latest encryption key for data in given encryption domain. // Get latest encryption key for data in given encryption domain.
virtual Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) { throw not_implemented(); } virtual Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) { throw not_implemented(); }
@ -94,10 +97,22 @@ public:
} }
// Get encryption domain of a page given encoding header. // Get encryption domain of a page given encoding header.
virtual int64_t getEncryptionDomain(void* encodingHeader) { throw not_implemented(); } virtual int64_t getEncryptionDomainIdFromHeader(const void* encodingHeader) { throw not_implemented(); }
// Setting tenant prefix to tenant name map. Used by TenantAwareEncryptionKeyProvider. // Setting tenant prefix to tenant name map. Used by TenantAwareEncryptionKeyProvider.
virtual void setTenantPrefixIndex(Reference<TenantPrefixIndex> tenantPrefixIndex) {} virtual void setTenantPrefixIndex(Reference<TenantPrefixIndex> tenantPrefixIndex) {}
// Helper methods.
// Check if a key fits in an encryption domain.
bool keyFitsInDomain(int64_t domainId, const KeyRef& key, bool canUseDefaultDomain) {
ASSERT(enableEncryptionDomain());
int64_t keyDomainId;
size_t prefixLength;
std::tie(keyDomainId, prefixLength) = getEncryptionDomain(key);
return keyDomainId == domainId ||
(canUseDefaultDomain && (domainId == getDefaultEncryptionDomainId() && key.size() == prefixLength));
}
}; };
// The null key provider is useful to simplify page decoding. // The null key provider is useful to simplify page decoding.
@ -133,39 +148,20 @@ public:
bool enableEncryption() const override { return true; } bool enableEncryption() const override { return true; }
bool enableEncryptionDomain() const override { return true; } Future<EncryptionKey> getEncryptionKey(const void* encodingHeader) override {
Future<EncryptionKey> getEncryptionKey(void* encodingHeader) override { const EncodingHeader* h = reinterpret_cast<const EncodingHeader*>(encodingHeader);
EncodingHeader* h = reinterpret_cast<EncodingHeader*>(encodingHeader);
EncryptionKey s; EncryptionKey s;
s.xorKey = h->xorKey; s.xorKey = h->xorKey;
return s; return s;
} }
Future<EncryptionKey> getLatestDefaultEncryptionKey() override { return getLatestEncryptionKey(0); } Future<EncryptionKey> getLatestDefaultEncryptionKey() override {
Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) override {
EncryptionKey s; EncryptionKey s;
s.xorKey = ~(uint8_t)domainId ^ xorWith; s.xorKey = xorWith;
return s; return s;
} }
int64_t getDefaultEncryptionDomainId() const override { return 0; }
std::tuple<int64_t, size_t> getEncryptionDomain(const KeyRef& key,
Optional<int64_t> /*possibleDomainId*/) override {
if (key.size() > 0) {
return { *key.begin(), 1 };
}
return { 0, 0 };
}
int64_t getEncryptionDomain(void* encodingHeader) override {
uint8_t xorKey = reinterpret_cast<EncodingHeader*>(encodingHeader)->xorKey;
return (int64_t)(~xorKey ^ xorWith);
}
uint8_t xorWith; uint8_t xorWith;
}; };
@ -173,11 +169,19 @@ public:
// Use for testing. // Use for testing.
class RandomEncryptionKeyProvider : public IPageEncryptionKeyProvider { class RandomEncryptionKeyProvider : public IPageEncryptionKeyProvider {
public: public:
RandomEncryptionKeyProvider() { enum EncryptionDomainMode : unsigned int {
DISABLED = 0, // disable encryption domain
RANDOM, // for each key prefix, deterministic randomly decide if there's an encryption domain for it.
ALL, // all key prefixes has an encryption domain assigned to it.
MAX,
};
explicit RandomEncryptionKeyProvider(EncryptionDomainMode mode) : mode(mode) {
ASSERT(mode < EncryptionDomainMode::MAX);
for (unsigned i = 0; i < NUM_CIPHER; i++) { for (unsigned i = 0; i < NUM_CIPHER; i++) {
BlobCipherDetails cipherDetails; BlobCipherDetails cipherDetails;
cipherDetails.encryptDomainId = i; cipherDetails.encryptDomainId = 0;
cipherDetails.baseCipherId = deterministicRandom()->randomUInt64(); cipherDetails.baseCipherId = i;
cipherDetails.salt = deterministicRandom()->randomUInt64(); cipherDetails.salt = deterministicRandom()->randomUInt64();
cipherKeys[i] = generateCipherKey(cipherDetails); cipherKeys[i] = generateCipherKey(cipherDetails);
} }
@ -188,22 +192,47 @@ public:
bool enableEncryption() const override { return true; } bool enableEncryption() const override { return true; }
Future<EncryptionKey> getEncryptionKey(void* encodingHeader) override { bool enableEncryptionDomain() const override { return mode > 1; }
Future<EncryptionKey> getEncryptionKey(const void* encodingHeader) override {
using Header = ArenaPage::AESEncryptionV1Encoder::Header; using Header = ArenaPage::AESEncryptionV1Encoder::Header;
Header* h = reinterpret_cast<Header*>(encodingHeader); const Header* h = reinterpret_cast<const Header*>(encodingHeader);
EncryptionKey s; EncryptionKey s;
s.aesKey.cipherTextKey = cipherKeys[h->cipherTextDetails.encryptDomainId]; s.aesKey.cipherTextKey = getCipherKey(h->cipherTextDetails.encryptDomainId, h->cipherTextDetails.baseCipherId);
s.aesKey.cipherHeaderKey = cipherKeys[h->cipherHeaderDetails.encryptDomainId]; s.aesKey.cipherHeaderKey =
getCipherKey(h->cipherHeaderDetails.encryptDomainId, h->cipherHeaderDetails.baseCipherId);
return s; return s;
} }
Future<EncryptionKey> getLatestDefaultEncryptionKey() override { Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) override {
domainId = checkDomainId(domainId);
EncryptionKey s; EncryptionKey s;
s.aesKey.cipherTextKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)]; s.aesKey.cipherTextKey = getCipherKey(domainId, deterministicRandom()->randomInt(0, NUM_CIPHER));
s.aesKey.cipherHeaderKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)]; s.aesKey.cipherHeaderKey =
getCipherKey(ENCRYPT_HEADER_DOMAIN_ID, deterministicRandom()->randomInt(0, NUM_CIPHER));
return s; return s;
} }
int64_t getDefaultEncryptionDomainId() const override { return FDB_DEFAULT_ENCRYPT_DOMAIN_ID; }
std::tuple<int64_t, size_t> getEncryptionDomain(const KeyRef& key, Optional<int64_t>) override {
int64_t domainId;
if (key.size() < PREFIX_LENGTH) {
domainId = getDefaultEncryptionDomainId();
} else {
// Use first 4 bytes as a 32-bit int for the domain id.
domainId = checkDomainId(static_cast<int64_t>(*reinterpret_cast<const int32_t*>(key.begin())));
}
return { domainId, (domainId == getDefaultEncryptionDomainId() ? 0 : PREFIX_LENGTH) };
}
int64_t getEncryptionDomainIdFromHeader(const void* encodingHeader) override {
ASSERT(encodingHeader != nullptr);
using Header = ArenaPage::AESEncryptionV1Encoder::Header;
const Header* h = reinterpret_cast<const Header*>(encodingHeader);
return h->cipherTextDetails.encryptDomainId;
}
private: private:
Reference<BlobCipherKey> generateCipherKey(const BlobCipherDetails& cipherDetails) { Reference<BlobCipherKey> generateCipherKey(const BlobCipherDetails& cipherDetails) {
static unsigned char SHA_KEY[] = "3ab9570b44b8315fdb261da6b1b6c13b"; static unsigned char SHA_KEY[] = "3ab9570b44b8315fdb261da6b1b6c13b";
@ -226,7 +255,28 @@ private:
std::numeric_limits<int64_t>::max() /* expireAt */); std::numeric_limits<int64_t>::max() /* expireAt */);
} }
int64_t checkDomainId(int64_t domainId) {
std::hash<int64_t> hasher;
if (mode == DISABLED || (mode == RANDOM && hasher(domainId) % 2 == 0)) {
return getDefaultEncryptionDomainId();
}
return domainId;
}
Reference<BlobCipherKey> getCipherKey(EncryptCipherDomainId domainId, EncryptCipherBaseKeyId cipherId) {
// Create a new cipher key by replacing the domain id.
return makeReference<BlobCipherKey>(domainId,
cipherId,
cipherKeys[cipherId]->rawBaseCipher(),
AES_256_KEY_LENGTH,
cipherKeys[cipherId]->getSalt(),
std::numeric_limits<int64_t>::max() /* refreshAt */,
std::numeric_limits<int64_t>::max() /* expireAt */);
}
static constexpr int NUM_CIPHER = 1000; static constexpr int NUM_CIPHER = 1000;
static constexpr size_t PREFIX_LENGTH = 4;
EncryptionDomainMode mode;
Reference<BlobCipherKey> cipherKeys[NUM_CIPHER]; Reference<BlobCipherKey> cipherKeys[NUM_CIPHER];
}; };
@ -248,8 +298,9 @@ public:
bool enableEncryptionDomain() const override { return true; } bool enableEncryptionDomain() const override { return true; }
ACTOR static Future<EncryptionKey> getEncryptionKey(TenantAwareEncryptionKeyProvider* self, void* encodingHeader) { ACTOR static Future<EncryptionKey> getEncryptionKey(TenantAwareEncryptionKeyProvider* self,
BlobCipherEncryptHeader* header = reinterpret_cast<EncodingHeader*>(encodingHeader); const void* encodingHeader) {
const BlobCipherEncryptHeader* header = reinterpret_cast<const EncodingHeader*>(encodingHeader);
TextAndHeaderCipherKeys cipherKeys = TextAndHeaderCipherKeys cipherKeys =
wait(getEncryptCipherKeys(self->db, *header, BlobCipherMetrics::KV_REDWOOD)); wait(getEncryptCipherKeys(self->db, *header, BlobCipherMetrics::KV_REDWOOD));
EncryptionKey encryptionKey; EncryptionKey encryptionKey;
@ -257,7 +308,7 @@ public:
return encryptionKey; return encryptionKey;
} }
Future<EncryptionKey> getEncryptionKey(void* encodingHeader) override { Future<EncryptionKey> getEncryptionKey(const void* encodingHeader) override {
return getEncryptionKey(this, encodingHeader); return getEncryptionKey(this, encodingHeader);
} }
@ -292,7 +343,7 @@ public:
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
} }
StringRef prefix = key.substr(0, TENANT_PREFIX_SIZE); StringRef prefix = key.substr(0, TENANT_PREFIX_SIZE);
int64_t tenantId = TenantMapEntry::prefixToId(prefix); int64_t tenantId = TenantMapEntry::prefixToId(prefix, EnforceValidTenantId::False);
// Tenant id must be non-negative. // Tenant id must be non-negative.
if (tenantId < 0) { if (tenantId < 0) {
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
@ -314,8 +365,9 @@ public:
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
} }
int64_t getEncryptionDomain(void* encodingHeader) override { int64_t getEncryptionDomainIdFromHeader(const void* encodingHeader) override {
BlobCipherEncryptHeader* header = reinterpret_cast<EncodingHeader*>(encodingHeader); ASSERT(encodingHeader != nullptr);
const BlobCipherEncryptHeader* header = reinterpret_cast<const EncodingHeader*>(encodingHeader);
return header->cipherTextDetails.encryptDomainId; return header->cipherTextDetails.encryptDomainId;
} }
@ -332,7 +384,7 @@ private:
return FDB_DEFAULT_ENCRYPT_DOMAIN_NAME; return FDB_DEFAULT_ENCRYPT_DOMAIN_NAME;
} }
if (tenantPrefixIndex.isValid()) { if (tenantPrefixIndex.isValid()) {
StringRef prefix = TenantMapEntry::idToPrefix(domainId); Key prefix(TenantMapEntry::idToPrefix(domainId));
auto view = tenantPrefixIndex->atLatest(); auto view = tenantPrefixIndex->atLatest();
auto itr = view.find(prefix); auto itr = view.find(prefix);
if (itr != view.end()) { if (itr != view.end()) {

View File

@ -498,7 +498,7 @@ public:
// Secret is set if needed // Secret is set if needed
// Post: Main and Encoding subheaders are updated // Post: Main and Encoding subheaders are updated
// Payload is possibly encrypted // Payload is possibly encrypted
void preWrite(PhysicalPageID pageID) const { void preWrite(PhysicalPageID pageID) {
// Explicitly check payload definedness to make the source of valgrind errors more clear. // Explicitly check payload definedness to make the source of valgrind errors more clear.
// Without this check, calculating a checksum on a payload with undefined bytes does not // Without this check, calculating a checksum on a payload with undefined bytes does not
// cause a valgrind error but the resulting checksum is undefined which causes errors later. // cause a valgrind error but the resulting checksum is undefined which causes errors later.
@ -519,6 +519,7 @@ public:
} else { } else {
throw page_header_version_not_supported(); throw page_header_version_not_supported();
} }
encodingHeaderAvailable = true;
} }
// Must be called after reading from disk to verify all non-payload bytes // Must be called after reading from disk to verify all non-payload bytes
@ -531,6 +532,7 @@ public:
void postReadHeader(PhysicalPageID pageID, bool verify = true) { void postReadHeader(PhysicalPageID pageID, bool verify = true) {
pPayload = page->getPayload(); pPayload = page->getPayload();
payloadSize = logicalSize - (pPayload - buffer); payloadSize = logicalSize - (pPayload - buffer);
encodingHeaderAvailable = true;
if (page->headerVersion == 1) { if (page->headerVersion == 1) {
if (verify) { if (verify) {
@ -568,7 +570,18 @@ public:
// Returns true if the page's encoding type employs encryption // Returns true if the page's encoding type employs encryption
bool isEncrypted() const { return isEncodingTypeEncrypted(getEncodingType()); } bool isEncrypted() const { return isEncodingTypeEncrypted(getEncodingType()); }
void* getEncodingHeader() { return page->getEncodingHeader(); } // Return encryption domain id used. This method only use information from the encryptionKey.
// Caller should make sure encryption domain is in use.
int64_t getEncryptionDomainId() const {
// encryption domain is only supported by AESEncryptionV1.
ASSERT(getEncodingType() == EncodingType::AESEncryptionV1);
const Reference<BlobCipherKey>& cipherKey = encryptionKey.aesKey.cipherTextKey;
ASSERT(cipherKey.isValid());
return cipherKey->getDomainId();
}
// Return pointer to encoding header.
const void* getEncodingHeader() const { return encodingHeaderAvailable ? page->getEncodingHeader() : nullptr; }
private: private:
Arena arena; Arena arena;
@ -608,6 +621,9 @@ public:
// Used by encodings that do encryption // Used by encodings that do encryption
EncryptionKey encryptionKey; EncryptionKey encryptionKey;
// Whether encoding header is set
bool encodingHeaderAvailable = false;
mutable ArbitraryObject extra; mutable ArbitraryObject extra;
}; };

View File

@ -31,7 +31,7 @@
#include "fdbserver/LogSystemConfig.h" #include "fdbserver/LogSystemConfig.h"
#include "fdbserver/RatekeeperInterface.h" #include "fdbserver/RatekeeperInterface.h"
#include "fdbserver/BlobManagerInterface.h" #include "fdbserver/BlobManagerInterface.h"
#include "fdbclient/ConsistencyScanInterface.h" #include "fdbclient/ConsistencyScanInterface.actor.h"
#include "fdbserver/RecoveryState.h" #include "fdbserver/RecoveryState.h"
#include "fdbserver/LatencyBandConfig.h" #include "fdbserver/LatencyBandConfig.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"

View File

@ -18,8 +18,11 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef FDBSERVER_STATUS_H #if defined(NO_INTELLISENSE) && !defined(FDBSERVER_ACTOR_STATUS_G_H)
#define FDBSERVER_STATUS_H #define FDBSERVER_ACTOR_STATUS_G_H
#include "fdbserver/Status.actor.g.h"
#elif !defined(FDBSERVER_ACTOR_STATUS_H)
#define FDBSERVER_ACTOR_STATUS_H
#pragma once #pragma once
#include "fdbrpc/fdbrpc.h" #include "fdbrpc/fdbrpc.h"
@ -27,6 +30,9 @@
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/MasterInterface.h" #include "fdbserver/MasterInterface.h"
#include "fdbclient/ClusterInterface.h" #include "fdbclient/ClusterInterface.h"
#include "fdbclient/Metacluster.h"
#include "flow/actorcompiler.h" // has to be last include
struct ProcessIssues { struct ProcessIssues {
NetworkAddress address; NetworkAddress address;
@ -44,10 +50,14 @@ Future<StatusReply> clusterGetStatus(
ServerCoordinators const& coordinators, ServerCoordinators const& coordinators,
std::vector<NetworkAddress> const& incompatibleConnections, std::vector<NetworkAddress> const& incompatibleConnections,
Version const& datacenterVersionDifference, Version const& datacenterVersionDifference,
ConfigBroadcaster const* const& conifgBroadcaster); ConfigBroadcaster const* const& conifgBroadcaster,
Optional<MetaclusterRegistrationEntry> const& metaclusterRegistration,
MetaclusterMetrics const& metaclusterMetrics);
struct WorkerEvents : std::map<NetworkAddress, TraceEventFields> {}; struct WorkerEvents : std::map<NetworkAddress, TraceEventFields> {};
Future<Optional<std::pair<WorkerEvents, std::set<std::string>>>> latestEventOnWorkers( ACTOR Future<Optional<std::pair<WorkerEvents, std::set<std::string>>>> latestEventOnWorkers(
std::vector<WorkerDetails> const& workers, std::vector<WorkerDetails> workers,
std::string const& eventName); std::string eventName);
#include "flow/unactorcompiler.h"
#endif #endif

View File

@ -31,7 +31,7 @@
#include "fdbserver/MasterInterface.h" #include "fdbserver/MasterInterface.h"
#include "fdbserver/TLogInterface.h" #include "fdbserver/TLogInterface.h"
#include "fdbserver/RatekeeperInterface.h" #include "fdbserver/RatekeeperInterface.h"
#include "fdbclient/ConsistencyScanInterface.h" #include "fdbclient/ConsistencyScanInterface.actor.h"
#include "fdbserver/BlobManagerInterface.h" #include "fdbserver/BlobManagerInterface.h"
#include "fdbserver/ResolverInterface.h" #include "fdbserver/ResolverInterface.h"
#include "fdbclient/BlobWorkerInterface.h" #include "fdbclient/BlobWorkerInterface.h"

View File

@ -159,46 +159,39 @@ FDB_DECLARE_BOOLEAN_PARAM(UnlimitedCommitBytes);
FDB_DEFINE_BOOLEAN_PARAM(UnlimitedCommitBytes); FDB_DEFINE_BOOLEAN_PARAM(UnlimitedCommitBytes);
// Immutable // Immutable
static const KeyValueRef persistFormat(LiteralStringRef(PERSIST_PREFIX "Format"), "FoundationDB/StorageServer/1/4"_sr); static const KeyValueRef persistFormat(PERSIST_PREFIX "Format"_sr, "FoundationDB/StorageServer/1/4"_sr);
static const KeyValueRef persistShardAwareFormat(LiteralStringRef(PERSIST_PREFIX "Format"), static const KeyValueRef persistShardAwareFormat(PERSIST_PREFIX "Format"_sr, "FoundationDB/StorageServer/1/5"_sr);
"FoundationDB/StorageServer/1/5"_sr);
static const KeyRangeRef persistFormatReadableRange("FoundationDB/StorageServer/1/2"_sr, static const KeyRangeRef persistFormatReadableRange("FoundationDB/StorageServer/1/2"_sr,
"FoundationDB/StorageServer/1/6"_sr); "FoundationDB/StorageServer/1/6"_sr);
static const KeyRef persistID = LiteralStringRef(PERSIST_PREFIX "ID"); static const KeyRef persistID = PERSIST_PREFIX "ID"_sr;
static const KeyRef persistTssPairID = LiteralStringRef(PERSIST_PREFIX "tssPairID"); static const KeyRef persistTssPairID = PERSIST_PREFIX "tssPairID"_sr;
static const KeyRef persistSSPairID = LiteralStringRef(PERSIST_PREFIX "ssWithTSSPairID"); static const KeyRef persistSSPairID = PERSIST_PREFIX "ssWithTSSPairID"_sr;
static const KeyRef persistTssQuarantine = LiteralStringRef(PERSIST_PREFIX "tssQ"); static const KeyRef persistTssQuarantine = PERSIST_PREFIX "tssQ"_sr;
static const KeyRef persistClusterIdKey = LiteralStringRef(PERSIST_PREFIX "clusterId"); static const KeyRef persistClusterIdKey = PERSIST_PREFIX "clusterId"_sr;
// (Potentially) change with the durable version or when fetchKeys completes // (Potentially) change with the durable version or when fetchKeys completes
static const KeyRef persistVersion = LiteralStringRef(PERSIST_PREFIX "Version"); static const KeyRef persistVersion = PERSIST_PREFIX "Version"_sr;
static const KeyRangeRef persistShardAssignedKeys = static const KeyRangeRef persistShardAssignedKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "ShardAssigned/"), LiteralStringRef(PERSIST_PREFIX "ShardAssigned0")); KeyRangeRef(PERSIST_PREFIX "ShardAssigned/"_sr, PERSIST_PREFIX "ShardAssigned0"_sr);
static const KeyRangeRef persistShardAvailableKeys = static const KeyRangeRef persistShardAvailableKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "ShardAvailable/"), LiteralStringRef(PERSIST_PREFIX "ShardAvailable0")); KeyRangeRef(PERSIST_PREFIX "ShardAvailable/"_sr, PERSIST_PREFIX "ShardAvailable0"_sr);
static const KeyRangeRef persistByteSampleKeys = static const KeyRangeRef persistByteSampleKeys = KeyRangeRef(PERSIST_PREFIX "BS/"_sr, PERSIST_PREFIX "BS0"_sr);
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "BS/"), LiteralStringRef(PERSIST_PREFIX "BS0"));
static const KeyRangeRef persistByteSampleSampleKeys = static const KeyRangeRef persistByteSampleSampleKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "BS/" PERSIST_PREFIX "BS/"), KeyRangeRef(PERSIST_PREFIX "BS/"_sr PERSIST_PREFIX "BS/"_sr, PERSIST_PREFIX "BS/"_sr PERSIST_PREFIX "BS0"_sr);
LiteralStringRef(PERSIST_PREFIX "BS/" PERSIST_PREFIX "BS0")); static const KeyRef persistLogProtocol = PERSIST_PREFIX "LogProtocol"_sr;
static const KeyRef persistLogProtocol = LiteralStringRef(PERSIST_PREFIX "LogProtocol"); static const KeyRef persistPrimaryLocality = PERSIST_PREFIX "PrimaryLocality"_sr;
static const KeyRef persistPrimaryLocality = LiteralStringRef(PERSIST_PREFIX "PrimaryLocality"); static const KeyRangeRef persistChangeFeedKeys = KeyRangeRef(PERSIST_PREFIX "CF/"_sr, PERSIST_PREFIX "CF0"_sr);
static const KeyRangeRef persistChangeFeedKeys = static const KeyRangeRef persistTenantMapKeys = KeyRangeRef(PERSIST_PREFIX "TM/"_sr, PERSIST_PREFIX "TM0"_sr);
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "CF/"), LiteralStringRef(PERSIST_PREFIX "CF0"));
static const KeyRangeRef persistTenantMapKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "TM/"), LiteralStringRef(PERSIST_PREFIX "TM0"));
// data keys are unmangled (but never start with PERSIST_PREFIX because they are always in allKeys) // data keys are unmangled (but never start with PERSIST_PREFIX because they are always in allKeys)
static const KeyRangeRef persistStorageServerShardKeys = static const KeyRangeRef persistStorageServerShardKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "StorageServerShard/"), KeyRangeRef(PERSIST_PREFIX "StorageServerShard/"_sr, PERSIST_PREFIX "StorageServerShard0"_sr);
LiteralStringRef(PERSIST_PREFIX "StorageServerShard0"));
// Checkpoint related prefixes. // Checkpoint related prefixes.
static const KeyRangeRef persistCheckpointKeys = static const KeyRangeRef persistCheckpointKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "Checkpoint/"), LiteralStringRef(PERSIST_PREFIX "Checkpoint0")); KeyRangeRef(PERSIST_PREFIX "Checkpoint/"_sr, PERSIST_PREFIX "Checkpoint0"_sr);
static const KeyRangeRef persistPendingCheckpointKeys = static const KeyRangeRef persistPendingCheckpointKeys =
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "PendingCheckpoint/"), KeyRangeRef(PERSIST_PREFIX "PendingCheckpoint/"_sr, PERSIST_PREFIX "PendingCheckpoint0"_sr);
LiteralStringRef(PERSIST_PREFIX "PendingCheckpoint0"));
static const std::string rocksdbCheckpointDirPrefix = "/rockscheckpoints_"; static const std::string rocksdbCheckpointDirPrefix = "/rockscheckpoints_";
struct AddingShard : NonCopyable { struct AddingShard : NonCopyable {

View File

@ -37,7 +37,7 @@
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/workloads.actor.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbserver/QuietDatabase.h" #include "fdbserver/QuietDatabase.h"
#include "fdbclient/MonitorLeader.h" #include "fdbclient/MonitorLeader.h"
#include "fdbserver/CoordinationInterface.h" #include "fdbserver/CoordinationInterface.h"

View File

@ -3023,34 +3023,45 @@ TEST_CASE("/fdbserver/worker/swversion/runCompatibleOlder") {
return Void(); return Void();
} }
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName, {
ProtocolVersion::withStorageInterfaceReadiness(), ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
ProtocolVersion::withStorageInterfaceReadiness(), ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS()))); ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS())));
ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
TraceEvent(SevInfo, "UT/swversion/runCompatibleOlder").detail("SWVersion", swversion.get());
} }
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName, {
ProtocolVersion::withTSS(), ErrorOr<SWVersion> swversion = wait(errorOr(
ProtocolVersion::withStorageInterfaceReadiness(), testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
ProtocolVersion::withTSS())));
ErrorOr<SWVersion> swversion = wait(errorOr( if (!swversion.isError()) {
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness()))); ASSERT(swversion.get().newestProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
if (!swversion.isError()) { TraceEvent(SevInfo, "UT/swversion/runCompatibleOlder").detail("SWVersion", swversion.get());
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version()); }
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withTSS().version()); }
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
{
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
ProtocolVersion::withTSS(),
ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS())));
}
{
ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withTSS().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
}
} }
wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true)); wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true));
@ -3064,24 +3075,32 @@ TEST_CASE("/fdbserver/worker/swversion/runIncompatibleOlder") {
return Void(); return Void();
} }
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName, {
ProtocolVersion::withStorageInterfaceReadiness(), ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
ProtocolVersion::withStorageInterfaceReadiness(), ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS()))); ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS())));
ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
} }
ErrorOr<SWVersion> swversion = {
wait(errorOr(testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withCacheRole()))); ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
ASSERT(swversion.isError() && swversion.getError().code() == error_code_incompatible_software_version); if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
}
}
{
ErrorOr<SWVersion> swversion =
wait(errorOr(testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withCacheRole())));
ASSERT(swversion.isError() && swversion.getError().code() == error_code_incompatible_software_version);
}
wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true)); wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true));
@ -3094,32 +3113,42 @@ TEST_CASE("/fdbserver/worker/swversion/runNewer") {
return Void(); return Void();
} }
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName, {
ProtocolVersion::withTSS(), ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
ProtocolVersion::withTSS(), ProtocolVersion::withTSS(),
ProtocolVersion::withCacheRole()))); ProtocolVersion::withTSS(),
ProtocolVersion::withCacheRole())));
ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withTSS().version());
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withTSS().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withCacheRole().version());
} }
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName, {
ProtocolVersion::withStorageInterfaceReadiness(), ErrorOr<SWVersion> swversion = wait(errorOr(
ProtocolVersion::withStorageInterfaceReadiness(), testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
ProtocolVersion::withTSS())));
ErrorOr<SWVersion> swversion = wait(errorOr( if (!swversion.isError()) {
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness()))); ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withTSS().version());
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withTSS().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withCacheRole().version());
}
}
if (!swversion.isError()) { {
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version()); ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version()); ProtocolVersion::withStorageInterfaceReadiness(),
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version()); ProtocolVersion::withStorageInterfaceReadiness(),
ProtocolVersion::withTSS())));
}
{
ErrorOr<SWVersion> swversion = wait(errorOr(
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
if (!swversion.isError()) {
ASSERT(swversion.get().newestProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lastRunProtocolVersion() ==
ProtocolVersion::withStorageInterfaceReadiness().version());
ASSERT(swversion.get().lowestCompatibleProtocolVersion() == ProtocolVersion::withTSS().version());
}
} }
wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true)); wait(IAsyncFileSystem::filesystem()->deleteFile(joinPath(swversionTestDirName, versionFileName), true));

View File

@ -35,7 +35,7 @@ struct ApiCorrectnessWorkload : ApiWorkload {
private: private:
// Enable to track the activity on a particular key // Enable to track the activity on a particular key
#if CENABLED(0, NOT_IN_CLEAN) #if CENABLED(0, NOT_IN_CLEAN)
#define targetKey LiteralStringRef( ??? ) #define targetKey "???"_sr
void debugKey(KeyRef key, std::string context) { void debugKey(KeyRef key, std::string context) {
if (key == targetKey) if (key == targetKey)
@ -435,7 +435,7 @@ public:
// Gets a single range of values from the database and memory stores and compares them, returning true if the // Gets a single range of values from the database and memory stores and compares them, returning true if the
// results were the same // results were the same
ACTOR Future<bool> runGetRange(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) { ACTOR Future<bool> runGetRange(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) {
state Reverse reverse = deterministicRandom()->coinflip(); state Reverse reverse(deterministicRandom()->coinflip());
// Generate a random range // Generate a random range
Key key = self->selectRandomKey(data, 0.5); Key key = self->selectRandomKey(data, 0.5);
@ -481,7 +481,7 @@ public:
// Gets a single range of values using key selectors from the database and memory store and compares them, returning // Gets a single range of values using key selectors from the database and memory store and compares them, returning
// true if the results were the same // true if the results were the same
ACTOR Future<bool> runGetRangeSelector(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) { ACTOR Future<bool> runGetRangeSelector(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) {
state Reverse reverse = deterministicRandom()->coinflip(); state Reverse reverse(deterministicRandom()->coinflip());
KeySelector selectors[2]; KeySelector selectors[2];
Key keys[2]; Key keys[2];

View File

@ -1007,6 +1007,13 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload {
ACTOR Future<bool> _check(Database cx, BlobGranuleCorrectnessWorkload* self) { ACTOR Future<bool> _check(Database cx, BlobGranuleCorrectnessWorkload* self) {
// check error counts, and do an availability check at the end // check error counts, and do an availability check at the end
state std::vector<Future<bool>> results; state std::vector<Future<bool>> results;
state Future<Void> checkFeedCleanupFuture;
if (self->clientId == 0) {
checkFeedCleanupFuture = checkFeedCleanup(cx, BGW_DEBUG);
} else {
checkFeedCleanupFuture = Future<Void>(Void());
}
for (auto& it : self->directories) { for (auto& it : self->directories) {
results.push_back(self->checkDirectory(cx, self, it)); results.push_back(self->checkDirectory(cx, self, it));
} }
@ -1015,6 +1022,7 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload {
bool dirSuccess = wait(f); bool dirSuccess = wait(f);
allSuccessful &= dirSuccess; allSuccessful &= dirSuccess;
} }
wait(checkFeedCleanupFuture);
return allSuccessful; return allSuccessful;
} }

View File

@ -527,66 +527,70 @@ struct BlobGranuleRangesWorkload : TestWorkload {
bool fail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), true)); bool fail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), true));
ASSERT(!fail8); ASSERT(!fail8);
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000)); {
ASSERT(blobRanges.size() == 1); Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
ASSERT(blobRanges[0] == activeRange); ASSERT(blobRanges.size() == 1);
ASSERT(blobRanges[0] == activeRange);
state Transaction tr(cx); state Transaction tr(cx);
loop { loop {
try { try {
Standalone<VectorRef<KeyRangeRef>> granules = wait(tr.getBlobGranuleRanges(range, 1000000)); Standalone<VectorRef<KeyRangeRef>> granules = wait(tr.getBlobGranuleRanges(range, 1000000));
ASSERT(granules.size() == 1); ASSERT(granules.size() == 1);
ASSERT(granules[0] == activeRange); ASSERT(granules[0] == activeRange);
break; break;
} catch (Error& e) { } catch (Error& e) {
wait(tr.onError(e)); wait(tr.onError(e));
}
}
// tear down + check that un-blobbifying at a non-aligned range also doesn't work
Key purgeKey = wait(self->versionedForcePurge(cx, activeRange, {}));
wait(cx->waitPurgeGranulesComplete(purgeKey));
if (deterministicRandom()->coinflip()) {
// force purge again and ensure it is idempotent
Key purgeKeyAgain = wait(cx->purgeBlobGranules(activeRange, 1, {}, true));
wait(cx->waitPurgeGranulesComplete(purgeKeyAgain));
} }
} }
// tear down + check that un-blobbifying at a non-aligned range also doesn't work
Key purgeKey = wait(self->versionedForcePurge(cx, activeRange, {}));
wait(cx->waitPurgeGranulesComplete(purgeKey));
if (deterministicRandom()->coinflip()) {
// force purge again and ensure it is idempotent
Key purgeKeyAgain = wait(cx->purgeBlobGranules(activeRange, 1, {}, true));
wait(cx->waitPurgeGranulesComplete(purgeKeyAgain));
}
// Check that the blob range is still listed // Check that the blob range is still listed
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000)); {
ASSERT(blobRanges.size() == 1); Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
ASSERT(blobRanges[0] == activeRange); ASSERT(blobRanges.size() == 1);
ASSERT(blobRanges[0] == activeRange);
bool unblobbifyFail1 = wait(self->setRange(cx, range, false)); bool unblobbifyFail1 = wait(self->setRange(cx, range, false));
ASSERT(!unblobbifyFail1); ASSERT(!unblobbifyFail1);
bool unblobbifyFail2 = wait(self->setRange(cx, KeyRangeRef(range.begin, activeRange.end), false)); bool unblobbifyFail2 = wait(self->setRange(cx, KeyRangeRef(range.begin, activeRange.end), false));
ASSERT(!unblobbifyFail2); ASSERT(!unblobbifyFail2);
bool unblobbifyFail3 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, range.end), false)); bool unblobbifyFail3 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, range.end), false));
ASSERT(!unblobbifyFail3); ASSERT(!unblobbifyFail3);
bool unblobbifyFail4 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false)); bool unblobbifyFail4 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
ASSERT(!unblobbifyFail4); ASSERT(!unblobbifyFail4);
bool unblobbifyFail5 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false)); bool unblobbifyFail5 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
ASSERT(!unblobbifyFail5); ASSERT(!unblobbifyFail5);
bool unblobbifyFail6 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false)); bool unblobbifyFail6 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
ASSERT(!unblobbifyFail6); ASSERT(!unblobbifyFail6);
bool unblobbifyFail7 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false)); bool unblobbifyFail7 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
ASSERT(!unblobbifyFail7); ASSERT(!unblobbifyFail7);
bool unblobbifyFail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), false)); bool unblobbifyFail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), false));
ASSERT(!unblobbifyFail8); ASSERT(!unblobbifyFail8);
bool unblobbifySuccess = wait(self->setRange(cx, activeRange, true)); bool unblobbifySuccess = wait(self->setRange(cx, activeRange, true));
ASSERT(unblobbifySuccess); ASSERT(unblobbifySuccess);
bool unblobbifySuccessAgain = wait(self->setRange(cx, activeRange, true)); bool unblobbifySuccessAgain = wait(self->setRange(cx, activeRange, true));
ASSERT(unblobbifySuccessAgain); ASSERT(unblobbifySuccessAgain);
}
return Void(); return Void();
} }

View File

@ -304,6 +304,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
state int64_t timeTravelChecksMemory = 0; state int64_t timeTravelChecksMemory = 0;
state Version prevPurgeVersion = -1; state Version prevPurgeVersion = -1;
state UID dbgId = debugRandom()->randomUniqueID(); state UID dbgId = debugRandom()->randomUniqueID();
state Version newPurgeVersion = 0;
TraceEvent("BlobGranuleVerifierStart"); TraceEvent("BlobGranuleVerifierStart");
if (BGV_DEBUG) { if (BGV_DEBUG) {
@ -321,6 +322,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
try { try {
state double currentTime = now(); state double currentTime = now();
state std::map<double, OldRead>::iterator timeTravelIt = timeTravelChecks.begin(); state std::map<double, OldRead>::iterator timeTravelIt = timeTravelChecks.begin();
newPurgeVersion = 0;
while (timeTravelIt != timeTravelChecks.end() && currentTime >= timeTravelIt->first) { while (timeTravelIt != timeTravelChecks.end() && currentTime >= timeTravelIt->first) {
state OldRead oldRead = timeTravelIt->second; state OldRead oldRead = timeTravelIt->second;
timeTravelChecksMemory -= oldRead.oldResult.expectedSize(); timeTravelChecksMemory -= oldRead.oldResult.expectedSize();
@ -331,7 +333,6 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
} }
// before doing read, purge just before read version // before doing read, purge just before read version
state Version newPurgeVersion = 0;
state bool doPurging = state bool doPurging =
allowPurging && !self->purgeAtLatest && deterministicRandom()->random01() < 0.5; allowPurging && !self->purgeAtLatest && deterministicRandom()->random01() < 0.5;
state bool forcePurge = doPurging && self->doForcePurge && deterministicRandom()->random01() < 0.25; state bool forcePurge = doPurging && self->doForcePurge && deterministicRandom()->random01() < 0.25;
@ -1028,6 +1029,13 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
wait(self->setUpBlobRange(cx)); wait(self->setUpBlobRange(cx));
} }
state Future<Void> checkFeedCleanupFuture;
if (self->clientId == 0) {
checkFeedCleanupFuture = checkFeedCleanup(cx, BGV_DEBUG);
} else {
checkFeedCleanupFuture = Future<Void>(Void());
}
state Version readVersion = wait(self->doGrv(&tr)); state Version readVersion = wait(self->doGrv(&tr));
state Version startReadVersion = readVersion; state Version startReadVersion = readVersion;
state int checks = 0; state int checks = 0;
@ -1124,6 +1132,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
} }
state bool dataPassed = wait(self->checkAllData(cx, self)); state bool dataPassed = wait(self->checkAllData(cx, self));
wait(checkFeedCleanupFuture);
state bool result = state bool result =
availabilityPassed && dataPassed && self->mismatches == 0 && (checks > 0) && (self->timeTravelTooOld == 0); availabilityPassed && dataPassed && self->mismatches == 0 && (checks > 0) && (self->timeTravelTooOld == 0);

View File

@ -35,7 +35,7 @@ struct BulkSetupWorkload : TestWorkload {
BulkSetupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { BulkSetupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount;
nodeCount = getOption(options, "nodeCount"_sr, transactionsPerSecond * clientCount); nodeCount = getOption(options, "nodeCount"_sr, transactionsPerSecond * clientCount);
keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, LiteralStringRef("")).toString()); keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, ""_sr).toString());
std::vector<std::string> tenants = getOption(options, "tenants"_sr, std::vector<std::string>()); std::vector<std::string> tenants = getOption(options, "tenants"_sr, std::vector<std::string>());
for (std::string tenant : tenants) { for (std::string tenant : tenants) {
tenantNames.push_back(TenantName(tenant)); tenantNames.push_back(TenantName(tenant));

View File

@ -56,13 +56,14 @@ class ConfigIncrementWorkload : public TestWorkload {
} }
ACTOR static Future<Void> incrementActor(ConfigIncrementWorkload* self, Database cx) { ACTOR static Future<Void> incrementActor(ConfigIncrementWorkload* self, Database cx) {
TraceEvent(SevDebug, "ConfigIncrementStartIncrementActor");
state int trsComplete = 0; state int trsComplete = 0;
state Reference<ISingleThreadTransaction> tr;
TraceEvent(SevDebug, "ConfigIncrementStartIncrementActor");
while (trsComplete < self->incrementsPerActor) { while (trsComplete < self->incrementsPerActor) {
try { try {
loop { loop {
try { try {
state Reference<ISingleThreadTransaction> tr = self->getTransaction(cx); tr = self->getTransaction(cx);
state int currentValue = wait(get(tr)); state int currentValue = wait(get(tr));
ASSERT_GE(currentValue, self->lastKnownValue); ASSERT_GE(currentValue, self->lastKnownValue);
set(tr, currentValue + 1); set(tr, currentValue + 1);
@ -100,9 +101,11 @@ class ConfigIncrementWorkload : public TestWorkload {
} }
ACTOR static Future<bool> check(ConfigIncrementWorkload* self, Database cx) { ACTOR static Future<bool> check(ConfigIncrementWorkload* self, Database cx) {
state Reference<ISingleThreadTransaction> tr;
loop { loop {
tr.clear();
try { try {
state Reference<ISingleThreadTransaction> tr = self->getTransaction(cx); tr = self->getTransaction(cx);
state int currentValue = wait(get(tr)); state int currentValue = wait(get(tr));
auto expectedValue = self->incrementActors * self->incrementsPerActor; auto expectedValue = self->incrementActors * self->incrementsPerActor;
TraceEvent("ConfigIncrementCheck") TraceEvent("ConfigIncrementCheck")

View File

@ -564,6 +564,9 @@ struct ConsistencyCheckWorkload : TestWorkload {
state int j = 0; state int j = 0;
for (j = 0; j < iter_ss.size(); j++) { for (j = 0; j < iter_ss.size(); j++) {
resetReply(req); resetReply(req);
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
cx->getLatestCommitVersion(iter_ss[j], req.version, req.ssLatestCommitVersions);
}
keyValueFutures.push_back(iter_ss[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); keyValueFutures.push_back(iter_ss[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
} }
@ -801,6 +804,9 @@ struct ConsistencyCheckWorkload : TestWorkload {
state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures; state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures;
for (const auto& kv : shards[i].second) { for (const auto& kv : shards[i].second) {
resetReply(req); resetReply(req);
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
cx->getLatestCommitVersion(kv, req.version, req.ssLatestCommitVersions);
}
keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
} }

View File

@ -20,7 +20,7 @@
#include "fdbclient/NativeAPI.actor.h" #include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbserver/QuietDatabase.h" #include "fdbserver/QuietDatabase.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/workloads.actor.h"

View File

@ -21,6 +21,7 @@
#include "fdbclient/ClusterConnectionMemoryRecord.h" #include "fdbclient/ClusterConnectionMemoryRecord.h"
#include "fdbclient/ManagementAPI.actor.h" #include "fdbclient/ManagementAPI.actor.h"
#include "fdbclient/RunTransaction.actor.h" #include "fdbclient/RunTransaction.actor.h"
#include "fdbclient/TenantManagement.actor.h"
#include "fdbrpc/simulator.h" #include "fdbrpc/simulator.h"
#include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/workloads.actor.h"
#include "flow/ApiVersion.h" #include "flow/ApiVersion.h"
@ -39,9 +40,7 @@ struct DifferentClustersSameRVWorkload : TestWorkload {
DifferentClustersSameRVWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { DifferentClustersSameRVWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
ASSERT(g_simulator->extraDatabases.size() == 1); ASSERT(g_simulator->extraDatabases.size() == 1);
auto extraFile = extraDB = Database::createSimulatedExtraDatabase(g_simulator->extraDatabases[0], wcx.defaultTenant);
makeReference<ClusterConnectionMemoryRecord>(ClusterConnectionString(g_simulator->extraDatabases[0]));
extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION);
testDuration = getOption(options, "testDuration"_sr, 100.0); testDuration = getOption(options, "testDuration"_sr, 100.0);
switchAfter = getOption(options, "switchAfter"_sr, 50.0); switchAfter = getOption(options, "switchAfter"_sr, 50.0);
keyToRead = getOption(options, "keyToRead"_sr, "someKey"_sr); keyToRead = getOption(options, "keyToRead"_sr, "someKey"_sr);
@ -50,13 +49,27 @@ struct DifferentClustersSameRVWorkload : TestWorkload {
std::string description() const override { return "DifferentClustersSameRV"; } std::string description() const override { return "DifferentClustersSameRV"; }
Future<Void> setup(Database const& cx) override { return Void(); } Future<Void> setup(Database const& cx) override {
if (clientId != 0) {
return Void();
}
return _setup(cx, this);
}
ACTOR static Future<Void> _setup(Database cx, DifferentClustersSameRVWorkload* self) {
if (self->extraDB->defaultTenant.present()) {
wait(success(TenantAPI::createTenant(self->extraDB.getReference(), self->extraDB->defaultTenant.get())));
}
return Void();
}
Future<Void> start(Database const& cx) override { Future<Void> start(Database const& cx) override {
if (clientId != 0) { if (clientId != 0) {
return Void(); return Void();
} }
auto switchConnFileDb = Database::createDatabase(cx->getConnectionRecord(), -1); auto switchConnFileDb = Database::createDatabase(cx->getConnectionRecord(), -1);
switchConnFileDb->defaultTenant = cx->defaultTenant;
originalDB = cx; originalDB = cx;
std::vector<Future<Void>> clients = { readerClientSeparateDBs(cx, this), std::vector<Future<Void>> clients = { readerClientSeparateDBs(cx, this),
doSwitch(switchConnFileDb, this), doSwitch(switchConnFileDb, this),

View File

@ -25,7 +25,7 @@
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "fdbserver/QuietDatabase.h" #include "fdbserver/QuietDatabase.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
struct DiskFailureInjectionWorkload : FailureInjectionWorkload { struct DiskFailureInjectionWorkload : FailureInjectionWorkload {

View File

@ -436,8 +436,10 @@ struct GetMappedRangeWorkload : ApiWorkload {
} }
wait(self->scanMappedRange(cx, 10, 490, mapper, self, matchIndex)); wait(self->scanMappedRange(cx, 10, 490, mapper, self, matchIndex));
Key mapper = getMapper(self, true); {
wait(self->scanMappedRange(cx, 10, 490, mapper, self, MATCH_INDEX_UNMATCHED_ONLY, true)); Key mapper = getMapper(self, true);
wait(self->scanMappedRange(cx, 10, 490, mapper, self, MATCH_INDEX_UNMATCHED_ONLY, true));
}
return Void(); return Void();
} }

View File

@ -65,7 +65,7 @@ struct IDDTxnProcessorApiWorkload : TestWorkload {
IDDTxnProcessorApiWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ddContext(UID()) { IDDTxnProcessorApiWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ddContext(UID()) {
enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client
testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); testDuration = getOption(options, "testDuration"_sr, 10.0);
} }
std::string description() const override { return desc; } std::string description() const override { return desc; }

View File

@ -906,18 +906,25 @@ struct MetaclusterManagementWorkload : TestWorkload {
std::map<ClusterName, DataClusterMetadata> dataClusters = wait(MetaclusterAPI::listClusters( std::map<ClusterName, DataClusterMetadata> dataClusters = wait(MetaclusterAPI::listClusters(
self->managementDb, ""_sr, "\xff\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS + 1)); self->managementDb, ""_sr, "\xff\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS + 1));
int totalTenantGroupsAllocated = 0;
std::vector<Future<Void>> dataClusterChecks; std::vector<Future<Void>> dataClusterChecks;
for (auto [clusterName, dataClusterData] : self->dataDbs) { for (auto [clusterName, dataClusterData] : self->dataDbs) {
auto dataClusterItr = dataClusters.find(clusterName); auto dataClusterItr = dataClusters.find(clusterName);
if (dataClusterData.registered) { if (dataClusterData.registered) {
ASSERT(dataClusterItr != dataClusters.end()); ASSERT(dataClusterItr != dataClusters.end());
ASSERT(dataClusterItr->second.entry.capacity.numTenantGroups == dataClusterData.tenantGroupCapacity); ASSERT(dataClusterItr->second.entry.capacity.numTenantGroups == dataClusterData.tenantGroupCapacity);
totalTenantGroupsAllocated +=
dataClusterData.tenantGroups.size() + dataClusterData.ungroupedTenants.size();
} else { } else {
ASSERT(dataClusterItr == dataClusters.end()); ASSERT(dataClusterItr == dataClusters.end());
} }
dataClusterChecks.push_back(checkDataCluster(self, clusterName, dataClusterData)); dataClusterChecks.push_back(checkDataCluster(self, clusterName, dataClusterData));
} }
auto capacityNumbers = MetaclusterAPI::metaclusterCapacity(dataClusters);
ASSERT(capacityNumbers.first.numTenantGroups == self->totalTenantGroupCapacity);
ASSERT(capacityNumbers.second.numTenantGroups == totalTenantGroupsAllocated);
wait(waitForAll(dataClusterChecks)); wait(waitForAll(dataClusterChecks));
wait(decommissionMetacluster(self)); wait(decommissionMetacluster(self));

View File

@ -74,6 +74,7 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
ACTOR Future<Void> _start(PhysicalShardMoveWorkLoad* self, Database cx) { ACTOR Future<Void> _start(PhysicalShardMoveWorkLoad* self, Database cx) {
int ignore = wait(setDDMode(cx, 0)); int ignore = wait(setDDMode(cx, 0));
state std::vector<UID> teamA;
state std::map<Key, Value> kvs({ { "TestKeyA"_sr, "TestValueA"_sr }, state std::map<Key, Value> kvs({ { "TestKeyA"_sr, "TestValueA"_sr },
{ "TestKeyB"_sr, "TestValueB"_sr }, { "TestKeyB"_sr, "TestValueB"_sr },
{ "TestKeyC"_sr, "TestValueC"_sr }, { "TestKeyC"_sr, "TestValueC"_sr },
@ -88,13 +89,14 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
state std::unordered_set<UID> excludes; state std::unordered_set<UID> excludes;
state std::unordered_set<UID> includes; state std::unordered_set<UID> includes;
state int teamSize = 1; state int teamSize = 1;
std::vector<UID> teamA = wait(self->moveShard(self, wait(store(teamA,
cx, self->moveShard(self,
deterministicRandom()->randomUniqueID(), cx,
KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr), deterministicRandom()->randomUniqueID(),
teamSize, KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr),
includes, teamSize,
excludes)); includes,
excludes)));
excludes.insert(teamA.begin(), teamA.end()); excludes.insert(teamA.begin(), teamA.end());
state uint64_t sh0 = deterministicRandom()->randomUInt64(); state uint64_t sh0 = deterministicRandom()->randomUInt64();
@ -102,13 +104,14 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
state uint64_t sh2 = deterministicRandom()->randomUInt64(); state uint64_t sh2 = deterministicRandom()->randomUInt64();
// Move range [TestKeyA, TestKeyB) to sh0. // Move range [TestKeyA, TestKeyB) to sh0.
state std::vector<UID> teamA = wait(self->moveShard(self, wait(store(teamA,
cx, self->moveShard(self,
UID(sh0, deterministicRandom()->randomUInt64()), cx,
KeyRangeRef("TestKeyA"_sr, "TestKeyB"_sr), UID(sh0, deterministicRandom()->randomUInt64()),
teamSize, KeyRangeRef("TestKeyA"_sr, "TestKeyB"_sr),
includes, teamSize,
excludes)); includes,
excludes)));
// Move range [TestKeyB, TestKeyC) to sh1, on the same server. // Move range [TestKeyB, TestKeyC) to sh1, on the same server.
includes.insert(teamA.begin(), teamA.end()); includes.insert(teamA.begin(), teamA.end());
@ -153,15 +156,19 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
wait(self->validateData(self, cx, KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr), &kvs)); wait(self->validateData(self, cx, KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr), &kvs));
TraceEvent("TestValueVerified").log(); TraceEvent("TestValueVerified").log();
int ignore = wait(setDDMode(cx, 1)); {
int _ = wait(setDDMode(cx, 1));
(void)_;
}
return Void(); return Void();
} }
ACTOR Future<Version> populateData(PhysicalShardMoveWorkLoad* self, Database cx, std::map<Key, Value>* kvs) { ACTOR Future<Version> populateData(PhysicalShardMoveWorkLoad* self, Database cx, std::map<Key, Value>* kvs) {
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
state Version version; state Version version;
state UID debugID;
loop { loop {
state UID debugID = deterministicRandom()->randomUniqueID(); debugID = deterministicRandom()->randomUniqueID();
try { try {
tr->debugTransaction(debugID); tr->debugTransaction(debugID);
for (const auto& [key, value] : *kvs) { for (const auto& [key, value] : *kvs) {
@ -188,8 +195,9 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
KeyRange range, KeyRange range,
std::map<Key, Value>* kvs) { std::map<Key, Value>* kvs) {
state Transaction tr(cx); state Transaction tr(cx);
state UID debugID;
loop { loop {
state UID debugID = deterministicRandom()->randomUniqueID(); debugID = deterministicRandom()->randomUniqueID();
try { try {
tr.debugTransaction(debugID); tr.debugTransaction(debugID);
RangeResult res = wait(tr.getRange(range, CLIENT_KNOBS->TOO_MANY)); RangeResult res = wait(tr.getRange(range, CLIENT_KNOBS->TOO_MANY));
@ -215,11 +223,13 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
Key key, Key key,
ErrorOr<Optional<Value>> expectedValue) { ErrorOr<Optional<Value>> expectedValue) {
state Transaction tr(cx); state Transaction tr(cx);
state Version readVersion;
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
loop { loop {
try { try {
state Version readVersion = wait(tr.getReadVersion()); Version _readVersion = wait(tr.getReadVersion());
readVersion = _readVersion;
state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0)); state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0));
const bool equal = !expectedValue.isError() && res == expectedValue.get(); const bool equal = !expectedValue.isError() && res == expectedValue.get();
if (!equal) { if (!equal) {
@ -244,8 +254,9 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
// state Transaction tr(cx); // state Transaction tr(cx);
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
state Version version; state Version version;
state UID debugID;
loop { loop {
state UID debugID = deterministicRandom()->randomUniqueID(); debugID = deterministicRandom()->randomUniqueID();
try { try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->debugTransaction(debugID); tr->debugTransaction(debugID);

View File

@ -25,7 +25,7 @@
#include "fdbclient/SystemData.h" #include "fdbclient/SystemData.h"
#include "fdbrpc/ContinuousSample.h" #include "fdbrpc/ContinuousSample.h"
#include "fdbclient/SimpleIni.h" #include "fdbclient/SimpleIni.h"
#include "fdbserver/Status.h" #include "fdbserver/Status.actor.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/workloads/BulkSetup.actor.h" #include "fdbserver/workloads/BulkSetup.actor.h"

View File

@ -74,6 +74,7 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
state Key endKey = "TestKey0"_sr; state Key endKey = "TestKey0"_sr;
state Value oldValue = "TestValue"_sr; state Value oldValue = "TestValue"_sr;
state KeyRange testRange = KeyRangeRef(key, endKey); state KeyRange testRange = KeyRangeRef(key, endKey);
state std::vector<CheckpointMetaData> records;
int ignore = wait(setDDMode(cx, 0)); int ignore = wait(setDDMode(cx, 0));
state Version version = wait(self->writeAndVerify(self, cx, key, oldValue)); state Version version = wait(self->writeAndVerify(self, cx, key, oldValue));
@ -98,9 +99,9 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
// Fetch checkpoint meta data. // Fetch checkpoint meta data.
loop { loop {
records.clear();
try { try {
state std::vector<CheckpointMetaData> records = wait(store(records, getCheckpointMetaData(cx, testRange, version, format)));
wait(getCheckpointMetaData(cx, testRange, version, format));
break; break;
} catch (Error& e) { } catch (Error& e) {
TraceEvent("TestFetchCheckpointMetadataError") TraceEvent("TestFetchCheckpointMetadataError")
@ -161,10 +162,11 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
// Compare the keyrange between the original database and the one restored from checkpoint. // Compare the keyrange between the original database and the one restored from checkpoint.
// For now, it should have been a single key. // For now, it should have been a single key.
tr.reset(); tr.reset();
state RangeResult res;
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE); tr.setOption(FDBTransactionOptions::LOCK_AWARE);
state RangeResult res = wait(tr.getRange(KeyRangeRef(key, endKey), CLIENT_KNOBS->TOO_MANY)); wait(store(res, tr.getRange(KeyRangeRef(key, endKey), CLIENT_KNOBS->TOO_MANY)));
break; break;
} catch (Error& e) { } catch (Error& e) {
wait(tr.onError(e)); wait(tr.onError(e));
@ -181,7 +183,10 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
kvStore->dispose(); kvStore->dispose();
wait(close); wait(close);
int ignore = wait(setDDMode(cx, 1)); {
int ignore = wait(setDDMode(cx, 1));
(void)ignore;
}
return Void(); return Void();
} }

View File

@ -419,8 +419,8 @@ TEST_CASE("/fdbclient/TaskBucket/Subspace") {
t3.append(ghi); t3.append(ghi);
printf("%d==========%s===%d\n", 13, printable(subspace_test5.pack(t3)).c_str(), subspace_test5.pack(t3).size()); printf("%d==========%s===%d\n", 13, printable(subspace_test5.pack(t3)).c_str(), subspace_test5.pack(t3).size());
ASSERT(subspace_test5.pack(t3) == subspace_test5.get(StringRef()).get(def).pack(ghi)); ASSERT(subspace_test5.pack(t3) == subspace_test5.get(StringRef()).get(def).pack(ghi));
ASSERT(subspace_test5.pack(t3) == LiteralStringRef("abc\x01user\x00\x15\x7b\x01\x00\x01" ASSERT(subspace_test5.pack(t3) == "abc\x01user\x00\x15\x7b\x01\x00\x01"
"def\x00\x01ghi\x00")); "def\x00\x01ghi\x00"_sr);
printf("%d==========%s===%d\n", printf("%d==========%s===%d\n",
14, 14,

View File

@ -154,9 +154,10 @@ struct WatchesSameKeyWorkload : TestWorkload {
* */ * */
state ReadYourWritesTransaction tr(cx); state ReadYourWritesTransaction tr(cx);
state ReadYourWritesTransaction tr2(cx); state ReadYourWritesTransaction tr2(cx);
state Value val;
loop { loop {
try { try {
state Value val = deterministicRandom()->randomUniqueID().toString(); val = deterministicRandom()->randomUniqueID().toString();
tr2.set(key, val); tr2.set(key, val);
state Future<Void> watch1 = tr2.watch(key); state Future<Void> watch1 = tr2.watch(key);
wait(tr2.commit()); wait(tr2.commit());
@ -185,7 +186,7 @@ struct WatchesSameKeyWorkload : TestWorkload {
loop { loop {
try { try {
// watch1 and watch2 are set on the same k/v pair // watch1 and watch2 are set on the same k/v pair
state Value val = deterministicRandom()->randomUniqueID().toString(); state Value val(deterministicRandom()->randomUniqueID().toString());
tr2.set(key, val); tr2.set(key, val);
state Future<Void> watch1 = tr2.watch(key); state Future<Void> watch1 = tr2.watch(key);
wait(tr2.commit()); wait(tr2.commit());

View File

@ -1,13 +1,22 @@
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
option(FLOW_USE_ZSTD "Enable zstd compression in flow" OFF)
fdb_find_sources(FLOW_SRCS) fdb_find_sources(FLOW_SRCS)
if (FLOW_USE_ZSTD)
# NOTE: To enable boost::iostreams with zstd library support, manually add
# zstd.cpp to source files is required. Ref:
# https://www.boost.org/doc/libs/1_79_0/libs/iostreams/doc/installation.html
list(APPEND FLOW_SRCS ../contrib/boost_zstd/zstd.cpp)
endif()
# Remove files with `main` defined so we can create a link test executable. # Remove files with `main` defined so we can create a link test executable.
list(REMOVE_ITEM FLOW_SRCS TLSTest.cpp) list(REMOVE_ITEM FLOW_SRCS TLSTest.cpp)
list(REMOVE_ITEM FLOW_SRCS MkCertCli.cpp) list(REMOVE_ITEM FLOW_SRCS MkCertCli.cpp)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
list(APPEND FLOW_SRCS aarch64/memcmp.S aarch64/memcpy.S) list(APPEND FLOW_SRCS aarch64/memcmp.S aarch64/memcpy.S)
endif() endif()
make_directory(${CMAKE_CURRENT_BINARY_DIR}/include/flow) make_directory(${CMAKE_CURRENT_BINARY_DIR}/include/flow)
@ -23,6 +32,14 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY
add_flow_target(STATIC_LIBRARY NAME flow SRCS ${FLOW_SRCS}) add_flow_target(STATIC_LIBRARY NAME flow SRCS ${FLOW_SRCS})
add_flow_target(STATIC_LIBRARY NAME flow_sampling SRCS ${FLOW_SRCS}) add_flow_target(STATIC_LIBRARY NAME flow_sampling SRCS ${FLOW_SRCS})
if (FLOW_USE_ZSTD)
include(CompileZstd)
compile_zstd()
target_link_libraries(flow PRIVATE libzstd_static)
target_compile_definitions(flow PUBLIC ZSTD_LIB_SUPPORTED)
endif()
# When creating a static or shared library, undefined symbols will be ignored. # When creating a static or shared library, undefined symbols will be ignored.
# Since we want to ensure no symbols from other modules are used, create an # Since we want to ensure no symbols from other modules are used, create an
# executable so the linker will throw errors if it can't find the declaration # executable so the linker will throw errors if it can't find the declaration
@ -40,67 +57,66 @@ target_link_libraries(flowlinktest PRIVATE flow stacktrace)
set(IS_ARM_MAC NO) set(IS_ARM_MAC NO)
if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(IS_ARM_MAC YES) set(IS_ARM_MAC YES)
endif()
# TODO: Limit ZSTD availability to CLANG, for gcc resolve library link ordering
if (CLANG AND NOT IS_ARM_MAC)
include(CompileZstd)
compile_zstd()
target_link_libraries(flow PUBLIC ZSTD::ZSTD)
target_compile_definitions(flow PUBLIC ZSTD_LIB_SUPPORTED)
endif() endif()
foreach(ft flow flow_sampling flowlinktest) foreach(ft flow flow_sampling flowlinktest)
target_include_directories(${ft} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include") target_include_directories(${ft} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include")
if (FLOW_USE_ZSTD)
target_link_libraries(${ft} PRIVATE stacktrace) target_include_directories(${ft} PRIVATE SYSTEM ${ZSTD_LIB_INCLUDE_DIR})
target_link_libraries(${ft} PUBLIC fmt::fmt SimpleOpt crc32)
if(UNIX AND NOT APPLE)
target_link_libraries(${ft} PRIVATE folly_memcpy)
target_compile_definitions(${ft} PRIVATE WITH_FOLLY_MEMCPY)
endif()
if (NOT APPLE AND NOT WIN32)
set (FLOW_LIBS ${FLOW_LIBS} rt)
elseif(WIN32)
target_link_libraries(${ft} PUBLIC winmm.lib)
target_link_libraries(${ft} PUBLIC psapi.lib)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set (FLOW_LIBS ${FLOW_LIBS} execinfo devstat)
find_library(EIO eio)
if(EIO)
target_link_libraries(${ft} PUBLIC ${EIO})
endif() endif()
endif()
target_link_libraries(${ft} PRIVATE ${FLOW_LIBS})
if(USE_VALGRIND) target_link_libraries(${ft} PRIVATE stacktrace)
target_link_libraries(${ft} PUBLIC Valgrind) target_link_libraries(${ft} PUBLIC fmt::fmt SimpleOpt crc32)
endif() if(UNIX AND NOT APPLE)
target_link_libraries(${ft} PUBLIC OpenSSL::SSL) target_link_libraries(${ft} PRIVATE folly_memcpy)
if(USE_WOLFSSL) target_compile_definitions(${ft} PRIVATE WITH_FOLLY_MEMCPY)
target_include_directories(${ft} SYSTEM BEFORE PUBLIC ${WOLFSSL_INCLUDE_DIR}/wolfssl) endif()
endif()
target_link_libraries(${ft} PUBLIC Threads::Threads ${CMAKE_DL_LIBS})
target_link_libraries(${ft} PUBLIC boost_target)
if(USE_VALGRIND)
target_link_libraries(${ft} PUBLIC Valgrind)
endif()
if(APPLE) if(NOT APPLE AND NOT WIN32)
find_library(IO_KIT IOKit) set(FLOW_LIBS ${FLOW_LIBS} rt)
find_library(CORE_FOUNDATION CoreFoundation) elseif(WIN32)
target_link_libraries(${ft} PRIVATE ${IO_KIT} ${CORE_FOUNDATION}) target_link_libraries(${ft} PUBLIC winmm.lib)
endif() target_link_libraries(${ft} PUBLIC psapi.lib)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(FLOW_LIBS ${FLOW_LIBS} execinfo devstat)
find_library(EIO eio)
if(EIO)
target_link_libraries(${ft} PUBLIC ${EIO})
endif()
endif()
target_link_libraries(${ft} PRIVATE ${FLOW_LIBS})
if(USE_VALGRIND)
target_link_libraries(${ft} PUBLIC Valgrind)
endif()
target_link_libraries(${ft} PUBLIC OpenSSL::SSL)
if(USE_WOLFSSL)
target_include_directories(${ft} SYSTEM BEFORE PUBLIC ${WOLFSSL_INCLUDE_DIR}/wolfssl)
endif()
target_link_libraries(${ft} PUBLIC Threads::Threads ${CMAKE_DL_LIBS})
target_link_libraries(${ft} PUBLIC boost_target)
if(USE_VALGRIND)
target_link_libraries(${ft} PUBLIC Valgrind)
endif()
if(APPLE)
find_library(IO_KIT IOKit)
find_library(CORE_FOUNDATION CoreFoundation)
target_link_libraries(${ft} PRIVATE ${IO_KIT} ${CORE_FOUNDATION})
endif()
endforeach() endforeach()
target_compile_definitions(flow_sampling PRIVATE -DENABLE_SAMPLING) target_compile_definitions(flow_sampling PRIVATE -DENABLE_SAMPLING)
if(WIN32) if(WIN32)
add_dependencies(flow_sampling_actors flow_actors) add_dependencies(flow_sampling_actors flow_actors)
endif() endif()
add_executable(mkcert MkCertCli.cpp) if(OPEN_FOR_IDE)
add_library(mkcert OBJECT MkCertCli.cpp)
else()
add_executable(mkcert MkCertCli.cpp)
endif()
target_link_libraries(mkcert PUBLIC flow) target_link_libraries(mkcert PUBLIC flow)

Some files were not shown because too many files have changed in this diff Show More