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
|
||||
COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/generate_asm.py ${os} ${cpu}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp
|
||||
${asm_file}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/fdb_c_function_pointers.g.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fdb_c.cpp
|
||||
${asm_file}
|
||||
${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
|
||||
COMMENT "Generate C bindings")
|
||||
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
|
||||
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)
|
||||
add_custom_command(OUTPUT ${symbols}
|
||||
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_internal.h
|
||||
${symbols}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/foundationdb/fdb_c_internal.h
|
||||
${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
|
||||
COMMENT "Generate exported_symbols_list")
|
||||
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}")
|
||||
elseif(WIN32)
|
||||
else()
|
||||
if (NOT USE_UBSAN)
|
||||
if(NOT USE_UBSAN)
|
||||
# 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.
|
||||
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)
|
||||
|
||||
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_link_libraries(fdb_cpp INTERFACE fdb_c fmt::fmt)
|
||||
|
||||
|
@ -157,7 +157,7 @@ if(NOT WIN32)
|
|||
test/apitester/TesterWatchAndWaitWorkload.cpp
|
||||
test/apitester/TesterWorkload.cpp
|
||||
test/apitester/TesterWorkload.h
|
||||
)
|
||||
)
|
||||
|
||||
add_library(fdb_c_unit_tests_impl OBJECT ${UNIT_TEST_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_link_libraries(fdb_c_api_tester_impl PRIVATE fdb_cpp toml11_target Threads::Threads fmt::fmt boost_target)
|
||||
if (NOT APPLE)
|
||||
target_link_libraries(fdb_c_api_tester_impl PRIVATE stdc++fs)
|
||||
if(NOT APPLE)
|
||||
target_link_libraries(fdb_c_api_tester_impl PRIVATE stdc++fs)
|
||||
endif()
|
||||
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>)
|
||||
else()
|
||||
set(FDB_C_TARGET $<TARGET_FILE:fdb_c>)
|
||||
endif()
|
||||
add_custom_command(
|
||||
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
|
||||
DEPENDS fdb_c
|
||||
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_dependencies(fdb_c_unit_tests_impl external_client)
|
||||
add_dependencies(disconnected_timeout_unit_tests external_client)
|
||||
add_dependencies(fdb_c_api_tester_impl external_client)
|
||||
add_custom_command(
|
||||
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
|
||||
DEPENDS fdb_c
|
||||
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_dependencies(fdb_c_unit_tests_impl external_client)
|
||||
add_dependencies(disconnected_timeout_unit_tests external_client)
|
||||
add_dependencies(fdb_c_api_tester_impl external_client)
|
||||
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_setup_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_setup_tests>)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_unit_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_unit_tests_version_510
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests_version_510>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME trace_partial_file_suffix_test
|
||||
COMMAND $<TARGET_FILE:trace_partial_file_suffix_test>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_external_client_unit_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
fdb
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
)
|
||||
add_unavailable_fdbclient_test(
|
||||
NAME disconnected_timeout_unit_tests
|
||||
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
)
|
||||
add_unavailable_fdbclient_test(
|
||||
NAME disconnected_timeout_external_client_unit_tests
|
||||
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests
|
||||
DISABLE_LOG_DUMP
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_setup_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_setup_tests>)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_unit_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_unit_tests_version_510
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests_version_510>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME trace_partial_file_suffix_test
|
||||
COMMAND $<TARGET_FILE:trace_partial_file_suffix_test>
|
||||
@CLUSTER_FILE@
|
||||
fdb)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_external_client_unit_tests
|
||||
COMMAND $<TARGET_FILE:fdb_c_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
fdb
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
)
|
||||
add_unavailable_fdbclient_test(
|
||||
NAME disconnected_timeout_unit_tests
|
||||
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
)
|
||||
add_unavailable_fdbclient_test(
|
||||
NAME disconnected_timeout_external_client_unit_tests
|
||||
COMMAND $<TARGET_FILE:disconnected_timeout_unit_tests>
|
||||
@CLUSTER_FILE@
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests
|
||||
DISABLE_LOG_DUMP
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_local_only
|
||||
DISABLE_LOG_DUMP
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/local_tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_local_only
|
||||
DISABLE_LOG_DUMP
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/local_tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_blob_granule
|
||||
DISABLE_LOG_DUMP
|
||||
API_TEST_BLOB_GRANULES_ENABLED
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/blobgranuletests
|
||||
--blob-granule-local-file-path
|
||||
@DATA_DIR@/fdbblob/
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_blob_granule
|
||||
DISABLE_LOG_DUMP
|
||||
API_TEST_BLOB_GRANULES_ENABLED
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/blobgranuletests
|
||||
--blob-granule-local-file-path
|
||||
@DATA_DIR@/fdbblob/
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
)
|
||||
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_with_tls
|
||||
DISABLE_LOG_DUMP
|
||||
TLS_ENABLED
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
--tls-cert-file
|
||||
@CLIENT_CERT_FILE@
|
||||
--tls-key-file
|
||||
@CLIENT_KEY_FILE@
|
||||
--tls-ca-file
|
||||
@SERVER_CA_FILE@
|
||||
)
|
||||
add_fdbclient_test(
|
||||
NAME fdb_c_api_tests_with_tls
|
||||
DISABLE_LOG_DUMP
|
||||
TLS_ENABLED
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/run_c_api_tests.py
|
||||
--cluster-file
|
||||
@CLUSTER_FILE@
|
||||
--tester-binary
|
||||
$<TARGET_FILE:fdb_c_api_tester>
|
||||
--external-client-library
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libfdb_c_external.so
|
||||
--test-dir
|
||||
${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
--tmp-dir
|
||||
@TMP_DIR@
|
||||
--log-dir
|
||||
@LOG_DIR@
|
||||
--tls-cert-file
|
||||
@CLIENT_CERT_FILE@
|
||||
--tls-key-file
|
||||
@CLIENT_KEY_FILE@
|
||||
--tls-ca-file
|
||||
@SERVER_CA_FILE@
|
||||
)
|
||||
|
||||
add_test(NAME fdb_c_upgrade_to_future_version
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/TestRunner/upgrade_test.py
|
||||
add_test(NAME fdb_c_upgrade_to_future_version
|
||||
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}
|
||||
--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
|
||||
)
|
||||
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
|
||||
add_test(NAME fdb_c_upgrade_multi_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/ApiBlobGranulesCorrectness.toml
|
||||
--upgrade-path "7.2.0" "7.3.0" "7.2.0"
|
||||
--blob-granules-enabled
|
||||
--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"
|
||||
--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
|
||||
add_test(NAME fdb_c_upgrade_multi_threaded_710api
|
||||
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 "6.3.23" "7.0.0" "7.1.9" "7.2.0" "7.1.9"
|
||||
--process-number 3
|
||||
)
|
||||
--build-dir ${CMAKE_BINARY_DIR}
|
||||
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
|
||||
--upgrade-path "7.1.9" "7.2.0" "7.1.9"
|
||||
--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
|
||||
--build-dir ${CMAKE_BINARY_DIR}
|
||||
--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"
|
||||
--process-number 3
|
||||
)
|
||||
--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_upgrade_multi_threaded_710api
|
||||
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" "7.2.0" "7.1.9"
|
||||
--process-number 3
|
||||
)
|
||||
|
||||
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
|
||||
--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
|
||||
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 "6.3.24" "wiggle" "7.0.0"
|
||||
--disable-log-dump
|
||||
--process-number 3
|
||||
--redundancy double
|
||||
)
|
||||
--build-dir ${CMAKE_BINARY_DIR}
|
||||
--test-file ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests/upgrade/MixedApiWorkloadMultiThr.toml
|
||||
--upgrade-path "6.3.24" "wiggle" "7.0.0"
|
||||
--disable-log-dump
|
||||
--process-number 3
|
||||
--redundancy double
|
||||
)
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
@ -462,12 +462,12 @@ set_target_properties(c_workloads PROPERTIES
|
|||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/share/foundationdb")
|
||||
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")
|
||||
endif()
|
||||
|
||||
# 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)
|
||||
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}
|
||||
COMMAND $<TARGET_FILE:Python3::Interpreter> ${IMPLIBSO_SRC_DIR}/implib-gen.py
|
||||
--target ${CMAKE_SYSTEM_PROCESSOR}
|
||||
--outdir ${SHIM_LIB_OUTPUT_DIR}
|
||||
--dlopen-callback=fdb_shim_dlopen_callback
|
||||
$<TARGET_FILE:fdb_c>
|
||||
--target ${CMAKE_SYSTEM_PROCESSOR}
|
||||
--outdir ${SHIM_LIB_OUTPUT_DIR}
|
||||
--dlopen-callback=fdb_shim_dlopen_callback
|
||||
$<TARGET_FILE:fdb_c>
|
||||
DEPENDS ${IMPLIBSO_SRC} fdb_c
|
||||
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
|
||||
COMMAND $<TARGET_FILE:Python3::Interpreter> ${CMAKE_CURRENT_SOURCE_DIR}/test/fdb_c_shim_tests.py
|
||||
--build-dir ${CMAKE_BINARY_DIR}
|
||||
--unit-tests-bin $<TARGET_FILE:fdb_c_shim_unit_tests>
|
||||
--api-tester-bin $<TARGET_FILE:fdb_c_shim_api_tester>
|
||||
--shim-lib-tester-bin $<TARGET_FILE:fdb_c_shim_lib_tester>
|
||||
--api-test-dir ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
)
|
||||
--build-dir ${CMAKE_BINARY_DIR}
|
||||
--unit-tests-bin $<TARGET_FILE:fdb_c_shim_unit_tests>
|
||||
--api-tester-bin $<TARGET_FILE:fdb_c_shim_api_tester>
|
||||
--shim-lib-tester-bin $<TARGET_FILE:fdb_c_shim_lib_tester>
|
||||
--api-test-dir ${CMAKE_SOURCE_DIR}/bindings/c/test/apitester/tests
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
fdb_install(
|
||||
FILES foundationdb/fdb_c_shim.h
|
||||
DESTINATION include
|
||||
DESTINATION_SUFFIX /foundationdb
|
||||
COMPONENT clients)
|
||||
fdb_install(
|
||||
FILES foundationdb/fdb_c_shim.h
|
||||
DESTINATION include
|
||||
DESTINATION_SUFFIX /foundationdb
|
||||
COMPONENT clients)
|
||||
|
||||
fdb_install(
|
||||
TARGETS fdb_c_shim
|
||||
EXPORT ${targets_export_name}
|
||||
DESTINATION lib
|
||||
COMPONENT clients)
|
||||
fdb_install(
|
||||
TARGETS fdb_c_shim
|
||||
EXPORT ${targets_export_name}
|
||||
DESTINATION lib
|
||||
COMPONENT clients)
|
||||
|
||||
endif() # End Linux only, non-ubsan only
|
||||
|
|
|
@ -1001,7 +1001,7 @@ GetMappedRangeResult getMappedIndexEntries(int beginId,
|
|||
TEST_CASE("versionstamp_unit_test") {
|
||||
// a random 12 bytes long StringRef as a versionstamp
|
||||
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.begin() != vs2.begin());
|
||||
|
||||
|
@ -1031,7 +1031,7 @@ TEST_CASE("versionstamp_unit_test") {
|
|||
TEST_CASE("tuple_support_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;
|
||||
Versionstamp vs(str);
|
||||
TupleVersionstamp vs(str);
|
||||
const Tuple t = Tuple::makeTuple(prefix, RECORD, vs, "{K[3]}"_sr, "{...}"_sr);
|
||||
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
|
||||
StringRef str = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11"_sr;
|
||||
try {
|
||||
Versionstamp truncatedVersionstamp(str);
|
||||
TupleVersionstamp truncatedVersionstamp(str);
|
||||
} catch (Error& e) {
|
||||
return;
|
||||
}
|
||||
|
@ -1058,7 +1058,7 @@ TEST_CASE("tuple_fail_to_append_longer_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;
|
||||
try {
|
||||
Versionstamp longerVersionstamp(str);
|
||||
TupleVersionstamp longerVersionstamp(str);
|
||||
} catch (Error& e) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "bindings/flow/fdb_flow.h"
|
||||
#include "fdbclient/Versionstamp.h"
|
||||
#include "fdbclient/TupleVersionstamp.h"
|
||||
|
||||
typedef TupleVersionstamp Versionstamp;
|
||||
|
||||
namespace FDB {
|
||||
struct Uuid {
|
||||
|
|
|
@ -4,20 +4,24 @@ function(compile_zstd)
|
|||
|
||||
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_TAG v1.5.2
|
||||
SOURCE_DIR ${ZSTD_SOURCE_DIR}
|
||||
BINARY_DIR ${ZSTD_SOURCE_DIR}
|
||||
SOURCE_SUBDIR "build/cmake"
|
||||
GIT_TAG v1.5.2
|
||||
SOURCE_SUBDIR "build/cmake"
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(ZSTD)
|
||||
FetchContent_GetProperties(ZSTD)
|
||||
if (NOT zstd_POPULATED)
|
||||
FetchContent_Populate(ZSTD)
|
||||
|
||||
add_library(ZSTD::ZSTD STATIC IMPORTED)
|
||||
set_target_properties(ZSTD::ZSTD PROPERTIES IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/libzstd.a")
|
||||
target_include_directories(ZSTD::ZSTD PUBLIC ${ZSTD_INCLUDE_DIRS})
|
||||
add_subdirectory(${zstd_SOURCE_DIR}/build/cmake ${zstd_BINARY_DIR})
|
||||
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
^^^^^^
|
||||
|
||||
``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.
|
||||
|
||||
|
@ -447,6 +447,8 @@ Creates a new tenant in the cluster.
|
|||
|
||||
``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
|
||||
^^^^^^
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ ACTOR Future<bool> blobKeyCommandActor(Database localDb,
|
|||
version = v;
|
||||
}
|
||||
|
||||
if (key >= LiteralStringRef("\xff")) {
|
||||
if (key >= "\xff"_sr) {
|
||||
fmt::print("No blob history for system keyspace\n", key.printable());
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "flow/Arena.h"
|
||||
#include "flow/FastRef.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.
|
||||
|
||||
namespace fdb_cli {
|
||||
|
|
|
@ -291,13 +291,7 @@ ACTOR Future<bool> metaclusterStatusCommand(Reference<IDatabase> db, std::vector
|
|||
std::map<ClusterName, DataClusterMetadata> clusters =
|
||||
wait(MetaclusterAPI::listClusters(db, ""_sr, "\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS));
|
||||
|
||||
ClusterUsage totalCapacity;
|
||||
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;
|
||||
}
|
||||
auto capacityNumbers = MetaclusterAPI::metaclusterCapacity(clusters);
|
||||
|
||||
if (useJson) {
|
||||
json_spirit::mObject obj;
|
||||
|
@ -305,15 +299,15 @@ ACTOR Future<bool> metaclusterStatusCommand(Reference<IDatabase> db, std::vector
|
|||
|
||||
json_spirit::mObject metaclusterObj;
|
||||
metaclusterObj["data_clusters"] = (int)clusters.size();
|
||||
metaclusterObj["capacity"] = totalCapacity.toJson();
|
||||
metaclusterObj["allocated"] = totalAllocated.toJson();
|
||||
metaclusterObj["capacity"] = capacityNumbers.first.toJson();
|
||||
metaclusterObj["allocated"] = capacityNumbers.second.toJson();
|
||||
|
||||
obj["metacluster"] = metaclusterObj;
|
||||
fmt::print("{}\n", json_spirit::write_string(json_spirit::mValue(obj), json_spirit::pretty_print).c_str());
|
||||
} else {
|
||||
fmt::print(" number of data clusters: {}\n", clusters.size());
|
||||
fmt::print(" tenant group capacity: {}\n", totalCapacity.numTenantGroups);
|
||||
fmt::print(" allocated tenant groups: {}\n", totalAllocated.numTenantGroups);
|
||||
fmt::print(" tenant group capacity: {}\n", capacityNumbers.first.numTenantGroups);
|
||||
fmt::print(" allocated tenant groups: {}\n", capacityNumbers.second.numTenantGroups);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -605,8 +605,10 @@ void tenantGenerator(const char* text,
|
|||
std::vector<const char*> tenantHintGenerator(std::vector<StringRef> const& tokens, bool inArgument) {
|
||||
if (tokens.size() == 1) {
|
||||
return { "<create|delete|list|get|configure|rename>", "[ARGS]" };
|
||||
} else if (tokencmp(tokens[1], "create") && tokens.size() < 4) {
|
||||
static std::vector<const char*> opts = { "<NAME> [tenant_group=<TENANT_GROUP>]" };
|
||||
} else if (tokencmp(tokens[1], "create") && tokens.size() < 5) {
|
||||
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());
|
||||
} else if (tokencmp(tokens[1], "delete") && tokens.size() < 3) {
|
||||
static std::vector<const char*> opts = { "<NAME>" };
|
||||
|
|
|
@ -1672,23 +1672,13 @@ TEST_CASE("/blobgranule/files/applyDelta") {
|
|||
printf("Testing blob granule delta applying\n");
|
||||
Arena a;
|
||||
|
||||
// do this 2 phase arena creation of string refs instead of LiteralStringRef because there is no char* StringRef
|
||||
// constructor, and valgrind might complain if the stringref data isn't in the arena
|
||||
std::string sk_a = "A";
|
||||
std::string sk_ab = "AB";
|
||||
std::string sk_b = "B";
|
||||
std::string sk_c = "C";
|
||||
std::string sk_z = "Z";
|
||||
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);
|
||||
StringRef k_a = StringRef(a, "A"_sr);
|
||||
StringRef k_ab = StringRef(a, "AB"_sr);
|
||||
StringRef k_b = StringRef(a, "B"_sr);
|
||||
StringRef k_c = StringRef(a, "C"_sr);
|
||||
StringRef k_z = StringRef(a, "Z"_sr);
|
||||
StringRef val1 = StringRef(a, "1"_sr);
|
||||
StringRef val2 = StringRef(a, "2"_sr);
|
||||
|
||||
std::map<KeyRef, ValueRef> data;
|
||||
data.insert({ k_a, val1 });
|
||||
|
|
|
@ -79,9 +79,9 @@ public:
|
|||
DRConfig(Reference<Task> task)
|
||||
: 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()); }
|
||||
|
||||
|
@ -136,7 +136,7 @@ struct BackupRangeTaskFunc : TaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
static const Key keyAddBackupRangeTasks;
|
||||
|
@ -704,7 +704,7 @@ struct CopyLogRangeTaskFunc : TaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
static const Key keyNextBeginVersion;
|
||||
|
@ -1455,7 +1455,7 @@ struct OldCopyLogRangeTaskFunc : TaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<int64_t> bytesWritten() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> bytesWritten() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
static const Key keyNextBeginVersion;
|
||||
|
|
|
@ -65,7 +65,7 @@ FDB_DEFINE_BOOLEAN_PARAM(IncrementalBackupOnly);
|
|||
FDB_DEFINE_BOOLEAN_PARAM(OnlyApplyMutationLogs);
|
||||
|
||||
#define SevFRTestInfo SevVerbose
|
||||
//#define SevFRTestInfo SevInfo
|
||||
// #define SevFRTestInfo SevInfo
|
||||
|
||||
static std::string boolToYesOrNo(bool val) {
|
||||
return val ? std::string("Yes") : std::string("No");
|
||||
|
@ -157,41 +157,37 @@ public:
|
|||
RestoreConfig(UID uid = UID()) : KeyBackedConfig(fileRestorePrefixRange.begin, uid) {}
|
||||
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) {
|
||||
return map(stateEnum().getD(tr),
|
||||
[](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); });
|
||||
}
|
||||
KeyBackedProperty<Key> addPrefix() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Key> removePrefix() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<bool> onlyApplyMutationLogs() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<bool> inconsistentSnapshotOnly() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Key> addPrefix() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<Key> removePrefix() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<bool> onlyApplyMutationLogs() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<bool> inconsistentSnapshotOnly() { return configSpace.pack(__FUNCTION__sr); }
|
||||
// XXX: Remove restoreRange() once it is safe to remove. It has been changed to restoreRanges
|
||||
KeyBackedProperty<KeyRange> restoreRange() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<std::vector<KeyRange>> restoreRanges() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<Key> batchFuture() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Version> beginVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Version> restoreVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Version> firstConsistentVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<KeyRange> restoreRange() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<std::vector<KeyRange>> restoreRanges() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<Key> batchFuture() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<Version> beginVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<Version> restoreVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
KeyBackedProperty<Version> firstConsistentVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
KeyBackedProperty<Reference<IBackupContainer>> sourceContainer() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<Reference<IBackupContainer>> sourceContainer() { return configSpace.pack(__FUNCTION__sr); }
|
||||
// Get the source container as a bare URL, without creating a container instance
|
||||
KeyBackedProperty<Value> sourceContainerURL() { return configSpace.pack("sourceContainer"_sr); }
|
||||
|
||||
// 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
|
||||
KeyBackedBinaryValue<int64_t> filesBlocksDispatched() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedBinaryValue<int64_t> filesBlocksDispatched() { return configSpace.pack(__FUNCTION__sr); }
|
||||
// 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
|
||||
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
|
||||
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) {
|
||||
return getRestoreRangesOrDefault_impl(this, tr);
|
||||
|
@ -234,7 +230,7 @@ public:
|
|||
};
|
||||
|
||||
typedef KeyBackedSet<RestoreFile> FileSetT;
|
||||
FileSetT fileSet() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
FileSetT fileSet() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
Future<bool> isRunnable(Reference<ReadYourWritesTransaction> tr) {
|
||||
return map(stateEnum().getD(tr), [](ERestoreState s) -> bool {
|
||||
|
@ -1533,9 +1529,9 @@ struct BackupRangeTaskFunc : BackupTaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<Key> beginKey() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Key> endKey() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<bool> addBackupRangeTasks() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Key> beginKey() { return __FUNCTION__sr; }
|
||||
static TaskParam<Key> endKey() { return __FUNCTION__sr; }
|
||||
static TaskParam<bool> addBackupRangeTasks() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
std::string toString(Reference<Task> task) const override {
|
||||
|
@ -1899,11 +1895,11 @@ struct BackupSnapshotDispatchTask : BackupTaskFuncBase {
|
|||
|
||||
static struct {
|
||||
// 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
|
||||
static TaskParam<bool> snapshotFinished() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<bool> snapshotFinished() { return __FUNCTION__sr; }
|
||||
// Set by Execute, used by Finish
|
||||
static TaskParam<Version> nextDispatchVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> nextDispatchVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
StringRef getName() const override { return name; };
|
||||
|
@ -2471,10 +2467,10 @@ struct BackupLogRangeTaskFunc : BackupTaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<bool> addBackupLogRangeTasks() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> fileSize() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<bool> addBackupLogRangeTasks() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> fileSize() { return __FUNCTION__sr; }
|
||||
static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
|
||||
static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
StringRef getName() const override { return name; };
|
||||
|
@ -2563,7 +2559,7 @@ struct BackupLogRangeTaskFunc : BackupTaskFuncBase {
|
|||
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())
|
||||
results.sendError(result.getError());
|
||||
else
|
||||
|
@ -2712,9 +2708,9 @@ struct EraseLogRangeTaskFunc : BackupTaskFuncBase {
|
|||
StringRef getName() const override { return name; };
|
||||
|
||||
static struct {
|
||||
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Key> destUidValue() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
|
||||
static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
|
||||
static TaskParam<Key> destUidValue() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr,
|
||||
|
@ -2786,8 +2782,8 @@ struct BackupLogsDispatchTask : BackupTaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<Version> prevBeginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> prevBeginVersion() { return __FUNCTION__sr; }
|
||||
static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
|
||||
|
@ -3015,7 +3011,7 @@ struct BackupSnapshotManifest : BackupTaskFuncBase {
|
|||
static StringRef name;
|
||||
static constexpr uint32_t version = 1;
|
||||
static struct {
|
||||
static TaskParam<Version> endVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> endVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
ACTOR static Future<Void> _execute(Database cx,
|
||||
|
@ -3214,7 +3210,7 @@ struct StartFullBackupTaskFunc : BackupTaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
ACTOR static Future<Void> _execute(Database cx,
|
||||
|
@ -3457,9 +3453,9 @@ REGISTER_TASKFUNC(RestoreCompleteTaskFunc);
|
|||
|
||||
struct RestoreFileTaskFuncBase : RestoreTaskFuncBase {
|
||||
struct InputParams {
|
||||
static TaskParam<RestoreFile> inputFile() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> readOffset() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> readLen() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<RestoreFile> inputFile() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> readOffset() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> readLen() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
std::string toString(Reference<Task> task) const override {
|
||||
|
@ -3474,8 +3470,8 @@ struct RestoreRangeTaskFunc : RestoreFileTaskFuncBase {
|
|||
static struct : InputParams {
|
||||
// The range of data that the (possibly empty) data represented, which is set if it intersects the target
|
||||
// restore range
|
||||
static TaskParam<KeyRange> originalFileRange() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<std::vector<KeyRange>> originalFileRanges() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<KeyRange> originalFileRange() { return __FUNCTION__sr; }
|
||||
static TaskParam<std::vector<KeyRange>> originalFileRanges() { return __FUNCTION__sr; }
|
||||
|
||||
static std::vector<KeyRange> getOriginalFileRanges(Reference<Task> task) {
|
||||
if (originalFileRanges().exists(task)) {
|
||||
|
@ -4071,11 +4067,11 @@ struct RestoreDispatchTaskFunc : RestoreTaskFuncBase {
|
|||
StringRef getName() const override { return name; };
|
||||
|
||||
static struct {
|
||||
static TaskParam<Version> beginVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<std::string> beginFile() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> beginBlock() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> batchSize() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<int64_t> remainingInBatch() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> beginVersion() { return __FUNCTION__sr; }
|
||||
static TaskParam<std::string> beginFile() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> beginBlock() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> batchSize() { return __FUNCTION__sr; }
|
||||
static TaskParam<int64_t> remainingInBatch() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
ACTOR static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
|
||||
|
@ -4544,7 +4540,7 @@ struct StartFullRestoreTaskFunc : RestoreTaskFuncBase {
|
|||
static constexpr uint32_t version = 1;
|
||||
|
||||
static struct {
|
||||
static TaskParam<Version> firstVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> firstVersion() { return __FUNCTION__sr; }
|
||||
} Params;
|
||||
|
||||
// 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) {
|
||||
state Backoff backoff;
|
||||
state Reference<ISingleThreadTransaction> configTr;
|
||||
loop {
|
||||
try {
|
||||
// Attempt to read a random value from the configuration
|
||||
// database to make sure it is online.
|
||||
state Reference<ISingleThreadTransaction> configTr =
|
||||
ISingleThreadTransaction::create(ISingleThreadTransaction::Type::PAXOS_CONFIG, cx);
|
||||
configTr = ISingleThreadTransaction::create(ISingleThreadTransaction::Type::PAXOS_CONFIG, cx);
|
||||
Tuple tuple;
|
||||
tuple.appendNull(); // config class
|
||||
tuple << "test"_sr;
|
||||
|
|
|
@ -24,6 +24,19 @@
|
|||
FDB_DEFINE_BOOLEAN_PARAM(AddNewTenants);
|
||||
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) {
|
||||
switch (clusterState) {
|
||||
case DataClusterState::READY:
|
||||
|
|
|
@ -27,6 +27,17 @@
|
|||
|
||||
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) {
|
||||
if (g_network->isSimulated()) {
|
||||
Reference<IClusterConnectionRecord> clusterFile =
|
||||
|
|
|
@ -376,7 +376,7 @@ TEST_CASE("/fdbclient/MonitorLeader/parseConnectionString/fuzz") {
|
|||
output += "#";
|
||||
int charCount = deterministicRandom()->randomInt(0, 20);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "flow/CodeProbe.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "fdbclient/FDBOptions.g.h"
|
||||
|
@ -222,6 +223,52 @@ void DatabaseContext::addSSIdTagMapping(const UID& uid, const Tag& 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,
|
||||
Version readVersion,
|
||||
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
|
||||
for (int i = 0; i < locationInfo->locations()->size(); i++) {
|
||||
bool updatedVersionMap = false;
|
||||
Version commitVersion = invalidVersion;
|
||||
Tag tag = invalidTag;
|
||||
auto iter = ssidTagMapping.find(locationInfo->locations()->getId(i));
|
||||
if (iter != ssidTagMapping.end()) {
|
||||
tag = iter->second;
|
||||
if (ssVersionVectorCache.hasVersion(tag)) {
|
||||
commitVersion = ssVersionVectorCache.getVersion(tag); // latest commit version
|
||||
if (commitVersion < readVersion) {
|
||||
updatedVersionMap = true;
|
||||
versionMap[commitVersion].insert(tag);
|
||||
}
|
||||
}
|
||||
Version commitVersion = invalidVersion; // latest commit version
|
||||
getLatestCommitVersionForSSID(locationInfo->locations()->getId(i), tag, commitVersion);
|
||||
|
||||
bool updatedVersionMap = false;
|
||||
if (tag != invalidTag && commitVersion != invalidVersion && commitVersion < readVersion) {
|
||||
updatedVersionMap = true;
|
||||
versionMap[commitVersion].insert(tag);
|
||||
}
|
||||
|
||||
// Do not log if commitVersion >= readVersion.
|
||||
if (!updatedVersionMap && commitVersion == invalidVersion) {
|
||||
TraceEvent(SevDebug, "CommitVersionNotFoundForSS")
|
||||
.detail("InSSIDMap", iter != ssidTagMapping.end() ? 1 : 0)
|
||||
.detail("InSSIDMap", tag != invalidTag ? 1 : 0)
|
||||
.detail("Tag", tag)
|
||||
.detail("CommitVersion", commitVersion)
|
||||
.detail("ReadVersion", readVersion)
|
||||
|
@ -3632,6 +3675,9 @@ ACTOR Future<Version> watchValue(Database cx, Reference<const WatchParameters> p
|
|||
parameters->useProvisionalProxies,
|
||||
Reverse::False,
|
||||
parameters->version));
|
||||
if (parameters->tenant.tenantId != locationInfo.tenantEntry.id) {
|
||||
throw tenant_not_found();
|
||||
}
|
||||
|
||||
try {
|
||||
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...
|
||||
ACTOR Future<Void> watch(Reference<Watch> watch,
|
||||
Database cx,
|
||||
|
@ -5332,16 +5416,15 @@ ACTOR Future<Void> watch(Reference<Watch> watch,
|
|||
when(wait(cx->connectionFileChanged())) {
|
||||
CODE_PROBE(true, "Recreated a watch after switch");
|
||||
cx->clearWatchMetadata();
|
||||
watch->watchFuture = watchValueMap(cx->minAcceptableReadVersion,
|
||||
tenantInfo,
|
||||
watch->key,
|
||||
watch->value,
|
||||
cx,
|
||||
tags,
|
||||
spanContext,
|
||||
taskID,
|
||||
debugID,
|
||||
useProvisionalProxies);
|
||||
watch->watchFuture = restartWatch(cx,
|
||||
tenantInfo,
|
||||
watch->key,
|
||||
watch->value,
|
||||
tags,
|
||||
spanContext,
|
||||
taskID,
|
||||
debugID,
|
||||
useProvisionalProxies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7928,6 +8011,23 @@ ACTOR Future<Standalone<VectorRef<BlobGranuleChunkRef>>> readBlobGranulesActor(
|
|||
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;
|
||||
req.keyRange = KeyRangeRef(StringRef(req.arena, granuleStartKey), StringRef(req.arena, granuleEndKey));
|
||||
req.beginVersion = begin;
|
||||
|
|
|
@ -459,7 +459,7 @@ public:
|
|||
|
||||
if (!it.is_unreadable() && !it.is_unknown_range() && key.offset > 1) {
|
||||
*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;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ S3BlobStoreEndpoint::BlobKnobs::BlobKnobs() {
|
|||
|
||||
bool S3BlobStoreEndpoint::BlobKnobs::set(StringRef name, int value) {
|
||||
#define TRY_PARAM(n, sn) \
|
||||
if (name == LiteralStringRef(#n) || name == LiteralStringRef(#sn)) { \
|
||||
if (name == #n || name == #sn) { \
|
||||
n = value; \
|
||||
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());
|
||||
// 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)
|
||||
done = map(done, [=](Void) {
|
||||
done = map(done, [=](Void) mutable {
|
||||
resultStream.sendError(end_of_stream());
|
||||
return Void();
|
||||
});
|
||||
|
@ -1193,7 +1193,7 @@ ACTOR Future<S3BlobStoreEndpoint::ListResult> listObjects_impl(Reference<S3BlobS
|
|||
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
|
||||
// stream
|
||||
done = map(done, [=](Void) {
|
||||
done = map(done, [=](Void) mutable {
|
||||
resultStream.sendError(end_of_stream());
|
||||
return Void();
|
||||
});
|
||||
|
@ -1759,7 +1759,7 @@ Future<Void> S3BlobStoreEndpoint::finishMultiPartUpload(std::string const& bucke
|
|||
}
|
||||
|
||||
TEST_CASE("/backup/s3/v4headers") {
|
||||
S3BlobStoreEndpoint::Credentials creds{ "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "" }
|
||||
S3BlobStoreEndpoint::Credentials creds{ "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "" };
|
||||
// GET without query parameters
|
||||
{
|
||||
S3BlobStoreEndpoint s3("s3.amazonaws.com", "443", "amazonaws", "proxy", "port", creds);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "fdbclient/Schemas.h"
|
||||
|
||||
// NOTE: also change mr-status-json-schemas.rst.inc
|
||||
const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
||||
const KeyRef JSONSchemas::statusSchema = R"statusSchema(
|
||||
{
|
||||
"cluster":{
|
||||
"storage_wiggler": {
|
||||
|
@ -602,7 +602,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
}
|
||||
],
|
||||
)statusSchema"
|
||||
R"statusSchema(
|
||||
R"statusSchema(
|
||||
"recovery_state":{
|
||||
"seconds_since_last_recovered":1,
|
||||
"required_resolvers":1,
|
||||
|
@ -964,6 +964,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
},
|
||||
"tenants":{
|
||||
"num_tenants":0
|
||||
},
|
||||
"metacluster" : {
|
||||
"cluster_type" : "standalone"
|
||||
}
|
||||
},
|
||||
"client":{
|
||||
|
@ -1005,9 +1008,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema(
|
|||
"up_to_date":true
|
||||
}
|
||||
}
|
||||
})statusSchema");
|
||||
})statusSchema"_sr;
|
||||
|
||||
const KeyRef JSONSchemas::clusterConfigurationSchema = LiteralStringRef(R"configSchema(
|
||||
const KeyRef JSONSchemas::clusterConfigurationSchema = R"configSchema(
|
||||
{
|
||||
"create":{
|
||||
"$enum":[
|
||||
|
@ -1077,9 +1080,9 @@ const KeyRef JSONSchemas::clusterConfigurationSchema = LiteralStringRef(R"config
|
|||
"auto_logs":3,
|
||||
"commit_proxies":5,
|
||||
"grv_proxies":1
|
||||
})configSchema");
|
||||
})configSchema"_sr;
|
||||
|
||||
const KeyRef JSONSchemas::latencyBandConfigurationSchema = LiteralStringRef(R"configSchema(
|
||||
const KeyRef JSONSchemas::latencyBandConfigurationSchema = R"configSchema(
|
||||
{
|
||||
"get_read_version":{
|
||||
"bands":[
|
||||
|
@ -1099,30 +1102,30 @@ const KeyRef JSONSchemas::latencyBandConfigurationSchema = LiteralStringRef(R"co
|
|||
],
|
||||
"max_commit_bytes":0
|
||||
}
|
||||
})configSchema");
|
||||
})configSchema"_sr;
|
||||
|
||||
const KeyRef JSONSchemas::dataDistributionStatsSchema = LiteralStringRef(R"""(
|
||||
const KeyRef JSONSchemas::dataDistributionStatsSchema = R"""(
|
||||
{
|
||||
"shard_bytes": 1947000
|
||||
}
|
||||
)""");
|
||||
)"""_sr;
|
||||
|
||||
const KeyRef JSONSchemas::logHealthSchema = LiteralStringRef(R"""(
|
||||
const KeyRef JSONSchemas::logHealthSchema = R"""(
|
||||
{
|
||||
"log_queue": 156
|
||||
}
|
||||
)""");
|
||||
)"""_sr;
|
||||
|
||||
const KeyRef JSONSchemas::storageHealthSchema = LiteralStringRef(R"""(
|
||||
const KeyRef JSONSchemas::storageHealthSchema = R"""(
|
||||
{
|
||||
"cpu_usage": 3.28629447047675,
|
||||
"disk_usage": 0.19997897369207954,
|
||||
"storage_durability_lag": 5050809,
|
||||
"storage_queue": 2030
|
||||
}
|
||||
)""");
|
||||
)"""_sr;
|
||||
|
||||
const KeyRef JSONSchemas::aggregateHealthSchema = LiteralStringRef(R"""(
|
||||
const KeyRef JSONSchemas::aggregateHealthSchema = R"""(
|
||||
{
|
||||
"batch_limited": false,
|
||||
"limiting_storage_durability_lag": 5050809,
|
||||
|
@ -1132,12 +1135,12 @@ const KeyRef JSONSchemas::aggregateHealthSchema = LiteralStringRef(R"""(
|
|||
"worst_storage_queue": 2030,
|
||||
"worst_log_queue": 156
|
||||
}
|
||||
)""");
|
||||
)"""_sr;
|
||||
|
||||
const KeyRef JSONSchemas::managementApiErrorSchema = LiteralStringRef(R"""(
|
||||
const KeyRef JSONSchemas::managementApiErrorSchema = R"""(
|
||||
{
|
||||
"retriable": false,
|
||||
"command": "exclude",
|
||||
"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.
|
||||
int64_t memtableBytes = isSimulated ? 32 * 1024 : 512 * 1024 * 1024;
|
||||
init( ROCKSDB_MEMTABLE_BYTES, memtableBytes );
|
||||
init( ROCKSDB_LEVEL_STYLE_COMPACTION, true );
|
||||
init( ROCKSDB_UNSAFE_AUTO_FSYNC, false );
|
||||
init( ROCKSDB_PERIODIC_COMPACTION_SECONDS, 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.
|
||||
init( ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE, true );
|
||||
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_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_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_INTERVAL, isSimulated ? 5.0 : 30.0 );
|
||||
init( BLOB_FULL_RESTORE_MODE, false );
|
||||
|
||||
init( BGCC_TIMEOUT, isSimulated ? 10.0 : 120.0 );
|
||||
|
|
|
@ -993,7 +993,7 @@ ACTOR Future<bool> checkExclusion(Database db,
|
|||
state int ssTotalCount = 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 totalKvStoreUsedBytes = 0;
|
||||
state int64_t totalKvStoreUsedBytesNonExcluded = 0;
|
||||
|
|
|
@ -412,7 +412,7 @@ public:
|
|||
Reference<FutureBucket> futureBucket,
|
||||
Reference<Task> task) {
|
||||
state Reference<TaskFuncBase> taskFunc;
|
||||
state VerifyTask verifyTask = false;
|
||||
state VerifyTask verifyTask(false);
|
||||
|
||||
if (!task || !TaskFuncBase::isValidTask(task))
|
||||
return false;
|
||||
|
|
|
@ -117,7 +117,7 @@ Tuple& Tuple::append(Tuple const& tuple) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Tuple& Tuple::append(Versionstamp const& vs) {
|
||||
Tuple& Tuple::append(TupleVersionstamp const& vs) {
|
||||
offsets.push_back(data.size());
|
||||
|
||||
data.push_back(data.arena(), VERSIONSTAMP_96_CODE);
|
||||
|
@ -413,7 +413,7 @@ double Tuple::getDouble(size_t index) const {
|
|||
return bigEndianDouble(swap);
|
||||
}
|
||||
|
||||
Versionstamp Tuple::getVersionstamp(size_t index) const {
|
||||
TupleVersionstamp Tuple::getVersionstamp(size_t index) const {
|
||||
if (index >= offsets.size()) {
|
||||
throw invalid_tuple_index();
|
||||
}
|
||||
|
@ -422,7 +422,7 @@ Versionstamp Tuple::getVersionstamp(size_t index) const {
|
|||
if (code != VERSIONSTAMP_96_CODE) {
|
||||
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 {
|
||||
|
@ -495,7 +495,7 @@ TEST_CASE("/fdbclient/Tuple/makeTuple") {
|
|||
"byteStr"_sr,
|
||||
Tuple::UnicodeStr("str"_sr),
|
||||
nullptr,
|
||||
Versionstamp("000000000000"_sr),
|
||||
TupleVersionstamp("000000000000"_sr),
|
||||
Tuple::UserTypeStr(0x41, "12345678"_sr));
|
||||
Tuple t2 = Tuple()
|
||||
.append(1)
|
||||
|
@ -505,7 +505,7 @@ TEST_CASE("/fdbclient/Tuple/makeTuple") {
|
|||
.append("byteStr"_sr)
|
||||
.append(Tuple::UnicodeStr("str"_sr))
|
||||
.append(nullptr)
|
||||
.append(Versionstamp("000000000000"_sr))
|
||||
.append(TupleVersionstamp("000000000000"_sr))
|
||||
.append(Tuple::UserTypeStr(0x41, "12345678"_sr));
|
||||
|
||||
ASSERT(t1.pack() == t2.pack());
|
||||
|
@ -531,7 +531,7 @@ TEST_CASE("/fdbclient/Tuple/unpack") {
|
|||
"byteStr"_sr,
|
||||
Tuple::UnicodeStr("str"_sr),
|
||||
nullptr,
|
||||
Versionstamp("000000000000"_sr),
|
||||
TupleVersionstamp("000000000000"_sr),
|
||||
Tuple::UserTypeStr(0x41, "12345678"_sr));
|
||||
|
||||
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) {
|
||||
throw invalid_versionstamp_size();
|
||||
}
|
||||
data = str;
|
||||
}
|
||||
|
||||
int16_t Versionstamp::getBatchNumber() const {
|
||||
int16_t TupleVersionstamp::getBatchNumber() const {
|
||||
const uint8_t* begin = data.begin();
|
||||
begin += 8;
|
||||
int16_t batchNumber = *(int16_t*)(begin);
|
||||
|
@ -15,7 +15,7 @@ int16_t Versionstamp::getBatchNumber() const {
|
|||
return batchNumber;
|
||||
}
|
||||
|
||||
int16_t Versionstamp::getUserVersion() const {
|
||||
int16_t TupleVersionstamp::getUserVersion() const {
|
||||
const uint8_t* begin = data.begin();
|
||||
begin += 10;
|
||||
int16_t userVersion = *(int16_t*)(begin);
|
||||
|
@ -23,22 +23,22 @@ int16_t Versionstamp::getUserVersion() const {
|
|||
return userVersion;
|
||||
}
|
||||
|
||||
const uint8_t* Versionstamp::begin() const {
|
||||
const uint8_t* TupleVersionstamp::begin() const {
|
||||
return data.begin();
|
||||
}
|
||||
|
||||
int64_t Versionstamp::getVersion() const {
|
||||
int64_t TupleVersionstamp::getVersion() const {
|
||||
const uint8_t* begin = data.begin();
|
||||
int64_t version = *(int64_t*)begin;
|
||||
version = bigEndian64(version);
|
||||
return version;
|
||||
}
|
||||
|
||||
size_t Versionstamp::size() const {
|
||||
size_t TupleVersionstamp::size() const {
|
||||
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() &&
|
||||
getUserVersion() == other.getUserVersion();
|
||||
}
|
|
@ -143,7 +143,7 @@ public:
|
|||
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) {
|
||||
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 {
|
||||
public:
|
||||
static struct {
|
||||
static TaskParam<UID> uid() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<UID> uid() { return __FUNCTION__sr; }
|
||||
} TaskParams;
|
||||
|
||||
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; }
|
||||
|
||||
|
@ -675,12 +675,10 @@ public:
|
|||
void clear(Reference<ReadYourWritesTransaction> tr) { tr->clear(configSpace.range()); }
|
||||
|
||||
// lastError is a pair of error message and timestamp expressed as an int64_t
|
||||
KeyBackedProperty<std::pair<std::string, Version>> lastError() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<std::pair<std::string, Version>> lastError() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
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
|
||||
|
@ -769,47 +767,41 @@ public:
|
|||
|
||||
// Map of range end boundaries to info about the backup file written for that range.
|
||||
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
|
||||
// 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.
|
||||
// This value exists to help with sizing of kv range folders for BackupContainers that
|
||||
// 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.
|
||||
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.
|
||||
KeyBackedProperty<int64_t> initialSnapshotIntervalSeconds() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<int64_t> initialSnapshotIntervalSeconds() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// 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
|
||||
KeyBackedProperty<Version> snapshotBeginVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Version> snapshotBeginVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// When the current snapshot is desired to end.
|
||||
// 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() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<int64_t> snapshotDispatchLastShardsBehind() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
KeyBackedProperty<Version> snapshotDispatchLastVersion() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<Version> snapshotDispatchLastVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
Future<Void> initNewSnapshot(Reference<ReadYourWritesTransaction> tr, int64_t intervalSeconds = -1) {
|
||||
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() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<Reference<IBackupContainer>> backupContainer() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
KeyBackedProperty<Version> latestBackupWorkerSavedVersion() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<Version> latestBackupWorkerSavedVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// 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
|
||||
KeyBackedProperty<bool> enableSnapshotBackupEncryption() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
}
|
||||
KeyBackedProperty<bool> enableSnapshotBackupEncryption() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// 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
|
||||
KeyBackedProperty<Version> latestSnapshotEndVersion() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); }
|
||||
KeyBackedProperty<Version> latestSnapshotEndVersion() { return configSpace.pack(__FUNCTION__sr); }
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
Key mutationLogsDestKey = destUidValue.withPrefix(backupLogKeys.begin);
|
||||
|
|
|
@ -18,8 +18,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FDBCLIENT_CONSISTENCYSCANINTERFACE_H
|
||||
#define FDBCLIENT_CONSISTENCYSCANINTERFACE_H
|
||||
#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_CONSISTENCYSCANINTERFACE_ACTOR_G_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/DatabaseConfiguration.h"
|
||||
|
@ -28,6 +31,8 @@
|
|||
#include "fdbrpc/fdbrpc.h"
|
||||
#include "fdbrpc/Locality.h"
|
||||
|
||||
#include "flow/actorcompiler.h" // must be last include
|
||||
|
||||
struct ConsistencyScanInterface {
|
||||
constexpr static FileIdentifier file_identifier = 4983265;
|
||||
RequestStream<ReplyPromise<Void>> waitFailure;
|
||||
|
@ -155,35 +160,37 @@ struct ConsistencyScanInfo {
|
|||
}
|
||||
};
|
||||
|
||||
Future<Version> getVersion(Database const& cx);
|
||||
Future<bool> getKeyServers(
|
||||
Database const& cx,
|
||||
Promise<std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>>> const& keyServersPromise,
|
||||
KeyRangeRef const& kr,
|
||||
bool const& performQuiescentChecks);
|
||||
Future<bool> getKeyLocations(Database const& cx,
|
||||
std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>> const& shards,
|
||||
Promise<Standalone<VectorRef<KeyValueRef>>> const& keyLocationPromise,
|
||||
bool const& performQuiescentChecks);
|
||||
Future<bool> checkDataConsistency(Database const& cx,
|
||||
VectorRef<KeyValueRef> const& keyLocations,
|
||||
DatabaseConfiguration const& configuration,
|
||||
std::map<UID, StorageServerInterface> const& tssMapping,
|
||||
bool const& performQuiescentChecks,
|
||||
bool const& performTSSCheck,
|
||||
bool const& firstClient,
|
||||
bool const& failureIsError,
|
||||
int const& clientId,
|
||||
int const& clientCount,
|
||||
bool const& distributed,
|
||||
bool const& shuffleShards,
|
||||
int const& shardSampleFactor,
|
||||
int64_t const& sharedRandomNumber,
|
||||
int64_t const& repetitions,
|
||||
int64_t* const& bytesReadInPreviousRound,
|
||||
int const& restart,
|
||||
int64_t const& maxRate,
|
||||
int64_t const& targetInterval,
|
||||
KeyRef const& progressKey);
|
||||
ACTOR Future<Version> getVersion(Database cx);
|
||||
ACTOR Future<bool> getKeyServers(
|
||||
Database cx,
|
||||
Promise<std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>>> keyServersPromise,
|
||||
KeyRangeRef kr,
|
||||
bool performQuiescentChecks);
|
||||
ACTOR Future<bool> getKeyLocations(Database cx,
|
||||
std::vector<std::pair<KeyRange, std::vector<StorageServerInterface>>> shards,
|
||||
Promise<Standalone<VectorRef<KeyValueRef>>> keyLocationPromise,
|
||||
bool performQuiescentChecks);
|
||||
ACTOR Future<bool> checkDataConsistency(Database cx,
|
||||
VectorRef<KeyValueRef> keyLocations,
|
||||
DatabaseConfiguration configuration,
|
||||
std::map<UID, StorageServerInterface> tssMapping,
|
||||
bool performQuiescentChecks,
|
||||
bool performTSSCheck,
|
||||
bool firstClient,
|
||||
bool failureIsError,
|
||||
int clientId,
|
||||
int clientCount,
|
||||
bool distributed,
|
||||
bool shuffleShards,
|
||||
int shardSampleFactor,
|
||||
int64_t sharedRandomNumber,
|
||||
int64_t repetitions,
|
||||
int64_t* bytesReadInPreviousRound,
|
||||
int restart,
|
||||
int64_t maxRate,
|
||||
int64_t targetInterval,
|
||||
KeyRef progressKey);
|
||||
|
||||
#include "flow/unactorcompiler.h"
|
||||
|
||||
#endif // FDBCLIENT_CONSISTENCYSCANINTERFACE_H
|
|
@ -655,6 +655,12 @@ public:
|
|||
// Adds or updates the specified (UID, Tag) pair in the tag mapping.
|
||||
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
|
||||
/// @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".
|
||||
|
@ -663,6 +669,14 @@ public:
|
|||
Reference<TransactionState> info,
|
||||
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
|
||||
using TransactionT = ReadYourWritesTransaction;
|
||||
Reference<TransactionT> createTransaction();
|
||||
|
|
|
@ -1654,4 +1654,36 @@ struct transaction_creator_traits<T, std::void_t<typename T::TransactionT>> : st
|
|||
template <typename T>
|
||||
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
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "fdbclient/GenericTransactionHelper.h"
|
||||
#include "fdbclient/Subspace.h"
|
||||
#include "flow/ObjectSerializer.h"
|
||||
#include "flow/Platform.h"
|
||||
#include "flow/genericactors.actor.h"
|
||||
#include "flow/serialize.h"
|
||||
|
||||
|
@ -305,6 +306,14 @@ public:
|
|||
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>
|
||||
void clear(Transaction tr) {
|
||||
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.
|
||||
//
|
||||
// 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 {
|
||||
constexpr static FileIdentifier file_identifier = 13448589;
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ struct ManagementClusterMetadata {
|
|||
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 template <class Transaction>
|
||||
|
|
|
@ -308,6 +308,7 @@ public:
|
|||
int ROCKSDB_BACKGROUND_PARALLELISM;
|
||||
int ROCKSDB_READ_PARALLELISM;
|
||||
int64_t ROCKSDB_MEMTABLE_BYTES;
|
||||
bool ROCKSDB_LEVEL_STYLE_COMPACTION;
|
||||
bool ROCKSDB_UNSAFE_AUTO_FSYNC;
|
||||
int64_t ROCKSDB_PERIODIC_COMPACTION_SECONDS;
|
||||
int ROCKSDB_PREFIX_LEN;
|
||||
|
@ -329,6 +330,7 @@ public:
|
|||
int64_t ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC;
|
||||
bool ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE;
|
||||
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
|
||||
double ROCKSDB_PERFCONTEXT_SAMPLE_RATE;
|
||||
double ROCKSDB_METRICS_SAMPLE_INTERVAL;
|
||||
|
@ -950,6 +952,7 @@ public:
|
|||
double BGCC_TIMEOUT;
|
||||
double BGCC_MIN_INTERVAL;
|
||||
bool BLOB_MANIFEST_BACKUP;
|
||||
double BLOB_MANIFEST_BACKUP_INTERVAL;
|
||||
bool BLOB_FULL_RESTORE_MODE;
|
||||
|
||||
// Blob metadata
|
||||
|
|
|
@ -583,7 +583,7 @@ Future<Void> enableAuto(Reference<DB> db, bool enabled) {
|
|||
tr->get(tagThrottleAutoEnabledKey);
|
||||
Optional<Value> value = wait(safeThreadFutureToFuture(valueF));
|
||||
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);
|
||||
|
||||
wait(safeThreadFutureToFuture(tr->commit()));
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
};
|
||||
|
||||
struct ReservedTaskParams {
|
||||
static TaskParam<Version> scheduledVersion() { return LiteralStringRef(__FUNCTION__); }
|
||||
static TaskParam<Version> scheduledVersion() { return __FUNCTION__sr; }
|
||||
};
|
||||
|
||||
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_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 {
|
||||
Future<Key> get(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "flow/flow.h"
|
||||
#include "fdbclient/FDBTypes.h"
|
||||
#include "fdbclient/Versionstamp.h"
|
||||
#include "fdbclient/TupleVersionstamp.h"
|
||||
|
||||
struct Tuple {
|
||||
struct UnicodeStr {
|
||||
|
@ -63,7 +63,7 @@ struct Tuple {
|
|||
Tuple& append(double);
|
||||
Tuple& append(std::nullptr_t);
|
||||
Tuple& appendNull();
|
||||
Tuple& append(Versionstamp const&);
|
||||
Tuple& append(TupleVersionstamp const&);
|
||||
Tuple& append(UserTypeStr const&);
|
||||
|
||||
Standalone<StringRef> pack() const {
|
||||
|
@ -92,7 +92,7 @@ struct Tuple {
|
|||
StringRef subTupleRawString(size_t index) const;
|
||||
ElementType getType(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;
|
||||
bool getBool(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
|
||||
*
|
||||
|
@ -27,15 +27,15 @@
|
|||
|
||||
const size_t VERSIONSTAMP_TUPLE_SIZE = 12;
|
||||
|
||||
struct Versionstamp {
|
||||
Versionstamp(StringRef);
|
||||
struct TupleVersionstamp {
|
||||
TupleVersionstamp(StringRef);
|
||||
|
||||
int64_t getVersion() const;
|
||||
int16_t getBatchNumber() const;
|
||||
int16_t getUserVersion() const;
|
||||
size_t size() const;
|
||||
const uint8_t* begin() const;
|
||||
bool operator==(const Versionstamp&) const;
|
||||
bool operator==(const TupleVersionstamp&) const;
|
||||
|
||||
private:
|
||||
Standalone<StringRef> data;
|
|
@ -48,7 +48,7 @@ TEST_CASE("/flow/actorcompiler/lineNumbers") {
|
|||
}
|
||||
break;
|
||||
}
|
||||
ASSERT(LiteralStringRef(__FILE__).endsWith("FlowTests.actor.cpp"_sr));
|
||||
ASSERT(__FILE__sr.endsWith("FlowTests.actor.cpp"_sr));
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,14 @@ ACTOR static Future<Void> cheeseWaitActor() {
|
|||
return Void();
|
||||
}
|
||||
|
||||
size_t cheeseWaitActorSize() {
|
||||
#ifndef OPEN_FOR_IDE
|
||||
return sizeof(CheeseWaitActorActor);
|
||||
#else
|
||||
return 0ul;
|
||||
#endif
|
||||
}
|
||||
|
||||
ACTOR static void trivialVoidActor(int* result) {
|
||||
*result = 1;
|
||||
}
|
||||
|
@ -353,7 +361,7 @@ TEST_CASE("/flow/flow/cancel2") {
|
|||
return Void();
|
||||
}
|
||||
|
||||
namespace {
|
||||
namespace flow_tests_details {
|
||||
// Simple message for flatbuffers unittests
|
||||
struct Int {
|
||||
constexpr static FileIdentifier file_identifier = 12345;
|
||||
|
@ -365,7 +373,7 @@ struct Int {
|
|||
serializer(ar, value);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
} // namespace flow_tests_details
|
||||
|
||||
TEST_CASE("/flow/flow/nonserializable futures") {
|
||||
// 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<Int> rpInt;
|
||||
Future<Int> f = rpInt.getFuture();
|
||||
ReplyPromise<flow_tests_details::Int> rpInt;
|
||||
Future<flow_tests_details::Int> f = rpInt.getFuture();
|
||||
ASSERT(!f.isReady());
|
||||
rpInt.send(123);
|
||||
ASSERT(f.get().value == 123);
|
||||
}
|
||||
|
||||
{
|
||||
RequestStream<Int> rsInt;
|
||||
FutureStream<Int> f = rsInt.getFuture();
|
||||
RequestStream<flow_tests_details::Int> rsInt;
|
||||
FutureStream<flow_tests_details::Int> f = rsInt.getFuture();
|
||||
rsInt.send(1);
|
||||
rsInt.send(2);
|
||||
ASSERT(f.pop().value == 1);
|
||||
|
@ -403,7 +411,7 @@ TEST_CASE("/flow/flow/nonserializable futures") {
|
|||
TEST_CASE("/flow/flow/networked futures") {
|
||||
// RequestStream can be serialized
|
||||
{
|
||||
RequestStream<Int> locInt;
|
||||
RequestStream<flow_tests_details::Int> locInt;
|
||||
BinaryWriter wr(IncludeVersion());
|
||||
wr << locInt;
|
||||
|
||||
|
@ -411,7 +419,7 @@ TEST_CASE("/flow/flow/networked futures") {
|
|||
locInt.getEndpoint().getPrimaryAddress() == FlowTransport::transport().getLocalAddress());
|
||||
|
||||
BinaryReader rd(wr.toValue(), IncludeVersion());
|
||||
RequestStream<Int> remoteInt;
|
||||
RequestStream<flow_tests_details::Int> remoteInt;
|
||||
rd >> remoteInt;
|
||||
|
||||
ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint());
|
||||
|
@ -420,14 +428,14 @@ TEST_CASE("/flow/flow/networked futures") {
|
|||
// ReplyPromise can be serialized
|
||||
// TODO: This needs to fiddle with g_currentDeliveryPeerAddress
|
||||
if (0) {
|
||||
ReplyPromise<Int> locInt;
|
||||
ReplyPromise<flow_tests_details::Int> locInt;
|
||||
BinaryWriter wr(IncludeVersion());
|
||||
wr << locInt;
|
||||
|
||||
ASSERT(locInt.getEndpoint().isValid() && locInt.getEndpoint().isLocal());
|
||||
|
||||
BinaryReader rd(wr.toValue(), IncludeVersion());
|
||||
ReplyPromise<Int> remoteInt;
|
||||
ReplyPromise<flow_tests_details::Int> remoteInt;
|
||||
rd >> remoteInt;
|
||||
|
||||
ASSERT(remoteInt.getEndpoint() == locInt.getEndpoint());
|
||||
|
@ -1084,7 +1092,7 @@ TEST_CASE("#flow/flow/perf/actor patterns") {
|
|||
ASSERT(out2[i].isReady());
|
||||
}
|
||||
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
|
||||
threadSleep(0.01);
|
||||
}
|
||||
state Standalone<StringRef> data =
|
||||
deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(0, maxDataLength + 1));
|
||||
state Standalone<StringRef> data(
|
||||
deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(0, maxDataLength + 1)));
|
||||
PacketWriter packetWriter(packetQueue.getWriteBuffer(data.size()), nullptr, Unversioned());
|
||||
packetWriter.serializeBytes(data);
|
||||
wait(externalConn->onWritable());
|
||||
|
|
|
@ -25,11 +25,16 @@
|
|||
inline void 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:
|
||||
bool testFuzzActor(Future<int> (*actor)(FutureStream<int> const&, PromiseStream<int> const&, Future<Void> const&),
|
||||
const char* desc,
|
||||
std::vector<int> const& expectedOutput);
|
||||
#endif
|
||||
|
||||
// This is defined by ActorFuzz.actor.cpp (generated by actorFuzz.py)
|
||||
// Returns (tests passed, tests total)
|
||||
|
|
|
@ -343,7 +343,7 @@ private:
|
|||
TraceEvent("AFCUnderlyingOpenEnd").detail("Filename", filename);
|
||||
int64_t l = wait(f->size());
|
||||
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) {
|
||||
if (e.code() != error_code_actor_cancelled)
|
||||
openFiles.erase(filename);
|
||||
|
|
|
@ -94,10 +94,12 @@ Future<Void> tssComparison(Req req,
|
|||
// we want to record ss/tss errors to metrics
|
||||
state int srcErrorCode = error_code_success;
|
||||
state int tssErrorCode = error_code_success;
|
||||
state ErrorOr<Resp> src;
|
||||
state Optional<ErrorOr<Resp>> tss;
|
||||
|
||||
loop {
|
||||
choose {
|
||||
when(state ErrorOr<Resp> src = wait(fSource)) {
|
||||
when(wait(store(src, fSource))) {
|
||||
srcEndTime = now();
|
||||
fSource = Never();
|
||||
finished++;
|
||||
|
@ -105,7 +107,7 @@ Future<Void> tssComparison(Req req,
|
|||
break;
|
||||
}
|
||||
}
|
||||
when(state Optional<ErrorOr<Resp>> tss = wait(fTssWithTimeout)) {
|
||||
when(wait(store(tss, fTssWithTimeout))) {
|
||||
tssEndTime = now();
|
||||
fTssWithTimeout = Never();
|
||||
finished++;
|
||||
|
|
|
@ -503,6 +503,8 @@ public:
|
|||
bool allowStorageMigrationTypeChange = false;
|
||||
double injectTargetedSSRestartTime = 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;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
|
||||
#include "fdbserver/BlobGranuleValidation.actor.h"
|
||||
#include "fdbserver/Knobs.h"
|
||||
#include "flow/actorcompiler.h" // has to be last include
|
||||
|
||||
ACTOR Future<std::pair<RangeResult, Version>> readFromFDB(Database cx, KeyRange range) {
|
||||
|
@ -332,3 +333,97 @@ ACTOR Future<Void> validateGranuleSummaries(Database cx,
|
|||
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 seqNo = 1;
|
||||
int64_t manifestDumperSeqNo = 1;
|
||||
|
||||
Promise<Void> iAmReplaced;
|
||||
|
||||
|
@ -484,6 +485,19 @@ struct BlobManagerData : NonCopyable, ReferenceCounted<BlobManagerData> {
|
|||
}
|
||||
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().
|
||||
|
@ -848,7 +862,18 @@ ACTOR Future<Void> doRangeAssignment(Reference<BlobManagerData> bmData,
|
|||
if (bmData->workersById.count(workerID.get()) == 0) {
|
||||
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()) {
|
||||
// previous assign failed and this one succeeded
|
||||
--bmData->stats.blockedAssignments;
|
||||
|
@ -1216,6 +1241,10 @@ ACTOR Future<Void> writeInitialGranuleMapping(Reference<BlobManagerData> bmData,
|
|||
}
|
||||
i += j;
|
||||
}
|
||||
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
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
|
||||
// it and re-assign it to cancel the old granule and retry
|
||||
CODE_PROBE(true, "BM successfully changed initial split too big");
|
||||
|
@ -1941,6 +1975,11 @@ ACTOR Future<Void> maybeSplitRange(Reference<BlobManagerData> bmData,
|
|||
|
||||
wait(tr->commit());
|
||||
|
||||
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
// Update BlobGranuleMergeBoundary in-memory state.
|
||||
for (auto it = splitPoints.boundaries.begin(); it != splitPoints.boundaries.end(); it++) {
|
||||
bmData->mergeBoundaries[it->first] = it->second;
|
||||
|
@ -2218,6 +2257,11 @@ ACTOR Future<std::pair<UID, Version>> persistMergeGranulesStart(Reference<BlobMa
|
|||
|
||||
wait(tr->commit());
|
||||
|
||||
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
Version mergeVersion = tr->getCommittedVersion();
|
||||
if (BM_DEBUG) {
|
||||
fmt::print("Granule merge intent persisted [{0} - {1}): {2} @ {3}!\n",
|
||||
|
@ -2377,6 +2421,12 @@ ACTOR Future<bool> persistMergeGranulesDone(Reference<BlobManagerData> bmData,
|
|||
tr->getCommittedVersion());
|
||||
}
|
||||
CODE_PROBE(true, "Granule merge complete");
|
||||
|
||||
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Error& 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
|
||||
std::unordered_set<UID> endingWorkers;
|
||||
for (auto& it : bmData->workersById) {
|
||||
|
@ -3760,6 +3814,10 @@ ACTOR Future<Void> recoverBlobManager(Reference<BlobManagerData> bmData) {
|
|||
ASSERT(bmData->doneRecovering.canBeSet());
|
||||
bmData->doneRecovering.send(Void());
|
||||
|
||||
if (BUGGIFY && bmData->maybeInjectTargetedRestart()) {
|
||||
throw blob_manager_replaced();
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
@ -4464,6 +4522,12 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
|
|||
// instead cover whole of intersecting granules at begin/end
|
||||
wait(krmSetRangeCoalescing(&tr, blobGranuleForcePurgedKeys.begin, range, normalKeys, "1"_sr));
|
||||
wait(tr.commit());
|
||||
|
||||
if (BUGGIFY && self->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (Error& e) {
|
||||
wait(tr.onError(e));
|
||||
|
@ -4681,7 +4745,7 @@ ACTOR Future<Void> purgeRange(Reference<BlobManagerData> self, KeyRangeRef range
|
|||
if (BM_PURGE_DEBUG) {
|
||||
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>();
|
||||
for (int i = 0; i < currHistoryNode.parentVersions.size(); i++) {
|
||||
// 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,
|
||||
// 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());
|
||||
}
|
||||
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) {
|
||||
|
@ -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: simpler solution could be to shuffle ranges
|
||||
ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) {
|
||||
|
||||
state Reference<IRateControl> rateLimiter =
|
||||
Reference<IRateControl>(new SpeedLimit(SERVER_KNOBS->BG_CONSISTENCY_CHECK_TARGET_SPEED_KB * 1024, 1));
|
||||
bmData->initBStore();
|
||||
|
||||
if (BM_DEBUG) {
|
||||
fmt::print("BGCC starting\n");
|
||||
}
|
||||
if (isFullRestoreMode())
|
||||
wait(printRestoreSummary(bmData->db, bmData->bstore));
|
||||
|
||||
loop {
|
||||
if (g_network->isSimulated() && g_simulator->speedUpSimulation) {
|
||||
|
@ -5091,14 +5155,6 @@ ACTOR Future<Void> bgConsistencyCheck(Reference<BlobManagerData> bmData) {
|
|||
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) {
|
||||
int tries = 10;
|
||||
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
|
||||
static std::map<int64_t, UID> managerEpochsSeen;
|
||||
|
||||
|
@ -5209,6 +5281,9 @@ ACTOR Future<Void> blobManager(BlobManagerInterface bmInterf,
|
|||
if (SERVER_KNOBS->BG_ENABLE_MERGING) {
|
||||
self->addActor.send(granuleMergeChecker(self));
|
||||
}
|
||||
if (SERVER_KNOBS->BLOB_MANIFEST_BACKUP && !isFullRestoreMode()) {
|
||||
self->addActor.send(backupManifest(self));
|
||||
}
|
||||
|
||||
if (BUGGIFY) {
|
||||
self->addActor.send(chaosRangeMover(self));
|
||||
|
|
|
@ -18,7 +18,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "fdbclient/BackupContainer.h"
|
||||
#include "fdbclient/BlobGranuleCommon.h"
|
||||
#include "fdbserver/Knobs.h"
|
||||
#include "flow/FastRef.h"
|
||||
#include "flow/flow.h"
|
||||
|
@ -32,13 +37,13 @@
|
|||
#include "fdbserver/BlobGranuleServerCommon.actor.h"
|
||||
|
||||
#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
|
||||
//
|
||||
|
||||
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
|
||||
template <typename... T>
|
||||
|
@ -47,10 +52,53 @@ inline void dprint(fmt::format_string<T...> fmt, 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.
|
||||
class BlobManifestDumper : public ReferenceCounted<BlobManifestDumper> {
|
||||
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() {}
|
||||
|
||||
// Execute the dumper
|
||||
|
@ -61,6 +109,7 @@ public:
|
|||
manifest.rows = rows;
|
||||
Value data = encode(manifest);
|
||||
wait(writeToFile(self, data));
|
||||
wait(cleanup(self));
|
||||
} catch (Error& e) {
|
||||
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
|
||||
ACTOR static Future<Void> writeToFile(Reference<BlobManifestDumper> self, Value data) {
|
||||
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));
|
||||
wait(file->append(data.begin(), data.size()));
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -117,8 +167,26 @@ private:
|
|||
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_;
|
||||
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
|
||||
|
@ -177,8 +245,9 @@ public:
|
|||
private:
|
||||
// Read data from a manifest file
|
||||
ACTOR static Future<Value> readFromFile(Reference<BlobManifestLoader> self) {
|
||||
state Reference<BackupContainerFileSystem> readBstore = self->blobConn_->getForRead(MANIFEST_FILENAME);
|
||||
state Reference<IAsyncFile> reader = wait(readBstore->readFile(MANIFEST_FILENAME));
|
||||
state Reference<BackupContainerFileSystem> container = self->blobConn_->getForRead(MANIFEST_FOLDER);
|
||||
std::string fileName = wait(BlobManifestFile::last(container));
|
||||
state Reference<IAsyncFile> reader = wait(container->readFile(fileName));
|
||||
state int64_t fileSize = wait(reader->size());
|
||||
state Arena arena;
|
||||
state uint8_t* data = new (arena) uint8_t[fileSize];
|
||||
|
@ -353,8 +422,8 @@ private:
|
|||
};
|
||||
|
||||
// API to dump a manifest copy to external storage
|
||||
ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn) {
|
||||
Reference<BlobManifestDumper> dumper = makeReference<BlobManifestDumper>(db, blobConn);
|
||||
ACTOR Future<Void> dumpManifest(Database db, Reference<BlobConnectionProvider> blobConn, int64_t epoch, int64_t seqNo) {
|
||||
Reference<BlobManifestDumper> dumper = makeReference<BlobManifestDumper>(db, blobConn, epoch, seqNo);
|
||||
wait(BlobManifestDumper::execute(dumper));
|
||||
return Void();
|
||||
}
|
||||
|
|
|
@ -199,6 +199,7 @@ struct BlobWorkerData : NonCopyable, ReferenceCounted<BlobWorkerData> {
|
|||
Promise<Void> doGRVCheck;
|
||||
NotifiedVersion grvVersion;
|
||||
Promise<Void> fatalError;
|
||||
Promise<Void> simInjectFailure;
|
||||
|
||||
Reference<FlowLock> initialSnapshotLock;
|
||||
Reference<FlowLock> resnapshotLock;
|
||||
|
@ -292,6 +293,19 @@ struct BlobWorkerData : NonCopyable, ReferenceCounted<BlobWorkerData> {
|
|||
|
||||
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 {
|
||||
|
@ -780,6 +794,11 @@ ACTOR Future<BlobFileIndex> writeDeltaFile(Reference<BlobWorkerData> bwData,
|
|||
tr->getCommittedVersion());
|
||||
}
|
||||
|
||||
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
if (BUGGIFY_WITH_PROB(0.01)) {
|
||||
wait(delay(deterministicRandom()->random01()));
|
||||
}
|
||||
|
@ -1007,6 +1026,11 @@ ACTOR Future<BlobFileIndex> writeSnapshot(Reference<BlobWorkerData> bwData,
|
|||
.detail("Compressed", compressFilter.present());
|
||||
}
|
||||
|
||||
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
// FIXME: change when we implement multiplexing
|
||||
return BlobFileIndex(version, fname, 0, serializedSize, serializedSize, cipherKeysMeta);
|
||||
}
|
||||
|
@ -1057,6 +1081,11 @@ ACTOR Future<BlobFileIndex> dumpInitialSnapshotFromFDB(Reference<BlobWorkerData>
|
|||
.detail("Version", readVersion);
|
||||
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
|
||||
inFlightPops->push_back(bwData->db->popChangeFeedMutations(cfKey, readVersion + 1));
|
||||
return snapshotWriter.get();
|
||||
|
@ -1443,6 +1472,10 @@ ACTOR Future<Void> reevaluateInitialSplit(Reference<BlobWorkerData> bwData,
|
|||
seqno);
|
||||
reply.proposedSplitKey = proposedSplitKey;
|
||||
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
|
||||
// amount of time of not hearing back
|
||||
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);
|
||||
}
|
||||
|
||||
if (BUGGIFY && bwData->maybeInjectTargetedRestart()) {
|
||||
wait(delay(0)); // should be cancelled
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
return info;
|
||||
} catch (Error& e) {
|
||||
if (e.code() == error_code_granule_assignment_conflict) {
|
||||
|
@ -4985,7 +5023,7 @@ ACTOR Future<Void> blobWorker(BlobWorkerInterface bwInterf,
|
|||
ASSERT(false);
|
||||
throw internal_error();
|
||||
}
|
||||
when(wait(selfRemoved)) {
|
||||
when(wait(selfRemoved || self->simInjectFailure.getFuture())) {
|
||||
if (BW_DEBUG) {
|
||||
printf("Blob worker detected removal. Exiting...\n");
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#include "fdbserver/ClusterRecovery.actor.h"
|
||||
#include "fdbserver/DataDistributorInterface.h"
|
||||
#include "fdbserver/DBCoreState.h"
|
||||
#include "fdbclient/Metacluster.h"
|
||||
#include "fdbclient/MetaclusterManagement.actor.h"
|
||||
#include "fdbserver/MoveKeys.actor.h"
|
||||
#include "fdbserver/LeaderElection.h"
|
||||
#include "fdbserver/LogSystem.h"
|
||||
|
@ -53,7 +55,7 @@
|
|||
#include "fdbserver/RatekeeperInterface.h"
|
||||
#include "fdbserver/BlobManagerInterface.h"
|
||||
#include "fdbserver/ServerDBInfo.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbserver/LatencyBandConfig.h"
|
||||
#include "fdbclient/GlobalConfig.actor.h"
|
||||
#include "fdbserver/RecoveryState.h"
|
||||
|
@ -1498,7 +1500,9 @@ ACTOR Future<Void> statusServer(FutureStream<StatusRequest> requests,
|
|||
coordinators,
|
||||
incompatibleConnections,
|
||||
self->datacenterVersionDifference,
|
||||
configBroadcaster)));
|
||||
configBroadcaster,
|
||||
self->db.metaclusterRegistration,
|
||||
self->db.metaclusterMetrics)));
|
||||
|
||||
if (result.isError() && result.getError().code() == error_code_actor_cancelled)
|
||||
throw result.getError();
|
||||
|
@ -2617,23 +2621,31 @@ ACTOR Future<Void> workerHealthMonitor(ClusterControllerData* self) {
|
|||
|
||||
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.
|
||||
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++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->degradationInfo.degradedServers.empty() || self->degradationInfo.degradedSatellite) {
|
||||
if (!self->degradationInfo.degradedServers.empty() || !self->degradationInfo.disconnectedServers.empty() ||
|
||||
self->degradationInfo.degradedSatellite) {
|
||||
std::string degradedServerString;
|
||||
for (const auto& server : self->degradationInfo.degradedServers) {
|
||||
degradedServerString += server.toString() + " ";
|
||||
}
|
||||
std::string disconnectedServerString;
|
||||
for (const auto& server : self->degradationInfo.disconnectedServers) {
|
||||
disconnectedServerString += server.toString() + " ";
|
||||
}
|
||||
TraceEvent("ClusterControllerHealthMonitor")
|
||||
.detail("DegradedServers", degradedServerString)
|
||||
.detail("DisconnectedServers", disconnectedServerString)
|
||||
.detail("DegradedSatellite", self->degradationInfo.degradedSatellite);
|
||||
|
||||
// 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) {
|
||||
self->recentHealthTriggeredRecoveryTime.push(now());
|
||||
self->excludedDegradedServers = self->degradationInfo.degradedServers;
|
||||
self->excludedDegradedServers.insert(self->degradationInfo.disconnectedServers.begin(),
|
||||
self->degradationInfo.disconnectedServers.end());
|
||||
TraceEvent("DegradedServerDetectedAndTriggerRecovery")
|
||||
.detail("RecentRecoveryCountDueToHealth", self->recentRecoveryCountDueToHealth());
|
||||
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,
|
||||
Future<Void> leaderFail,
|
||||
ServerCoordinators coordinators,
|
||||
|
@ -2721,6 +2785,7 @@ ACTOR Future<Void> clusterControllerCore(ClusterControllerFullInterface interf,
|
|||
self.addActor.send(monitorBlobManager(&self));
|
||||
self.addActor.send(watchBlobGranulesConfigKey(&self));
|
||||
self.addActor.send(monitorConsistencyScan(&self));
|
||||
self.addActor.send(metaclusterMetricsUpdater(&self));
|
||||
self.addActor.send(dbInfoUpdater(&self));
|
||||
self.addActor.send(traceCounters("ClusterControllerMetrics",
|
||||
self.id,
|
||||
|
@ -2942,6 +3007,8 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
|
|||
req.address = workerAddress;
|
||||
req.degradedPeers.push_back(badPeer1);
|
||||
req.degradedPeers.push_back(badPeer2);
|
||||
req.disconnectedPeers.push_back(badPeer1);
|
||||
req.disconnectedPeers.push_back(badPeer2);
|
||||
data.updateWorkerHealth(req);
|
||||
ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end());
|
||||
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(health.degradedPeers.find(badPeer2) != health.degradedPeers.end());
|
||||
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.
|
||||
|
@ -2964,6 +3036,8 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
|
|||
req.address = workerAddress;
|
||||
req.degradedPeers.push_back(badPeer1);
|
||||
req.degradedPeers.push_back(badPeer3);
|
||||
req.disconnectedPeers.push_back(badPeer1);
|
||||
req.disconnectedPeers.push_back(badPeer3);
|
||||
data.updateWorkerHealth(req);
|
||||
ASSERT(data.workerHealth.find(workerAddress) != data.workerHealth.end());
|
||||
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(health.degradedPeers.find(badPeer3) != health.degradedPeers.end());
|
||||
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;
|
||||
previousRefreshTime = health.degradedPeers[badPeer3].lastRefreshTime;
|
||||
}
|
||||
|
@ -2992,20 +3075,10 @@ TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {
|
|||
ASSERT(health.degradedPeers.find(badPeer3) != health.degradedPeers.end());
|
||||
ASSERT_EQ(health.degradedPeers[badPeer3].startTime, previousStartTime);
|
||||
ASSERT_EQ(health.degradedPeers[badPeer3].lastRefreshTime, previousRefreshTime);
|
||||
}
|
||||
|
||||
// Create a `UpdateWorkerHealthRequest` with disconnected peers, which should update the bad peer's lastRefreshTime.
|
||||
{
|
||||
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);
|
||||
ASSERT_EQ(health.disconnectedPeers.size(), 3);
|
||||
ASSERT(health.disconnectedPeers.find(badPeer3) != health.disconnectedPeers.end());
|
||||
ASSERT_EQ(health.disconnectedPeers[badPeer3].startTime, previousStartTime);
|
||||
ASSERT_EQ(health.disconnectedPeers[badPeer3].lastRefreshTime, previousRefreshTime);
|
||||
}
|
||||
|
||||
return Void();
|
||||
|
@ -3021,11 +3094,14 @@ TEST_CASE("/fdbserver/clustercontroller/updateRecoveredWorkers") {
|
|||
NetworkAddress worker2(IPAddress(0x11111111), 1);
|
||||
NetworkAddress badPeer1(IPAddress(0x02020202), 1);
|
||||
NetworkAddress badPeer2(IPAddress(0x03030303), 1);
|
||||
NetworkAddress disconnectedPeer3(IPAddress(0x04040404), 1);
|
||||
|
||||
// Create following test scenario:
|
||||
// worker1 -> badPeer1 active
|
||||
// worker1 -> badPeer2 recovered
|
||||
// worker1 -> disconnectedPeer3 active
|
||||
// worker2 -> badPeer2 recovered
|
||||
// worker2 -> disconnectedPeer3 recovered
|
||||
data.workerHealth[worker1].degradedPeers[badPeer1] = {
|
||||
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
|
||||
};
|
||||
data.workerHealth[worker1].degradedPeers[disconnectedPeer3] = {
|
||||
now() - SERVER_KNOBS->CC_DEGRADED_LINK_EXPIRATION_INTERVAL - 1, now()
|
||||
};
|
||||
data.workerHealth[worker2].degradedPeers[badPeer2] = {
|
||||
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();
|
||||
|
||||
ASSERT_EQ(data.workerHealth.size(), 1);
|
||||
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(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());
|
||||
|
||||
return Void();
|
||||
|
@ -3064,6 +3149,7 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
|
|||
// cluster controller.
|
||||
{
|
||||
data.workerHealth[worker].degradedPeers[badPeer1] = { now(), now() };
|
||||
data.workerHealth[worker].disconnectedPeers[badPeer2] = { now(), now() };
|
||||
ASSERT(data.getDegradationInfo().degradedServers.empty());
|
||||
data.workerHealth.clear();
|
||||
}
|
||||
|
@ -3076,6 +3162,19 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
|
|||
auto degradationInfo = data.getDegradationInfo();
|
||||
ASSERT(degradationInfo.degradedServers.size() == 1);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -3085,16 +3184,25 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
|
|||
now() };
|
||||
data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
|
||||
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();
|
||||
ASSERT(degradationInfo.degradedServers.size() == 1);
|
||||
ASSERT(degradationInfo.degradedServers.find(worker) != 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();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// test for both degraded peers and disconnected peers.
|
||||
data.workerHealth[worker].degradedPeers[badPeer1] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
|
||||
now() };
|
||||
data.workerHealth[badPeer1].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
|
||||
|
@ -3103,9 +3211,19 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
|
|||
now() };
|
||||
data.workerHealth[badPeer2].degradedPeers[worker] = { now() - SERVER_KNOBS->CC_MIN_DEGRADATION_INTERVAL - 1,
|
||||
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();
|
||||
ASSERT(degradationInfo.degradedServers.size() == 1);
|
||||
ASSERT(degradationInfo.degradedServers.find(worker) != degradationInfo.degradedServers.end());
|
||||
ASSERT(degradationInfo.disconnectedServers.size() == 1);
|
||||
ASSERT(degradationInfo.disconnectedServers.find(worker) != degradationInfo.disconnectedServers.end());
|
||||
data.workerHealth.clear();
|
||||
}
|
||||
|
||||
|
@ -3124,6 +3242,23 @@ TEST_CASE("/fdbserver/clustercontroller/getDegradationInfo") {
|
|||
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
|
||||
// returned.
|
||||
{
|
||||
|
@ -3245,40 +3380,65 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerRecoveryDueToDegradedServer
|
|||
data.degradationInfo.degradedServers.insert(master);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(master);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// Trigger recovery when primary TLog is degraded.
|
||||
data.degradationInfo.degradedServers.insert(tlog);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(tlog);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// No recovery when satellite Tlog is degraded.
|
||||
data.degradationInfo.degradedServers.insert(satelliteTlog);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(satelliteTlog);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// No recovery when remote tlog is degraded.
|
||||
data.degradationInfo.degradedServers.insert(remoteTlog);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(remoteTlog);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// No recovery when log router is degraded.
|
||||
data.degradationInfo.degradedServers.insert(logRouter);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(logRouter);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// No recovery when backup worker is degraded.
|
||||
data.degradationInfo.degradedServers.insert(backup);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(backup);
|
||||
ASSERT(!data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// Trigger recovery when proxy is degraded.
|
||||
data.degradationInfo.degradedServers.insert(proxy);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(proxy);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
// Trigger recovery when resolver is degraded.
|
||||
data.degradationInfo.degradedServers.insert(resolver);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(resolver);
|
||||
ASSERT(data.shouldTriggerRecoveryDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
@ -3359,6 +3519,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
|
|||
data.degradationInfo.degradedServers.insert(master);
|
||||
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
|
||||
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.
|
||||
data.degradationInfo.degradedServers.insert(master);
|
||||
|
@ -3367,6 +3530,13 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
|
|||
data.degradationInfo.degradedServers.insert(proxy2);
|
||||
data.degradationInfo.degradedServers.insert(resolver);
|
||||
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.
|
||||
data.db.config.usableRegions = 1;
|
||||
|
@ -3377,6 +3547,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
|
|||
data.degradationInfo.degradedServers.insert(remoteTlog);
|
||||
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
|
||||
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
|
||||
data.degradationInfo.degradedServers.insert(NetworkAddress(IPAddress(0x13131313), 1));
|
||||
|
@ -3397,6 +3570,9 @@ TEST_CASE("/fdbserver/clustercontroller/shouldTriggerFailoverDueToDegradedServer
|
|||
data.degradationInfo.degradedServers.insert(remoteTlog);
|
||||
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
|
||||
data.degradationInfo.degradedServers.clear();
|
||||
data.degradationInfo.disconnectedServers.insert(remoteTlog);
|
||||
ASSERT(!data.shouldTriggerFailoverDueToDegradedServers());
|
||||
data.degradationInfo.disconnectedServers.clear();
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
|
|
@ -1164,13 +1164,33 @@ ACTOR Future<Void> readTransactionSystemState(Reference<ClusterRecoveryData> sel
|
|||
wait(self->txnStateStore->readValue(MetaclusterMetadata::metaclusterRegistration().key));
|
||||
Optional<MetaclusterRegistrationEntry> metaclusterRegistration =
|
||||
MetaclusterRegistrationEntry::decode(metaclusterRegistrationVal);
|
||||
Optional<ClusterName> metaclusterName;
|
||||
Optional<UID> metaclusterId;
|
||||
Optional<ClusterName> clusterName;
|
||||
Optional<UID> clusterId;
|
||||
self->controllerData->db.metaclusterRegistration = metaclusterRegistration;
|
||||
if (metaclusterRegistration.present()) {
|
||||
self->controllerData->db.metaclusterName = metaclusterRegistration.get().metaclusterName;
|
||||
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 {
|
||||
self->controllerData->db.metaclusterName = Optional<ClusterName>();
|
||||
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);
|
||||
|
||||
// auto kvs = self->txnStateStore->readRange( systemKeys );
|
||||
|
|
|
@ -2505,9 +2505,9 @@ ACTOR Future<Void> processCompleteTransactionStateRequest(TransactionStateResolv
|
|||
std::vector<UID> src, dest;
|
||||
ServerCacheInfo info;
|
||||
// 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,
|
||||
std::vector<Tag>& tags,
|
||||
std::vector<Reference<StorageInfo>>& storageInfoItems) {
|
||||
auto updateTagInfo = [pContext = pContext](const std::vector<UID>& uids,
|
||||
std::vector<Tag>& tags,
|
||||
std::vector<Reference<StorageInfo>>& storageInfoItems) {
|
||||
for (const auto& id : uids) {
|
||||
auto storageInfo = getStorageInfo(id, &pContext->pCommitData->storageCache, pContext->pTxnStateStore);
|
||||
ASSERT(storageInfo->tag != invalidTag);
|
||||
|
|
|
@ -189,6 +189,9 @@ ACTOR Future<bool> getKeyLocations(Database cx,
|
|||
state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures;
|
||||
for (const auto& kv : shards[i].second) {
|
||||
resetReply(req);
|
||||
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
|
||||
cx->getLatestCommitVersion(kv, req.version, req.ssLatestCommitVersions);
|
||||
}
|
||||
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());
|
||||
for (j = 0; j < storageServerInterfaces.size(); j++) {
|
||||
resetReply(req);
|
||||
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
|
||||
cx->getLatestCommitVersion(
|
||||
storageServerInterfaces[j], req.version, req.ssLatestCommitVersions);
|
||||
}
|
||||
keyValueFutures.push_back(
|
||||
storageServerInterfaces[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
|
||||
}
|
||||
|
@ -1129,4 +1136,4 @@ ACTOR Future<Void> consistencyScan(ConsistencyScanInterface csInterf, Reference<
|
|||
wait(self.consistencyScanEnabled.onChange());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "fdbserver/Knobs.h"
|
||||
#include "fdbserver/OnDemandStore.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "flow/ActorCollection.h"
|
||||
#include "flow/ProtocolVersion.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
|
||||
// alphanumeric character (they are always derived from a ClusterConnectionString key). Forwarding values are stored in
|
||||
// this range:
|
||||
const KeyRangeRef fwdKeys(LiteralStringRef("\xff"
|
||||
"fwd"),
|
||||
LiteralStringRef("\xff"
|
||||
"fwe"));
|
||||
const KeyRangeRef fwdKeys("\xff"
|
||||
"fwd"_sr,
|
||||
"\xff"
|
||||
"fwe"_sr);
|
||||
|
||||
// The time when forwarding was last set is stored in this range:
|
||||
const KeyRangeRef fwdTimeKeys(LiteralStringRef("\xff"
|
||||
"fwdTime"),
|
||||
LiteralStringRef("\xff"
|
||||
"fwdTimf"));
|
||||
const KeyRangeRef fwdTimeKeys("\xff"
|
||||
"fwdTime"_sr,
|
||||
"\xff"
|
||||
"fwdTimf"_sr);
|
||||
struct LeaderRegisterCollection {
|
||||
// SOMEDAY: Factor this into a generic tool? Extend ActorCollection to support removal actions? What?
|
||||
ActorCollection actors;
|
||||
|
|
|
@ -2684,33 +2684,21 @@ public:
|
|||
.detail("Primary", self->primary)
|
||||
.detail("DcId", dcId)
|
||||
.detail("Replicas", self->configuration.storageTeamSize);
|
||||
state Transaction tr(self->cx);
|
||||
loop {
|
||||
try {
|
||||
Optional<Value> val = wait(tr.get(datacenterReplicasKeyFor(dcId)));
|
||||
state int oldReplicas = val.present() ? decodeDatacenterReplicasValue(val.get()) : 0;
|
||||
if (oldReplicas == self->configuration.storageTeamSize) {
|
||||
TraceEvent("DDUpdatedAlready", self->distributorId)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("DcId", dcId)
|
||||
.detail("Replicas", self->configuration.storageTeamSize);
|
||||
return Void();
|
||||
}
|
||||
if (oldReplicas < self->configuration.storageTeamSize) {
|
||||
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));
|
||||
}
|
||||
|
||||
int oldReplicas = wait(self->db->tryUpdateReplicasKeyForDc(dcId, self->configuration.storageTeamSize));
|
||||
if (oldReplicas == self->configuration.storageTeamSize) {
|
||||
TraceEvent("DDUpdatedAlready", self->distributorId)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("DcId", dcId)
|
||||
.detail("Replicas", self->configuration.storageTeamSize);
|
||||
} else {
|
||||
TraceEvent("DDUpdatedReplicas", self->distributorId)
|
||||
.detail("Primary", self->primary)
|
||||
.detail("DcId", dcId)
|
||||
.detail("Replicas", self->configuration.storageTeamSize)
|
||||
.detail("OldReplicas", oldReplicas);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
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,
|
||||
FutureStream<Void> serverRemoved,
|
||||
const DDEnabledState* ddEnabledState) {
|
||||
state Future<Void> checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch);
|
||||
state Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> serverListAndProcessClasses =
|
||||
Never();
|
||||
state Future<ServerWorkerInfos> serverListAndProcessClasses = Never();
|
||||
state bool isFetchingResults = false;
|
||||
state Transaction tr(self->cx);
|
||||
loop {
|
||||
try {
|
||||
choose {
|
||||
when(wait(checkSignal)) {
|
||||
checkSignal = Never();
|
||||
isFetchingResults = true;
|
||||
serverListAndProcessClasses = NativeAPI::getServerListAndProcessClasses(&tr);
|
||||
}
|
||||
when(std::vector<std::pair<StorageServerInterface, ProcessClass>> results =
|
||||
wait(serverListAndProcessClasses)) {
|
||||
serverListAndProcessClasses = Never();
|
||||
isFetchingResults = false;
|
||||
choose {
|
||||
when(wait(checkSignal)) {
|
||||
checkSignal = Never();
|
||||
isFetchingResults = true;
|
||||
serverListAndProcessClasses = self->db->getServerListAndProcessClasses();
|
||||
}
|
||||
when(ServerWorkerInfos infos = wait(serverListAndProcessClasses)) {
|
||||
auto& servers = infos.servers;
|
||||
serverListAndProcessClasses = Never();
|
||||
isFetchingResults = false;
|
||||
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
UID serverId = results[i].first.id();
|
||||
StorageServerInterface const& ssi = results[i].first;
|
||||
ProcessClass const& processClass = results[i].second;
|
||||
if (!self->shouldHandleServer(ssi)) {
|
||||
continue;
|
||||
} else if (self->server_and_tss_info.count(serverId)) {
|
||||
auto& serverInfo = self->server_and_tss_info[serverId];
|
||||
if (ssi.getValue.getEndpoint() !=
|
||||
serverInfo->getLastKnownInterface().getValue.getEndpoint() ||
|
||||
processClass != serverInfo->getLastKnownClass().classType()) {
|
||||
Promise<std::pair<StorageServerInterface, ProcessClass>> currentInterfaceChanged =
|
||||
serverInfo->interfaceChanged;
|
||||
serverInfo->interfaceChanged =
|
||||
Promise<std::pair<StorageServerInterface, ProcessClass>>();
|
||||
serverInfo->onInterfaceChanged =
|
||||
Future<std::pair<StorageServerInterface, ProcessClass>>(
|
||||
serverInfo->interfaceChanged.getFuture());
|
||||
currentInterfaceChanged.send(std::make_pair(ssi, processClass));
|
||||
}
|
||||
} else if (!self->recruitingIds.count(ssi.id())) {
|
||||
self->addServer(ssi,
|
||||
processClass,
|
||||
self->serverTrackerErrorOut,
|
||||
tr.getReadVersion().get(),
|
||||
*ddEnabledState);
|
||||
for (int i = 0; i < servers.size(); i++) {
|
||||
UID serverId = servers[i].first.id();
|
||||
StorageServerInterface const& ssi = servers[i].first;
|
||||
ProcessClass const& processClass = servers[i].second;
|
||||
if (!self->shouldHandleServer(ssi)) {
|
||||
continue;
|
||||
} else if (self->server_and_tss_info.count(serverId)) {
|
||||
auto& serverInfo = self->server_and_tss_info[serverId];
|
||||
if (ssi.getValue.getEndpoint() !=
|
||||
serverInfo->getLastKnownInterface().getValue.getEndpoint() ||
|
||||
processClass != serverInfo->getLastKnownClass().classType()) {
|
||||
Promise<std::pair<StorageServerInterface, ProcessClass>> currentInterfaceChanged =
|
||||
serverInfo->interfaceChanged;
|
||||
serverInfo->interfaceChanged =
|
||||
Promise<std::pair<StorageServerInterface, ProcessClass>>();
|
||||
serverInfo->onInterfaceChanged =
|
||||
Future<std::pair<StorageServerInterface, ProcessClass>>(
|
||||
serverInfo->interfaceChanged.getFuture());
|
||||
currentInterfaceChanged.send(std::make_pair(ssi, processClass));
|
||||
}
|
||||
} 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) {
|
||||
tr = Transaction(self->cx);
|
||||
serverListAndProcessClasses = NativeAPI::getServerListAndProcessClasses(&tr);
|
||||
}
|
||||
|
||||
checkSignal = delay(SERVER_KNOBS->SERVER_LIST_DELAY, TaskPriority::DataDistributionLaunch);
|
||||
}
|
||||
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 {
|
||||
wait(self->init(initData, *ddEnabledState));
|
||||
initData = Reference<InitialDataDistribution>();
|
||||
initData.clear(); // release reference count
|
||||
self->addActor.send(self->serverGetTeamRequests(tci));
|
||||
|
||||
TraceEvent("DDTeamCollectionBegin", self->distributorId).detail("Primary", self->primary);
|
||||
|
@ -2962,10 +2924,13 @@ public:
|
|||
self->addActor.send(self->storageRecruiter(recruitStorage, *ddEnabledState));
|
||||
self->addActor.send(self->monitorStorageServerRecruitment());
|
||||
self->addActor.send(self->waitServerListChange(serverRemoved.getFuture(), *ddEnabledState));
|
||||
self->addActor.send(self->trackExcludedServers());
|
||||
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
|
||||
|
||||
loop choose {
|
||||
|
@ -3039,215 +3004,209 @@ public:
|
|||
state int traceEventsPrinted = 0;
|
||||
state std::vector<const UID*> serverIDs;
|
||||
state double lastPrintTime = 0;
|
||||
state ReadYourWritesTransaction tr(self->cx);
|
||||
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;
|
||||
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;
|
||||
traceEventsPrinted = 0;
|
||||
|
||||
auto const& keys = self->server_status.getKeys();
|
||||
for (auto const& key : keys) {
|
||||
// Add to or update the local server_status map
|
||||
server_status[key] = self->server_status.get(key);
|
||||
}
|
||||
double snapshotStart = now();
|
||||
|
||||
TraceEvent("DDPrintSnapshotTeamsInfo", self->getDistributorId())
|
||||
.detail("SnapshotSpeed", now() - snapshotStart)
|
||||
.detail("Primary", self->isPrimary());
|
||||
configuration = self->configuration;
|
||||
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;
|
||||
|
||||
// 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());
|
||||
auto const& keys = self->server_status.getKeys();
|
||||
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())
|
||||
.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())
|
||||
.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());
|
||||
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())
|
||||
.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());
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
server++;
|
||||
if (++traceEventsPrinted % SERVER_KNOBS->DD_TEAMS_INFO_PRINT_YIELD_COUNT == 0) {
|
||||
wait(yield());
|
||||
}
|
||||
}
|
||||
|
||||
server = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
const UID& uid = server->first;
|
||||
|
||||
TraceEvent 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 = server_info.begin();
|
||||
for (i = 0; i < server_info.size(); i++) {
|
||||
const UID& uid = server->first;
|
||||
|
||||
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());
|
||||
}
|
||||
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())
|
||||
.detail("Size", teams.size())
|
||||
.detail("Primary", self->isPrimary());
|
||||
for (i = 0; i < teams.size(); i++) {
|
||||
const auto& team = teams[i];
|
||||
|
||||
TraceEvent("ServerTeamInfo", self->getDistributorId())
|
||||
.detail("TeamIndex", i)
|
||||
.detail("Healthy", team->isHealthy())
|
||||
.detail("TeamSize", team->size())
|
||||
.detail("MemberIDs", team->getServerIDsStr())
|
||||
.detail("Primary", self->isPrimary())
|
||||
.detail("TeamID", team->getTeamID())
|
||||
.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());
|
||||
}
|
||||
.detail("TeamIndex", i)
|
||||
.detail("Healthy", team->isHealthy())
|
||||
.detail("TeamSize", team->size())
|
||||
.detail("MemberIDs", team->getServerIDsStr())
|
||||
.detail("Primary", self->isPrimary())
|
||||
.detail("TeamID", team->getTeamID())
|
||||
.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() {
|
||||
ASSERT(!db->isMocked());
|
||||
return DDTeamCollectionImpl::trackExcludedServers(this);
|
||||
}
|
||||
|
||||
|
@ -3483,6 +3443,7 @@ Future<Void> DDTeamCollection::perpetualStorageWiggler(AsyncVar<bool>& stopSigna
|
|||
}
|
||||
|
||||
Future<Void> DDTeamCollection::monitorPerpetualStorageWiggle() {
|
||||
ASSERT(!db->isMocked());
|
||||
return DDTeamCollectionImpl::monitorPerpetualStorageWiggle(this);
|
||||
}
|
||||
|
||||
|
@ -3492,6 +3453,7 @@ Future<Void> DDTeamCollection::waitServerListChange(FutureStream<Void> serverRem
|
|||
}
|
||||
|
||||
Future<Void> DDTeamCollection::waitHealthyZoneChange() {
|
||||
ASSERT(!db->isMocked());
|
||||
return DDTeamCollectionImpl::waitHealthyZoneChange(this);
|
||||
}
|
||||
|
||||
|
@ -3525,7 +3487,7 @@ Future<Void> DDTeamCollection::monitorHealthyTeams() {
|
|||
}
|
||||
|
||||
Future<UID> DDTeamCollection::getClusterId() {
|
||||
return DDTeamCollectionImpl::getClusterId(this);
|
||||
return db->getClusterId();
|
||||
}
|
||||
|
||||
Future<UID> DDTeamCollection::getNextWigglingServerID() {
|
||||
|
|
|
@ -28,6 +28,20 @@
|
|||
class DDTxnProcessorImpl {
|
||||
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}
|
||||
ACTOR static Future<IDDTxnProcessor::SourceServers> getSourceServersForRange(Database cx, KeyRangeRef keys) {
|
||||
state std::set<UID> servers;
|
||||
|
@ -127,6 +141,43 @@ class DDTxnProcessorImpl {
|
|||
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
|
||||
ACTOR static Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
|
||||
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) {
|
||||
return DDTxnProcessorImpl::getSourceServersForRange(cx, range);
|
||||
}
|
||||
|
||||
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> DDTxnProcessor::getServerListAndProcessClasses() {
|
||||
Transaction tr(cx);
|
||||
return NativeAPI::getServerListAndProcessClasses(&tr);
|
||||
Future<ServerWorkerInfos> DDTxnProcessor::getServerListAndProcessClasses() {
|
||||
return DDTxnProcessorImpl::getServerListAndProcessClasses(cx);
|
||||
}
|
||||
|
||||
Future<MoveKeysLock> DDTxnProcessor::takeMoveKeysLock(const UID& ddId) const {
|
||||
|
@ -539,12 +604,25 @@ Future<Optional<Value>> DDTxnProcessor::readRebalanceDDIgnoreKey() const {
|
|||
return DDTxnProcessorImpl::readRebalanceDDIgnoreKey(cx);
|
||||
}
|
||||
|
||||
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>>
|
||||
DDMockTxnProcessor::getServerListAndProcessClasses() {
|
||||
std::vector<std::pair<StorageServerInterface, ProcessClass>> res;
|
||||
Future<int> DDTxnProcessor::tryUpdateReplicasKeyForDc(const Optional<Key>& dcId, const int& storageTeamSize) const {
|
||||
return DDTxnProcessorImpl::tryUpdateReplicasKeyForDc(cx, dcId, storageTeamSize);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -619,7 +697,7 @@ Future<Reference<InitialDataDistribution>> DDMockTxnProcessor::getInitialDataDis
|
|||
// FIXME: now we just ignore ddEnabledState and moveKeysLock, will fix it in the future
|
||||
Reference<InitialDataDistribution> res = makeReference<InitialDataDistribution>();
|
||||
res->mode = 1;
|
||||
res->allServers = getServerListAndProcessClasses().get();
|
||||
res->allServers = getServerListAndProcessClasses().get().servers;
|
||||
res->shards = getDDShardInfos();
|
||||
std::tie(res->primaryTeams, res->remoteTeams) = getAllTeamsInRegion(res->shards);
|
||||
return res;
|
||||
|
|
|
@ -79,7 +79,11 @@ using rocksdb::BackgroundErrorReason;
|
|||
|
||||
class SharedRocksDBState {
|
||||
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; }
|
||||
bool isClosing() const { return this->closing; }
|
||||
|
@ -90,6 +94,7 @@ public:
|
|||
rocksdb::ReadOptions& getReadOptions() { return this->readOptions; }
|
||||
|
||||
private:
|
||||
const UID id;
|
||||
rocksdb::ColumnFamilyOptions initialCfOptions();
|
||||
rocksdb::DBOptions initialDbOptions();
|
||||
rocksdb::ReadOptions initialReadOptions();
|
||||
|
@ -100,13 +105,34 @@ private:
|
|||
rocksdb::ReadOptions readOptions;
|
||||
};
|
||||
|
||||
SharedRocksDBState::SharedRocksDBState()
|
||||
: closing(false), dbOptions(initialDbOptions()), cfOptions(initialCfOptions()), readOptions(initialReadOptions()) {}
|
||||
SharedRocksDBState::SharedRocksDBState(UID id)
|
||||
: 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 options;
|
||||
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) {
|
||||
options.periodic_compaction_seconds = SERVER_KNOBS->ROCKSDB_PERIODIC_COMPACTION_SECONDS;
|
||||
|
@ -258,7 +284,7 @@ using DB = rocksdb::DB*;
|
|||
using CF = rocksdb::ColumnFamilyHandle*;
|
||||
|
||||
#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 ROCKSDB_COMMIT_LATENCY_HISTOGRAM = "RocksDBCommitLatency"_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 },
|
||||
{ "CountWalFileBytes", rocksdb::WAL_FILE_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 },
|
||||
{ "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 },
|
||||
{ "CountBlocksCompressed", rocksdb::NUMBER_BLOCK_COMPRESSED, 0 },
|
||||
{ "CountBlocksDecompressed", rocksdb::NUMBER_BLOCK_DECOMPRESSED, 0 },
|
||||
{ "RowCacheHit", rocksdb::ROW_CACHE_HIT, 0 },
|
||||
{ "RowCacheMiss", rocksdb::ROW_CACHE_MISS, 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 = {
|
||||
|
@ -857,6 +898,13 @@ ACTOR Future<Void> rocksDBMetricLogger(UID id,
|
|||
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) {
|
||||
stat = 0;
|
||||
// GetAggregatedIntProperty gets the aggregated int property from all column families.
|
||||
|
@ -1129,9 +1177,9 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
if (doPerfContextMetrics) {
|
||||
perfContextMetrics->reset();
|
||||
}
|
||||
double commitBeginTime;
|
||||
double commitBeginTime = timer_monotonic();
|
||||
sharedState->commitQueueLatency->addMeasurement(commitBeginTime - a.startTime);
|
||||
if (a.getHistograms) {
|
||||
commitBeginTime = timer_monotonic();
|
||||
metricPromiseStream->send(
|
||||
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());
|
||||
readIterPool->update();
|
||||
double currTime = timer_monotonic();
|
||||
sharedState->dbWriteLatency->addMeasurement(currTime - writeBeginTime);
|
||||
if (a.getHistograms) {
|
||||
metricPromiseStream->send(
|
||||
std::make_pair(ROCKSDB_WRITE_HISTOGRAM.toString(), timer_monotonic() - writeBeginTime));
|
||||
std::make_pair(ROCKSDB_WRITE_HISTOGRAM.toString(), currTime - writeBeginTime));
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
|
@ -1167,21 +1217,23 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
} else {
|
||||
a.done.send(Void());
|
||||
|
||||
double compactRangeBeginTime = a.getHistograms ? timer_monotonic() : 0;
|
||||
for (const auto& keyRange : deletes) {
|
||||
auto begin = toSlice(keyRange.begin);
|
||||
auto end = toSlice(keyRange.end);
|
||||
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
|
||||
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
|
||||
double compactRangeBeginTime = a.getHistograms ? timer_monotonic() : 0;
|
||||
for (const auto& keyRange : deletes) {
|
||||
auto begin = toSlice(keyRange.begin);
|
||||
auto end = toSlice(keyRange.end);
|
||||
|
||||
ASSERT(db->SuggestCompactRange(cf, &begin, &end).ok());
|
||||
}
|
||||
}
|
||||
if (a.getHistograms) {
|
||||
metricPromiseStream->send(std::make_pair(ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM.toString(),
|
||||
timer_monotonic() - compactRangeBeginTime));
|
||||
if (a.getHistograms) {
|
||||
metricPromiseStream->send(std::make_pair(ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM.toString(),
|
||||
timer_monotonic() - compactRangeBeginTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
currTime = timer_monotonic();
|
||||
sharedState->commitLatency->addMeasurement(currTime - a.startTime);
|
||||
if (a.getHistograms) {
|
||||
double currTime = timer_monotonic();
|
||||
metricPromiseStream->send(
|
||||
std::make_pair(ROCKSDB_COMMIT_ACTION_HISTOGRAM.toString(), currTime - commitBeginTime));
|
||||
metricPromiseStream->send(
|
||||
|
@ -1593,7 +1645,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
|
|||
};
|
||||
|
||||
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()),
|
||||
readIterPool(new ReadIteratorPool(id, db, defaultFdbCF, sharedState->getReadOptions())),
|
||||
readSemaphore(SERVER_KNOBS->ROCKSDB_READ_QUEUE_SOFT_MAX),
|
||||
|
@ -2331,12 +2383,16 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/RocksDBReopen") {
|
|||
// Confirm that `init()` is idempotent.
|
||||
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();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->dispose();
|
||||
wait(closed);
|
||||
}
|
||||
|
||||
platform::eraseDirectoryRecursive(rocksDBTestDir);
|
||||
return Void();
|
||||
|
@ -2374,8 +2430,10 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/CheckpointRestoreColumnFamily")
|
|||
checkpoints.push_back(metaData);
|
||||
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;
|
||||
closes.push_back(kvStore->onClosed());
|
||||
|
|
|
@ -309,7 +309,17 @@ struct SQLiteDB : NonCopyable {
|
|||
db, 0, restart ? SQLITE_CHECKPOINT_RESTART : SQLITE_CHECKPOINT_FULL, &logSize, &checkpointCount);
|
||||
if (!rc)
|
||||
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("#");
|
||||
// threadSleep(.010);
|
||||
sqlite3_sleep(10);
|
||||
|
|
|
@ -1894,12 +1894,13 @@ struct ShardedRocksDBKeyValueStore : IKeyValueStore {
|
|||
}
|
||||
|
||||
a.done.send(Void());
|
||||
for (const auto& [id, range] : deletes) {
|
||||
auto cf = columnFamilyMap->find(id);
|
||||
ASSERT(cf != columnFamilyMap->end());
|
||||
auto begin = toSlice(range.begin);
|
||||
auto end = toSlice(range.end);
|
||||
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
|
||||
if (SERVER_KNOBS->ROCKSDB_SUGGEST_COMPACT_CLEAR_RANGE) {
|
||||
for (const auto& [id, range] : deletes) {
|
||||
auto cf = columnFamilyMap->find(id);
|
||||
ASSERT(cf != columnFamilyMap->end());
|
||||
auto begin = toSlice(range.begin);
|
||||
auto end = toSlice(range.end);
|
||||
|
||||
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));
|
||||
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();
|
||||
kvStore->dispose();
|
||||
|
@ -2662,17 +2665,21 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
|
|||
}
|
||||
|
||||
// Read backward full range.
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), -1000, 10000));
|
||||
ASSERT_EQ(result.size(), expectedRows.size());
|
||||
for (int i = 0; i < expectedRows.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[59 - i]);
|
||||
{
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), -1000, 10000));
|
||||
ASSERT_EQ(result.size(), expectedRows.size());
|
||||
for (int i = 0; i < expectedRows.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[59 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Forward with row limit.
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("2"_sr, "6"_sr), 10, 10000));
|
||||
ASSERT_EQ(result.size(), 10);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT(result[i] == expectedRows[20 + i]);
|
||||
{
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("2"_sr, "6"_sr), 10, 10000));
|
||||
ASSERT_EQ(result.size(), 10);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT(result[i] == expectedRows[20 + i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add another range on shard-1.
|
||||
|
@ -2688,32 +2695,40 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
|
|||
|
||||
wait(kvStore->commit(false));
|
||||
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
}
|
||||
kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
|
||||
wait(kvStore->init());
|
||||
|
||||
// Read all values.
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000));
|
||||
ASSERT_EQ(result.size(), expectedRows.size());
|
||||
for (int i = 0; i < expectedRows.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[i]);
|
||||
{
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("0"_sr, ":"_sr), 1000, 10000));
|
||||
ASSERT_EQ(result.size(), expectedRows.size());
|
||||
for (int i = 0; i < expectedRows.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Read partial range with row limit
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("5"_sr, ":"_sr), 35, 10000));
|
||||
ASSERT_EQ(result.size(), 35);
|
||||
for (int i = 0; i < result.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[40 + i]);
|
||||
{
|
||||
RangeResult result = wait(kvStore->readRange(KeyRangeRef("5"_sr, ":"_sr), 35, 10000));
|
||||
ASSERT_EQ(result.size(), 35);
|
||||
for (int i = 0; i < result.size(); ++i) {
|
||||
ASSERT(result[i] == expectedRows[40 + i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear a range on a single shard.
|
||||
kvStore->clear(KeyRangeRef("40"_sr, "45"_sr));
|
||||
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.
|
||||
kvStore->clear(KeyRangeRef("01"_sr, keyAfter("01"_sr)));
|
||||
|
@ -2726,21 +2741,29 @@ TEST_CASE("noSim/ShardedRocksDB/RangeOps") {
|
|||
kvStore->clear(KeyRangeRef("1"_sr, "8"_sr));
|
||||
wait(kvStore->commit(false));
|
||||
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
}
|
||||
kvStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
|
||||
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();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->dispose();
|
||||
wait(closed);
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
@ -2755,17 +2778,21 @@ TEST_CASE("noSim/ShardedRocksDB/ShardOps") {
|
|||
wait(kvStore->init());
|
||||
|
||||
// Add some ranges.
|
||||
std::vector<Future<Void>> addRangeFutures;
|
||||
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("a"_sr, "c"_sr), "shard-1"));
|
||||
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("c"_sr, "f"_sr), "shard-2"));
|
||||
{
|
||||
std::vector<Future<Void>> addRangeFutures;
|
||||
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"));
|
||||
addRangeFutures.push_back(kvStore->addRange(KeyRangeRef("l"_sr, "n"_sr), "shard-3"));
|
||||
{
|
||||
std::vector<Future<Void>> addRangeFutures;
|
||||
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.
|
||||
std::vector<std::string> shardsToCleanUp;
|
||||
|
@ -2821,30 +2848,37 @@ TEST_CASE("noSim/ShardedRocksDB/ShardOps") {
|
|||
kvStore = rocksdbStore;
|
||||
wait(kvStore->init());
|
||||
|
||||
auto dataMap = rocksdbStore->getDataMapping();
|
||||
for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {
|
||||
std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id "
|
||||
<< it->second << "\n";
|
||||
{
|
||||
auto dataMap = rocksdbStore->getDataMapping();
|
||||
for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {
|
||||
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.
|
||||
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.
|
||||
wait(kvStore->addRange(KeyRangeRef("h"_sr, "i"_sr), "shard-2"));
|
||||
// Add another range to shard-2.
|
||||
wait(kvStore->addRange(KeyRangeRef("h"_sr, "i"_sr), "shard-2"));
|
||||
|
||||
// Clean up shards. Shard-2 should not be removed.
|
||||
wait(kvStore->cleanUpShardsIfNeeded(shardsToCleanUp));
|
||||
// Clean up shards. Shard-2 should not be removed.
|
||||
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);
|
||||
ASSERT(dataMap[0].second == "shard-2");
|
||||
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->dispose();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->dispose();
|
||||
wait(closed);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
@ -2865,8 +2899,10 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
|
|||
kvStore->set(KeyValueRef(testSpecialKey, testSpecialValue));
|
||||
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.
|
||||
std::vector<Future<Void>> addRangeFutures;
|
||||
|
@ -2893,10 +2929,14 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
|
|||
}
|
||||
|
||||
// Read value back.
|
||||
Optional<Value> val = wait(kvStore->readValue("a1"_sr));
|
||||
ASSERT(val == Optional<Value>("foo"_sr));
|
||||
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
|
||||
ASSERT(val == Optional<Value>("bar"_sr));
|
||||
{
|
||||
Optional<Value> val = wait(kvStore->readValue("a1"_sr));
|
||||
ASSERT(val == Optional<Value>("foo"_sr));
|
||||
}
|
||||
{
|
||||
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
|
||||
ASSERT(val == Optional<Value>("bar"_sr));
|
||||
}
|
||||
|
||||
// Remove range containing a1.
|
||||
kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "b"_sr), false);
|
||||
|
@ -2904,22 +2944,30 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
|
|||
wait(kvStore->commit(false));
|
||||
|
||||
// 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.
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
}
|
||||
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
|
||||
kvStore = rocksdbStore;
|
||||
wait(kvStore->init());
|
||||
|
||||
// Read again.
|
||||
Optional<Value> val = wait(kvStore->readValue("a1"_sr));
|
||||
ASSERT(!val.present());
|
||||
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
|
||||
ASSERT(val == Optional<Value>("bar"_sr));
|
||||
{
|
||||
Optional<Value> val = wait(kvStore->readValue("a1"_sr));
|
||||
ASSERT(!val.present());
|
||||
}
|
||||
{
|
||||
Optional<Value> val = wait(kvStore->readValue("d1"_sr));
|
||||
ASSERT(val == Optional<Value>("bar"_sr));
|
||||
}
|
||||
|
||||
auto mapping = rocksdbStore->getDataMapping();
|
||||
ASSERT(mapping.size() == 3);
|
||||
|
@ -2930,46 +2978,56 @@ TEST_CASE("noSim/ShardedRocksDB/Metadata") {
|
|||
ASSERT(mapping.size() == 1);
|
||||
|
||||
// Restart.
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
}
|
||||
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
|
||||
kvStore = rocksdbStore;
|
||||
wait(kvStore->init());
|
||||
|
||||
// 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.
|
||||
kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "f"_sr), false);
|
||||
kvStore->removeRange(KeyRangeRef("a"_sr, "f"_sr));
|
||||
// Remove ranges again.
|
||||
kvStore->persistRangeMapping(KeyRangeRef("a"_sr, "f"_sr), false);
|
||||
kvStore->removeRange(KeyRangeRef("a"_sr, "f"_sr));
|
||||
|
||||
mapping = rocksdbStore->getDataMapping();
|
||||
ASSERT(mapping.size() == 1);
|
||||
mapping = rocksdbStore->getDataMapping();
|
||||
ASSERT(mapping.size() == 1);
|
||||
|
||||
wait(kvStore->commit(false));
|
||||
wait(kvStore->commit(false));
|
||||
}
|
||||
|
||||
// Restart.
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->close();
|
||||
wait(closed);
|
||||
}
|
||||
|
||||
rocksdbStore = new ShardedRocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID());
|
||||
kvStore = rocksdbStore;
|
||||
wait(kvStore->init());
|
||||
|
||||
// No range available.
|
||||
auto mapping = rocksdbStore->getDataMapping();
|
||||
for (auto it = mapping.begin(); it != mapping.end(); ++it) {
|
||||
std::cout << "Begin " << it->first.begin.toString() << ", End " << it->first.end.toString() << ", id "
|
||||
<< it->second << "\n";
|
||||
{
|
||||
auto mapping = rocksdbStore->getDataMapping();
|
||||
for (auto it = mapping.begin(); it != mapping.end(); ++it) {
|
||||
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();
|
||||
wait(closed);
|
||||
{
|
||||
Future<Void> closed = kvStore->onClosed();
|
||||
kvStore->dispose();
|
||||
wait(closed);
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
|
|
@ -2325,6 +2325,7 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
|
|||
FlowLock* cleanUpDataMoveParallelismLock,
|
||||
KeyRange keys,
|
||||
const DDEnabledState* ddEnabledState) {
|
||||
state KeyRange range;
|
||||
TraceEvent(SevVerbose, "CleanUpDataMoveBegin", dataMoveId).detail("DataMoveID", dataMoveId).detail("Range", keys);
|
||||
state bool complete = false;
|
||||
|
||||
|
@ -2336,7 +2337,9 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
|
|||
state Transaction tr(occ);
|
||||
state std::unordered_map<UID, std::vector<Shard>> physicalShardMap;
|
||||
state std::set<UID> oldDests;
|
||||
state KeyRange range;
|
||||
state DataMoveMetaData dataMove;
|
||||
|
||||
range = KeyRange();
|
||||
|
||||
try {
|
||||
tr.trState->taskID = TaskPriority::MoveKeys;
|
||||
|
@ -2346,7 +2349,7 @@ ACTOR Future<Void> cleanUpDataMove(Database occ,
|
|||
|
||||
Optional<Value> val = wait(tr.get(dataMoveKeyFor(dataMoveId)));
|
||||
if (val.present()) {
|
||||
state DataMoveMetaData dataMove = decodeDataMoveValue(val.get());
|
||||
dataMove = decodeDataMoveValue(val.get());
|
||||
TraceEvent(SevVerbose, "CleanUpDataMoveMetaData", dataMoveId)
|
||||
.detail("DataMoveID", dataMoveId)
|
||||
.detail("DataMoveMetaData", dataMove.toString());
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "fdbserver/TesterInterface.actor.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/ServerDBInfo.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbclient/ManagementAPI.actor.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
|
|
@ -533,9 +533,9 @@ ACTOR Future<Void> processCompleteTransactionStateRequest(TransactionStateResolv
|
|||
std::vector<UID> src, dest;
|
||||
ServerCacheInfo info;
|
||||
// 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,
|
||||
std::vector<Tag>& tags,
|
||||
std::vector<Reference<StorageInfo>>& storageInfoItems) {
|
||||
auto updateTagInfo = [pContext = pContext](const std::vector<UID>& uids,
|
||||
std::vector<Tag>& tags,
|
||||
std::vector<Reference<StorageInfo>>& storageInfoItems) {
|
||||
for (const auto& id : uids) {
|
||||
auto storageInfo = getStorageInfo(id, &pContext->pResolverData->storageCache, pContext->pTxnStateStore);
|
||||
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 ActorCollection actors(false);
|
||||
state Future<Void> doPollMetrics = self->resolverCount > 1 ? Void() : Future<Void>(Never());
|
||||
state PromiseStream<Future<Void>> addActor;
|
||||
actors.add(waitFailureServer(resolver.waitFailure.getFuture()));
|
||||
actors.add(traceRole(Role::RESOLVER, resolver.id()));
|
||||
|
||||
|
@ -647,7 +648,6 @@ ACTOR Future<Void> resolverCore(ResolverInterface resolver,
|
|||
// Initialize txnStateStore
|
||||
self->logSystem = ILogSystem::fromServerDBInfo(resolver.id(), db->get(), false, addActor);
|
||||
self->localTLogCount = db->get().logSystemConfig.numLogs();
|
||||
state PromiseStream<Future<Void>> addActor;
|
||||
state Future<Void> onError =
|
||||
transformError(actorCollection(addActor.getFuture()), broken_promise(), resolver_failed());
|
||||
state TransactionStateResolveContext transactionStateResolveContext;
|
||||
|
|
|
@ -38,33 +38,33 @@
|
|||
// RestoreCommon.actor.cpp
|
||||
|
||||
KeyBackedProperty<ERestoreState> RestoreConfigFR::stateEnum() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
Future<StringRef> RestoreConfigFR::stateText(Reference<ReadYourWritesTransaction> tr) {
|
||||
return map(stateEnum().getD(tr), [](ERestoreState s) -> StringRef { return FileBackupAgent::restoreStateText(s); });
|
||||
}
|
||||
KeyBackedProperty<Key> RestoreConfigFR::addPrefix() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
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
|
||||
KeyBackedProperty<KeyRange> RestoreConfigFR::restoreRange() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
KeyBackedProperty<std::vector<KeyRange>> RestoreConfigFR::restoreRanges() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
KeyBackedProperty<Key> RestoreConfigFR::batchFuture() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
KeyBackedProperty<Version> RestoreConfigFR::restoreVersion() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
|
||||
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
|
||||
KeyBackedProperty<Value> RestoreConfigFR::sourceContainerURL() {
|
||||
|
@ -73,23 +73,23 @@ KeyBackedProperty<Value> RestoreConfigFR::sourceContainerURL() {
|
|||
|
||||
// Total bytes written by all log and range restore tasks.
|
||||
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
|
||||
KeyBackedBinaryValue<int64_t> RestoreConfigFR::filesBlocksDispatched() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
// File blocks whose tasks have finished
|
||||
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlocksFinished() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
// Total number of files in the fileMap
|
||||
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileCount() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
// Total number of file blocks in the fileMap
|
||||
KeyBackedBinaryValue<int64_t> RestoreConfigFR::fileBlockCount() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
|
||||
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() {
|
||||
return configSpace.pack(LiteralStringRef(__FUNCTION__));
|
||||
return configSpace.pack(__FUNCTION__sr);
|
||||
}
|
||||
|
||||
Future<bool> RestoreConfigFR::isRunnable(Reference<ReadYourWritesTransaction> tr) {
|
||||
|
|
|
@ -526,8 +526,11 @@ ACTOR Future<Void> fetchCheckpointFile(Database cx,
|
|||
}
|
||||
|
||||
state int attempt = 0;
|
||||
state int64_t offset = 0;
|
||||
state Reference<IAsyncFile> asyncFile;
|
||||
loop {
|
||||
try {
|
||||
asyncFile = Reference<IAsyncFile>();
|
||||
++attempt;
|
||||
TraceEvent("FetchCheckpointFileBegin")
|
||||
.detail("RemoteFile", remoteFile)
|
||||
|
@ -539,8 +542,7 @@ ACTOR Future<Void> fetchCheckpointFile(Database cx,
|
|||
wait(IAsyncFileSystem::filesystem()->deleteFile(localFile, true));
|
||||
const int64_t flags = IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_READWRITE |
|
||||
IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_NO_AIO;
|
||||
state int64_t offset = 0;
|
||||
state Reference<IAsyncFile> asyncFile = wait(IAsyncFileSystem::filesystem()->open(localFile, flags, 0666));
|
||||
wait(store(asyncFile, IAsyncFileSystem::filesystem()->open(localFile, flags, 0666)));
|
||||
|
||||
state ReplyPromiseStream<FetchCheckpointReply> stream =
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
allowList.addTrustedSubnet("0.0.0.0/2"sv);
|
||||
allowList.addTrustedSubnet("abcd::/16"sv);
|
||||
|
|
|
@ -19,15 +19,16 @@
|
|||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include "fdbclient/BackupAgent.actor.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fdbclient/BackupAgent.actor.h"
|
||||
#include "fdbclient/BlobWorkerInterface.h"
|
||||
#include "fdbclient/KeyBackedTypes.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "flow/ITrace.h"
|
||||
#include "flow/ProtocolVersion.h"
|
||||
#include "flow/Trace.h"
|
||||
#include "fdbclient/NativeAPI.actor.h"
|
||||
#include "fdbclient/Metacluster.h"
|
||||
#include "fdbclient/SystemData.h"
|
||||
#include "fdbclient/ReadYourWrites.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
|
@ -35,7 +36,7 @@
|
|||
#include "fdbserver/ClusterRecovery.actor.h"
|
||||
#include "fdbserver/CoordinationInterface.h"
|
||||
#include "fdbserver/DataDistribution.actor.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.actor.h"
|
||||
#include "flow/UnitTest.h"
|
||||
#include "fdbserver/QuietDatabase.h"
|
||||
#include "fdbserver/RecoveryState.h"
|
||||
|
@ -2924,7 +2925,9 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
|||
ServerCoordinators coordinators,
|
||||
std::vector<NetworkAddress> incompatibleConnections,
|
||||
Version datacenterVersionDifference,
|
||||
ConfigBroadcaster const* configBroadcaster) {
|
||||
ConfigBroadcaster const* configBroadcaster,
|
||||
Optional<MetaclusterRegistrationEntry> metaclusterRegistration,
|
||||
MetaclusterMetrics metaclusterMetrics) {
|
||||
state double tStart = timer();
|
||||
|
||||
state JsonBuilderArray messages;
|
||||
|
@ -3066,6 +3069,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
|||
state JsonBuilderObject qos;
|
||||
state JsonBuilderObject dataOverlay;
|
||||
state JsonBuilderObject tenants;
|
||||
state JsonBuilderObject metacluster;
|
||||
state JsonBuilderObject storageWiggler;
|
||||
state std::unordered_set<UID> wiggleServers;
|
||||
|
||||
|
@ -3248,6 +3252,25 @@ ACTOR Future<StatusReply> clusterGetStatus(
|
|||
if (!qos.empty())
|
||||
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())
|
||||
statusObj["tenants"] = tenants;
|
||||
|
||||
|
|
|
@ -72,8 +72,7 @@ class TagThrottlerImpl {
|
|||
}
|
||||
self->autoThrottlingEnabled = SERVER_KNOBS->AUTO_TAG_THROTTLING_ENABLED;
|
||||
if (!committed)
|
||||
tr.set(tagThrottleAutoEnabledKey,
|
||||
LiteralStringRef(self->autoThrottlingEnabled ? "1" : "0"));
|
||||
tr.set(tagThrottleAutoEnabledKey, self->autoThrottlingEnabled ? "1"_sr : "0"_sr);
|
||||
}
|
||||
|
||||
RkTagThrottleCollection updatedTagThrottles;
|
||||
|
|
|
@ -18,37 +18,40 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "fdbclient/CommitTransaction.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/WorkerInterface.actor.h"
|
||||
#include "flow/ActorCollection.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/Knobs.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/genericactors.actor.h"
|
||||
#include "flow/Trace.h"
|
||||
#include "flow/UnitTest.h"
|
||||
#include "flow/IAsyncFile.h"
|
||||
#include "flow/ActorCollection.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#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
|
||||
|
||||
#define REDWOOD_DEBUG 0
|
||||
|
@ -4202,7 +4205,6 @@ std::string toString(BTreeNodeLinkRef id) {
|
|||
return std::string("BTreePageID") + toString(id.begin(), id.end());
|
||||
}
|
||||
|
||||
#define STR(x) LiteralStringRef(x)
|
||||
struct RedwoodRecordRef {
|
||||
typedef uint8_t byte;
|
||||
|
||||
|
@ -4769,6 +4771,8 @@ struct DecodeBoundaryVerifier {
|
|||
struct DecodeBoundaries {
|
||||
Key lower;
|
||||
Key upper;
|
||||
unsigned int height;
|
||||
Optional<int64_t> domainId;
|
||||
bool empty() const { return lower.empty() && upper.empty(); }
|
||||
};
|
||||
|
||||
|
@ -4777,6 +4781,11 @@ struct DecodeBoundaryVerifier {
|
|||
std::vector<Key> boundarySamples;
|
||||
int boundarySampleSize = 1000;
|
||||
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 std::map<std::string, DecodeBoundaryVerifier> verifiers;
|
||||
|
@ -4787,6 +4796,8 @@ struct DecodeBoundaryVerifier {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void setKeyProvider(Reference<IPageEncryptionKeyProvider> kp) { keyProvider = kp; }
|
||||
|
||||
void sampleBoundary(Key b) {
|
||||
if (boundaryPopulation <= boundarySampleSize) {
|
||||
boundarySamples.push_back(b);
|
||||
|
@ -4803,21 +4814,53 @@ struct DecodeBoundaryVerifier {
|
|||
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(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(v).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];
|
||||
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);
|
||||
ASSERT(i != boundariesByPageID.end());
|
||||
ASSERT(!i->second.empty());
|
||||
|
@ -4836,10 +4879,66 @@ struct DecodeBoundaryVerifier {
|
|||
b->second.upper.printable().c_str());
|
||||
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;
|
||||
}
|
||||
|
||||
void update(Version v, LogicalPageID oldID, LogicalPageID newID) {
|
||||
void updatePageId(Version v, LogicalPageID oldID, LogicalPageID newID) {
|
||||
auto& old = boundariesByPageID[oldID];
|
||||
ASSERT(!old.empty());
|
||||
auto i = old.end();
|
||||
|
@ -5054,6 +5153,10 @@ public:
|
|||
}
|
||||
|
||||
m_pBoundaryVerifier = DecodeBoundaryVerifier::getVerifier(name);
|
||||
if (m_pBoundaryVerifier != nullptr) {
|
||||
m_pBoundaryVerifier->setKeyProvider(m_keyProvider);
|
||||
}
|
||||
|
||||
m_pDecodeCacheMemory = m_pager->getPageCachePenaltySource();
|
||||
m_lazyClearActor = 0;
|
||||
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
|
||||
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),
|
||||
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.
|
||||
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 count; // Number of records added to the page
|
||||
|
@ -5596,6 +5710,16 @@ private:
|
|||
int blockCount; // The number of blocks in pageSize
|
||||
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
|
||||
int usedBytes() const { return pageSize - bytesLeft; }
|
||||
|
||||
|
@ -5615,17 +5739,18 @@ private:
|
|||
int endIndex() const { return startIndex + count; }
|
||||
|
||||
std::string toString() const {
|
||||
return format(
|
||||
"{start=%d count=%d used %d/%d bytes (%.2f%% slack) kvBytes=%d blocks=%d blockSize=%d large=%d}",
|
||||
startIndex,
|
||||
count,
|
||||
usedBytes(),
|
||||
pageSize,
|
||||
slackFraction() * 100,
|
||||
kvBytes,
|
||||
blockCount,
|
||||
blockSize,
|
||||
largeDeltaTree);
|
||||
return format("{start=%d count=%d used %d/%d bytes (%.2f%% slack) kvBytes=%d blocks=%d blockSize=%d "
|
||||
"large=%d, domain=%s}",
|
||||
startIndex,
|
||||
count,
|
||||
usedBytes(),
|
||||
pageSize,
|
||||
slackFraction() * 100,
|
||||
kvBytes,
|
||||
blockCount,
|
||||
blockSize,
|
||||
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
|
||||
|
@ -5657,7 +5782,8 @@ private:
|
|||
// 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.
|
||||
// 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);
|
||||
|
||||
// If the record doesn't fit and the page can't be expanded then return false
|
||||
|
@ -5665,6 +5791,53 @@ private:
|
|||
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;
|
||||
bytesLeft -= nodeSize;
|
||||
kvBytes += rec.kvBytes();
|
||||
|
@ -5687,6 +5860,12 @@ private:
|
|||
}
|
||||
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
|
||||
|
@ -5706,8 +5885,25 @@ private:
|
|||
// Leaves can have just one record if it's large, but internal pages should have at least 4
|
||||
int minRecords = height == 1 ? 1 : 4;
|
||||
double maxSlack = SERVER_KNOBS->REDWOOD_PAGE_REBUILD_MAX_SLACK;
|
||||
RedwoodRecordRef emptyRecord;
|
||||
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]
|
||||
std::vector<int> deltaSizes(records.size() + 1);
|
||||
deltaSizes.front() = records.front().deltaSize(*lowerBound, prefixLen, true);
|
||||
|
@ -5716,28 +5912,34 @@ private:
|
|||
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;
|
||||
debug_printf(
|
||||
" before addRecord i=%d records=%d deltaSize=%d kvSize=%d force=%d pageToBuild=%s record=%s",
|
||||
i,
|
||||
records.size(),
|
||||
deltaSizes[i],
|
||||
records[i].kvBytes(),
|
||||
force,
|
||||
p.toString().c_str(),
|
||||
records[i].toString(height == 1).c_str());
|
||||
if (i == 0 || p.count > 0) {
|
||||
debug_printf(" before addRecord i=%d records=%d deltaSize=%d kvSize=%d force=%d pageToBuild=%s "
|
||||
"record=%s",
|
||||
i,
|
||||
records.size(),
|
||||
deltaSizes[i],
|
||||
records[i].kvBytes(),
|
||||
force,
|
||||
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);
|
||||
p = p.next(m_encodingType);
|
||||
p.addRecord(records[i], deltaSizes[i], true);
|
||||
p = p.next();
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.count > 0) {
|
||||
p.finish();
|
||||
pages.push_back(p);
|
||||
}
|
||||
|
||||
|
@ -5750,15 +5952,20 @@ private:
|
|||
PageToBuild& a = pages[pages.size() - 2];
|
||||
PageToBuild& b = pages.back();
|
||||
|
||||
// While the last page page has too much slack and the second to last page
|
||||
// has more than the minimum record count, shift a record from the second
|
||||
// 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;
|
||||
// We can rebalance the two pages only if they are in the same encryption domain.
|
||||
ASSERT(!useEncryptionDomain || (a.domainId.present() && b.domainId.present()));
|
||||
if (!useEncryptionDomain || a.domainId.get() == b.domainId.get()) {
|
||||
|
||||
// While the last page page has too much slack and the second to last page
|
||||
// has more than the minimum record count, shift a record from the second
|
||||
// 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
|
||||
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 =
|
||||
self->splitPages(lowerBound, upperBound, prefixLen, entries, height);
|
||||
ASSERT(pagesToBuild.size() > 0);
|
||||
debug_printf("splitPages returning %s\n", toString(pagesToBuild).c_str());
|
||||
|
||||
// Lower bound of the page being added to
|
||||
|
@ -5792,6 +6004,18 @@ private:
|
|||
|
||||
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) {
|
||||
debug_printf("building page %d of %zu %s\n",
|
||||
pageIndex + 1,
|
||||
|
@ -5799,6 +6023,30 @@ private:
|
|||
pagesToBuild[pageIndex].toString().c_str());
|
||||
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
|
||||
// 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
|
||||
|
@ -5812,38 +6060,34 @@ private:
|
|||
--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
|
||||
// be more than 1 item since internal pages need to have multiple children. While there is no page
|
||||
// to be built here, a record must be added to the output set because the upper boundary of the last
|
||||
// In case encryption or encryption domain is not enabled, if the page is now empty then it must be the
|
||||
// last page in pagesToBuild, otherwise there would be more than 1 item since internal pages need to
|
||||
// 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
|
||||
// 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
|
||||
// adding the extra null link fixes this.
|
||||
if (p.count == 0) {
|
||||
ASSERT(pageIndex == pagesToBuild.size() - 1);
|
||||
records.push_back_deep(records.arena(), pageUpperBound);
|
||||
break;
|
||||
ASSERT(useEncryptionDomain || lastPage);
|
||||
records.push_back_deep(records.arena(), pageLowerBound);
|
||||
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
|
||||
state Reference<ArenaPage> page = self->m_pager->newPageBuffer(pagesToBuild[pageIndex].blockCount);
|
||||
page->init(self->m_encodingType,
|
||||
(pagesToBuild[pageIndex].blockCount == 1) ? PageType::BTreeNode : PageType::BTreeSuperNode,
|
||||
height);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -5936,7 +6180,8 @@ private:
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -5985,9 +6230,26 @@ private:
|
|||
// 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
|
||||
// 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 ||
|
||||
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");
|
||||
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;
|
||||
ASSERT(height < std::numeric_limits<int8_t>::max());
|
||||
Standalone<VectorRef<RedwoodRecordRef>> newRecords = wait(
|
||||
|
@ -6166,7 +6428,7 @@ private:
|
|||
self->m_pager->updatePage(PagerEventReasons::Commit, height, newID, page);
|
||||
|
||||
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);
|
||||
|
@ -6328,8 +6590,14 @@ private:
|
|||
|
||||
struct InternalPageModifier {
|
||||
InternalPageModifier() {}
|
||||
InternalPageModifier(Reference<const ArenaPage> p, bool alreadyCloned, bool updating, ParentInfo* parentInfo)
|
||||
: updating(updating), page(p), clonedPage(alreadyCloned), changesMade(false), parentInfo(parentInfo) {}
|
||||
InternalPageModifier(Reference<const ArenaPage> p,
|
||||
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
|
||||
bool updating;
|
||||
|
@ -6344,6 +6612,9 @@ private:
|
|||
bool changesMade;
|
||||
ParentInfo* parentInfo;
|
||||
|
||||
Reference<IPageEncryptionKeyProvider> keyProvider;
|
||||
Optional<int64_t> pageDomainId;
|
||||
|
||||
BTreePage* btPage() const { return (BTreePage*)page->mutateData(); }
|
||||
|
||||
bool empty() const {
|
||||
|
@ -6366,6 +6637,7 @@ private:
|
|||
void insert(BTreePage::BinaryTree::Cursor end, const VectorRef<RedwoodRecordRef>& recs) {
|
||||
int i = 0;
|
||||
if (updating) {
|
||||
cloneForUpdate();
|
||||
// Update must be done in the new tree, not the original tree where the end cursor will be from
|
||||
end.switchTree(btPage()->tree());
|
||||
|
||||
|
@ -6374,7 +6646,18 @@ private:
|
|||
const RedwoodRecordRef& rec = recs[i];
|
||||
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",
|
||||
rec.toString(false).c_str());
|
||||
|
||||
|
@ -6433,14 +6716,11 @@ private:
|
|||
|
||||
// If the children changed, replace [cBegin, cEnd) with newLinks
|
||||
if (u.childrenChanged) {
|
||||
cloneForUpdate();
|
||||
if (updating) {
|
||||
auto c = u.cBegin;
|
||||
|
||||
if (c != u.cEnd) {
|
||||
cloneForUpdate();
|
||||
// must point c to the tree to erase from
|
||||
c.switchTree(btPage()->tree());
|
||||
}
|
||||
// must point c to the tree to erase from
|
||||
c.switchTree(btPage()->tree());
|
||||
|
||||
while (c != u.cEnd) {
|
||||
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(
|
||||
VersionedBTree* self,
|
||||
CommitBatch* batch,
|
||||
|
@ -6546,6 +6875,12 @@ private:
|
|||
// 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 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_print(addPrefix(context,
|
||||
btPage->toString("commitSubtreeStart",
|
||||
|
@ -6563,7 +6898,9 @@ private:
|
|||
ASSERT(self->m_pBoundaryVerifier->verify(rootID.front(),
|
||||
batch->snapshot->getVersion(),
|
||||
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 (updatingDeltaTree) {
|
||||
copyForUpdate();
|
||||
if (cursor.insert(rec, update->skipLen, maxHeightAllowed)) {
|
||||
bool canInsert = true;
|
||||
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();
|
||||
debug_printf("%s Inserted %s [mutation, boundary start]\n",
|
||||
context.c_str(),
|
||||
|
@ -6882,6 +7227,25 @@ private:
|
|||
|
||||
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()) {
|
||||
slices.emplace_back(new InternalPageSliceUpdate());
|
||||
InternalPageSliceUpdate& u = *slices.back();
|
||||
|
@ -7064,7 +7428,8 @@ private:
|
|||
// 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
|
||||
// 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.
|
||||
// For each range, the expected next record, if any, is checked against the first boundary
|
||||
|
@ -7083,8 +7448,11 @@ private:
|
|||
modifier.changesMade);
|
||||
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.changesMade ? &update->subtreeUpperBound : &update->decodeUpperBound);
|
||||
modifier.changesMade || slices.back()->childrenChanged ? &update->subtreeUpperBound
|
||||
: &update->decodeUpperBound);
|
||||
|
||||
state bool detachChildren = (parentInfo->count > 2);
|
||||
state bool forceUpdate = false;
|
||||
|
@ -7147,7 +7515,8 @@ private:
|
|||
if (newID != invalidPhysicalPageID) {
|
||||
debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID);
|
||||
if (self->m_pBoundaryVerifier != nullptr) {
|
||||
self->m_pBoundaryVerifier->update(batch->writeVersion, p, newID);
|
||||
self->m_pBoundaryVerifier->updatePageId(
|
||||
batch->writeVersion, p, newID);
|
||||
}
|
||||
p = newID;
|
||||
++stats.metrics.detachChild;
|
||||
|
@ -7213,7 +7582,8 @@ private:
|
|||
rec.setChildPage(newPages);
|
||||
debug_printf("%s Detach updated %u -> %u\n", context.c_str(), p, newID);
|
||||
if (self->m_pBoundaryVerifier != nullptr) {
|
||||
self->m_pBoundaryVerifier->update(batch->writeVersion, p, newID);
|
||||
self->m_pBoundaryVerifier->updatePageId(
|
||||
batch->writeVersion, p, newID);
|
||||
}
|
||||
++stats.metrics.detachChild;
|
||||
}
|
||||
|
@ -7223,7 +7593,6 @@ private:
|
|||
}
|
||||
parentInfo->clear();
|
||||
}
|
||||
|
||||
Standalone<VectorRef<RedwoodRecordRef>> newChildEntries =
|
||||
wait(writePages(self,
|
||||
&update->subtreeLowerBound,
|
||||
|
@ -7431,17 +7800,24 @@ public:
|
|||
false,
|
||||
!options.present() || options.get().cacheResult || path.back().btPage()->height != 2),
|
||||
[=](Reference<const ArenaPage> p) {
|
||||
BTreePage::BinaryTree::Cursor cursor = btree->getCursor(p.getPtr(), link);
|
||||
#if REDWOOD_DEBUG
|
||||
path.push_back({ p, btree->getCursor(p.getPtr(), link), link.get().getChildPage() });
|
||||
path.push_back({ p, cursor, link.get().getChildPage() });
|
||||
#else
|
||||
path.push_back({ p, btree->getCursor(p.getPtr(), link) });
|
||||
path.push_back({ p, cursor });
|
||||
#endif
|
||||
|
||||
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(),
|
||||
pager->getVersion(),
|
||||
link.get().key,
|
||||
link.next().getOrUpperBound().key));
|
||||
link.next().getOrUpperBound().key,
|
||||
domainId,
|
||||
cursor));
|
||||
}
|
||||
return Void();
|
||||
});
|
||||
|
@ -7724,8 +8100,13 @@ public:
|
|||
// 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.
|
||||
if (encryptionKeyProvider && encryptionKeyProvider->enableEncryption()) {
|
||||
ASSERT(encryptionKeyProvider->expectedEncodingType() == EncodingType::AESEncryptionV1);
|
||||
encodingType = EncodingType::AESEncryptionV1;
|
||||
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,
|
||||
|
@ -9753,6 +10134,11 @@ TEST_CASE("Lredwood/correctness/btree") {
|
|||
state bool serialTest = params.getInt("serialTest").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 =
|
||||
shortTest ? 250 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(250, 400));
|
||||
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
|
||||
state int64_t maxRecordsRead = params.getInt("maxRecordsRead").orDefault(300e6);
|
||||
|
||||
state EncodingType encodingType =
|
||||
static_cast<EncodingType>(deterministicRandom()->randomInt(0, EncodingType::MAX_ENCODING_TYPE));
|
||||
state EncodingType encodingType = static_cast<EncodingType>(encoding);
|
||||
state Reference<IPageEncryptionKeyProvider> keyProvider;
|
||||
if (encodingType == EncodingType::AESEncryptionV1) {
|
||||
keyProvider = makeReference<RandomEncryptionKeyProvider>();
|
||||
keyProvider = makeReference<RandomEncryptionKeyProvider>(
|
||||
RandomEncryptionKeyProvider::EncryptionDomainMode(encryptionDomainMode));
|
||||
} else if (encodingType == EncodingType::XOREncryption_TestOnly) {
|
||||
keyProvider = makeReference<XOREncryptionKeyProvider_TestOnly>(file);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("file: %s\n", file.c_str());
|
||||
printf("encodingType: %d\n", encodingType);
|
||||
printf("maxPageOps: %" PRId64 "\n", maxPageOps);
|
||||
printf("maxVerificationMapEntries: %d\n", maxVerificationMapEntries);
|
||||
printf("maxRecordsRead: %" PRId64 "\n", maxRecordsRead);
|
||||
printf("pagerMemoryOnly: %d\n", pagerMemoryOnly);
|
||||
printf("serialTest: %d\n", serialTest);
|
||||
printf("shortTest: %d\n", shortTest);
|
||||
printf("encodingType: %d\n", encodingType);
|
||||
printf("domainMode: %d\n", encryptionDomainMode);
|
||||
printf("pageSize: %d\n", pageSize);
|
||||
printf("extentSize: %d\n", extentSize);
|
||||
printf("maxKeySize: %d\n", maxKeySize);
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
#include "fdbserver/RestoreWorkerInterface.actor.h"
|
||||
#include "fdbserver/ServerDBInfo.h"
|
||||
#include "fdbserver/SimulatedCluster.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbserver/TesterInterface.actor.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/pubsub.h"
|
||||
|
|
|
@ -135,7 +135,7 @@ private:
|
|||
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> printRestoreSummary(Database db, Reference<BlobConnectionProvider> blobConn);
|
||||
inline bool isFullRestoreMode() {
|
||||
|
|
|
@ -60,6 +60,8 @@ ACTOR Future<Void> validateGranuleSummaries(Database cx,
|
|||
Optional<TenantName> tenantName,
|
||||
Promise<Void> testComplete);
|
||||
|
||||
ACTOR Future<Void> checkFeedCleanup(Database cx, bool debug);
|
||||
|
||||
#include "flow/unactorcompiler.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define FDBSERVER_CLUSTERCONTROLLER_ACTOR_H
|
||||
|
||||
#include "fdbclient/DatabaseContext.h"
|
||||
#include "fdbclient/Metacluster.h"
|
||||
#include "fdbrpc/Replication.h"
|
||||
#include "fdbrpc/ReplicationUtils.h"
|
||||
#include "fdbserver/Knobs.h"
|
||||
|
@ -142,6 +143,8 @@ public:
|
|||
AsyncVar<bool> blobGranulesEnabled;
|
||||
ClusterType clusterType = ClusterType::STANDALONE;
|
||||
Optional<ClusterName> metaclusterName;
|
||||
Optional<MetaclusterRegistrationEntry> metaclusterRegistration;
|
||||
MetaclusterMetrics metaclusterMetrics;
|
||||
|
||||
DBInfo()
|
||||
: clientInfo(new AsyncVar<ClientDBInfo>()), serverInfo(new AsyncVar<ServerDBInfo>()),
|
||||
|
@ -2965,9 +2968,8 @@ public:
|
|||
workerHealth[req.address].degradedPeers[degradedPeer] = { currentTime, currentTime };
|
||||
}
|
||||
|
||||
// TODO(zhewu): add disconnected peers in worker health.
|
||||
for (const auto& degradedPeer : req.disconnectedPeers) {
|
||||
workerHealth[req.address].degradedPeers[degradedPeer] = { currentTime, currentTime };
|
||||
workerHealth[req.address].disconnectedPeers[degradedPeer] = { currentTime, currentTime };
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -2977,23 +2979,24 @@ public:
|
|||
|
||||
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.
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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();) {
|
||||
if (it->second.degradedPeers.empty()) {
|
||||
if (it->second.degradedPeers.empty() && it->second.disconnectedPeers.empty()) {
|
||||
TraceEvent("WorkerAllPeerHealthRecovered").detail("Worker", it->first);
|
||||
workerHealth.erase(it++);
|
||||
} else {
|
||||
|
@ -3025,6 +3036,8 @@ public:
|
|||
std::unordered_set<NetworkAddress>
|
||||
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`.
|
||||
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.
|
||||
};
|
||||
|
@ -3035,6 +3048,7 @@ public:
|
|||
|
||||
// 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>> disconnectedLinkDst2Src;
|
||||
double currentTime = now();
|
||||
for (const auto& [server, health] : workerHealth) {
|
||||
for (const auto& [degradedPeer, times] : health.degradedPeers) {
|
||||
|
@ -3044,6 +3058,13 @@ public:
|
|||
}
|
||||
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.
|
||||
|
@ -3053,6 +3074,12 @@ public:
|
|||
}
|
||||
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
|
||||
// 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
|
||||
|
@ -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
|
||||
// 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) {
|
||||
if (degradedLinkDst2Src[badServer].size() <= SERVER_KNOBS->CC_DEGRADED_PEER_DEGREE_TO_EXCLUDE) {
|
||||
currentDegradationInfo.degradedServers.insert(badServer);
|
||||
|
@ -3101,43 +3144,48 @@ public:
|
|||
|
||||
// Whether the transaction system (in primary DC if in HA setting) contains degraded servers.
|
||||
bool transactionSystemContainsDegradedServers() {
|
||||
const ServerDBInfo dbi = db.serverInfo->get();
|
||||
for (const auto& excludedServer : degradationInfo.degradedServers) {
|
||||
if (dbi.master.addresses().contains(excludedServer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto& logSet : dbi.logSystemConfig.tLogs) {
|
||||
if (!logSet.isLocal || logSet.locality == tagLocalitySatellite) {
|
||||
continue;
|
||||
const ServerDBInfo& dbi = db.serverInfo->get();
|
||||
auto transactionWorkerInList = [&dbi](const std::unordered_set<NetworkAddress>& serverList) -> bool {
|
||||
for (const auto& server : serverList) {
|
||||
if (dbi.master.addresses().contains(server)) {
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& proxy : dbi.client.grvProxies) {
|
||||
if (proxy.addresses().contains(excludedServer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for (auto& proxy : dbi.client.commitProxies) {
|
||||
if (proxy.addresses().contains(excludedServer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& resolver : dbi.resolvers) {
|
||||
if (resolver.addresses().contains(excludedServer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return transactionWorkerInList(degradationInfo.degradedServers) ||
|
||||
transactionWorkerInList(degradationInfo.disconnectedServers);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -3185,7 +3239,8 @@ public:
|
|||
// Returns true when the cluster controller should trigger a recovery due to degraded servers used in the
|
||||
// transaction system in the primary data center.
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3224,8 +3279,9 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
if (degradationInfo.degradedServers.size() < SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MIN_DEGRADATION ||
|
||||
degradationInfo.degradedServers.size() > SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MAX_DEGRADATION) {
|
||||
int degradedServerSize = degradationInfo.degradedServers.size() + degradationInfo.disconnectedServers.size();
|
||||
if (degradedServerSize < SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MIN_DEGRADATION ||
|
||||
degradedServerSize > SERVER_KNOBS->CC_FAILOVER_DUE_TO_HEALTH_MAX_DEGRADATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3316,6 +3372,7 @@ public:
|
|||
double lastRefreshTime = 0;
|
||||
};
|
||||
std::unordered_map<NetworkAddress, DegradedTimes> degradedPeers;
|
||||
std::unordered_map<NetworkAddress, DegradedTimes> disconnectedPeers;
|
||||
|
||||
// TODO(zhewu): Include disk and CPU signals.
|
||||
};
|
||||
|
|
|
@ -247,6 +247,7 @@ struct ClusterRecoveryData : NonCopyable, ReferenceCounted<ClusterRecoveryData>
|
|||
|
||||
Future<Void> logger;
|
||||
|
||||
Reference<EventCacheHolder> metaclusterEventHolder;
|
||||
Reference<EventCacheHolder> swVersionCheckedEventHolder;
|
||||
Reference<EventCacheHolder> recoveredConfigEventHolder;
|
||||
Reference<EventCacheHolder> clusterRecoveryStateEventHolder;
|
||||
|
@ -277,6 +278,7 @@ struct ClusterRecoveryData : NonCopyable, ReferenceCounted<ClusterRecoveryData>
|
|||
backupWorkerDoneRequests("BackupWorkerDoneRequests", cc),
|
||||
getLiveCommittedVersionRequests("GetLiveCommittedVersionRequests", cc),
|
||||
reportLiveCommittedVersionRequests("ReportLiveCommittedVersionRequests", cc),
|
||||
metaclusterEventHolder(makeReference<EventCacheHolder>("MetaclusterMetadata")),
|
||||
swVersionCheckedEventHolder(makeReference<EventCacheHolder>("SWVersionCompatibilityChecked")),
|
||||
recoveredConfigEventHolder(makeReference<EventCacheHolder>("RecoveredConfig")) {
|
||||
clusterRecoveryStateEventHolder = makeReference<EventCacheHolder>(
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
struct InitialDataDistribution;
|
||||
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:
|
||||
* 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
|
||||
|
@ -44,8 +49,8 @@ public:
|
|||
// get the source server list and complete source server list for range
|
||||
virtual Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) { return SourceServers{}; };
|
||||
|
||||
// get the storage server list and Process class
|
||||
virtual Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> getServerListAndProcessClasses() = 0;
|
||||
// get the storage server list and Process class, only throw transaction non-retryable exceptions
|
||||
virtual Future<ServerWorkerInfos> getServerListAndProcessClasses() = 0;
|
||||
|
||||
virtual Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
|
||||
const UID& distributorId,
|
||||
|
@ -65,6 +70,10 @@ public:
|
|||
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<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const {
|
||||
|
@ -109,6 +118,10 @@ public:
|
|||
virtual Future<HealthMetrics> getHealthMetrics(bool detailed = false) const = 0;
|
||||
|
||||
virtual Future<Optional<Value>> readRebalanceDDIgnoreKey() const { return {}; }
|
||||
|
||||
virtual Future<UID> getClusterId() const { return {}; }
|
||||
|
||||
virtual Future<Void> waitDDTeamInfoPrintSignal() const { return Never(); }
|
||||
};
|
||||
|
||||
class DDTxnProcessorImpl;
|
||||
|
@ -128,7 +141,7 @@ public:
|
|||
Future<SourceServers> getSourceServersForRange(const KeyRangeRef range) override;
|
||||
|
||||
// Call NativeAPI implementation directly
|
||||
Future<std::vector<std::pair<StorageServerInterface, ProcessClass>>> getServerListAndProcessClasses() override;
|
||||
Future<ServerWorkerInfos> getServerListAndProcessClasses() override;
|
||||
|
||||
Future<Reference<InitialDataDistribution>> getInitialDataDistribution(
|
||||
const UID& distributorId,
|
||||
|
@ -144,6 +157,8 @@ public:
|
|||
const std::vector<Optional<Key>>& remoteIds,
|
||||
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<bool> isDataDistributionEnabled(const DDEnabledState* ddEnabledState) const override;
|
||||
|
@ -183,6 +198,10 @@ public:
|
|||
Future<HealthMetrics> getHealthMetrics(bool detailed) 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.
|
||||
|
@ -196,7 +215,7 @@ class DDMockTxnProcessor : public IDDTxnProcessor {
|
|||
public:
|
||||
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(
|
||||
const UID& distributorId,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define XXH_INLINE_ALL
|
||||
#include "flow/xxhash.h"
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
@ -72,10 +73,12 @@ public:
|
|||
virtual bool enableEncryptionDomain() const { return false; }
|
||||
|
||||
// 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.
|
||||
virtual Future<EncryptionKey> getLatestDefaultEncryptionKey() { throw not_implemented(); }
|
||||
virtual Future<EncryptionKey> getLatestDefaultEncryptionKey() {
|
||||
return getLatestEncryptionKey(getDefaultEncryptionDomainId());
|
||||
}
|
||||
|
||||
// Get latest encryption key for data in given encryption domain.
|
||||
virtual Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) { throw not_implemented(); }
|
||||
|
@ -94,10 +97,22 @@ public:
|
|||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
|
@ -133,39 +148,20 @@ public:
|
|||
|
||||
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 {
|
||||
|
||||
EncodingHeader* h = reinterpret_cast<EncodingHeader*>(encodingHeader);
|
||||
const EncodingHeader* h = reinterpret_cast<const EncodingHeader*>(encodingHeader);
|
||||
EncryptionKey s;
|
||||
s.xorKey = h->xorKey;
|
||||
return s;
|
||||
}
|
||||
|
||||
Future<EncryptionKey> getLatestDefaultEncryptionKey() override { return getLatestEncryptionKey(0); }
|
||||
|
||||
Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) override {
|
||||
Future<EncryptionKey> getLatestDefaultEncryptionKey() override {
|
||||
EncryptionKey s;
|
||||
s.xorKey = ~(uint8_t)domainId ^ xorWith;
|
||||
s.xorKey = xorWith;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -173,11 +169,19 @@ public:
|
|||
// Use for testing.
|
||||
class RandomEncryptionKeyProvider : public IPageEncryptionKeyProvider {
|
||||
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++) {
|
||||
BlobCipherDetails cipherDetails;
|
||||
cipherDetails.encryptDomainId = i;
|
||||
cipherDetails.baseCipherId = deterministicRandom()->randomUInt64();
|
||||
cipherDetails.encryptDomainId = 0;
|
||||
cipherDetails.baseCipherId = i;
|
||||
cipherDetails.salt = deterministicRandom()->randomUInt64();
|
||||
cipherKeys[i] = generateCipherKey(cipherDetails);
|
||||
}
|
||||
|
@ -188,22 +192,47 @@ public:
|
|||
|
||||
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;
|
||||
Header* h = reinterpret_cast<Header*>(encodingHeader);
|
||||
const Header* h = reinterpret_cast<const Header*>(encodingHeader);
|
||||
EncryptionKey s;
|
||||
s.aesKey.cipherTextKey = cipherKeys[h->cipherTextDetails.encryptDomainId];
|
||||
s.aesKey.cipherHeaderKey = cipherKeys[h->cipherHeaderDetails.encryptDomainId];
|
||||
s.aesKey.cipherTextKey = getCipherKey(h->cipherTextDetails.encryptDomainId, h->cipherTextDetails.baseCipherId);
|
||||
s.aesKey.cipherHeaderKey =
|
||||
getCipherKey(h->cipherHeaderDetails.encryptDomainId, h->cipherHeaderDetails.baseCipherId);
|
||||
return s;
|
||||
}
|
||||
|
||||
Future<EncryptionKey> getLatestDefaultEncryptionKey() override {
|
||||
Future<EncryptionKey> getLatestEncryptionKey(int64_t domainId) override {
|
||||
domainId = checkDomainId(domainId);
|
||||
EncryptionKey s;
|
||||
s.aesKey.cipherTextKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)];
|
||||
s.aesKey.cipherHeaderKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)];
|
||||
s.aesKey.cipherTextKey = getCipherKey(domainId, deterministicRandom()->randomInt(0, NUM_CIPHER));
|
||||
s.aesKey.cipherHeaderKey =
|
||||
getCipherKey(ENCRYPT_HEADER_DOMAIN_ID, deterministicRandom()->randomInt(0, NUM_CIPHER));
|
||||
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:
|
||||
Reference<BlobCipherKey> generateCipherKey(const BlobCipherDetails& cipherDetails) {
|
||||
static unsigned char SHA_KEY[] = "3ab9570b44b8315fdb261da6b1b6c13b";
|
||||
|
@ -226,7 +255,28 @@ private:
|
|||
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 size_t PREFIX_LENGTH = 4;
|
||||
EncryptionDomainMode mode;
|
||||
Reference<BlobCipherKey> cipherKeys[NUM_CIPHER];
|
||||
};
|
||||
|
||||
|
@ -248,8 +298,9 @@ public:
|
|||
|
||||
bool enableEncryptionDomain() const override { return true; }
|
||||
|
||||
ACTOR static Future<EncryptionKey> getEncryptionKey(TenantAwareEncryptionKeyProvider* self, void* encodingHeader) {
|
||||
BlobCipherEncryptHeader* header = reinterpret_cast<EncodingHeader*>(encodingHeader);
|
||||
ACTOR static Future<EncryptionKey> getEncryptionKey(TenantAwareEncryptionKeyProvider* self,
|
||||
const void* encodingHeader) {
|
||||
const BlobCipherEncryptHeader* header = reinterpret_cast<const EncodingHeader*>(encodingHeader);
|
||||
TextAndHeaderCipherKeys cipherKeys =
|
||||
wait(getEncryptCipherKeys(self->db, *header, BlobCipherMetrics::KV_REDWOOD));
|
||||
EncryptionKey encryptionKey;
|
||||
|
@ -257,7 +308,7 @@ public:
|
|||
return encryptionKey;
|
||||
}
|
||||
|
||||
Future<EncryptionKey> getEncryptionKey(void* encodingHeader) override {
|
||||
Future<EncryptionKey> getEncryptionKey(const void* encodingHeader) override {
|
||||
return getEncryptionKey(this, encodingHeader);
|
||||
}
|
||||
|
||||
|
@ -292,7 +343,7 @@ public:
|
|||
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
|
||||
}
|
||||
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.
|
||||
if (tenantId < 0) {
|
||||
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
|
||||
|
@ -314,8 +365,9 @@ public:
|
|||
return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 };
|
||||
}
|
||||
|
||||
int64_t getEncryptionDomain(void* encodingHeader) override {
|
||||
BlobCipherEncryptHeader* header = reinterpret_cast<EncodingHeader*>(encodingHeader);
|
||||
int64_t getEncryptionDomainIdFromHeader(const void* encodingHeader) override {
|
||||
ASSERT(encodingHeader != nullptr);
|
||||
const BlobCipherEncryptHeader* header = reinterpret_cast<const EncodingHeader*>(encodingHeader);
|
||||
return header->cipherTextDetails.encryptDomainId;
|
||||
}
|
||||
|
||||
|
@ -332,7 +384,7 @@ private:
|
|||
return FDB_DEFAULT_ENCRYPT_DOMAIN_NAME;
|
||||
}
|
||||
if (tenantPrefixIndex.isValid()) {
|
||||
StringRef prefix = TenantMapEntry::idToPrefix(domainId);
|
||||
Key prefix(TenantMapEntry::idToPrefix(domainId));
|
||||
auto view = tenantPrefixIndex->atLatest();
|
||||
auto itr = view.find(prefix);
|
||||
if (itr != view.end()) {
|
||||
|
|
|
@ -498,7 +498,7 @@ public:
|
|||
// Secret is set if needed
|
||||
// Post: Main and Encoding subheaders are updated
|
||||
// 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.
|
||||
// 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.
|
||||
|
@ -519,6 +519,7 @@ public:
|
|||
} else {
|
||||
throw page_header_version_not_supported();
|
||||
}
|
||||
encodingHeaderAvailable = true;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
pPayload = page->getPayload();
|
||||
payloadSize = logicalSize - (pPayload - buffer);
|
||||
encodingHeaderAvailable = true;
|
||||
|
||||
if (page->headerVersion == 1) {
|
||||
if (verify) {
|
||||
|
@ -568,7 +570,18 @@ public:
|
|||
// Returns true if the page's encoding type employs encryption
|
||||
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:
|
||||
Arena arena;
|
||||
|
@ -608,6 +621,9 @@ public:
|
|||
// Used by encodings that do encryption
|
||||
EncryptionKey encryptionKey;
|
||||
|
||||
// Whether encoding header is set
|
||||
bool encodingHeaderAvailable = false;
|
||||
|
||||
mutable ArbitraryObject extra;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include "fdbserver/LogSystemConfig.h"
|
||||
#include "fdbserver/RatekeeperInterface.h"
|
||||
#include "fdbserver/BlobManagerInterface.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.actor.h"
|
||||
#include "fdbserver/RecoveryState.h"
|
||||
#include "fdbserver/LatencyBandConfig.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
|
|
|
@ -18,8 +18,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FDBSERVER_STATUS_H
|
||||
#define FDBSERVER_STATUS_H
|
||||
#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_ACTOR_STATUS_G_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
|
||||
|
||||
#include "fdbrpc/fdbrpc.h"
|
||||
|
@ -27,6 +30,9 @@
|
|||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/MasterInterface.h"
|
||||
#include "fdbclient/ClusterInterface.h"
|
||||
#include "fdbclient/Metacluster.h"
|
||||
|
||||
#include "flow/actorcompiler.h" // has to be last include
|
||||
|
||||
struct ProcessIssues {
|
||||
NetworkAddress address;
|
||||
|
@ -44,10 +50,14 @@ Future<StatusReply> clusterGetStatus(
|
|||
ServerCoordinators const& coordinators,
|
||||
std::vector<NetworkAddress> const& incompatibleConnections,
|
||||
Version const& datacenterVersionDifference,
|
||||
ConfigBroadcaster const* const& conifgBroadcaster);
|
||||
ConfigBroadcaster const* const& conifgBroadcaster,
|
||||
Optional<MetaclusterRegistrationEntry> const& metaclusterRegistration,
|
||||
MetaclusterMetrics const& metaclusterMetrics);
|
||||
|
||||
struct WorkerEvents : std::map<NetworkAddress, TraceEventFields> {};
|
||||
Future<Optional<std::pair<WorkerEvents, std::set<std::string>>>> latestEventOnWorkers(
|
||||
std::vector<WorkerDetails> const& workers,
|
||||
std::string const& eventName);
|
||||
ACTOR Future<Optional<std::pair<WorkerEvents, std::set<std::string>>>> latestEventOnWorkers(
|
||||
std::vector<WorkerDetails> workers,
|
||||
std::string eventName);
|
||||
|
||||
#include "flow/unactorcompiler.h"
|
||||
#endif
|
|
@ -31,7 +31,7 @@
|
|||
#include "fdbserver/MasterInterface.h"
|
||||
#include "fdbserver/TLogInterface.h"
|
||||
#include "fdbserver/RatekeeperInterface.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.h"
|
||||
#include "fdbclient/ConsistencyScanInterface.actor.h"
|
||||
#include "fdbserver/BlobManagerInterface.h"
|
||||
#include "fdbserver/ResolverInterface.h"
|
||||
#include "fdbclient/BlobWorkerInterface.h"
|
||||
|
|
|
@ -159,46 +159,39 @@ FDB_DECLARE_BOOLEAN_PARAM(UnlimitedCommitBytes);
|
|||
FDB_DEFINE_BOOLEAN_PARAM(UnlimitedCommitBytes);
|
||||
|
||||
// Immutable
|
||||
static const KeyValueRef persistFormat(LiteralStringRef(PERSIST_PREFIX "Format"), "FoundationDB/StorageServer/1/4"_sr);
|
||||
static const KeyValueRef persistShardAwareFormat(LiteralStringRef(PERSIST_PREFIX "Format"),
|
||||
"FoundationDB/StorageServer/1/5"_sr);
|
||||
static const KeyValueRef persistFormat(PERSIST_PREFIX "Format"_sr, "FoundationDB/StorageServer/1/4"_sr);
|
||||
static const KeyValueRef persistShardAwareFormat(PERSIST_PREFIX "Format"_sr, "FoundationDB/StorageServer/1/5"_sr);
|
||||
static const KeyRangeRef persistFormatReadableRange("FoundationDB/StorageServer/1/2"_sr,
|
||||
"FoundationDB/StorageServer/1/6"_sr);
|
||||
static const KeyRef persistID = LiteralStringRef(PERSIST_PREFIX "ID");
|
||||
static const KeyRef persistTssPairID = LiteralStringRef(PERSIST_PREFIX "tssPairID");
|
||||
static const KeyRef persistSSPairID = LiteralStringRef(PERSIST_PREFIX "ssWithTSSPairID");
|
||||
static const KeyRef persistTssQuarantine = LiteralStringRef(PERSIST_PREFIX "tssQ");
|
||||
static const KeyRef persistClusterIdKey = LiteralStringRef(PERSIST_PREFIX "clusterId");
|
||||
static const KeyRef persistID = PERSIST_PREFIX "ID"_sr;
|
||||
static const KeyRef persistTssPairID = PERSIST_PREFIX "tssPairID"_sr;
|
||||
static const KeyRef persistSSPairID = PERSIST_PREFIX "ssWithTSSPairID"_sr;
|
||||
static const KeyRef persistTssQuarantine = PERSIST_PREFIX "tssQ"_sr;
|
||||
static const KeyRef persistClusterIdKey = PERSIST_PREFIX "clusterId"_sr;
|
||||
|
||||
// (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 =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "ShardAssigned/"), LiteralStringRef(PERSIST_PREFIX "ShardAssigned0"));
|
||||
KeyRangeRef(PERSIST_PREFIX "ShardAssigned/"_sr, PERSIST_PREFIX "ShardAssigned0"_sr);
|
||||
static const KeyRangeRef persistShardAvailableKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "ShardAvailable/"), LiteralStringRef(PERSIST_PREFIX "ShardAvailable0"));
|
||||
static const KeyRangeRef persistByteSampleKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "BS/"), LiteralStringRef(PERSIST_PREFIX "BS0"));
|
||||
KeyRangeRef(PERSIST_PREFIX "ShardAvailable/"_sr, PERSIST_PREFIX "ShardAvailable0"_sr);
|
||||
static const KeyRangeRef persistByteSampleKeys = KeyRangeRef(PERSIST_PREFIX "BS/"_sr, PERSIST_PREFIX "BS0"_sr);
|
||||
static const KeyRangeRef persistByteSampleSampleKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "BS/" PERSIST_PREFIX "BS/"),
|
||||
LiteralStringRef(PERSIST_PREFIX "BS/" PERSIST_PREFIX "BS0"));
|
||||
static const KeyRef persistLogProtocol = LiteralStringRef(PERSIST_PREFIX "LogProtocol");
|
||||
static const KeyRef persistPrimaryLocality = LiteralStringRef(PERSIST_PREFIX "PrimaryLocality");
|
||||
static const KeyRangeRef persistChangeFeedKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "CF/"), LiteralStringRef(PERSIST_PREFIX "CF0"));
|
||||
static const KeyRangeRef persistTenantMapKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "TM/"), LiteralStringRef(PERSIST_PREFIX "TM0"));
|
||||
KeyRangeRef(PERSIST_PREFIX "BS/"_sr PERSIST_PREFIX "BS/"_sr, PERSIST_PREFIX "BS/"_sr PERSIST_PREFIX "BS0"_sr);
|
||||
static const KeyRef persistLogProtocol = PERSIST_PREFIX "LogProtocol"_sr;
|
||||
static const KeyRef persistPrimaryLocality = PERSIST_PREFIX "PrimaryLocality"_sr;
|
||||
static const KeyRangeRef persistChangeFeedKeys = KeyRangeRef(PERSIST_PREFIX "CF/"_sr, PERSIST_PREFIX "CF0"_sr);
|
||||
static const KeyRangeRef persistTenantMapKeys = KeyRangeRef(PERSIST_PREFIX "TM/"_sr, PERSIST_PREFIX "TM0"_sr);
|
||||
// data keys are unmangled (but never start with PERSIST_PREFIX because they are always in allKeys)
|
||||
|
||||
static const KeyRangeRef persistStorageServerShardKeys =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "StorageServerShard/"),
|
||||
LiteralStringRef(PERSIST_PREFIX "StorageServerShard0"));
|
||||
KeyRangeRef(PERSIST_PREFIX "StorageServerShard/"_sr, PERSIST_PREFIX "StorageServerShard0"_sr);
|
||||
|
||||
// Checkpoint related prefixes.
|
||||
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 =
|
||||
KeyRangeRef(LiteralStringRef(PERSIST_PREFIX "PendingCheckpoint/"),
|
||||
LiteralStringRef(PERSIST_PREFIX "PendingCheckpoint0"));
|
||||
KeyRangeRef(PERSIST_PREFIX "PendingCheckpoint/"_sr, PERSIST_PREFIX "PendingCheckpoint0"_sr);
|
||||
static const std::string rocksdbCheckpointDirPrefix = "/rockscheckpoints_";
|
||||
|
||||
struct AddingShard : NonCopyable {
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "fdbserver/TesterInterface.actor.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/workloads/workloads.actor.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbserver/QuietDatabase.h"
|
||||
#include "fdbclient/MonitorLeader.h"
|
||||
#include "fdbserver/CoordinationInterface.h"
|
||||
|
|
|
@ -3023,34 +3023,45 @@ TEST_CASE("/fdbserver/worker/swversion/runCompatibleOlder") {
|
|||
return Void();
|
||||
}
|
||||
|
||||
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
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::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withTSS())));
|
||||
}
|
||||
|
||||
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withTSS(),
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withTSS())));
|
||||
{
|
||||
ErrorOr<SWVersion> swversion = wait(errorOr(
|
||||
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
|
||||
|
||||
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());
|
||||
|
||||
if (!swversion.isError()) {
|
||||
ASSERT(swversion.get().newestProtocolVersion() == ProtocolVersion::withStorageInterfaceReadiness().version());
|
||||
ASSERT(swversion.get().lastRunProtocolVersion() == ProtocolVersion::withTSS().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(),
|
||||
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));
|
||||
|
@ -3064,24 +3075,32 @@ TEST_CASE("/fdbserver/worker/swversion/runIncompatibleOlder") {
|
|||
return Void();
|
||||
}
|
||||
|
||||
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
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<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withTSS())));
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
|
@ -3094,32 +3113,42 @@ TEST_CASE("/fdbserver/worker/swversion/runNewer") {
|
|||
return Void();
|
||||
}
|
||||
|
||||
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withTSS(),
|
||||
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::withTSS(),
|
||||
ProtocolVersion::withTSS(),
|
||||
ProtocolVersion::withCacheRole())));
|
||||
}
|
||||
|
||||
ErrorOr<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
ProtocolVersion::withTSS())));
|
||||
{
|
||||
ErrorOr<SWVersion> swversion = wait(errorOr(
|
||||
testSoftwareVersionCompatibility(swversionTestDirName, ProtocolVersion::withStorageInterfaceReadiness())));
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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<Void> f = wait(errorOr(updateNewestSoftwareVersion(swversionTestDirName,
|
||||
ProtocolVersion::withStorageInterfaceReadiness(),
|
||||
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));
|
||||
|
|
|
@ -35,7 +35,7 @@ struct ApiCorrectnessWorkload : ApiWorkload {
|
|||
private:
|
||||
// Enable to track the activity on a particular key
|
||||
#if CENABLED(0, NOT_IN_CLEAN)
|
||||
#define targetKey LiteralStringRef( ??? )
|
||||
#define targetKey "???"_sr
|
||||
|
||||
void debugKey(KeyRef key, std::string context) {
|
||||
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
|
||||
// results were the same
|
||||
ACTOR Future<bool> runGetRange(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) {
|
||||
state Reverse reverse = deterministicRandom()->coinflip();
|
||||
state Reverse reverse(deterministicRandom()->coinflip());
|
||||
|
||||
// Generate a random range
|
||||
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
|
||||
// true if the results were the same
|
||||
ACTOR Future<bool> runGetRangeSelector(VectorRef<KeyValueRef> data, ApiCorrectnessWorkload* self) {
|
||||
state Reverse reverse = deterministicRandom()->coinflip();
|
||||
state Reverse reverse(deterministicRandom()->coinflip());
|
||||
|
||||
KeySelector selectors[2];
|
||||
Key keys[2];
|
||||
|
|
|
@ -1007,6 +1007,13 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload {
|
|||
ACTOR Future<bool> _check(Database cx, BlobGranuleCorrectnessWorkload* self) {
|
||||
// check error counts, and do an availability check at the end
|
||||
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) {
|
||||
results.push_back(self->checkDirectory(cx, self, it));
|
||||
}
|
||||
|
@ -1015,6 +1022,7 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload {
|
|||
bool dirSuccess = wait(f);
|
||||
allSuccessful &= dirSuccess;
|
||||
}
|
||||
wait(checkFeedCleanupFuture);
|
||||
return allSuccessful;
|
||||
}
|
||||
|
||||
|
|
|
@ -527,66 +527,70 @@ struct BlobGranuleRangesWorkload : TestWorkload {
|
|||
bool fail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), true));
|
||||
ASSERT(!fail8);
|
||||
|
||||
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
|
||||
ASSERT(blobRanges.size() == 1);
|
||||
ASSERT(blobRanges[0] == activeRange);
|
||||
{
|
||||
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
|
||||
ASSERT(blobRanges.size() == 1);
|
||||
ASSERT(blobRanges[0] == activeRange);
|
||||
|
||||
state Transaction tr(cx);
|
||||
loop {
|
||||
try {
|
||||
Standalone<VectorRef<KeyRangeRef>> granules = wait(tr.getBlobGranuleRanges(range, 1000000));
|
||||
ASSERT(granules.size() == 1);
|
||||
ASSERT(granules[0] == activeRange);
|
||||
break;
|
||||
} catch (Error& e) {
|
||||
wait(tr.onError(e));
|
||||
state Transaction tr(cx);
|
||||
loop {
|
||||
try {
|
||||
Standalone<VectorRef<KeyRangeRef>> granules = wait(tr.getBlobGranuleRanges(range, 1000000));
|
||||
ASSERT(granules.size() == 1);
|
||||
ASSERT(granules[0] == activeRange);
|
||||
break;
|
||||
} catch (Error& 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
|
||||
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
|
||||
ASSERT(blobRanges.size() == 1);
|
||||
ASSERT(blobRanges[0] == activeRange);
|
||||
{
|
||||
Standalone<VectorRef<KeyRangeRef>> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000));
|
||||
ASSERT(blobRanges.size() == 1);
|
||||
ASSERT(blobRanges[0] == activeRange);
|
||||
|
||||
bool unblobbifyFail1 = wait(self->setRange(cx, range, false));
|
||||
ASSERT(!unblobbifyFail1);
|
||||
bool unblobbifyFail1 = wait(self->setRange(cx, range, false));
|
||||
ASSERT(!unblobbifyFail1);
|
||||
|
||||
bool unblobbifyFail2 = wait(self->setRange(cx, KeyRangeRef(range.begin, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail2);
|
||||
bool unblobbifyFail2 = wait(self->setRange(cx, KeyRangeRef(range.begin, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail2);
|
||||
|
||||
bool unblobbifyFail3 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, range.end), false));
|
||||
ASSERT(!unblobbifyFail3);
|
||||
bool unblobbifyFail3 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, range.end), false));
|
||||
ASSERT(!unblobbifyFail3);
|
||||
|
||||
bool unblobbifyFail4 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
|
||||
ASSERT(!unblobbifyFail4);
|
||||
bool unblobbifyFail4 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
|
||||
ASSERT(!unblobbifyFail4);
|
||||
|
||||
bool unblobbifyFail5 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail5);
|
||||
bool unblobbifyFail5 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail5);
|
||||
|
||||
bool unblobbifyFail6 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
|
||||
ASSERT(!unblobbifyFail6);
|
||||
bool unblobbifyFail6 = wait(self->setRange(cx, KeyRangeRef(activeRange.begin, middleKey), false));
|
||||
ASSERT(!unblobbifyFail6);
|
||||
|
||||
bool unblobbifyFail7 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail7);
|
||||
bool unblobbifyFail7 = wait(self->setRange(cx, KeyRangeRef(middleKey, activeRange.end), false));
|
||||
ASSERT(!unblobbifyFail7);
|
||||
|
||||
bool unblobbifyFail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), false));
|
||||
ASSERT(!unblobbifyFail8);
|
||||
bool unblobbifyFail8 = wait(self->setRange(cx, KeyRangeRef(middleKey, middleKey2), false));
|
||||
ASSERT(!unblobbifyFail8);
|
||||
|
||||
bool unblobbifySuccess = wait(self->setRange(cx, activeRange, true));
|
||||
ASSERT(unblobbifySuccess);
|
||||
bool unblobbifySuccess = wait(self->setRange(cx, activeRange, true));
|
||||
ASSERT(unblobbifySuccess);
|
||||
|
||||
bool unblobbifySuccessAgain = wait(self->setRange(cx, activeRange, true));
|
||||
ASSERT(unblobbifySuccessAgain);
|
||||
bool unblobbifySuccessAgain = wait(self->setRange(cx, activeRange, true));
|
||||
ASSERT(unblobbifySuccessAgain);
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
|
|
@ -304,6 +304,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
|
|||
state int64_t timeTravelChecksMemory = 0;
|
||||
state Version prevPurgeVersion = -1;
|
||||
state UID dbgId = debugRandom()->randomUniqueID();
|
||||
state Version newPurgeVersion = 0;
|
||||
|
||||
TraceEvent("BlobGranuleVerifierStart");
|
||||
if (BGV_DEBUG) {
|
||||
|
@ -321,6 +322,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
|
|||
try {
|
||||
state double currentTime = now();
|
||||
state std::map<double, OldRead>::iterator timeTravelIt = timeTravelChecks.begin();
|
||||
newPurgeVersion = 0;
|
||||
while (timeTravelIt != timeTravelChecks.end() && currentTime >= timeTravelIt->first) {
|
||||
state OldRead oldRead = timeTravelIt->second;
|
||||
timeTravelChecksMemory -= oldRead.oldResult.expectedSize();
|
||||
|
@ -331,7 +333,6 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
|
|||
}
|
||||
|
||||
// before doing read, purge just before read version
|
||||
state Version newPurgeVersion = 0;
|
||||
state bool doPurging =
|
||||
allowPurging && !self->purgeAtLatest && deterministicRandom()->random01() < 0.5;
|
||||
state bool forcePurge = doPurging && self->doForcePurge && deterministicRandom()->random01() < 0.25;
|
||||
|
@ -1028,6 +1029,13 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
|
|||
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 startReadVersion = readVersion;
|
||||
state int checks = 0;
|
||||
|
@ -1124,6 +1132,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload {
|
|||
}
|
||||
|
||||
state bool dataPassed = wait(self->checkAllData(cx, self));
|
||||
wait(checkFeedCleanupFuture);
|
||||
|
||||
state bool result =
|
||||
availabilityPassed && dataPassed && self->mismatches == 0 && (checks > 0) && (self->timeTravelTooOld == 0);
|
||||
|
|
|
@ -35,7 +35,7 @@ struct BulkSetupWorkload : TestWorkload {
|
|||
BulkSetupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
|
||||
transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / 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>());
|
||||
for (std::string tenant : tenants) {
|
||||
tenantNames.push_back(TenantName(tenant));
|
||||
|
|
|
@ -56,13 +56,14 @@ class ConfigIncrementWorkload : public TestWorkload {
|
|||
}
|
||||
|
||||
ACTOR static Future<Void> incrementActor(ConfigIncrementWorkload* self, Database cx) {
|
||||
TraceEvent(SevDebug, "ConfigIncrementStartIncrementActor");
|
||||
state int trsComplete = 0;
|
||||
state Reference<ISingleThreadTransaction> tr;
|
||||
TraceEvent(SevDebug, "ConfigIncrementStartIncrementActor");
|
||||
while (trsComplete < self->incrementsPerActor) {
|
||||
try {
|
||||
loop {
|
||||
try {
|
||||
state Reference<ISingleThreadTransaction> tr = self->getTransaction(cx);
|
||||
tr = self->getTransaction(cx);
|
||||
state int currentValue = wait(get(tr));
|
||||
ASSERT_GE(currentValue, self->lastKnownValue);
|
||||
set(tr, currentValue + 1);
|
||||
|
@ -100,9 +101,11 @@ class ConfigIncrementWorkload : public TestWorkload {
|
|||
}
|
||||
|
||||
ACTOR static Future<bool> check(ConfigIncrementWorkload* self, Database cx) {
|
||||
state Reference<ISingleThreadTransaction> tr;
|
||||
loop {
|
||||
tr.clear();
|
||||
try {
|
||||
state Reference<ISingleThreadTransaction> tr = self->getTransaction(cx);
|
||||
tr = self->getTransaction(cx);
|
||||
state int currentValue = wait(get(tr));
|
||||
auto expectedValue = self->incrementActors * self->incrementsPerActor;
|
||||
TraceEvent("ConfigIncrementCheck")
|
||||
|
|
|
@ -564,6 +564,9 @@ struct ConsistencyCheckWorkload : TestWorkload {
|
|||
state int j = 0;
|
||||
for (j = 0; j < iter_ss.size(); j++) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -801,6 +804,9 @@ struct ConsistencyCheckWorkload : TestWorkload {
|
|||
state std::vector<Future<ErrorOr<GetKeyValuesReply>>> keyValueFutures;
|
||||
for (const auto& kv : shards[i].second) {
|
||||
resetReply(req);
|
||||
if (SERVER_KNOBS->ENABLE_VERSION_VECTOR) {
|
||||
cx->getLatestCommitVersion(kv, req.version, req.ssLatestCommitVersions);
|
||||
}
|
||||
keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "fdbclient/NativeAPI.actor.h"
|
||||
#include "fdbserver/TesterInterface.actor.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbserver/QuietDatabase.h"
|
||||
#include "fdbserver/ServerDBInfo.h"
|
||||
#include "fdbserver/workloads/workloads.actor.h"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "fdbclient/ClusterConnectionMemoryRecord.h"
|
||||
#include "fdbclient/ManagementAPI.actor.h"
|
||||
#include "fdbclient/RunTransaction.actor.h"
|
||||
#include "fdbclient/TenantManagement.actor.h"
|
||||
#include "fdbrpc/simulator.h"
|
||||
#include "fdbserver/workloads/workloads.actor.h"
|
||||
#include "flow/ApiVersion.h"
|
||||
|
@ -39,9 +40,7 @@ struct DifferentClustersSameRVWorkload : TestWorkload {
|
|||
|
||||
DifferentClustersSameRVWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
|
||||
ASSERT(g_simulator->extraDatabases.size() == 1);
|
||||
auto extraFile =
|
||||
makeReference<ClusterConnectionMemoryRecord>(ClusterConnectionString(g_simulator->extraDatabases[0]));
|
||||
extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION);
|
||||
extraDB = Database::createSimulatedExtraDatabase(g_simulator->extraDatabases[0], wcx.defaultTenant);
|
||||
testDuration = getOption(options, "testDuration"_sr, 100.0);
|
||||
switchAfter = getOption(options, "switchAfter"_sr, 50.0);
|
||||
keyToRead = getOption(options, "keyToRead"_sr, "someKey"_sr);
|
||||
|
@ -50,13 +49,27 @@ struct DifferentClustersSameRVWorkload : TestWorkload {
|
|||
|
||||
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 {
|
||||
if (clientId != 0) {
|
||||
return Void();
|
||||
}
|
||||
auto switchConnFileDb = Database::createDatabase(cx->getConnectionRecord(), -1);
|
||||
switchConnFileDb->defaultTenant = cx->defaultTenant;
|
||||
originalDB = cx;
|
||||
std::vector<Future<Void>> clients = { readerClientSeparateDBs(cx, this),
|
||||
doSwitch(switchConnFileDb, this),
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/ServerDBInfo.h"
|
||||
#include "fdbserver/QuietDatabase.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "flow/actorcompiler.h" // This must be the last #include.
|
||||
|
||||
struct DiskFailureInjectionWorkload : FailureInjectionWorkload {
|
||||
|
|
|
@ -436,8 +436,10 @@ struct GetMappedRangeWorkload : ApiWorkload {
|
|||
}
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ struct IDDTxnProcessorApiWorkload : TestWorkload {
|
|||
|
||||
IDDTxnProcessorApiWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ddContext(UID()) {
|
||||
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; }
|
||||
|
|
|
@ -906,18 +906,25 @@ struct MetaclusterManagementWorkload : TestWorkload {
|
|||
std::map<ClusterName, DataClusterMetadata> dataClusters = wait(MetaclusterAPI::listClusters(
|
||||
self->managementDb, ""_sr, "\xff\xff"_sr, CLIENT_KNOBS->MAX_DATA_CLUSTERS + 1));
|
||||
|
||||
int totalTenantGroupsAllocated = 0;
|
||||
std::vector<Future<Void>> dataClusterChecks;
|
||||
for (auto [clusterName, dataClusterData] : self->dataDbs) {
|
||||
auto dataClusterItr = dataClusters.find(clusterName);
|
||||
if (dataClusterData.registered) {
|
||||
ASSERT(dataClusterItr != dataClusters.end());
|
||||
ASSERT(dataClusterItr->second.entry.capacity.numTenantGroups == dataClusterData.tenantGroupCapacity);
|
||||
totalTenantGroupsAllocated +=
|
||||
dataClusterData.tenantGroups.size() + dataClusterData.ungroupedTenants.size();
|
||||
} else {
|
||||
ASSERT(dataClusterItr == dataClusters.end());
|
||||
}
|
||||
|
||||
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(decommissionMetacluster(self));
|
||||
|
|
|
@ -74,6 +74,7 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
|
||||
ACTOR Future<Void> _start(PhysicalShardMoveWorkLoad* self, Database cx) {
|
||||
int ignore = wait(setDDMode(cx, 0));
|
||||
state std::vector<UID> teamA;
|
||||
state std::map<Key, Value> kvs({ { "TestKeyA"_sr, "TestValueA"_sr },
|
||||
{ "TestKeyB"_sr, "TestValueB"_sr },
|
||||
{ "TestKeyC"_sr, "TestValueC"_sr },
|
||||
|
@ -88,13 +89,14 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
state std::unordered_set<UID> excludes;
|
||||
state std::unordered_set<UID> includes;
|
||||
state int teamSize = 1;
|
||||
std::vector<UID> teamA = wait(self->moveShard(self,
|
||||
cx,
|
||||
deterministicRandom()->randomUniqueID(),
|
||||
KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr),
|
||||
teamSize,
|
||||
includes,
|
||||
excludes));
|
||||
wait(store(teamA,
|
||||
self->moveShard(self,
|
||||
cx,
|
||||
deterministicRandom()->randomUniqueID(),
|
||||
KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr),
|
||||
teamSize,
|
||||
includes,
|
||||
excludes)));
|
||||
excludes.insert(teamA.begin(), teamA.end());
|
||||
|
||||
state uint64_t sh0 = deterministicRandom()->randomUInt64();
|
||||
|
@ -102,13 +104,14 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
state uint64_t sh2 = deterministicRandom()->randomUInt64();
|
||||
|
||||
// Move range [TestKeyA, TestKeyB) to sh0.
|
||||
state std::vector<UID> teamA = wait(self->moveShard(self,
|
||||
cx,
|
||||
UID(sh0, deterministicRandom()->randomUInt64()),
|
||||
KeyRangeRef("TestKeyA"_sr, "TestKeyB"_sr),
|
||||
teamSize,
|
||||
includes,
|
||||
excludes));
|
||||
wait(store(teamA,
|
||||
self->moveShard(self,
|
||||
cx,
|
||||
UID(sh0, deterministicRandom()->randomUInt64()),
|
||||
KeyRangeRef("TestKeyA"_sr, "TestKeyB"_sr),
|
||||
teamSize,
|
||||
includes,
|
||||
excludes)));
|
||||
|
||||
// Move range [TestKeyB, TestKeyC) to sh1, on the same server.
|
||||
includes.insert(teamA.begin(), teamA.end());
|
||||
|
@ -153,15 +156,19 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
wait(self->validateData(self, cx, KeyRangeRef("TestKeyA"_sr, "TestKeyF"_sr), &kvs));
|
||||
TraceEvent("TestValueVerified").log();
|
||||
|
||||
int ignore = wait(setDDMode(cx, 1));
|
||||
{
|
||||
int _ = wait(setDDMode(cx, 1));
|
||||
(void)_;
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
ACTOR Future<Version> populateData(PhysicalShardMoveWorkLoad* self, Database cx, std::map<Key, Value>* kvs) {
|
||||
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
|
||||
state Version version;
|
||||
state UID debugID;
|
||||
loop {
|
||||
state UID debugID = deterministicRandom()->randomUniqueID();
|
||||
debugID = deterministicRandom()->randomUniqueID();
|
||||
try {
|
||||
tr->debugTransaction(debugID);
|
||||
for (const auto& [key, value] : *kvs) {
|
||||
|
@ -188,8 +195,9 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
KeyRange range,
|
||||
std::map<Key, Value>* kvs) {
|
||||
state Transaction tr(cx);
|
||||
state UID debugID;
|
||||
loop {
|
||||
state UID debugID = deterministicRandom()->randomUniqueID();
|
||||
debugID = deterministicRandom()->randomUniqueID();
|
||||
try {
|
||||
tr.debugTransaction(debugID);
|
||||
RangeResult res = wait(tr.getRange(range, CLIENT_KNOBS->TOO_MANY));
|
||||
|
@ -215,11 +223,13 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
Key key,
|
||||
ErrorOr<Optional<Value>> expectedValue) {
|
||||
state Transaction tr(cx);
|
||||
state Version readVersion;
|
||||
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
||||
|
||||
loop {
|
||||
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));
|
||||
const bool equal = !expectedValue.isError() && res == expectedValue.get();
|
||||
if (!equal) {
|
||||
|
@ -244,8 +254,9 @@ struct PhysicalShardMoveWorkLoad : TestWorkload {
|
|||
// state Transaction tr(cx);
|
||||
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
|
||||
state Version version;
|
||||
state UID debugID;
|
||||
loop {
|
||||
state UID debugID = deterministicRandom()->randomUniqueID();
|
||||
debugID = deterministicRandom()->randomUniqueID();
|
||||
try {
|
||||
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
||||
tr->debugTransaction(debugID);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "fdbclient/SystemData.h"
|
||||
#include "fdbrpc/ContinuousSample.h"
|
||||
#include "fdbclient/SimpleIni.h"
|
||||
#include "fdbserver/Status.h"
|
||||
#include "fdbserver/Status.actor.h"
|
||||
#include "fdbserver/TesterInterface.actor.h"
|
||||
#include "fdbserver/WorkerInterface.actor.h"
|
||||
#include "fdbserver/workloads/BulkSetup.actor.h"
|
||||
|
|
|
@ -74,6 +74,7 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
|
|||
state Key endKey = "TestKey0"_sr;
|
||||
state Value oldValue = "TestValue"_sr;
|
||||
state KeyRange testRange = KeyRangeRef(key, endKey);
|
||||
state std::vector<CheckpointMetaData> records;
|
||||
|
||||
int ignore = wait(setDDMode(cx, 0));
|
||||
state Version version = wait(self->writeAndVerify(self, cx, key, oldValue));
|
||||
|
@ -98,9 +99,9 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
|
|||
|
||||
// Fetch checkpoint meta data.
|
||||
loop {
|
||||
records.clear();
|
||||
try {
|
||||
state std::vector<CheckpointMetaData> records =
|
||||
wait(getCheckpointMetaData(cx, testRange, version, format));
|
||||
wait(store(records, getCheckpointMetaData(cx, testRange, version, format)));
|
||||
break;
|
||||
} catch (Error& e) {
|
||||
TraceEvent("TestFetchCheckpointMetadataError")
|
||||
|
@ -161,10 +162,11 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
|
|||
// Compare the keyrange between the original database and the one restored from checkpoint.
|
||||
// For now, it should have been a single key.
|
||||
tr.reset();
|
||||
state RangeResult res;
|
||||
loop {
|
||||
try {
|
||||
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;
|
||||
} catch (Error& e) {
|
||||
wait(tr.onError(e));
|
||||
|
@ -181,7 +183,10 @@ struct SSCheckpointRestoreWorkload : TestWorkload {
|
|||
kvStore->dispose();
|
||||
wait(close);
|
||||
|
||||
int ignore = wait(setDDMode(cx, 1));
|
||||
{
|
||||
int ignore = wait(setDDMode(cx, 1));
|
||||
(void)ignore;
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
|
|
@ -419,8 +419,8 @@ TEST_CASE("/fdbclient/TaskBucket/Subspace") {
|
|||
t3.append(ghi);
|
||||
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) == LiteralStringRef("abc\x01user\x00\x15\x7b\x01\x00\x01"
|
||||
"def\x00\x01ghi\x00"));
|
||||
ASSERT(subspace_test5.pack(t3) == "abc\x01user\x00\x15\x7b\x01\x00\x01"
|
||||
"def\x00\x01ghi\x00"_sr);
|
||||
|
||||
printf("%d==========%s===%d\n",
|
||||
14,
|
||||
|
|
|
@ -154,9 +154,10 @@ struct WatchesSameKeyWorkload : TestWorkload {
|
|||
* */
|
||||
state ReadYourWritesTransaction tr(cx);
|
||||
state ReadYourWritesTransaction tr2(cx);
|
||||
state Value val;
|
||||
loop {
|
||||
try {
|
||||
state Value val = deterministicRandom()->randomUniqueID().toString();
|
||||
val = deterministicRandom()->randomUniqueID().toString();
|
||||
tr2.set(key, val);
|
||||
state Future<Void> watch1 = tr2.watch(key);
|
||||
wait(tr2.commit());
|
||||
|
@ -185,7 +186,7 @@ struct WatchesSameKeyWorkload : TestWorkload {
|
|||
loop {
|
||||
try {
|
||||
// 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);
|
||||
state Future<Void> watch1 = tr2.watch(key);
|
||||
wait(tr2.commit());
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
find_package(Threads REQUIRED)
|
||||
|
||||
option(FLOW_USE_ZSTD "Enable zstd compression in flow" OFF)
|
||||
|
||||
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.
|
||||
list(REMOVE_ITEM FLOW_SRCS TLSTest.cpp)
|
||||
list(REMOVE_ITEM FLOW_SRCS MkCertCli.cpp)
|
||||
|
||||
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()
|
||||
|
||||
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_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.
|
||||
# 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
|
||||
|
@ -40,67 +57,66 @@ target_link_libraries(flowlinktest PRIVATE flow stacktrace)
|
|||
|
||||
set(IS_ARM_MAC NO)
|
||||
if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
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)
|
||||
set(IS_ARM_MAC YES)
|
||||
endif()
|
||||
|
||||
foreach(ft flow flow_sampling flowlinktest)
|
||||
target_include_directories(${ft} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
|
||||
target_link_libraries(${ft} PRIVATE stacktrace)
|
||||
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})
|
||||
target_include_directories(${ft} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
if (FLOW_USE_ZSTD)
|
||||
target_include_directories(${ft} PRIVATE SYSTEM ${ZSTD_LIB_INCLUDE_DIR})
|
||||
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()
|
||||
target_link_libraries(${ft} PRIVATE stacktrace)
|
||||
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(APPLE)
|
||||
find_library(IO_KIT IOKit)
|
||||
find_library(CORE_FOUNDATION CoreFoundation)
|
||||
target_link_libraries(${ft} PRIVATE ${IO_KIT} ${CORE_FOUNDATION})
|
||||
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()
|
||||
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()
|
||||
|
||||
target_compile_definitions(flow_sampling PRIVATE -DENABLE_SAMPLING)
|
||||
if(WIN32)
|
||||
add_dependencies(flow_sampling_actors flow_actors)
|
||||
add_dependencies(flow_sampling_actors flow_actors)
|
||||
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)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue