Merge remote-tracking branch 'apple/main' into vgasiunas-retry-dboperations
This commit is contained in:
commit
0e09978bde
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>" };
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue