diff --git a/CMakeLists.txt b/CMakeLists.txt index 5666b1f202..4b631b5df4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,11 @@ else() cmake_minimum_required(VERSION 3.13) endif() +# silence deprecation warnings in newer versions of cmake +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() + project(foundationdb VERSION 7.2.0 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." diff --git a/bindings/bindingtester/bindingtester.py b/bindings/bindingtester/bindingtester.py index 508ede8998..a5de827fe9 100755 --- a/bindings/bindingtester/bindingtester.py +++ b/bindings/bindingtester/bindingtester.py @@ -49,6 +49,17 @@ from bindingtester.known_testers import Tester import fdb import fdb.tuple + +API_VERSIONS = [ + 13, 14, 16, 21, 22, 23, + 100, 200, 300, + 400, 410, 420, 430, 440, 450, 460, + 500, 510, 520, + 600, 610, 620, 630, + 700, 710, 720, +] + + fdb.api_version(FDB_API_VERSION) @@ -156,8 +167,7 @@ def choose_api_version(selected_api_version, tester_min_version, tester_max_vers elif random.random() < 0.7: api_version = min_version elif random.random() < 0.9: - api_version = random.choice([v for v in [13, 14, 16, 21, 22, 23, 100, 200, 300, 400, 410, 420, 430, - 440, 450, 460, 500, 510, 520, 600, 610, 620, 630, 700, 710, 720] if v >= min_version and v <= max_version]) + api_version = random.choice([v for v in API_VERSIONS if v >= min_version and v <= max_version]) else: api_version = random.randint(min_version, max_version) diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt index 78d8af6476..ecc77a9322 100644 --- a/bindings/c/CMakeLists.txt +++ b/bindings/c/CMakeLists.txt @@ -129,7 +129,7 @@ if(NOT WIN32) add_library(fdb_cpp INTERFACE test/fdb_api.hpp) target_sources(fdb_cpp INTERFACE ) target_include_directories(fdb_cpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/test) - target_link_libraries(fdb_cpp INTERFACE fmt::fmt) + target_link_libraries(fdb_cpp INTERFACE fdb_c fmt::fmt) set(API_TESTER_SRCS test/apitester/fdb_c_api_tester.cpp @@ -199,6 +199,9 @@ 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) + endif() target_link_libraries(fdb_c_api_tester_impl PRIVATE SimpleOpt) target_include_directories(fdb_c_unit_tests_impl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/foundationdb/) diff --git a/bindings/c/foundationdb/fdb_c.h b/bindings/c/foundationdb/fdb_c.h index 701620cd3c..2e4e977d76 100644 --- a/bindings/c/foundationdb/fdb_c.h +++ b/bindings/c/foundationdb/fdb_c.h @@ -84,12 +84,12 @@ DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_network_set_option(FDBNetworkOption int value_length); #if FDB_API_VERSION >= 14 -DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_setup_network(); +DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_setup_network(void); #endif -DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_run_network(); +DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_run_network(void); -DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_stop_network(); +DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_stop_network(void); DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_add_network_thread_completion_hook(void (*hook)(void*), void* hook_parameter); @@ -548,8 +548,8 @@ DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_summarize_blob_granules( DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_select_api_version_impl(int runtime_version, int header_version); -DLLEXPORT int fdb_get_max_api_version(); -DLLEXPORT const char* fdb_get_client_version(); +DLLEXPORT int fdb_get_max_api_version(void); +DLLEXPORT const char* fdb_get_client_version(void); /* LEGACY API VERSIONS */ diff --git a/bindings/c/test/apitester/TesterApiWorkload.cpp b/bindings/c/test/apitester/TesterApiWorkload.cpp index c1499adb0c..90d4e18bd2 100644 --- a/bindings/c/test/apitester/TesterApiWorkload.cpp +++ b/bindings/c/test/apitester/TesterApiWorkload.cpp @@ -41,6 +41,10 @@ ApiWorkload::ApiWorkload(const WorkloadConfig& config) : WorkloadBase(config) { stopReceived = false; checkingProgress = false; apiVersion = config.apiVersion; + + for (int i = 0; i < config.numTenants; ++i) { + tenants.push_back(fdb::ByteString(fdb::toBytesRef("tenant" + std::to_string(i)))); + } } IWorkloadControlIfc* ApiWorkload::getControlIfc() { @@ -107,49 +111,57 @@ void ApiWorkload::randomOperation(TTaskFct cont) { } fdb::Key ApiWorkload::randomKeyName() { - return keyPrefix + Random::get().randomStringLowerCase(minKeyLength, maxKeyLength); + return keyPrefix + Random::get().randomByteStringLowerCase(minKeyLength, maxKeyLength); } fdb::Value ApiWorkload::randomValue() { - return Random::get().randomStringLowerCase(minValueLength, maxValueLength); + return Random::get().randomByteStringLowerCase(minValueLength, maxValueLength); } -fdb::Key ApiWorkload::randomNotExistingKey() { +fdb::Key ApiWorkload::randomNotExistingKey(std::optional tenantId) { while (true) { fdb::Key key = randomKeyName(); - if (!store.exists(key)) { + if (!stores[tenantId].exists(key)) { return key; } } } -fdb::Key ApiWorkload::randomExistingKey() { +fdb::Key ApiWorkload::randomExistingKey(std::optional tenantId) { fdb::Key genKey = randomKeyName(); - fdb::Key key = store.getKey(genKey, true, 1); - if (key != store.endKey()) { + fdb::Key key = stores[tenantId].getKey(genKey, true, 1); + if (key != stores[tenantId].endKey()) { return key; } - key = store.getKey(genKey, true, 0); - if (key != store.startKey()) { + key = stores[tenantId].getKey(genKey, true, 0); + if (key != stores[tenantId].startKey()) { return key; } info("No existing key found, using a new random key."); return genKey; } -fdb::Key ApiWorkload::randomKey(double existingKeyRatio) { +fdb::Key ApiWorkload::randomKey(double existingKeyRatio, std::optional tenantId) { if (Random::get().randomBool(existingKeyRatio)) { - return randomExistingKey(); + return randomExistingKey(tenantId); } else { - return randomNotExistingKey(); + return randomNotExistingKey(tenantId); } } -void ApiWorkload::populateDataTx(TTaskFct cont) { +std::optional ApiWorkload::randomTenant() { + if (tenants.size() > 0) { + return Random::get().randomInt(0, tenants.size() - 1); + } else { + return {}; + } +} + +void ApiWorkload::populateDataTx(TTaskFct cont, std::optional tenantId) { int numKeys = maxKeysPerTransaction; auto kvPairs = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - kvPairs->push_back(fdb::KeyValue{ randomNotExistingKey(), randomValue() }); + kvPairs->push_back(fdb::KeyValue{ randomNotExistingKey(tenantId), randomValue() }); } execTransaction( [kvPairs](auto ctx) { @@ -158,12 +170,29 @@ void ApiWorkload::populateDataTx(TTaskFct cont) { } ctx->commit(); }, - [this, kvPairs, cont]() { + [this, tenantId, kvPairs, cont]() { for (const fdb::KeyValue& kv : *kvPairs) { - store.set(kv.key, kv.value); + stores[tenantId].set(kv.key, kv.value); } schedule(cont); - }); + }, + getTenant(tenantId)); +} + +void ApiWorkload::clearTenantData(TTaskFct cont, std::optional tenantId) { + execTransaction( + [this](auto ctx) { + ctx->tx().clearRange(keyPrefix, keyPrefix + fdb::Key(1, '\xff')); + ctx->commit(); + }, + [this, tenantId, cont]() { + if (tenantId && tenantId.value() < tenants.size() - 1) { + clearTenantData(cont, tenantId.value() + 1); + } else { + schedule(cont); + } + }, + getTenant(tenantId)); } void ApiWorkload::clearData(TTaskFct cont) { @@ -175,20 +204,51 @@ void ApiWorkload::clearData(TTaskFct cont) { [this, cont]() { schedule(cont); }); } -void ApiWorkload::populateData(TTaskFct cont) { - if (store.size() < initialSize) { - populateDataTx([this, cont]() { populateData(cont); }); - } else { +void ApiWorkload::populateTenantData(TTaskFct cont, std::optional tenantId) { + while (stores[tenantId].size() >= initialSize && tenantId && tenantId.value() < tenants.size()) { + ++tenantId.value(); + } + + if (tenantId >= tenants.size() || stores[tenantId].size() >= initialSize) { info("Data population completed"); schedule(cont); + } else { + populateDataTx([this, cont, tenantId]() { populateTenantData(cont, tenantId); }, tenantId); } } -void ApiWorkload::randomInsertOp(TTaskFct cont) { +void ApiWorkload::createTenants(TTaskFct cont) { + execTransaction( + [this](auto ctx) { + auto futures = std::make_shared>(); + for (auto tenant : tenants) { + futures->push_back(fdb::Tenant::getTenant(ctx->tx(), tenant)); + } + ctx->continueAfterAll(*futures, [this, ctx, futures]() { + for (int i = 0; i < futures->size(); ++i) { + if (!(*futures)[i].get()) { + fdb::Tenant::createTenant(ctx->tx(), tenants[i]); + } + } + ctx->commit(); + }); + }, + [this, cont]() { schedule(cont); }); +} + +void ApiWorkload::populateData(TTaskFct cont) { + if (tenants.size() > 0) { + createTenants([this, cont]() { populateTenantData(cont, std::make_optional(0)); }); + } else { + populateTenantData(cont, {}); + } +} + +void ApiWorkload::randomInsertOp(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto kvPairs = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - kvPairs->push_back(fdb::KeyValue{ randomNotExistingKey(), randomValue() }); + kvPairs->push_back(fdb::KeyValue{ randomNotExistingKey(tenantId), randomValue() }); } execTransaction( [kvPairs](auto ctx) { @@ -197,19 +257,20 @@ void ApiWorkload::randomInsertOp(TTaskFct cont) { } ctx->commit(); }, - [this, kvPairs, cont]() { + [this, kvPairs, cont, tenantId]() { for (const fdb::KeyValue& kv : *kvPairs) { - store.set(kv.key, kv.value); + stores[tenantId].set(kv.key, kv.value); } schedule(cont); - }); + }, + getTenant(tenantId)); } -void ApiWorkload::randomClearOp(TTaskFct cont) { +void ApiWorkload::randomClearOp(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto keys = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - keys->push_back(randomExistingKey()); + keys->push_back(randomExistingKey(tenantId)); } execTransaction( [keys](auto ctx) { @@ -218,15 +279,16 @@ void ApiWorkload::randomClearOp(TTaskFct cont) { } ctx->commit(); }, - [this, keys, cont]() { + [this, keys, cont, tenantId]() { for (const auto& key : *keys) { - store.clear(key); + stores[tenantId].clear(key); } schedule(cont); - }); + }, + getTenant(tenantId)); } -void ApiWorkload::randomClearRangeOp(TTaskFct cont) { +void ApiWorkload::randomClearRangeOp(TTaskFct cont, std::optional tenantId) { fdb::Key begin = randomKeyName(); fdb::Key end = randomKeyName(); if (begin > end) { @@ -237,10 +299,19 @@ void ApiWorkload::randomClearRangeOp(TTaskFct cont) { ctx->tx().clearRange(begin, end); ctx->commit(); }, - [this, begin, end, cont]() { - store.clear(begin, end); + [this, begin, end, cont, tenantId]() { + stores[tenantId].clear(begin, end); schedule(cont); - }); + }, + getTenant(tenantId)); +} + +std::optional ApiWorkload::getTenant(std::optional tenantId) { + if (tenantId) { + return tenants[*tenantId]; + } else { + return {}; + } } } // namespace FdbApiTester diff --git a/bindings/c/test/apitester/TesterApiWorkload.h b/bindings/c/test/apitester/TesterApiWorkload.h index fd3630ceee..a3a13e964d 100644 --- a/bindings/c/test/apitester/TesterApiWorkload.h +++ b/bindings/c/test/apitester/TesterApiWorkload.h @@ -96,17 +96,23 @@ protected: // Key prefix fdb::Key keyPrefix; + // The number of tenants to configure in the cluster + std::vector tenants; + // In-memory store maintaining expected database state - KeyValueStore store; + std::unordered_map, KeyValueStore> stores; ApiWorkload(const WorkloadConfig& config); // Methods for generating random keys and values fdb::Key randomKeyName(); fdb::Value randomValue(); - fdb::Key randomNotExistingKey(); - fdb::Key randomExistingKey(); - fdb::Key randomKey(double existingKeyRatio); + fdb::Key randomNotExistingKey(std::optional tenantId); + fdb::Key randomExistingKey(std::optional tenantId); + fdb::Key randomKey(double existingKeyRatio, std::optional tenantId); + + // Chooses a random tenant from the available tenants (or an empty optional if tenants aren't used in the test) + std::optional randomTenant(); // Generate initial random data for the workload void populateData(TTaskFct cont); @@ -115,12 +121,18 @@ protected: void clearData(TTaskFct cont); // common operations - void randomInsertOp(TTaskFct cont); - void randomClearOp(TTaskFct cont); - void randomClearRangeOp(TTaskFct cont); + void randomInsertOp(TTaskFct cont, std::optional tenantId); + void randomClearOp(TTaskFct cont, std::optional tenantId); + void randomClearRangeOp(TTaskFct cont, std::optional tenantId); + + std::optional getTenant(std::optional tenantId); private: - void populateDataTx(TTaskFct cont); + void populateDataTx(TTaskFct cont, std::optional tenantId); + void populateTenantData(TTaskFct cont, std::optional tenantId); + void createTenants(TTaskFct cont); + + void clearTenantData(TTaskFct cont, std::optional tenantId); void randomOperations(); }; diff --git a/bindings/c/test/apitester/TesterBlobGranuleCorrectnessWorkload.cpp b/bindings/c/test/apitester/TesterBlobGranuleCorrectnessWorkload.cpp index bbec603d32..5f35c7b7ff 100644 --- a/bindings/c/test/apitester/TesterBlobGranuleCorrectnessWorkload.cpp +++ b/bindings/c/test/apitester/TesterBlobGranuleCorrectnessWorkload.cpp @@ -36,14 +36,23 @@ public: private: // FIXME: use other new blob granule apis! - enum OpType { OP_INSERT, OP_CLEAR, OP_CLEAR_RANGE, OP_READ, OP_GET_RANGES, OP_SUMMARIZE, OP_LAST = OP_SUMMARIZE }; + enum OpType { + OP_INSERT, + OP_CLEAR, + OP_CLEAR_RANGE, + OP_READ, + OP_GET_GRANULES, + OP_SUMMARIZE, + OP_GET_BLOB_RANGES, + OP_LAST = OP_GET_BLOB_RANGES + }; std::vector excludedOpTypes; // Allow reads at the start to get blob_granule_transaction_too_old if BG data isn't initialized yet // FIXME: should still guarantee a read succeeds eventually somehow bool seenReadSuccess = false; - void randomReadOp(TTaskFct cont) { + void randomReadOp(TTaskFct cont, std::optional tenantId) { fdb::Key begin = randomKeyName(); fdb::Key end = randomKeyName(); if (begin > end) { @@ -82,9 +91,10 @@ private: ctx->done(); } }, - [this, begin, end, results, tooOld, cont]() { + [this, begin, end, results, tooOld, cont, tenantId]() { if (!*tooOld) { - std::vector expected = store.getRange(begin, end, store.size(), false); + std::vector expected = + stores[tenantId].getRange(begin, end, stores[tenantId].size(), false); if (results->size() != expected.size()) { error(fmt::format("randomReadOp result size mismatch. expected: {} actual: {}", expected.size(), @@ -115,10 +125,11 @@ private: } } schedule(cont); - }); + }, + getTenant(tenantId)); } - void randomGetRangesOp(TTaskFct cont) { + void randomGetGranulesOp(TTaskFct cont, std::optional tenantId) { fdb::Key begin = randomKeyName(); fdb::Key end = randomKeyName(); if (begin > end) { @@ -138,41 +149,13 @@ private: true); }, [this, begin, end, results, cont]() { - if (seenReadSuccess) { - ASSERT(results->size() > 0); - ASSERT(results->front().beginKey <= begin); - ASSERT(results->back().endKey >= end); - } - - for (int i = 0; i < results->size(); i++) { - // no empty or inverted ranges - if ((*results)[i].beginKey >= (*results)[i].endKey) { - error(fmt::format("Empty/inverted range [{0} - {1}) for getBlobGranuleRanges({2} - {3})", - fdb::toCharsRef((*results)[i].beginKey), - fdb::toCharsRef((*results)[i].endKey), - fdb::toCharsRef(begin), - fdb::toCharsRef(end))); - } - ASSERT((*results)[i].beginKey < (*results)[i].endKey); - } - - for (int i = 1; i < results->size(); i++) { - // ranges contain entire requested key range - if ((*results)[i].beginKey != (*results)[i].endKey) { - error(fmt::format("Non-contiguous range [{0} - {1}) for getBlobGranuleRanges({2} - {3})", - fdb::toCharsRef((*results)[i].beginKey), - fdb::toCharsRef((*results)[i].endKey), - fdb::toCharsRef(begin), - fdb::toCharsRef(end))); - } - ASSERT((*results)[i].beginKey == (*results)[i - 1].endKey); - } - + this->validateRanges(results, begin, end, seenReadSuccess); schedule(cont); - }); + }, + getTenant(tenantId)); } - void randomSummarizeOp(TTaskFct cont) { + void randomSummarizeOp(TTaskFct cont, std::optional tenantId) { fdb::Key begin = randomKeyName(); fdb::Key end = randomKeyName(); if (begin > end) { @@ -211,32 +194,95 @@ private: } schedule(cont); - }); + }, + getTenant(tenantId)); + } + + void validateRanges(std::shared_ptr> results, + fdb::Key begin, + fdb::Key end, + bool shouldBeRanges) { + if (shouldBeRanges) { + ASSERT(results->size() > 0); + ASSERT(results->front().beginKey <= begin); + ASSERT(results->back().endKey >= end); + } + for (int i = 0; i < results->size(); i++) { + // no empty or inverted ranges + if ((*results)[i].beginKey >= (*results)[i].endKey) { + error(fmt::format("Empty/inverted range [{0} - {1}) for getBlobGranuleRanges({2} - {3})", + fdb::toCharsRef((*results)[i].beginKey), + fdb::toCharsRef((*results)[i].endKey), + fdb::toCharsRef(begin), + fdb::toCharsRef(end))); + } + ASSERT((*results)[i].beginKey < (*results)[i].endKey); + } + + for (int i = 1; i < results->size(); i++) { + // ranges contain entire requested key range + if ((*results)[i].beginKey != (*results)[i].endKey) { + error(fmt::format("Non-contiguous range [{0} - {1}) for getBlobGranuleRanges({2} - {3})", + fdb::toCharsRef((*results)[i].beginKey), + fdb::toCharsRef((*results)[i].endKey), + fdb::toCharsRef(begin), + fdb::toCharsRef(end))); + } + ASSERT((*results)[i].beginKey == (*results)[i - 1].endKey); + } + } + + void randomGetBlobRangesOp(TTaskFct cont) { + fdb::Key begin = randomKeyName(); + fdb::Key end = randomKeyName(); + auto results = std::make_shared>(); + if (begin > end) { + std::swap(begin, end); + } + execOperation( + [begin, end, results](auto ctx) { + fdb::Future f = ctx->db().listBlobbifiedRanges(begin, end, 1000).eraseType(); + ctx->continueAfter(f, [ctx, f, results]() { + *results = copyKeyRangeArray(f.get()); + ctx->done(); + }); + }, + [this, begin, end, results, cont]() { + this->validateRanges(results, begin, end, seenReadSuccess); + schedule(cont); + }, + /* failOnError = */ false); } void randomOperation(TTaskFct cont) { - OpType txType = (store.size() == 0) ? OP_INSERT : (OpType)Random::get().randomInt(0, OP_LAST); + std::optional tenantId = randomTenant(); + + OpType txType = (stores[tenantId].size() == 0) ? OP_INSERT : (OpType)Random::get().randomInt(0, OP_LAST); while (std::count(excludedOpTypes.begin(), excludedOpTypes.end(), txType)) { txType = (OpType)Random::get().randomInt(0, OP_LAST); } + switch (txType) { case OP_INSERT: - randomInsertOp(cont); + randomInsertOp(cont, tenantId); break; case OP_CLEAR: - randomClearOp(cont); + randomClearOp(cont, tenantId); break; case OP_CLEAR_RANGE: - randomClearRangeOp(cont); + randomClearRangeOp(cont, tenantId); break; case OP_READ: - randomReadOp(cont); + randomReadOp(cont, tenantId); break; - case OP_GET_RANGES: - randomGetRangesOp(cont); + case OP_GET_GRANULES: + randomGetGranulesOp(cont, tenantId); break; case OP_SUMMARIZE: - randomSummarizeOp(cont); + randomSummarizeOp(cont, tenantId); + break; + case OP_GET_BLOB_RANGES: + randomGetBlobRangesOp(cont); break; } } diff --git a/bindings/c/test/apitester/TesterBlobGranuleErrorsWorkload.cpp b/bindings/c/test/apitester/TesterBlobGranuleErrorsWorkload.cpp index 7bb879a185..386f8b43cd 100644 --- a/bindings/c/test/apitester/TesterBlobGranuleErrorsWorkload.cpp +++ b/bindings/c/test/apitester/TesterBlobGranuleErrorsWorkload.cpp @@ -78,7 +78,7 @@ private: seenReadSuccess = true; } if (err.code() != expectedError) { - info(fmt::format("incorrect error. Expected {}, Got {}", err.code(), expectedError)); + info(fmt::format("incorrect error. Expected {}, Got {}", expectedError, err.code())); if (err.code() == error_code_blob_granule_transaction_too_old) { ASSERT(!seenReadSuccess); ctx->done(); diff --git a/bindings/c/test/apitester/TesterCancelTransactionWorkload.cpp b/bindings/c/test/apitester/TesterCancelTransactionWorkload.cpp index b569cdb35f..b4cd205143 100644 --- a/bindings/c/test/apitester/TesterCancelTransactionWorkload.cpp +++ b/bindings/c/test/apitester/TesterCancelTransactionWorkload.cpp @@ -31,11 +31,11 @@ private: enum OpType { OP_CANCEL_GET, OP_CANCEL_AFTER_FIRST_GET, OP_LAST = OP_CANCEL_AFTER_FIRST_GET }; // Start multiple concurrent gets and cancel the transaction - void randomCancelGetTx(TTaskFct cont) { + void randomCancelGetTx(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto keys = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - keys->push_back(randomKey(readExistingKeysRatio)); + keys->push_back(randomKey(readExistingKeysRatio, tenantId)); } execTransaction( [keys](auto ctx) { @@ -45,25 +45,26 @@ private: } ctx->done(); }, - [this, cont]() { schedule(cont); }); + [this, cont]() { schedule(cont); }, + getTenant(tenantId)); } // Start multiple concurrent gets and cancel the transaction after the first get returns - void randomCancelAfterFirstResTx(TTaskFct cont) { + void randomCancelAfterFirstResTx(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto keys = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - keys->push_back(randomKey(readExistingKeysRatio)); + keys->push_back(randomKey(readExistingKeysRatio, tenantId)); } execTransaction( - [this, keys](auto ctx) { + [this, keys, tenantId](auto ctx) { std::vector futures; for (const auto& key : *keys) { futures.push_back(ctx->tx().get(key, false).eraseType()); } for (int i = 0; i < keys->size(); i++) { fdb::Future f = futures[i]; - auto expectedVal = store.get((*keys)[i]); + auto expectedVal = stores[tenantId].get((*keys)[i]); ctx->continueAfter(f, [expectedVal, f, this, ctx]() { auto val = f.get(); if (expectedVal != val) { @@ -75,17 +76,20 @@ private: }); } }, - [this, cont]() { schedule(cont); }); + [this, cont]() { schedule(cont); }, + getTenant(tenantId)); } void randomOperation(TTaskFct cont) override { + std::optional tenantId = randomTenant(); OpType txType = (OpType)Random::get().randomInt(0, OP_LAST); + switch (txType) { case OP_CANCEL_GET: - randomCancelGetTx(cont); + randomCancelGetTx(cont, tenantId); break; case OP_CANCEL_AFTER_FIRST_GET: - randomCancelAfterFirstResTx(cont); + randomCancelAfterFirstResTx(cont, tenantId); break; } } diff --git a/bindings/c/test/apitester/TesterCorrectnessWorkload.cpp b/bindings/c/test/apitester/TesterCorrectnessWorkload.cpp index 9219bb7056..4486abdf97 100644 --- a/bindings/c/test/apitester/TesterCorrectnessWorkload.cpp +++ b/bindings/c/test/apitester/TesterCorrectnessWorkload.cpp @@ -41,11 +41,11 @@ private: OP_LAST = OP_COMMIT_READ }; - void randomCommitReadOp(TTaskFct cont) { + void randomCommitReadOp(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto kvPairs = std::make_shared>(); for (int i = 0; i < numKeys; i++) { - kvPairs->push_back(fdb::KeyValue{ randomKey(readExistingKeysRatio), randomValue() }); + kvPairs->push_back(fdb::KeyValue{ randomKey(readExistingKeysRatio, tenantId), randomValue() }); } execTransaction( [kvPairs](auto ctx) { @@ -54,9 +54,9 @@ private: } ctx->commit(); }, - [this, kvPairs, cont]() { + [this, kvPairs, cont, tenantId]() { for (const fdb::KeyValue& kv : *kvPairs) { - store.set(kv.key, kv.value); + stores[tenantId].set(kv.key, kv.value); } auto results = std::make_shared>>(); execTransaction( @@ -78,10 +78,10 @@ private: ctx->done(); }); }, - [this, kvPairs, results, cont]() { + [this, kvPairs, results, cont, tenantId]() { ASSERT(results->size() == kvPairs->size()); for (int i = 0; i < kvPairs->size(); i++) { - auto expected = store.get((*kvPairs)[i].key); + auto expected = stores[tenantId].get((*kvPairs)[i].key); auto actual = (*results)[i]; if (actual != expected) { error( @@ -93,16 +93,18 @@ private: } } schedule(cont); - }); - }); + }, + getTenant(tenantId)); + }, + getTenant(tenantId)); } - void randomGetOp(TTaskFct cont) { + void randomGetOp(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto keys = std::make_shared>(); auto results = std::make_shared>>(); for (int i = 0; i < numKeys; i++) { - keys->push_back(randomKey(readExistingKeysRatio)); + keys->push_back(randomKey(readExistingKeysRatio, tenantId)); } execTransaction( [keys, results](auto ctx) { @@ -119,10 +121,10 @@ private: ctx->done(); }); }, - [this, keys, results, cont]() { + [this, keys, results, cont, tenantId]() { ASSERT(results->size() == keys->size()); for (int i = 0; i < keys->size(); i++) { - auto expected = store.get((*keys)[i]); + auto expected = stores[tenantId].get((*keys)[i]); if ((*results)[i] != expected) { error(fmt::format("randomGetOp mismatch. key: {} expected: {:.80} actual: {:.80}", fdb::toCharsRef((*keys)[i]), @@ -131,16 +133,17 @@ private: } } schedule(cont); - }); + }, + getTenant(tenantId)); } - void randomGetKeyOp(TTaskFct cont) { + void randomGetKeyOp(TTaskFct cont, std::optional tenantId) { int numKeys = Random::get().randomInt(1, maxKeysPerTransaction); auto keysWithSelectors = std::make_shared>>(); auto results = std::make_shared>(); keysWithSelectors->reserve(numKeys); for (int i = 0; i < numKeys; i++) { - auto key = randomKey(readExistingKeysRatio); + auto key = randomKey(readExistingKeysRatio, tenantId); fdb::KeySelector selector; selector.keyLength = key.size(); selector.orEqual = Random::get().randomBool(0.5); @@ -169,20 +172,20 @@ private: ctx->done(); }); }, - [this, keysWithSelectors, results, cont]() { + [this, keysWithSelectors, results, cont, tenantId]() { ASSERT(results->size() == keysWithSelectors->size()); for (int i = 0; i < keysWithSelectors->size(); i++) { auto const& key = (*keysWithSelectors)[i].first; auto const& selector = (*keysWithSelectors)[i].second; - auto expected = store.getKey(key, selector.orEqual, selector.offset); + auto expected = stores[tenantId].getKey(key, selector.orEqual, selector.offset); auto actual = (*results)[i]; // Local store only contains data for the current client, while fdb contains data from multiple // clients. If getKey returned a key outside of the range for the current client, adjust the result // to match what would be expected in the local store. if (actual.substr(0, keyPrefix.size()) < keyPrefix) { - actual = store.startKey(); + actual = stores[tenantId].startKey(); } else if ((*results)[i].substr(0, keyPrefix.size()) > keyPrefix) { - actual = store.endKey(); + actual = stores[tenantId].endKey(); } if (actual != expected) { error(fmt::format("randomGetKeyOp mismatch. key: {}, orEqual: {}, offset: {}, expected: {} " @@ -195,37 +198,38 @@ private: } } schedule(cont); - }); + }, + getTenant(tenantId)); } void getRangeLoop(std::shared_ptr ctx, fdb::KeySelector begin, - fdb::KeySelector end, + fdb::Key endKey, std::shared_ptr> results) { auto f = ctx->tx().getRange(begin, - end, + fdb::key_select::firstGreaterOrEqual(endKey), 0 /*limit*/, 0 /*target_bytes*/, FDB_STREAMING_MODE_WANT_ALL, 0 /*iteration*/, false /*snapshot*/, false /*reverse*/); - ctx->continueAfter(f, [this, ctx, f, end, results]() { + ctx->continueAfter(f, [this, ctx, f, endKey, results]() { auto out = copyKeyValueArray(f.get()); results->insert(results->end(), out.first.begin(), out.first.end()); const bool more = out.second; if (more) { // Fetch the remaining results. - getRangeLoop(ctx, fdb::key_select::firstGreaterThan(results->back().key), end, results); + getRangeLoop(ctx, fdb::key_select::firstGreaterThan(results->back().key), endKey, results); } else { ctx->done(); } }); } - void randomGetRangeOp(TTaskFct cont) { - auto begin = randomKey(readExistingKeysRatio); - auto end = randomKey(readExistingKeysRatio); + void randomGetRangeOp(TTaskFct cont, std::optional tenantId) { + auto begin = randomKey(readExistingKeysRatio, tenantId); + auto end = randomKey(readExistingKeysRatio, tenantId); auto results = std::make_shared>(); execTransaction( @@ -233,13 +237,10 @@ private: // Clear the results vector, in case the transaction is retried. results->clear(); - getRangeLoop(ctx, - fdb::key_select::firstGreaterOrEqual(begin), - fdb::key_select::firstGreaterOrEqual(end), - results); + getRangeLoop(ctx, fdb::key_select::firstGreaterOrEqual(begin), end, results); }, - [this, begin, end, results, cont]() { - auto expected = store.getRange(begin, end, results->size() + 10, false); + [this, begin, end, results, cont, tenantId]() { + auto expected = stores[tenantId].getRange(begin, end, results->size() + 10, false); if (results->size() != expected.size()) { error(fmt::format("randomGetRangeOp mismatch. expected {} keys, actual {} keys", expected.size(), @@ -260,32 +261,35 @@ private: } } schedule(cont); - }); + }, + getTenant(tenantId)); } void randomOperation(TTaskFct cont) { - OpType txType = (store.size() == 0) ? OP_INSERT : (OpType)Random::get().randomInt(0, OP_LAST); + std::optional tenantId = randomTenant(); + OpType txType = (stores[tenantId].size() == 0) ? OP_INSERT : (OpType)Random::get().randomInt(0, OP_LAST); + switch (txType) { case OP_INSERT: - randomInsertOp(cont); + randomInsertOp(cont, tenantId); break; case OP_GET: - randomGetOp(cont); + randomGetOp(cont, tenantId); break; case OP_GET_KEY: - randomGetKeyOp(cont); + randomGetKeyOp(cont, tenantId); break; case OP_CLEAR: - randomClearOp(cont); + randomClearOp(cont, tenantId); break; case OP_GET_RANGE: - randomGetRangeOp(cont); + randomGetRangeOp(cont, tenantId); break; case OP_CLEAR_RANGE: - randomClearRangeOp(cont); + randomClearRangeOp(cont, tenantId); break; case OP_COMMIT_READ: - randomCommitReadOp(cont); + randomCommitReadOp(cont, tenantId); break; } } diff --git a/bindings/c/test/apitester/TesterExampleWorkload.cpp b/bindings/c/test/apitester/TesterExampleWorkload.cpp index 3765dc50fb..882fdc62e4 100644 --- a/bindings/c/test/apitester/TesterExampleWorkload.cpp +++ b/bindings/c/test/apitester/TesterExampleWorkload.cpp @@ -35,8 +35,8 @@ public: void start() override { setAndGet(NO_OP_TASK); } void setAndGet(TTaskFct cont) { - fdb::Key key = keyPrefix + random.randomStringLowerCase(10, 100); - fdb::Value value = random.randomStringLowerCase(10, 1000); + fdb::Key key = keyPrefix + random.randomByteStringLowerCase(10, 100); + fdb::Value value = random.randomByteStringLowerCase(10, 1000); execTransaction( [key, value](auto ctx) { ctx->tx().set(key, value); diff --git a/bindings/c/test/apitester/TesterOptions.h b/bindings/c/test/apitester/TesterOptions.h index 1160b696b0..7c7d0fc948 100644 --- a/bindings/c/test/apitester/TesterOptions.h +++ b/bindings/c/test/apitester/TesterOptions.h @@ -49,6 +49,7 @@ public: int numClientThreads; int numDatabases; int numClients; + int numTenants = -1; int statsIntervalMs = 0; std::vector> knobs; TestSpec testSpec; diff --git a/bindings/c/test/apitester/TesterTestSpec.cpp b/bindings/c/test/apitester/TesterTestSpec.cpp index 49d19b4386..1048aab493 100644 --- a/bindings/c/test/apitester/TesterTestSpec.cpp +++ b/bindings/c/test/apitester/TesterTestSpec.cpp @@ -65,6 +65,10 @@ std::unordered_mapdatabasePerTransaction = (value == "true"); } }, + { "tamperClusterFile", + [](const std::string& value, TestSpec* spec) { // + spec->tamperClusterFile = (value == "true"); + } }, { "minFdbThreads", [](const std::string& value, TestSpec* spec) { // processIntOption(value, "minFdbThreads", spec->minFdbThreads, 1, 1000); @@ -100,6 +104,14 @@ std::unordered_mapdisableClientBypass = (value == "true"); + } }, + { "minTenants", + [](const std::string& value, TestSpec* spec) { // + processIntOption(value, "minTenants", spec->minTenants, 1, 1000); + } }, + { "maxTenants", + [](const std::string& value, TestSpec* spec) { // + processIntOption(value, "maxTenants", spec->maxTenants, 1, 1000); } } }; diff --git a/bindings/c/test/apitester/TesterTestSpec.h b/bindings/c/test/apitester/TesterTestSpec.h index 22b003ed27..c0e9c0caf1 100644 --- a/bindings/c/test/apitester/TesterTestSpec.h +++ b/bindings/c/test/apitester/TesterTestSpec.h @@ -58,6 +58,9 @@ struct TestSpec { // Execute each transaction in a separate database instance bool databasePerTransaction = false; + // Test tampering the cluster file + bool tamperClusterFile = false; + // Size of the FDB client thread pool (a random number in the [min,max] range) int minFdbThreads = 1; int maxFdbThreads = 1; @@ -78,6 +81,9 @@ struct TestSpec { // Disable the ability to bypass the MVC API, for // cases when there are no external clients bool disableClientBypass = false; + // Number of tenants (a random number in the [min,max] range) + int minTenants = 0; + int maxTenants = 0; // List of workloads with their options std::vector workloads; diff --git a/bindings/c/test/apitester/TesterTransactionExecutor.cpp b/bindings/c/test/apitester/TesterTransactionExecutor.cpp index 995f1ac2cc..e5f4905051 100644 --- a/bindings/c/test/apitester/TesterTransactionExecutor.cpp +++ b/bindings/c/test/apitester/TesterTransactionExecutor.cpp @@ -23,25 +23,23 @@ #include "foundationdb/fdb_c_types.h" #include "test/apitester/TesterScheduler.h" #include "test/fdb_api.hpp" +#include #include #include +#include #include #include #include #include #include #include +#include namespace FdbApiTester { constexpr int LONG_WAIT_TIME_US = 2000000; constexpr int LARGE_NUMBER_OF_RETRIES = 10; -void TransactionActorBase::complete(fdb::Error err) { - error = err; - context = {}; -} - void ITransactionContext::continueAfterAll(std::vector futures, TTaskFct cont) { auto counter = std::make_shared>(futures.size()); auto errorCode = std::make_shared>(fdb::Error::success()); @@ -73,22 +71,32 @@ void ITransactionContext::continueAfterAll(std::vector futures, TTa class TransactionContextBase : public ITransactionContext { public: TransactionContextBase(ITransactionExecutor* executor, - std::shared_ptr txActor, - TTaskFct cont, + TOpStartFct startFct, + TOpContFct cont, IScheduler* scheduler, int retryLimit, - std::string bgBasePath) - : executor(executor), txActor(txActor), contAfterDone(cont), scheduler(scheduler), retryLimit(retryLimit), - txState(TxState::IN_PROGRESS), commitCalled(false), bgBasePath(bgBasePath) { + std::string bgBasePath, + std::optional tenantName, + bool transactional) + : executor(executor), startFct(startFct), contAfterDone(cont), scheduler(scheduler), retryLimit(retryLimit), + txState(TxState::IN_PROGRESS), commitCalled(false), bgBasePath(bgBasePath), tenantName(tenantName), + transactional(transactional) { databaseCreateErrorInjected = executor->getOptions().injectDatabaseCreateErrors && Random::get().randomBool(executor->getOptions().databaseCreateErrorRatio); - fdb::Database db; if (databaseCreateErrorInjected) { - db = fdb::Database("not_existing_file"); + fdbDb = fdb::Database(executor->getClusterFileForErrorInjection()); } else { - db = executor->selectDatabase(); + fdbDb = executor->selectDatabase(); + } + + if (transactional) { + if (tenantName) { + fdb::Tenant tenant = fdbDb.openTenant(*tenantName); + fdbTx = tenant.createTransaction(); + } else { + fdbTx = fdbDb.createTransaction(); + } } - fdbTx = db.createTransaction(); } virtual ~TransactionContextBase() { ASSERT(txState == TxState::DONE); } @@ -97,7 +105,9 @@ public: // IN_PROGRESS -> (ON_ERROR -> IN_PROGRESS)* [-> ON_ERROR] -> DONE enum class TxState { IN_PROGRESS, ON_ERROR, DONE }; - fdb::Transaction tx() override { return fdbTx; } + fdb::Database db() override { return fdbDb.atomic_load(); } + + fdb::Transaction tx() override { return fdbTx.atomic_load(); } // Set a continuation to be executed when a future gets ready void continueAfter(fdb::Future f, TTaskFct cont, bool retryOnError) override { @@ -106,6 +116,7 @@ public: // Complete the transaction with a commit void commit() override { + ASSERT(transactional); std::unique_lock lock(mutex); if (txState != TxState::IN_PROGRESS) { return; @@ -135,13 +146,15 @@ public: retriedErrors.size(), fmt::join(retriedErrorCodes(), ", ")); } - // cancel transaction so that any pending operations on it - // fail gracefully - fdbTx.cancel(); - txActor->complete(fdb::Error::success()); - cleanUp(); + + if (transactional) { + // cancel transaction so that any pending operations on it + // fail gracefully + fdbTx.cancel(); + cleanUp(); + } ASSERT(txState == TxState::DONE); - contAfterDone(); + contAfterDone(fdb::Error::success()); } std::string getBGBasePath() override { return bgBasePath; } @@ -164,19 +177,29 @@ public: ASSERT(!onErrorFuture); - if (databaseCreateErrorInjected) { + if (databaseCreateErrorInjected && canBeInjectedDatabaseCreateError(err.code())) { // Failed to create a database because of failure injection // Restart by recreating the transaction in a valid database - scheduler->schedule([this]() { - databaseCreateErrorInjected = false; - fdb::Database db = executor->selectDatabase(); - fdbTx = db.createTransaction(); - restartTransaction(); + auto thisRef = std::static_pointer_cast(shared_from_this()); + scheduler->schedule([thisRef]() { + fdb::Database db = thisRef->executor->selectDatabase(); + thisRef->fdbDb.atomic_store(db); + if (thisRef->transactional) { + if (thisRef->tenantName) { + fdb::Tenant tenant = db.openTenant(*thisRef->tenantName); + thisRef->fdbTx.atomic_store(tenant.createTransaction()); + } else { + thisRef->fdbTx.atomic_store(db.createTransaction()); + } + } + thisRef->restartTransaction(); }); - } else { + } else if (transactional) { onErrorArg = err; onErrorFuture = tx().onError(err); handleOnErrorFuture(); + } else { + transactionFailed(err); } } @@ -188,10 +211,16 @@ protected: // Clean up transaction state after completing the transaction // Note that the object may live longer, because it is referenced // by not yet triggered callbacks - virtual void cleanUp() { + void cleanUp() { ASSERT(txState == TxState::DONE); ASSERT(!onErrorFuture); - txActor = {}; + cancelPendingFutures(); + } + + virtual void cancelPendingFutures() {} + + bool canBeInjectedDatabaseCreateError(fdb::Error::CodeType errCode) { + return errCode == error_code_no_cluster_file_found || errCode == error_code_connection_string_invalid; } // Complete the transaction with an (unretriable) error @@ -207,9 +236,8 @@ protected: // No need for lock from here on, because only one thread // can enter DONE state and handle it - txActor->complete(err); cleanUp(); - contAfterDone(); + contAfterDone(err); } // Handle result of an a transaction onError call @@ -225,12 +253,13 @@ protected: } void restartTransaction() { - std::unique_lock lock(mutex); ASSERT(txState == TxState::ON_ERROR); + cancelPendingFutures(); + std::unique_lock lock(mutex); txState = TxState::IN_PROGRESS; commitCalled = false; lock.unlock(); - txActor->start(); + startFct(shared_from_this()); } // Checks if a transaction can be retried. Fails the transaction if the check fails @@ -262,13 +291,17 @@ protected: // Set in contructor, stays immutable ITransactionExecutor* const executor; + // FDB database + // Provides a thread safe interface by itself (no need for mutex) + fdb::Database fdbDb; + // FDB transaction // Provides a thread safe interface by itself (no need for mutex) fdb::Transaction fdbTx; - // Actor implementing the transaction worklflow + // The function implementing the starting point of the transaction // Set in constructor and reset on cleanup (no need for mutex) - std::shared_ptr txActor; + TOpStartFct startFct; // Mutex protecting access to shared mutable state // Only the state that is accessible unter IN_PROGRESS state @@ -277,7 +310,7 @@ protected: // Continuation to be called after completion of the transaction // Set in contructor, stays immutable - const TTaskFct contAfterDone; + const TOpContFct contAfterDone; // Reference to the scheduler // Set in contructor, stays immutable @@ -319,6 +352,12 @@ protected: // Indicates if the database error was injected // Accessed on initialization and in ON_ERROR state only (no need for mutex) bool databaseCreateErrorInjected; + + // The tenant that we will run this transaction in + const std::optional tenantName; + + // Specifies whether the operation is transactional + const bool transactional; }; /** @@ -327,12 +366,15 @@ protected: class BlockingTransactionContext : public TransactionContextBase { public: BlockingTransactionContext(ITransactionExecutor* executor, - std::shared_ptr txActor, - TTaskFct cont, + TOpStartFct startFct, + TOpContFct cont, IScheduler* scheduler, int retryLimit, - std::string bgBasePath) - : TransactionContextBase(executor, txActor, cont, scheduler, retryLimit, bgBasePath) {} + std::string bgBasePath, + std::optional tenantName, + bool transactional) + : TransactionContextBase(executor, startFct, cont, scheduler, retryLimit, bgBasePath, tenantName, transactional) { + } protected: void doContinueAfter(fdb::Future f, TTaskFct cont, bool retryOnError) override { @@ -402,12 +444,15 @@ protected: class AsyncTransactionContext : public TransactionContextBase { public: AsyncTransactionContext(ITransactionExecutor* executor, - std::shared_ptr txActor, - TTaskFct cont, + TOpStartFct startFct, + TOpContFct cont, IScheduler* scheduler, int retryLimit, - std::string bgBasePath) - : TransactionContextBase(executor, txActor, cont, scheduler, retryLimit, bgBasePath) {} + std::string bgBasePath, + std::optional tenantName, + bool transactional) + : TransactionContextBase(executor, startFct, cont, scheduler, retryLimit, bgBasePath, tenantName, transactional) { + } protected: void doContinueAfter(fdb::Future f, TTaskFct cont, bool retryOnError) override { @@ -415,7 +460,7 @@ protected: if (txState != TxState::IN_PROGRESS) { return; } - callbackMap[f] = CallbackInfo{ f, cont, shared_from_this(), retryOnError, timeNow() }; + callbackMap[f] = CallbackInfo{ f, cont, shared_from_this(), retryOnError, timeNow(), false }; lock.unlock(); try { f.then([this](fdb::Future f) { futureReadyCallback(f, this); }); @@ -462,7 +507,7 @@ protected: err.code(), err.what()); } - if (err.code() == error_code_transaction_cancelled) { + if (err.code() == error_code_transaction_cancelled || cbInfo.cancelled) { return; } if (err.code() == error_code_success || !cbInfo.retryOnError) { @@ -518,17 +563,17 @@ protected: scheduler->schedule([thisRef]() { thisRef->handleOnErrorResult(); }); } - void cleanUp() override { - TransactionContextBase::cleanUp(); - + void cancelPendingFutures() override { // Cancel all pending operations // Note that the callbacks of the cancelled futures will still be called std::unique_lock lock(mutex); std::vector futures; for (auto& iter : callbackMap) { + iter.second.cancelled = true; futures.push_back(iter.second.future); } lock.unlock(); + for (auto& f : futures) { f.cancel(); } @@ -548,6 +593,7 @@ protected: std::shared_ptr thisRef; bool retryOnError; TimePoint startTime; + bool cancelled; }; // Map for keeping track of future waits and holding necessary object references @@ -567,29 +613,86 @@ class TransactionExecutorBase : public ITransactionExecutor { public: TransactionExecutorBase(const TransactionExecutorOptions& options) : options(options), scheduler(nullptr) {} + ~TransactionExecutorBase() { + if (tamperClusterFileThread.joinable()) { + tamperClusterFileThread.join(); + } + } + void init(IScheduler* scheduler, const char* clusterFile, const std::string& bgBasePath) override { this->scheduler = scheduler; this->clusterFile = clusterFile; this->bgBasePath = bgBasePath; + + ASSERT(!options.tmpDir.empty()); + emptyClusterFile.create(options.tmpDir, "fdbempty.cluster"); + invalidClusterFile.create(options.tmpDir, "fdbinvalid.cluster"); + invalidClusterFile.write(Random().get().randomStringLowerCase(1, 100)); + + emptyListClusterFile.create(options.tmpDir, "fdbemptylist.cluster"); + emptyListClusterFile.write(fmt::format("{}:{}@", + Random().get().randomStringLowerCase(3, 8), + Random().get().randomStringLowerCase(1, 100))); + + if (options.tamperClusterFile) { + tamperedClusterFile.create(options.tmpDir, "fdb.cluster"); + originalClusterFile = clusterFile; + this->clusterFile = tamperedClusterFile.getFileName(); + + // begin with a valid cluster file, but with non existing address + tamperedClusterFile.write(fmt::format("{}:{}@192.168.{}.{}:{}", + Random().get().randomStringLowerCase(3, 8), + Random().get().randomStringLowerCase(1, 100), + Random().get().randomInt(1, 254), + Random().get().randomInt(1, 254), + Random().get().randomInt(2000, 10000))); + + tamperClusterFileThread = std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(2)); + // now write an invalid connection string + tamperedClusterFile.write(fmt::format("{}:{}@", + Random().get().randomStringLowerCase(3, 8), + Random().get().randomStringLowerCase(1, 100))); + std::this_thread::sleep_for(std::chrono::seconds(2)); + // finally use correct cluster file contents + std::filesystem::copy_file(std::filesystem::path(originalClusterFile), + std::filesystem::path(tamperedClusterFile.getFileName()), + std::filesystem::copy_options::overwrite_existing); + }); + } } const TransactionExecutorOptions& getOptions() override { return options; } - void execute(std::shared_ptr txActor, TTaskFct cont) override { + void execute(TOpStartFct startFct, + TOpContFct cont, + std::optional tenantName, + bool transactional) override { try { std::shared_ptr ctx; if (options.blockOnFutures) { ctx = std::make_shared( - this, txActor, cont, scheduler, options.transactionRetryLimit, bgBasePath); + this, startFct, cont, scheduler, options.transactionRetryLimit, bgBasePath, tenantName, true); } else { ctx = std::make_shared( - this, txActor, cont, scheduler, options.transactionRetryLimit, bgBasePath); + this, startFct, cont, scheduler, options.transactionRetryLimit, bgBasePath, tenantName, true); } - txActor->init(ctx); - txActor->start(); + startFct(ctx); } catch (...) { - txActor->complete(fdb::Error(error_code_operation_failed)); - cont(); + cont(fdb::Error(error_code_operation_failed)); + } + } + + std::string getClusterFileForErrorInjection() override { + switch (Random::get().randomInt(0, 3)) { + case 0: + return fmt::format("{}{}", "not-existing-file", Random::get().randomStringLowerCase(0, 2)); + case 1: + return emptyClusterFile.getFileName(); + case 2: + return invalidClusterFile.getFileName(); + default: // case 3 + return emptyListClusterFile.getFileName(); } } @@ -598,6 +701,12 @@ protected: std::string bgBasePath; std::string clusterFile; IScheduler* scheduler; + TmpFile emptyClusterFile; + TmpFile invalidClusterFile; + TmpFile emptyListClusterFile; + TmpFile tamperedClusterFile; + std::thread tamperClusterFileThread; + std::string originalClusterFile; }; /** @@ -612,7 +721,7 @@ public: void init(IScheduler* scheduler, const char* clusterFile, const std::string& bgBasePath) override { TransactionExecutorBase::init(scheduler, clusterFile, bgBasePath); for (int i = 0; i < options.numDatabases; i++) { - fdb::Database db(clusterFile); + fdb::Database db(this->clusterFile); databases.push_back(db); } } diff --git a/bindings/c/test/apitester/TesterTransactionExecutor.h b/bindings/c/test/apitester/TesterTransactionExecutor.h index ac87058f0c..b0e5268d14 100644 --- a/bindings/c/test/apitester/TesterTransactionExecutor.h +++ b/bindings/c/test/apitester/TesterTransactionExecutor.h @@ -38,6 +38,9 @@ class ITransactionContext : public std::enable_shared_from_this futures, TTaskFct cont); }; -/** - * Interface of an actor object implementing a concrete transaction - */ -class ITransactionActor { -public: - virtual ~ITransactionActor() {} +// Type of the lambda functions implementing a database operation +using TOpStartFct = std::function)>; - // Initialize with the given transaction context - virtual void init(std::shared_ptr ctx) = 0; - - // Start execution of the transaction, also called on retries - virtual void start() = 0; - - // Transaction completion result (error_code_success in case of success) - virtual fdb::Error getError() = 0; - - // Notification about the completion of the transaction - virtual void complete(fdb::Error err) = 0; -}; - -/** - * A helper base class for transaction actors - */ -class TransactionActorBase : public ITransactionActor { -public: - void init(std::shared_ptr ctx) override { context = ctx; } - fdb::Error getError() override { return error; } - void complete(fdb::Error err) override; - -protected: - std::shared_ptr ctx() { return context; } - -private: - std::shared_ptr context; - fdb::Error error = fdb::Error::success(); -}; - -// Type of the lambda functions implementing a transaction -using TTxStartFct = std::function)>; - -/** - * A wrapper class for transactions implemented by lambda functions - */ -class TransactionFct : public TransactionActorBase { -public: - TransactionFct(TTxStartFct startFct) : startFct(startFct) {} - void start() override { startFct(this->ctx()); } - -private: - TTxStartFct startFct; -}; +// Type of the lambda functions implementing a database operation +using TOpContFct = std::function; /** * Configuration of transaction execution mode @@ -127,15 +84,24 @@ struct TransactionExecutorOptions { // Enable injection of database create errors bool injectDatabaseCreateErrors = false; + // Test tampering cluster file contents + bool tamperClusterFile = false; + // The probability of injected database create errors - // Used if buggify = true + // Used if injectDatabaseCreateErrors = true double databaseCreateErrorRatio = 0.1; // The size of the database instance pool int numDatabases = 1; + // The number of tenants to create in the cluster. If 0, no tenants are used. + int numTenants = 0; + // Maximum number of retries per transaction (0 - unlimited) int transactionRetryLimit = 0; + + // Temporary directory + std::string tmpDir; }; /** @@ -147,8 +113,12 @@ class ITransactionExecutor { public: virtual ~ITransactionExecutor() {} virtual void init(IScheduler* sched, const char* clusterFile, const std::string& bgBasePath) = 0; - virtual void execute(std::shared_ptr tx, TTaskFct cont) = 0; + virtual void execute(TOpStartFct start, + TOpContFct cont, + std::optional tenantName, + bool transactional) = 0; virtual fdb::Database selectDatabase() = 0; + virtual std::string getClusterFileForErrorInjection() = 0; virtual const TransactionExecutorOptions& getOptions() = 0; }; diff --git a/bindings/c/test/apitester/TesterUtil.cpp b/bindings/c/test/apitester/TesterUtil.cpp index 351efd9c31..6ec9f76f04 100644 --- a/bindings/c/test/apitester/TesterUtil.cpp +++ b/bindings/c/test/apitester/TesterUtil.cpp @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include namespace FdbApiTester { @@ -46,16 +49,6 @@ Random& Random::get() { return random; } -fdb::ByteString Random::randomStringLowerCase(int minLength, int maxLength) { - int length = randomInt(minLength, maxLength); - fdb::ByteString str; - str.reserve(length); - for (int i = 0; i < length; i++) { - str += (char)randomInt('a', 'z'); - } - return str; -} - bool Random::randomBool(double trueRatio) { return std::uniform_real_distribution(0.0, 1.0)(random) <= trueRatio; } @@ -119,4 +112,39 @@ GranuleSummaryArray copyGranuleSummaryArray(fdb::future_var::GranuleSummaryRefAr return out; }; +TmpFile::~TmpFile() { + if (!filename.empty()) { + remove(); + } +} + +void TmpFile::create(std::string_view dir, std::string_view prefix) { + while (true) { + filename = fmt::format("{}/{}-{}", dir, prefix, Random::get().randomStringLowerCase(6, 6)); + if (!std::filesystem::exists(std::filesystem::path(filename))) { + break; + } + } + + // Create an empty tmp file + std::fstream tmpFile(filename, std::fstream::out); + if (!tmpFile.good()) { + throw TesterError(fmt::format("Failed to create temporary file {}\n", filename)); + } +} + +void TmpFile::write(std::string_view data) { + std::ofstream ofs(filename, std::fstream::out | std::fstream::binary); + if (!ofs.good()) { + throw TesterError(fmt::format("Failed to write to the temporary file {}\n", filename)); + } + ofs.write(data.data(), data.size()); +} + +void TmpFile::remove() { + if (!std::filesystem::remove(std::filesystem::path(filename))) { + fmt::print(stderr, "Failed to remove file {}\n", filename); + } +} + } // namespace FdbApiTester \ No newline at end of file diff --git a/bindings/c/test/apitester/TesterUtil.h b/bindings/c/test/apitester/TesterUtil.h index ee361002c2..1ace2c9721 100644 --- a/bindings/c/test/apitester/TesterUtil.h +++ b/bindings/c/test/apitester/TesterUtil.h @@ -66,7 +66,20 @@ public: int randomInt(int min, int max); - fdb::ByteString randomStringLowerCase(int minLength, int maxLength); + template + StringType randomStringLowerCase(int minLength, int maxLength) { + int length = randomInt(minLength, maxLength); + StringType str; + str.reserve(length); + for (int i = 0; i < length; i++) { + str += (char)randomInt('a', 'z'); + } + return str; + } + + fdb::ByteString randomByteStringLowerCase(int minLength, int maxLength) { + return randomStringLowerCase(minLength, maxLength); + } bool randomBool(double trueRatio); @@ -142,6 +155,19 @@ static fdb::ByteString toByteString(T value) { return output; } +// Creates a temporary file; file gets destroyed/deleted along with object destruction. +struct TmpFile { +public: + ~TmpFile(); + void create(std::string_view dir, std::string_view prefix); + void write(std::string_view data); + void remove(); + const std::string& getFileName() const { return filename; } + +private: + std::string filename; +}; + } // namespace FdbApiTester #endif diff --git a/bindings/c/test/apitester/TesterWorkload.cpp b/bindings/c/test/apitester/TesterWorkload.cpp index c11caf5023..8e7289f437 100644 --- a/bindings/c/test/apitester/TesterWorkload.cpp +++ b/bindings/c/test/apitester/TesterWorkload.cpp @@ -106,30 +106,49 @@ void WorkloadBase::schedule(TTaskFct task) { }); } -void WorkloadBase::execTransaction(std::shared_ptr tx, TTaskFct cont, bool failOnError) { +void WorkloadBase::execTransaction(TOpStartFct startFct, + TTaskFct cont, + std::optional tenant, + bool failOnError) { + doExecute(startFct, cont, tenant, failOnError, true); +} + +// Execute a non-transactional database operation within the workload +void WorkloadBase::execOperation(TOpStartFct startFct, TTaskFct cont, bool failOnError) { + doExecute(startFct, cont, {}, failOnError, false); +} + +void WorkloadBase::doExecute(TOpStartFct startFct, + TTaskFct cont, + std::optional tenant, + bool failOnError, + bool transactional) { ASSERT(inProgress); if (failed) { return; } tasksScheduled++; numTxStarted++; - manager->txExecutor->execute(tx, [this, tx, cont, failOnError]() { - numTxCompleted++; - fdb::Error err = tx->getError(); - if (err.code() == error_code_success) { - cont(); - } else { - std::string msg = fmt::format("Transaction failed with error: {} ({})", err.code(), err.what()); - if (failOnError) { - error(msg); - failed = true; - } else { - info(msg); - cont(); - } - } - scheduledTaskDone(); - }); + manager->txExecutor->execute( + startFct, + [this, startFct, cont, failOnError](fdb::Error err) { + numTxCompleted++; + if (err.code() == error_code_success) { + cont(); + } else { + std::string msg = fmt::format("Transaction failed with error: {} ({})", err.code(), err.what()); + if (failOnError) { + error(msg); + failed = true; + } else { + info(msg); + cont(); + } + } + scheduledTaskDone(); + }, + tenant, + transactional); } void WorkloadBase::info(const std::string& msg) { diff --git a/bindings/c/test/apitester/TesterWorkload.h b/bindings/c/test/apitester/TesterWorkload.h index 9820493cd6..ea1c6816f9 100644 --- a/bindings/c/test/apitester/TesterWorkload.h +++ b/bindings/c/test/apitester/TesterWorkload.h @@ -82,6 +82,9 @@ struct WorkloadConfig { // Total number of clients int numClients; + // Number of Tenants + int numTenants; + // Selected FDB API version int apiVersion; @@ -116,12 +119,13 @@ protected: void schedule(TTaskFct task); // Execute a transaction within the workload - void execTransaction(std::shared_ptr tx, TTaskFct cont, bool failOnError = true); + void execTransaction(TOpStartFct startFct, + TTaskFct cont, + std::optional tenant = std::optional(), + bool failOnError = true); - // Execute a transaction within the workload, a convenience method for a tranasaction defined by a lambda function - void execTransaction(TTxStartFct start, TTaskFct cont, bool failOnError = true) { - execTransaction(std::make_shared(start), cont, failOnError); - } + // Execute a non-transactional database operation within the workload + void execOperation(TOpStartFct startFct, TTaskFct cont, bool failOnError = true); // Log an error message, increase error counter void error(const std::string& msg); @@ -135,6 +139,12 @@ protected: private: WorkloadManager* manager; + void doExecute(TOpStartFct startFct, + TTaskFct cont, + std::optional tenant, + bool failOnError, + bool transactional); + // Decrease scheduled task counter, notify the workload manager // that the task is done if no more tasks schedule void scheduledTaskDone(); diff --git a/bindings/c/test/apitester/fdb_c_api_tester.cpp b/bindings/c/test/apitester/fdb_c_api_tester.cpp index 597b96c2d4..1d79dd754c 100644 --- a/bindings/c/test/apitester/fdb_c_api_tester.cpp +++ b/bindings/c/test/apitester/fdb_c_api_tester.cpp @@ -36,6 +36,8 @@ namespace FdbApiTester { namespace { +#define API_VERSION_CLIENT_TMP_DIR 720 + enum TesterOptionId { OPT_CONNFILE, OPT_HELP, @@ -285,7 +287,7 @@ void fdb_check(fdb::Error e) { } void applyNetworkOptions(TesterOptions& options) { - if (!options.tmpDir.empty() && options.apiVersion >= 720) { + if (!options.tmpDir.empty() && options.apiVersion >= API_VERSION_CLIENT_TMP_DIR) { fdb::network::setOption(FDBNetworkOption::FDB_NET_OPTION_CLIENT_TMP_DIR, options.tmpDir); } if (!options.externalClientLibrary.empty()) { @@ -354,6 +356,12 @@ void randomizeOptions(TesterOptions& options) { options.numClientThreads = random.randomInt(options.testSpec.minClientThreads, options.testSpec.maxClientThreads); options.numDatabases = random.randomInt(options.testSpec.minDatabases, options.testSpec.maxDatabases); options.numClients = random.randomInt(options.testSpec.minClients, options.testSpec.maxClients); + + // Choose a random number of tenants. If a test is configured to allow 0 tenants, then use 0 tenants half the time. + if (options.testSpec.maxTenants >= options.testSpec.minTenants && + (options.testSpec.minTenants > 0 || random.randomBool(0.5))) { + options.numTenants = random.randomInt(options.testSpec.minTenants, options.testSpec.maxTenants); + } } bool runWorkloads(TesterOptions& options) { @@ -365,6 +373,9 @@ bool runWorkloads(TesterOptions& options) { // 7.1 and older releases crash on database create errors txExecOptions.injectDatabaseCreateErrors = options.testSpec.buggify && options.apiVersion > 710; txExecOptions.transactionRetryLimit = options.transactionRetryLimit; + txExecOptions.tmpDir = options.tmpDir.empty() ? std::string("/tmp") : options.tmpDir; + txExecOptions.tamperClusterFile = options.testSpec.tamperClusterFile; + txExecOptions.numTenants = options.numTenants; std::vector> workloads; workloads.reserve(options.testSpec.workloads.size() * options.numClients); @@ -376,6 +387,7 @@ bool runWorkloads(TesterOptions& options) { config.options = workloadSpec.options; config.clientId = i; config.numClients = options.numClients; + config.numTenants = options.numTenants; config.apiVersion = options.apiVersion; std::shared_ptr workload = IWorkloadFactory::create(workloadSpec.name, config); if (!workload) { diff --git a/bindings/c/test/apitester/tests/CApiMultiTenantCorrectnessMultiThr.toml b/bindings/c/test/apitester/tests/CApiMultiTenantCorrectnessMultiThr.toml new file mode 100644 index 0000000000..2a5a0d30e1 --- /dev/null +++ b/bindings/c/test/apitester/tests/CApiMultiTenantCorrectnessMultiThr.toml @@ -0,0 +1,21 @@ +[[test]] +title = 'Multi-tenant API Correctness Multi Threaded' +multiThreaded = true +buggify = true +minFdbThreads = 2 +maxFdbThreads = 8 +minClients = 2 +maxClients = 8 +minTenants = 2 +maxTenants = 5 + + [[test.workload]] + name = 'ApiCorrectness' + minKeyLength = 1 + maxKeyLength = 64 + minValueLength = 1 + maxValueLength = 1000 + maxKeysPerTransaction = 5 + initialSize = 100 + numRandomOperations = 200 + readExistingKeysRatio = 0.9 \ No newline at end of file diff --git a/bindings/c/test/apitester/tests/CApiTamperClusterFile.toml b/bindings/c/test/apitester/tests/CApiTamperClusterFile.toml new file mode 100644 index 0000000000..60a9715bd8 --- /dev/null +++ b/bindings/c/test/apitester/tests/CApiTamperClusterFile.toml @@ -0,0 +1,24 @@ +[[test]] +title = 'Test tampering the cluster file' +multiThreaded = true +buggify = true +tamperClusterFile = true +minFdbThreads = 2 +maxFdbThreads = 4 +minDatabases = 2 +maxDatabases = 4 +minClientThreads = 2 +maxClientThreads = 4 +minClients = 2 +maxClients = 4 + + [[test.workload]] + name = 'ApiCorrectness' + minKeyLength = 1 + maxKeyLength = 64 + minValueLength = 1 + maxValueLength = 1000 + maxKeysPerTransaction = 50 + initialSize = 100 + numRandomOperations = 100 + readExistingKeysRatio = 0.9 \ No newline at end of file diff --git a/bindings/c/test/client_memory_test.cpp b/bindings/c/test/client_memory_test.cpp index c6cff85574..3ea2f74a8a 100644 --- a/bindings/c/test/client_memory_test.cpp +++ b/bindings/c/test/client_memory_test.cpp @@ -44,7 +44,7 @@ int main(int argc, char** argv) { if (argc != 2) { printf("Usage: %s ", argv[0]); } - fdb_check(fdb_select_api_version(720)); + fdb_check(fdb_select_api_version(FDB_API_VERSION)); fdb_check(fdb_setup_network()); std::thread network_thread{ &fdb_run_network }; diff --git a/bindings/c/test/fdb_api.hpp b/bindings/c/test/fdb_api.hpp index 28c8fcb5bc..0d755c9fc0 100644 --- a/bindings/c/test/fdb_api.hpp +++ b/bindings/c/test/fdb_api.hpp @@ -46,6 +46,8 @@ namespace native { #include } +#define TENANT_API_VERSION_GUARD 720 + using ByteString = std::basic_string; using BytesRef = std::basic_string_view; using CharsRef = std::string_view; @@ -347,6 +349,7 @@ public: class Future { protected: friend class Transaction; + friend class Database; friend std::hash; std::shared_ptr f; @@ -505,6 +508,14 @@ public: Transaction(const Transaction&) noexcept = default; Transaction& operator=(const Transaction&) noexcept = default; + void atomic_store(Transaction other) { std::atomic_store(&tr, other.tr); } + + Transaction atomic_load() { + Transaction retVal; + retVal.tr = std::atomic_load(&tr); + return retVal; + } + bool valid() const noexcept { return tr != nullptr; } explicit operator bool() const noexcept { return valid(); } @@ -708,6 +719,14 @@ public: } Database() noexcept : db(nullptr) {} + void atomic_store(Database other) { std::atomic_store(&db, other.db); } + + Database atomic_load() { + Database retVal; + retVal.db = std::atomic_load(&db); + return retVal; + } + Error setOptionNothrow(FDBDatabaseOption option, int64_t value) noexcept { return Error(native::fdb_database_set_option( db.get(), option, reinterpret_cast(&value), static_cast(sizeof(value)))); @@ -753,10 +772,17 @@ public: throwError("Failed to create transaction: ", err); return Transaction(tx_native); } + + TypedFuture listBlobbifiedRanges(KeyRef begin, KeyRef end, int rangeLimit) { + if (!db) + throw std::runtime_error("list_blobbified_ranges from null database"); + return native::fdb_database_list_blobbified_ranges( + db.get(), begin.data(), intSize(begin), end.data(), intSize(end), rangeLimit); + } }; inline Error selectApiVersionNothrow(int version) { - if (version < 720) { + if (version < TENANT_API_VERSION_GUARD) { Tenant::tenantManagementMapPrefix = "\xff\xff/management/tenant_map/"; } return Error(native::fdb_select_api_version(version)); @@ -769,7 +795,7 @@ inline void selectApiVersion(int version) { } inline Error selectApiVersionCappedNothrow(int version) { - if (version < 720) { + if (version < TENANT_API_VERSION_GUARD) { Tenant::tenantManagementMapPrefix = "\xff\xff/management/tenant_map/"; } return Error( diff --git a/bindings/c/test/fdb_c90_test.c b/bindings/c/test/fdb_c90_test.c index e2011286ed..1bcdf63284 100644 --- a/bindings/c/test/fdb_c90_test.c +++ b/bindings/c/test/fdb_c90_test.c @@ -4,6 +4,6 @@ int main(int argc, char* argv[]) { (void)argc; (void)argv; - fdb_select_api_version(720); + fdb_select_api_version(FDB_API_VERSION); return 0; } diff --git a/bindings/c/test/performance_test.c b/bindings/c/test/performance_test.c index 5cd9f64bc0..ab90395e05 100644 --- a/bindings/c/test/performance_test.c +++ b/bindings/c/test/performance_test.c @@ -641,7 +641,7 @@ void runTests(struct ResultSet* rs) { int main(int argc, char** argv) { srand(time(NULL)); struct ResultSet* rs = newResultSet(); - checkError(fdb_select_api_version(720), "select API version", rs); + checkError(fdb_select_api_version(FDB_API_VERSION), "select API version", rs); printf("Running performance test at client version: %s\n", fdb_get_client_version()); valueStr = (uint8_t*)malloc((sizeof(uint8_t)) * valueSize); diff --git a/bindings/c/test/ryw_benchmark.c b/bindings/c/test/ryw_benchmark.c index cf2754bcec..731a2ce0a2 100644 --- a/bindings/c/test/ryw_benchmark.c +++ b/bindings/c/test/ryw_benchmark.c @@ -285,7 +285,7 @@ void runTests(struct ResultSet* rs) { int main(int argc, char** argv) { srand(time(NULL)); struct ResultSet* rs = newResultSet(); - checkError(fdb_select_api_version(720), "select API version", rs); + checkError(fdb_select_api_version(FDB_API_VERSION), "select API version", rs); printf("Running RYW Benchmark test at client version: %s\n", fdb_get_client_version()); keys = generateKeys(numKeys, keySize); diff --git a/bindings/c/test/txn_size_test.c b/bindings/c/test/txn_size_test.c index 97081f24a6..57c74a9bca 100644 --- a/bindings/c/test/txn_size_test.c +++ b/bindings/c/test/txn_size_test.c @@ -97,7 +97,7 @@ void runTests(struct ResultSet* rs) { int main(int argc, char** argv) { srand(time(NULL)); struct ResultSet* rs = newResultSet(); - checkError(fdb_select_api_version(720), "select API version", rs); + checkError(fdb_select_api_version(FDB_API_VERSION), "select API version", rs); printf("Running performance test at client version: %s\n", fdb_get_client_version()); keys = generateKeys(numKeys, KEY_SIZE); diff --git a/bindings/c/test/unit/disconnected_timeout_tests.cpp b/bindings/c/test/unit/disconnected_timeout_tests.cpp index b1c6b72730..7d006faa23 100644 --- a/bindings/c/test/unit/disconnected_timeout_tests.cpp +++ b/bindings/c/test/unit/disconnected_timeout_tests.cpp @@ -255,7 +255,7 @@ int main(int argc, char** argv) { << std::endl; return 1; } - fdb_check(fdb_select_api_version(720)); + fdb_check(fdb_select_api_version(FDB_API_VERSION)); if (argc >= 3) { std::string externalClientLibrary = argv[2]; if (externalClientLibrary.substr(0, 2) != "--") { diff --git a/bindings/c/test/unit/setup_tests.cpp b/bindings/c/test/unit/setup_tests.cpp index 6ac65b7850..2e96eb00b9 100644 --- a/bindings/c/test/unit/setup_tests.cpp +++ b/bindings/c/test/unit/setup_tests.cpp @@ -42,13 +42,13 @@ TEST_CASE("setup") { CHECK(err); // Select current API version - fdb_check(fdb_select_api_version(720)); + fdb_check(fdb_select_api_version(FDB_API_VERSION)); // Error to call again after a successful return - err = fdb_select_api_version(720); + err = fdb_select_api_version(FDB_API_VERSION); CHECK(err); - CHECK(fdb_get_max_api_version() >= 720); + CHECK(fdb_get_max_api_version() >= FDB_API_VERSION); fdb_check(fdb_setup_network()); // Calling a second time should fail diff --git a/bindings/c/test/unit/trace_partial_file_suffix_test.cpp b/bindings/c/test/unit/trace_partial_file_suffix_test.cpp index 810cc066fd..73dc8132a5 100644 --- a/bindings/c/test/unit/trace_partial_file_suffix_test.cpp +++ b/bindings/c/test/unit/trace_partial_file_suffix_test.cpp @@ -53,7 +53,7 @@ bool file_exists(const char* path) { } int main(int argc, char** argv) { - fdb_check(fdb_select_api_version(720)); + fdb_check(fdb_select_api_version(FDB_API_VERSION)); std::string file_identifier = "trace_partial_file_suffix_test" + std::to_string(std::random_device{}()); std::string trace_partial_file_suffix = ".tmp"; diff --git a/bindings/c/test/unit/unit_tests.cpp b/bindings/c/test/unit/unit_tests.cpp index 95effdf595..2d8dc2456f 100644 --- a/bindings/c/test/unit/unit_tests.cpp +++ b/bindings/c/test/unit/unit_tests.cpp @@ -2979,7 +2979,7 @@ int main(int argc, char** argv) { << std::endl; return 1; } - fdb_check(fdb_select_api_version(720)); + fdb_check(fdb_select_api_version(FDB_API_VERSION)); if (argc >= 4) { std::string externalClientLibrary = argv[3]; if (externalClientLibrary.substr(0, 2) != "--") { diff --git a/bindings/c/test/workloads/SimpleWorkload.cpp b/bindings/c/test/workloads/SimpleWorkload.cpp index 95be0ddd59..4dd7a800fe 100644 --- a/bindings/c/test/workloads/SimpleWorkload.cpp +++ b/bindings/c/test/workloads/SimpleWorkload.cpp @@ -266,7 +266,7 @@ struct SimpleWorkload final : FDBWorkload { insertsPerTx = context->getOption("insertsPerTx", 100ul); opsPerTx = context->getOption("opsPerTx", 100ul); runFor = context->getOption("runFor", 10.0); - auto err = fdb_select_api_version(720); + auto err = fdb_select_api_version(FDB_API_VERSION); if (err) { context->trace( FDBSeverity::Info, "SelectAPIVersionFailed", { { "Error", std::string(fdb_get_error(err)) } }); diff --git a/bindings/flow/DirectoryLayer.actor.cpp b/bindings/flow/DirectoryLayer.actor.cpp index 056b203a2e..13d3970ed3 100644 --- a/bindings/flow/DirectoryLayer.actor.cpp +++ b/bindings/flow/DirectoryLayer.actor.cpp @@ -23,17 +23,17 @@ namespace FDB { const uint8_t DirectoryLayer::LITTLE_ENDIAN_LONG_ONE[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; -const StringRef DirectoryLayer::HIGH_CONTENTION_KEY = LiteralStringRef("hca"); -const StringRef DirectoryLayer::LAYER_KEY = LiteralStringRef("layer"); -const StringRef DirectoryLayer::VERSION_KEY = LiteralStringRef("version"); +const StringRef DirectoryLayer::HIGH_CONTENTION_KEY = "hca"_sr; +const StringRef DirectoryLayer::LAYER_KEY = "layer"_sr; +const StringRef DirectoryLayer::VERSION_KEY = "version"_sr; const int64_t DirectoryLayer::SUB_DIR_KEY = 0; const uint32_t DirectoryLayer::VERSION[3] = { 1, 0, 0 }; -const StringRef DirectoryLayer::DEFAULT_NODE_SUBSPACE_PREFIX = LiteralStringRef("\xfe"); +const StringRef DirectoryLayer::DEFAULT_NODE_SUBSPACE_PREFIX = "\xfe"_sr; const Subspace DirectoryLayer::DEFAULT_NODE_SUBSPACE = Subspace(DEFAULT_NODE_SUBSPACE_PREFIX); const Subspace DirectoryLayer::DEFAULT_CONTENT_SUBSPACE = Subspace(); -const StringRef DirectoryLayer::PARTITION_LAYER = LiteralStringRef("partition"); +const StringRef DirectoryLayer::PARTITION_LAYER = "partition"_sr; DirectoryLayer::DirectoryLayer(Subspace nodeSubspace, Subspace contentSubspace, bool allowManualPrefixes) : rootNode(nodeSubspace.get(nodeSubspace.key())), nodeSubspace(nodeSubspace), contentSubspace(contentSubspace), diff --git a/bindings/flow/FDBLoanerTypes.h b/bindings/flow/FDBLoanerTypes.h index 01000f6e27..ddd9a577b5 100644 --- a/bindings/flow/FDBLoanerTypes.h +++ b/bindings/flow/FDBLoanerTypes.h @@ -31,7 +31,7 @@ typedef Standalone Key; typedef Standalone Value; inline Key keyAfter(const KeyRef& key) { - if (key == LiteralStringRef("\xff\xff")) + if (key == "\xff\xff"_sr) return key; Standalone r; @@ -43,7 +43,7 @@ inline Key keyAfter(const KeyRef& key) { } inline KeyRef keyAfter(const KeyRef& key, Arena& arena) { - if (key == LiteralStringRef("\xff\xff")) + if (key == "\xff\xff"_sr) return key; uint8_t* t = new (arena) uint8_t[key.size() + 1]; memcpy(t, key.begin(), key.size()); diff --git a/bindings/flow/fdb_flow.actor.cpp b/bindings/flow/fdb_flow.actor.cpp index 72ee49dcf4..d6e1431c77 100644 --- a/bindings/flow/fdb_flow.actor.cpp +++ b/bindings/flow/fdb_flow.actor.cpp @@ -38,7 +38,7 @@ THREAD_FUNC networkThread(void* fdb) { } ACTOR Future _test() { - API* fdb = FDB::API::selectAPIVersion(720); + API* fdb = FDB::API::selectAPIVersion(FDB_API_VERSION); auto db = fdb->createDatabase(); state Reference tr = db->createTransaction(); @@ -63,15 +63,14 @@ ACTOR Future _test() { // wait( waitForAllReady( versions ) ); printf("Elapsed: %lf\n", timer_monotonic() - starttime); - tr->set(LiteralStringRef("foo"), LiteralStringRef("bar")); + tr->set("foo"_sr, "bar"_sr); - Optional> v = wait(tr->get(LiteralStringRef("foo"))); + Optional> v = wait(tr->get("foo"_sr)); if (v.present()) { printf("%s\n", v.get().toString().c_str()); } - FDBStandalone r = - wait(tr->getRange(KeyRangeRef(LiteralStringRef("a"), LiteralStringRef("z")), 100)); + FDBStandalone r = wait(tr->getRange(KeyRangeRef("a"_sr, "z"_sr), 100)); for (auto kv : r) { printf("%s is %s\n", kv.key.toString().c_str(), kv.value.toString().c_str()); @@ -82,7 +81,7 @@ ACTOR Future _test() { } void fdb_flow_test() { - API* fdb = FDB::API::selectAPIVersion(720); + API* fdb = FDB::API::selectAPIVersion(FDB_API_VERSION); fdb->setupNetwork(); startThread(networkThread, fdb); diff --git a/bindings/flow/tester/DirectoryTester.actor.cpp b/bindings/flow/tester/DirectoryTester.actor.cpp index b21da1097c..a8fabdca4c 100644 --- a/bindings/flow/tester/DirectoryTester.actor.cpp +++ b/bindings/flow/tester/DirectoryTester.actor.cpp @@ -545,11 +545,10 @@ struct DirectoryLogDirectoryFunc : InstructionFunc { pathTuple.append(p, true); } - instruction->tr->set(logSubspace.pack(LiteralStringRef("path"), true), pathTuple.pack()); - instruction->tr->set(logSubspace.pack(LiteralStringRef("layer"), true), - Tuple().append(directory->getLayer()).pack()); - instruction->tr->set(logSubspace.pack(LiteralStringRef("exists"), true), Tuple().append(exists ? 1 : 0).pack()); - instruction->tr->set(logSubspace.pack(LiteralStringRef("children"), true), childrenTuple.pack()); + instruction->tr->set(logSubspace.pack("path"_sr, true), pathTuple.pack()); + instruction->tr->set(logSubspace.pack("layer"_sr, true), Tuple().append(directory->getLayer()).pack()); + instruction->tr->set(logSubspace.pack("exists"_sr, true), Tuple().append(exists ? 1 : 0).pack()); + instruction->tr->set(logSubspace.pack("children"_sr, true), childrenTuple.pack()); return Void(); } diff --git a/bindings/flow/tester/Tester.actor.cpp b/bindings/flow/tester/Tester.actor.cpp index 941e1b97b2..f300127e5d 100644 --- a/bindings/flow/tester/Tester.actor.cpp +++ b/bindings/flow/tester/Tester.actor.cpp @@ -470,12 +470,12 @@ ACTOR Future> waitForVoid(Future f) { try { wait(f); Tuple t; - t.append(LiteralStringRef("RESULT_NOT_PRESENT")); + t.append("RESULT_NOT_PRESENT"_sr); return t.pack(); } catch (Error& e) { // printf("FDBError1:%d\n", e.code()); Tuple t; - t.append(LiteralStringRef("ERROR")); + t.append("ERROR"_sr); t.append(format("%d", e.code())); // pack above as error string into another tuple Tuple ret; @@ -493,7 +493,7 @@ ACTOR Future> waitForValue(Future> f } catch (Error& e) { // printf("FDBError2:%d\n", e.code()); Tuple t; - t.append(LiteralStringRef("ERROR")); + t.append("ERROR"_sr); t.append(format("%d", e.code())); // pack above as error string into another tuple Tuple ret; @@ -509,7 +509,7 @@ ACTOR Future> waitForValue(Future> waitForValue(Future> getKey(Future> f, Stan } catch (Error& e) { // printf("FDBError4:%d\n", e.code()); Tuple t; - t.append(LiteralStringRef("ERROR")); + t.append("ERROR"_sr); t.append(format("%d", e.code())); // pack above as error string into another tuple Tuple ret; @@ -670,7 +670,7 @@ struct GetEstimatedRangeSize : InstructionFunc { state Standalone endKey = Tuple::unpack(s2).getString(0); Future fsize = instruction->tr->getEstimatedRangeSizeBytes(KeyRangeRef(beginKey, endKey)); int64_t size = wait(fsize); - data->stack.pushTuple(LiteralStringRef("GOT_ESTIMATED_RANGE_SIZE")); + data->stack.pushTuple("GOT_ESTIMATED_RANGE_SIZE"_sr); return Void(); } @@ -698,7 +698,7 @@ struct GetRangeSplitPoints : InstructionFunc { Future>> fsplitPoints = instruction->tr->getRangeSplitPoints(KeyRangeRef(beginKey, endKey), chunkSize); FDBStandalone> splitPoints = wait(fsplitPoints); - data->stack.pushTuple(LiteralStringRef("GOT_RANGE_SPLIT_POINTS")); + data->stack.pushTuple("GOT_RANGE_SPLIT_POINTS"_sr); return Void(); } @@ -743,7 +743,7 @@ struct GetReadVersionFunc : InstructionFunc { ACTOR static Future call(Reference data, Reference instruction) { Version v = wait(instruction->tr->getReadVersion()); data->lastVersion = v; - data->stack.pushTuple(LiteralStringRef("GOT_READ_VERSION")); + data->stack.pushTuple("GOT_READ_VERSION"_sr); return Void(); } }; @@ -767,7 +767,7 @@ struct GetCommittedVersionFunc : InstructionFunc { static Future call(Reference const& data, Reference const& instruction) { data->lastVersion = instruction->tr->getCommittedVersion(); - data->stack.pushTuple(LiteralStringRef("GOT_COMMITTED_VERSION")); + data->stack.pushTuple("GOT_COMMITTED_VERSION"_sr); return Void(); } }; @@ -781,7 +781,7 @@ struct GetApproximateSizeFunc : InstructionFunc { ACTOR static Future call(Reference data, Reference instruction) { int64_t _ = wait(instruction->tr->getApproximateSize()); (void)_; // disable unused variable warning - data->stack.pushTuple(LiteralStringRef("GOT_APPROXIMATE_SIZE")); + data->stack.pushTuple("GOT_APPROXIMATE_SIZE"_sr); return Void(); } }; @@ -1485,7 +1485,7 @@ struct ReadConflictKeyFunc : InstructionFunc { // printf("=========READ_CONFLICT_KEY:%s\n", printable(key).c_str()); instruction->tr->addReadConflictKey(key); - data->stack.pushTuple(LiteralStringRef("SET_CONFLICT_KEY")); + data->stack.pushTuple("SET_CONFLICT_KEY"_sr); return Void(); } }; @@ -1506,7 +1506,7 @@ struct WriteConflictKeyFunc : InstructionFunc { // printf("=========WRITE_CONFLICT_KEY:%s\n", printable(key).c_str()); instruction->tr->addWriteConflictKey(key); - data->stack.pushTuple(LiteralStringRef("SET_CONFLICT_KEY")); + data->stack.pushTuple("SET_CONFLICT_KEY"_sr); return Void(); } }; @@ -1529,7 +1529,7 @@ struct ReadConflictRangeFunc : InstructionFunc { // printf("=========READ_CONFLICT_RANGE:%s:%s\n", printable(begin).c_str(), printable(end).c_str()); instruction->tr->addReadConflictRange(KeyRange(KeyRangeRef(begin, end))); - data->stack.pushTuple(LiteralStringRef("SET_CONFLICT_RANGE")); + data->stack.pushTuple("SET_CONFLICT_RANGE"_sr); return Void(); } }; @@ -1553,7 +1553,7 @@ struct WriteConflictRangeFunc : InstructionFunc { // printf("=========WRITE_CONFLICT_RANGE:%s:%s\n", printable(begin).c_str(), printable(end).c_str()); instruction->tr->addWriteConflictRange(KeyRange(KeyRangeRef(begin, end))); - data->stack.pushTuple(LiteralStringRef("SET_CONFLICT_RANGE")); + data->stack.pushTuple("SET_CONFLICT_RANGE"_sr); return Void(); } }; @@ -1643,10 +1643,8 @@ struct UnitTestsFunc : InstructionFunc { Optional(StringRef((const uint8_t*)&locationCacheSize, 8))); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_MAX_WATCHES, Optional(StringRef((const uint8_t*)&maxWatches, 8))); - data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_DATACENTER_ID, - Optional(LiteralStringRef("dc_id"))); - data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_MACHINE_ID, - Optional(LiteralStringRef("machine_id"))); + data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_DATACENTER_ID, Optional("dc_id"_sr)); + data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_MACHINE_ID, Optional("machine_id"_sr)); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_SNAPSHOT_RYW_ENABLE); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_SNAPSHOT_RYW_DISABLE); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_TRANSACTION_LOGGING_MAX_FIELD_LENGTH, @@ -1685,13 +1683,13 @@ struct UnitTestsFunc : InstructionFunc { Optional(StringRef((const uint8_t*)&maxRetryDelay, 8))); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_USED_DURING_COMMIT_PROTECTION_DISABLE); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_TRANSACTION_LOGGING_ENABLE, - Optional(LiteralStringRef("my_transaction"))); + Optional("my_transaction"_sr)); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_READ_LOCK_AWARE); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_LOCK_AWARE); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_INCLUDE_PORT_IN_ADDRESS); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_REPORT_CONFLICTING_KEYS); - Optional> _ = wait(tr->get(LiteralStringRef("\xff"))); + Optional> _ = wait(tr->get("\xff"_sr)); tr->cancel(); return Void(); @@ -1724,13 +1722,13 @@ ACTOR static Future doInstructions(Reference data) { Tuple opTuple = Tuple::unpack(data->instructions[idx].value); state Standalone op = opTuple.getString(0); - state bool isDatabase = op.endsWith(LiteralStringRef("_DATABASE")); - state bool isSnapshot = op.endsWith(LiteralStringRef("_SNAPSHOT")); - state bool isDirectory = op.startsWith(LiteralStringRef("DIRECTORY_")); + state bool isDatabase = op.endsWith("_DATABASE"_sr); + state bool isSnapshot = op.endsWith("_SNAPSHOT"_sr); + state bool isDirectory = op.startsWith("DIRECTORY_"_sr); try { if (LOG_INSTRUCTIONS) { - if (op != LiteralStringRef("SWAP") && op != LiteralStringRef("PUSH")) { + if (op != "SWAP"_sr && op != "PUSH"_sr) { printf("%zu. %s\n", idx, tupleToString(opTuple).c_str()); fflush(stdout); } @@ -1773,7 +1771,7 @@ ACTOR static Future doInstructions(Reference data) { if (opsThatCreateDirectories.count(op.toString())) { data->directoryData.directoryList.push_back(DirectoryOrSubspace()); } - data->stack.pushTuple(LiteralStringRef("DIRECTORY_ERROR")); + data->stack.pushTuple("DIRECTORY_ERROR"_sr); } else { data->stack.pushError(e.code()); } @@ -1873,7 +1871,7 @@ ACTOR void _test_versionstamp() { try { g_network = newNet2(TLSConfig()); - API* fdb = FDB::API::selectAPIVersion(720); + API* fdb = FDB::API::selectAPIVersion(FDB_API_VERSION); fdb->setupNetwork(); startThread(networkThread, fdb); @@ -1883,15 +1881,14 @@ ACTOR void _test_versionstamp() { state Future> ftrVersion = tr->getVersionstamp(); - tr->atomicOp(LiteralStringRef("foo"), - LiteralStringRef("blahblahbl\x00\x00\x00\x00"), - FDBMutationType::FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_VALUE); + tr->atomicOp( + "foo"_sr, "blahblahbl\x00\x00\x00\x00"_sr, FDBMutationType::FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_VALUE); wait(tr->commit()); // should use retry loop tr->reset(); - Optional> optionalDbVersion = wait(tr->get(LiteralStringRef("foo"))); + Optional> optionalDbVersion = wait(tr->get("foo"_sr)); state FDBStandalone dbVersion = optionalDbVersion.get(); FDBStandalone trVersion = wait(ftrVersion); diff --git a/bindings/flow/tester/Tester.actor.h b/bindings/flow/tester/Tester.actor.h index 63fc9fe9a3..f42f8d74ee 100644 --- a/bindings/flow/tester/Tester.actor.h +++ b/bindings/flow/tester/Tester.actor.h @@ -71,7 +71,7 @@ struct FlowTesterStack { void pushError(int errorCode) { FDB::Tuple t; - t.append(LiteralStringRef("ERROR")); + t.append("ERROR"_sr); t.append(format("%d", errorCode)); // pack above as error string into another tuple pushTuple(t.pack().toString()); diff --git a/bindings/go/src/fdb/fdb.go b/bindings/go/src/fdb/fdb.go index 7800dd9bf5..e308049be0 100644 --- a/bindings/go/src/fdb/fdb.go +++ b/bindings/go/src/fdb/fdb.go @@ -128,7 +128,7 @@ func APIVersion(version int) error { return errAPIVersionAlreadySet } - if version < 200 || version > 720 { + if version < 200 || version > headerVersion { return errAPIVersionNotSupported } diff --git a/bindings/go/src/fdb/fdb_test.go b/bindings/go/src/fdb/fdb_test.go index 976a3ec9d0..00b3f41304 100644 --- a/bindings/go/src/fdb/fdb_test.go +++ b/bindings/go/src/fdb/fdb_test.go @@ -29,10 +29,12 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb" ) +const API_VERSION int = 720 + func ExampleOpenDefault() { var e error - e = fdb.APIVersion(720) + e = fdb.APIVersion(API_VERSION) if e != nil { fmt.Printf("Unable to set API version: %v\n", e) return @@ -52,7 +54,7 @@ func ExampleOpenDefault() { } func TestVersionstamp(t *testing.T) { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() setVs := func(t fdb.Transactor, key fdb.Key) (fdb.FutureKey, error) { @@ -98,7 +100,7 @@ func TestVersionstamp(t *testing.T) { } func TestReadTransactionOptions(t *testing.T) { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() _, e := db.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { rtr.Options().SetAccessSystemKeys() @@ -110,7 +112,7 @@ func TestReadTransactionOptions(t *testing.T) { } func ExampleTransactor() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() setOne := func(t fdb.Transactor, key fdb.Key, value []byte) error { @@ -161,7 +163,7 @@ func ExampleTransactor() { } func ExampleReadTransactor() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() getOne := func(rt fdb.ReadTransactor, key fdb.Key) ([]byte, error) { @@ -214,7 +216,7 @@ func ExampleReadTransactor() { } func ExamplePrefixRange() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() tr, e := db.CreateTransaction() @@ -253,7 +255,7 @@ func ExamplePrefixRange() { } func ExampleRangeIterator() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() tr, e := db.CreateTransaction() diff --git a/bindings/java/JavaWorkload.cpp b/bindings/java/JavaWorkload.cpp index e9bf309fa4..6c65313f4b 100644 --- a/bindings/java/JavaWorkload.cpp +++ b/bindings/java/JavaWorkload.cpp @@ -379,7 +379,7 @@ struct JVM { jmethodID selectMethod = env->GetStaticMethodID(fdbClass, "selectAPIVersion", "(I)Lcom/apple/foundationdb/FDB;"); checkException(); - auto fdbInstance = env->CallStaticObjectMethod(fdbClass, selectMethod, jint(720)); + auto fdbInstance = env->CallStaticObjectMethod(fdbClass, selectMethod, jint(FDB_API_VERSION)); checkException(); env->CallObjectMethod(fdbInstance, getMethod(fdbClass, "disableShutdownHook", "()V")); checkException(); diff --git a/bindings/java/src/integration/com/apple/foundationdb/CycleMultiClientIntegrationTest.java b/bindings/java/src/integration/com/apple/foundationdb/CycleMultiClientIntegrationTest.java index 5087361c43..80afa6c761 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/CycleMultiClientIntegrationTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/CycleMultiClientIntegrationTest.java @@ -40,6 +40,8 @@ import org.junit.jupiter.api.Assertions; * This test is to verify the atomicity of transactions. */ public class CycleMultiClientIntegrationTest { + public static final int API_VERSION = 720; + public static final MultiClientHelper clientHelper = new MultiClientHelper(); // more write txn than validate txn, as parent thread waits only for validate txn. @@ -51,7 +53,7 @@ public class CycleMultiClientIntegrationTest { private static List expected = new ArrayList<>(Arrays.asList("0", "1", "2", "3")); public static void main(String[] args) throws Exception { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); setupThreads(fdb); Collection dbs = clientHelper.openDatabases(fdb); // the clientHelper will close the databases for us System.out.println("Starting tests"); diff --git a/bindings/java/src/integration/com/apple/foundationdb/DirectoryTest.java b/bindings/java/src/integration/com/apple/foundationdb/DirectoryTest.java index 59fbc3fe55..b91c9e7de3 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/DirectoryTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/DirectoryTest.java @@ -40,7 +40,8 @@ import org.junit.jupiter.api.extension.ExtendWith; */ @ExtendWith(RequiresDatabase.class) class DirectoryTest { - private static final FDB fdb = FDB.selectAPIVersion(720); + public static final int API_VERSION = 720; + private static final FDB fdb = FDB.selectAPIVersion(API_VERSION); @Test void testCanCreateDirectory() throws Exception { diff --git a/bindings/java/src/integration/com/apple/foundationdb/MappedRangeQueryIntegrationTest.java b/bindings/java/src/integration/com/apple/foundationdb/MappedRangeQueryIntegrationTest.java index 063e9e276d..3aedef4d1e 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/MappedRangeQueryIntegrationTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/MappedRangeQueryIntegrationTest.java @@ -41,7 +41,8 @@ import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(RequiresDatabase.class) class MappedRangeQueryIntegrationTest { - private static final FDB fdb = FDB.selectAPIVersion(720); + public static final int API_VERSION = 720; + private static final FDB fdb = FDB.selectAPIVersion(API_VERSION); public String databaseArg = null; private Database openFDB() { return fdb.open(databaseArg); } @@ -110,7 +111,7 @@ class MappedRangeQueryIntegrationTest { boolean validate = true; @Test void comparePerformance() { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try (Database db = openFDB()) { insertRecordsWithIndexes(numRecords, db); instrument(rangeQueryAndThenRangeQueries, "rangeQueryAndThenRangeQueries", db); diff --git a/bindings/java/src/integration/com/apple/foundationdb/RangeQueryIntegrationTest.java b/bindings/java/src/integration/com/apple/foundationdb/RangeQueryIntegrationTest.java index bc64877199..fb6d3afd9f 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/RangeQueryIntegrationTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/RangeQueryIntegrationTest.java @@ -41,7 +41,8 @@ import org.junit.jupiter.api.extension.ExtendWith; */ @ExtendWith(RequiresDatabase.class) class RangeQueryIntegrationTest { - private static final FDB fdb = FDB.selectAPIVersion(720); + public static final int API_VERSION = 720; + private static final FDB fdb = FDB.selectAPIVersion(API_VERSION); @BeforeEach @AfterEach diff --git a/bindings/java/src/integration/com/apple/foundationdb/RepeatableReadMultiThreadClientTest.java b/bindings/java/src/integration/com/apple/foundationdb/RepeatableReadMultiThreadClientTest.java index c11940d41a..ab8ab1364a 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/RepeatableReadMultiThreadClientTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/RepeatableReadMultiThreadClientTest.java @@ -41,6 +41,8 @@ import org.junit.jupiter.api.Assertions; * are still seeting the initialValue even after new transactions set them to a new value. */ public class RepeatableReadMultiThreadClientTest { + public static final int API_VERSION = 720; + public static final MultiClientHelper clientHelper = new MultiClientHelper(); private static final int oldValueReadCount = 30; @@ -52,7 +54,7 @@ public class RepeatableReadMultiThreadClientTest { private static final Map threadToOldValueReaders = new HashMap<>(); public static void main(String[] args) throws Exception { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); setupThreads(fdb); Collection dbs = clientHelper.openDatabases(fdb); // the clientHelper will close the databases for us System.out.println("Starting tests"); diff --git a/bindings/java/src/integration/com/apple/foundationdb/RequiresDatabase.java b/bindings/java/src/integration/com/apple/foundationdb/RequiresDatabase.java index 785008902c..ead1f499c1 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/RequiresDatabase.java +++ b/bindings/java/src/integration/com/apple/foundationdb/RequiresDatabase.java @@ -47,6 +47,7 @@ import org.opentest4j.TestAbortedException; * be running a server and you don't want to deal with spurious test failures. */ public class RequiresDatabase implements ExecutionCondition, BeforeAllCallback { + public static final int API_VERSION = 720; public static boolean canRunIntegrationTest() { String prop = System.getProperty("run.integration.tests"); @@ -80,7 +81,7 @@ public class RequiresDatabase implements ExecutionCondition, BeforeAllCallback { * assume that if we are here, then canRunIntegrationTest() is returning true and we don't have to bother * checking it. */ - try (Database db = FDB.selectAPIVersion(720).open()) { + try (Database db = FDB.selectAPIVersion(API_VERSION).open()) { db.run(tr -> { CompletableFuture future = tr.get("test".getBytes()); diff --git a/bindings/java/src/integration/com/apple/foundationdb/SidebandMultiThreadClientTest.java b/bindings/java/src/integration/com/apple/foundationdb/SidebandMultiThreadClientTest.java index 4a4736d566..30f86632eb 100644 --- a/bindings/java/src/integration/com/apple/foundationdb/SidebandMultiThreadClientTest.java +++ b/bindings/java/src/integration/com/apple/foundationdb/SidebandMultiThreadClientTest.java @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Assertions; * This test is to verify the causal consistency of transactions for mutli-threaded client. */ public class SidebandMultiThreadClientTest { + public static final int API_VERSION = 720; + public static final MultiClientHelper clientHelper = new MultiClientHelper(); private static final Map> db2Queues = new HashMap<>(); @@ -26,7 +28,7 @@ public class SidebandMultiThreadClientTest { private static final int txnCnt = 1000; public static void main(String[] args) throws Exception { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); setupThreads(fdb); Collection dbs = clientHelper.openDatabases(fdb); // the clientHelper will close the databases for us for (Database db : dbs) { diff --git a/bindings/java/src/junit/com/apple/foundationdb/FDBLibraryRule.java b/bindings/java/src/junit/com/apple/foundationdb/FDBLibraryRule.java index 6e53b179e5..fc54f6c333 100644 --- a/bindings/java/src/junit/com/apple/foundationdb/FDBLibraryRule.java +++ b/bindings/java/src/junit/com/apple/foundationdb/FDBLibraryRule.java @@ -29,6 +29,8 @@ import org.junit.jupiter.api.extension.ExtensionContext; * are not available for any reason. */ public class FDBLibraryRule implements BeforeAllCallback { + public static final int CURRENT_API_VERSION = 720; + private final int apiVersion; // because FDB is a singleton (currently), this isn't a super-useful cache, @@ -37,7 +39,7 @@ public class FDBLibraryRule implements BeforeAllCallback { public FDBLibraryRule(int apiVersion) { this.apiVersion = apiVersion; } - public static FDBLibraryRule current() { return new FDBLibraryRule(720); } + public static FDBLibraryRule current() { return new FDBLibraryRule(CURRENT_API_VERSION); } public static FDBLibraryRule v63() { return new FDBLibraryRule(630); } diff --git a/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java b/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java index a376db79a3..58f223fa4b 100644 --- a/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java +++ b/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java @@ -52,10 +52,6 @@ public class TenantManagement { * @param tenantName The name of the tenant. Can be any byte string that does not begin a 0xFF byte. */ public static void createTenant(Transaction tr, byte[] tenantName) { - if (FDB.instance().getAPIVersion() < 720) { - tr.options().setSpecialKeySpaceRelaxed(); - } - tr.options().setSpecialKeySpaceEnableWrites(); tr.set(ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName), new byte[0]); } @@ -90,7 +86,6 @@ public class TenantManagement { final AtomicBoolean checkedExistence = new AtomicBoolean(false); final byte[] key = ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName); return db.runAsync(tr -> { - tr.options().setSpecialKeySpaceRelaxed(); tr.options().setSpecialKeySpaceEnableWrites(); if(checkedExistence.get()) { tr.set(key, new byte[0]); @@ -138,10 +133,6 @@ public class TenantManagement { * @param tenantName The name of the tenant being deleted. */ public static void deleteTenant(Transaction tr, byte[] tenantName) { - if (FDB.instance().getAPIVersion() < 720) { - tr.options().setSpecialKeySpaceRelaxed(); - } - tr.options().setSpecialKeySpaceEnableWrites(); tr.clear(ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName)); } @@ -182,7 +173,6 @@ public class TenantManagement { final AtomicBoolean checkedExistence = new AtomicBoolean(false); final byte[] key = ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName); return db.runAsync(tr -> { - tr.options().setSpecialKeySpaceRelaxed(); tr.options().setSpecialKeySpaceEnableWrites(); if(checkedExistence.get()) { tr.clear(key); @@ -248,12 +238,7 @@ public class TenantManagement { * and the value is the unprocessed JSON string containing the tenant's metadata */ public static CloseableAsyncIterator listTenants(Database db, Tuple begin, Tuple end, int limit) { - Transaction tr = db.createTransaction(); - if (FDB.instance().getAPIVersion() < 720) { - tr.options().setSpecialKeySpaceRelaxed(); - } - - return listTenants_internal(tr, begin.pack(), end.pack(), limit); + return listTenants_internal(db.createTransaction(), begin.pack(), end.pack(), limit); } private static CloseableAsyncIterator listTenants_internal(Transaction tr, byte[] begin, byte[] end, diff --git a/bindings/java/src/main/overview.html.in b/bindings/java/src/main/overview.html.in index 3154efbfc3..a37a8859f9 100644 --- a/bindings/java/src/main/overview.html.in +++ b/bindings/java/src/main/overview.html.in @@ -28,8 +28,10 @@ import com.apple.foundationdb.FDB; import com.apple.foundationdb.tuple.Tuple; public class Example { + public static final int apiVersion = 720; + public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(apiVersion); try(Database db = fdb.open()) { // Run an operation on the database diff --git a/bindings/java/src/test/com/apple/foundationdb/test/BlockingBenchmark.java b/bindings/java/src/test/com/apple/foundationdb/test/BlockingBenchmark.java index a1d7a4d976..425c5d2369 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/BlockingBenchmark.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/BlockingBenchmark.java @@ -29,11 +29,13 @@ import com.apple.foundationdb.FDB; import com.apple.foundationdb.Transaction; public class BlockingBenchmark { + public static final int API_VERSION = 720; + private static final int REPS = 100000; private static final int PARALLEL = 100; public static void main(String[] args) throws InterruptedException { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); // The cluster file DOES NOT need to be valid, although it must exist. // This is because the database is never really contacted in this test. diff --git a/bindings/java/src/test/com/apple/foundationdb/test/ConcurrentGetSetGet.java b/bindings/java/src/test/com/apple/foundationdb/test/ConcurrentGetSetGet.java index 38be19a60f..0eabef64c0 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/ConcurrentGetSetGet.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/ConcurrentGetSetGet.java @@ -30,6 +30,8 @@ import com.apple.foundationdb.Database; import com.apple.foundationdb.FDB; public class ConcurrentGetSetGet { + public static final int API_VERSION = 720; + public static final Charset UTF8 = Charset.forName("UTF-8"); final Semaphore semaphore = new Semaphore(CONCURRENCY); @@ -48,7 +50,7 @@ public class ConcurrentGetSetGet { } public static void main(String[] args) { - try(Database database = FDB.selectAPIVersion(720).open()) { + try(Database database = FDB.selectAPIVersion(API_VERSION).open()) { new ConcurrentGetSetGet().apply(database); } } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/Example.java b/bindings/java/src/test/com/apple/foundationdb/test/Example.java index da5bbfdc2a..d7f1336d51 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/Example.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/Example.java @@ -25,8 +25,10 @@ import com.apple.foundationdb.FDB; import com.apple.foundationdb.tuple.Tuple; public class Example { + public static final int API_VERSION = 720; + public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { // Run an operation on the database diff --git a/bindings/java/src/test/com/apple/foundationdb/test/IterableTest.java b/bindings/java/src/test/com/apple/foundationdb/test/IterableTest.java index 35adfa5e1f..2af2152cf3 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/IterableTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/IterableTest.java @@ -28,10 +28,12 @@ import com.apple.foundationdb.KeyValue; import com.apple.foundationdb.TransactionContext; public class IterableTest { + public static final int API_VERSION = 720; + public static void main(String[] args) throws InterruptedException { final int reps = 1000; try { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { runTests(reps, db); } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/LocalityTests.java b/bindings/java/src/test/com/apple/foundationdb/test/LocalityTests.java index 6410165f27..969b6c75e3 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/LocalityTests.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/LocalityTests.java @@ -32,9 +32,10 @@ import com.apple.foundationdb.async.AsyncUtil; import com.apple.foundationdb.tuple.ByteArrayUtil; public class LocalityTests { + public static final int API_VERSION = 720; public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database database = fdb.open(args[0])) { try(Transaction tr = database.createTransaction()) { String[] keyAddresses = LocalityUtil.getAddressesForKey(tr, "a".getBytes()).join(); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/ParallelRandomScan.java b/bindings/java/src/test/com/apple/foundationdb/test/ParallelRandomScan.java index 6518116324..e5e0c9f9e6 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/ParallelRandomScan.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/ParallelRandomScan.java @@ -36,6 +36,8 @@ import com.apple.foundationdb.async.AsyncIterator; import com.apple.foundationdb.tuple.ByteArrayUtil; public class ParallelRandomScan { + public static final int API_VERSION = 720; + private static final int ROWS = 1000000; private static final int DURATION_MS = 2000; private static final int PARALLELISM_MIN = 10; @@ -43,7 +45,7 @@ public class ParallelRandomScan { private static final int PARALLELISM_STEP = 5; public static void main(String[] args) throws InterruptedException { - FDB api = FDB.selectAPIVersion(720); + FDB api = FDB.selectAPIVersion(API_VERSION); try(Database database = api.open(args[0])) { for(int i = PARALLELISM_MIN; i <= PARALLELISM_MAX; i += PARALLELISM_STEP) { runTest(database, i, ROWS, DURATION_MS); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/SerialInsertion.java b/bindings/java/src/test/com/apple/foundationdb/test/SerialInsertion.java index d847556cdc..e11f8b9793 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/SerialInsertion.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/SerialInsertion.java @@ -29,12 +29,14 @@ import com.apple.foundationdb.FDB; import com.apple.foundationdb.Transaction; public class SerialInsertion { + public static final int API_VERSION = 720; + private static final int THREAD_COUNT = 10; private static final int BATCH_SIZE = 1000; private static final int NODES = 1000000; public static void main(String[] args) { - FDB api = FDB.selectAPIVersion(720); + FDB api = FDB.selectAPIVersion(API_VERSION); try(Database database = api.open()) { long start = System.currentTimeMillis(); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/SerialIteration.java b/bindings/java/src/test/com/apple/foundationdb/test/SerialIteration.java index 6e262e561f..f55af41c35 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/SerialIteration.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/SerialIteration.java @@ -34,12 +34,14 @@ import com.apple.foundationdb.Transaction; import com.apple.foundationdb.async.AsyncIterable; public class SerialIteration { + public static final int API_VERSION = 720; + private static final int ROWS = 1000000; private static final int RUNS = 25; private static final int THREAD_COUNT = 1; public static void main(String[] args) throws InterruptedException { - FDB api = FDB.selectAPIVersion(720); + FDB api = FDB.selectAPIVersion(API_VERSION); try(Database database = api.open(args[0])) { for(int i = 1; i <= THREAD_COUNT; i++) { runThreadedTest(database, i); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/SerialTest.java b/bindings/java/src/test/com/apple/foundationdb/test/SerialTest.java index 9313543d02..ea3210e2de 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/SerialTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/SerialTest.java @@ -27,10 +27,12 @@ import com.apple.foundationdb.FDB; import com.apple.foundationdb.TransactionContext; public class SerialTest { + public static final int API_VERSION = 720; + public static void main(String[] args) throws InterruptedException { final int reps = 1000; try { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { runTests(reps, db); } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/SnapshotTransactionTest.java b/bindings/java/src/test/com/apple/foundationdb/test/SnapshotTransactionTest.java index 1f3aec5501..6fdee20cad 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/SnapshotTransactionTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/SnapshotTransactionTest.java @@ -35,11 +35,13 @@ import com.apple.foundationdb.tuple.Tuple; * Some tests regarding conflict ranges to make sure they do what we expect. */ public class SnapshotTransactionTest { + public static final int API_VERSION = 720; + private static final int CONFLICT_CODE = 1020; private static final Subspace SUBSPACE = new Subspace(Tuple.from("test", "conflict_ranges")); public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { snapshotReadShouldNotConflict(db); snapshotShouldNotAddConflictRange(db); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/TupleTest.java b/bindings/java/src/test/com/apple/foundationdb/test/TupleTest.java index 4fa45f7cbe..2ce8e76343 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/TupleTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/TupleTest.java @@ -32,12 +32,14 @@ import com.apple.foundationdb.tuple.Tuple; import com.apple.foundationdb.tuple.Versionstamp; public class TupleTest { + public static final int API_VERSION = 720; + private static final byte FF = (byte)0xff; public static void main(String[] args) throws NoSuchFieldException { final int reps = 1000; try { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { runTests(reps, db); } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java b/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java index b39744dd32..421db6c542 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java @@ -31,8 +31,10 @@ import com.apple.foundationdb.tuple.Tuple; import com.apple.foundationdb.tuple.Versionstamp; public class VersionstampSmokeTest { + public static final int API_VERSION = 720; + public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database db = fdb.open()) { db.run(tr -> { tr.clear(Tuple.from("prefix").range()); diff --git a/bindings/java/src/test/com/apple/foundationdb/test/WatchTest.java b/bindings/java/src/test/com/apple/foundationdb/test/WatchTest.java index 29e05db04e..ef8db81fd4 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/WatchTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/WatchTest.java @@ -32,9 +32,10 @@ import com.apple.foundationdb.FDBException; import com.apple.foundationdb.Transaction; public class WatchTest { + public static final int API_VERSION = 720; public static void main(String[] args) { - FDB fdb = FDB.selectAPIVersion(720); + FDB fdb = FDB.selectAPIVersion(API_VERSION); try(Database database = fdb.open(args[0])) { database.options().setLocationCacheSize(42); try(Transaction tr = database.createTransaction()) { diff --git a/bindings/python/fdb/tenant_management.py b/bindings/python/fdb/tenant_management.py index 5b67b798fa..ebe36594a5 100644 --- a/bindings/python/fdb/tenant_management.py +++ b/bindings/python/fdb/tenant_management.py @@ -23,7 +23,6 @@ """Documentation for this API can be found at https://apple.github.io/foundationdb/api-python.html""" -import fdb from fdb import impl as _impl _tenant_map_prefix = b'\xff\xff/management/tenant/map/' @@ -53,9 +52,6 @@ def _check_tenant_existence(tr, key, existence_check_marker, force_maybe_commite # If the existence_check_marker is a non-empty list, then the existence check is skipped. @_impl.transactional def _create_tenant_impl(tr, tenant_name, existence_check_marker, force_existence_check_maybe_committed=False): - if fdb._version < 720: - tr.options.set_special_key_space_relaxed() - tr.options.set_special_key_space_enable_writes() key = b'%s%s' % (_tenant_map_prefix, tenant_name) @@ -74,9 +70,6 @@ def _create_tenant_impl(tr, tenant_name, existence_check_marker, force_existence # If the existence_check_marker is a non-empty list, then the existence check is skipped. @_impl.transactional def _delete_tenant_impl(tr, tenant_name, existence_check_marker, force_existence_check_maybe_committed=False): - if fdb._version < 720: - tr.options.set_special_key_space_relaxed() - tr.options.set_special_key_space_enable_writes() key = b'%s%s' % (_tenant_map_prefix, tenant_name) @@ -110,9 +103,6 @@ class FDBTenantList(object): # JSON strings of the tenant metadata @_impl.transactional def _list_tenants_impl(tr, begin, end, limit): - if fdb._version < 720: - tr.options.set_special_key_space_relaxed() - tr.options.set_raw_access() begin_key = b'%s%s' % (_tenant_map_prefix, begin) end_key = b'%s%s' % (_tenant_map_prefix, end) diff --git a/cmake/CompileBoost.cmake b/cmake/CompileBoost.cmake index de5560439e..5b87dafbd2 100644 --- a/cmake/CompileBoost.cmake +++ b/cmake/CompileBoost.cmake @@ -36,7 +36,7 @@ function(compile_boost) set(B2_COMMAND "./b2") set(BOOST_COMPILER_FLAGS -fvisibility=hidden -fPIC -std=c++17 -w) set(BOOST_LINK_FLAGS "") - if(APPLE OR CLANG OR ICX OR USE_LIBCXX) + if(APPLE OR ICX OR USE_LIBCXX) list(APPEND BOOST_COMPILER_FLAGS -stdlib=libc++ -nostdlib++) list(APPEND BOOST_LINK_FLAGS -lc++ -lc++abi) if (NOT APPLE) @@ -133,7 +133,7 @@ if(WIN32) return() endif() -find_package(Boost 1.78.0 EXACT QUIET COMPONENTS context filesystem CONFIG PATHS ${BOOST_HINT_PATHS}) +find_package(Boost 1.78.0 EXACT QUIET COMPONENTS context filesystem iostreams CONFIG PATHS ${BOOST_HINT_PATHS}) set(FORCE_BOOST_BUILD OFF CACHE BOOL "Forces cmake to build boost and ignores any installed boost") if(Boost_FOUND AND Boost_filesystem_FOUND AND Boost_context_FOUND AND Boost_iostreams_FOUND AND NOT FORCE_BOOST_BUILD) diff --git a/cmake/CompileRocksDB.cmake b/cmake/CompileRocksDB.cmake index 4634e57e7c..3a67e0b017 100644 --- a/cmake/CompileRocksDB.cmake +++ b/cmake/CompileRocksDB.cmake @@ -12,6 +12,7 @@ if (RocksDB_FOUND) -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DFAIL_ON_WARNINGS=OFF -DWITH_GFLAGS=OFF -DWITH_TESTS=OFF -DWITH_TOOLS=OFF @@ -43,6 +44,7 @@ else() -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DFAIL_ON_WARNINGS=OFF -DWITH_GFLAGS=OFF -DWITH_TESTS=OFF -DWITH_TOOLS=OFF diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index d753cf394d..85084682f4 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -290,6 +290,10 @@ else() add_link_options(-stdlib=libc++ -Wl,-build-id=sha1) endif() endif() + if (NOT APPLE AND NOT USE_LIBCXX) + message(STATUS "Linking libatomic") + add_link_options(-latomic) + endif() if (OPEN_FOR_IDE) add_compile_options( -Wno-unknown-attributes) @@ -307,11 +311,19 @@ else() -Wno-unknown-warning-option -Wno-unused-parameter -Wno-constant-logical-operand + # These need to be disabled for FDB's RocksDB storage server implementation + -Wno-deprecated-copy + -Wno-delete-non-abstract-non-virtual-dtor + -Wno-range-loop-construct + -Wno-reorder-ctor + # Needed for clang 13 (todo: Update above logic so that it figures out when to pass in -static-libstdc++ and when it will be ignored) + # When you remove this, you might need to move it back to the USE_CCACHE stanza. It was (only) there before I moved it here. + -Wno-unused-command-line-argument ) if (USE_CCACHE) add_compile_options( -Wno-register - -Wno-unused-command-line-argument) + ) endif() if (PROFILE_INSTR_GENERATE) add_compile_options(-fprofile-instr-generate) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 02f0aa2a16..208ac2c3e3 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -178,7 +178,7 @@ set(PORTABLE_ROCKSDB ON CACHE BOOL "Compile RocksDB in portable mode") # Set thi set(WITH_LIBURING OFF CACHE BOOL "Build with liburing enabled") # Set this to ON to include liburing # RocksDB is currently enabled by default for GCC but does not build with the latest # Clang. -if (SSD_ROCKSDB_EXPERIMENTAL AND GCC) +if (SSD_ROCKSDB_EXPERIMENTAL AND NOT WIN32) set(WITH_ROCKSDB_EXPERIMENTAL ON) else() set(WITH_ROCKSDB_EXPERIMENTAL OFF) @@ -200,6 +200,9 @@ else() URL "https://github.com/ToruNiina/toml11/archive/v3.4.0.tar.gz" URL_HASH SHA256=bc6d733efd9216af8c119d8ac64a805578c79cc82b813e4d1d880ca128bd154d CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/toml11 -Dtoml11_BUILD_TEST:BOOL=OFF BUILD_ALWAYS ON) diff --git a/cmake/Jemalloc.cmake b/cmake/Jemalloc.cmake index bfdd2f5898..8d04ebccca 100644 --- a/cmake/Jemalloc.cmake +++ b/cmake/Jemalloc.cmake @@ -14,7 +14,7 @@ ExternalProject_add(Jemalloc_project BUILD_BYPRODUCTS "${JEMALLOC_DIR}/include/jemalloc/jemalloc.h" "${JEMALLOC_DIR}/lib/libjemalloc.a" "${JEMALLOC_DIR}/lib/libjemalloc_pic.a" - CONFIGURE_COMMAND ./configure --prefix=${JEMALLOC_DIR} --enable-static --disable-cxx --enable-prof + CONFIGURE_COMMAND CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ./configure --prefix=${JEMALLOC_DIR} --enable-static --disable-cxx --enable-prof BUILD_IN_SOURCE ON BUILD_COMMAND make INSTALL_DIR "${JEMALLOC_DIR}" @@ -24,4 +24,4 @@ add_dependencies(im_jemalloc_pic Jemalloc_project) set_target_properties(im_jemalloc_pic PROPERTIES IMPORTED_LOCATION "${JEMALLOC_DIR}/lib/libjemalloc_pic.a") set_target_properties(im_jemalloc PROPERTIES IMPORTED_LOCATION "${JEMALLOC_DIR}/lib/libjemalloc.a") target_include_directories(jemalloc INTERFACE "${JEMALLOC_DIR}/include") -target_link_libraries(jemalloc INTERFACE im_jemalloc_pic im_jemalloc) \ No newline at end of file +target_link_libraries(jemalloc INTERFACE im_jemalloc_pic im_jemalloc) diff --git a/contrib/TestHarness2/test_harness/config.py b/contrib/TestHarness2/test_harness/config.py index b814d5429d..191fab629d 100644 --- a/contrib/TestHarness2/test_harness/config.py +++ b/contrib/TestHarness2/test_harness/config.py @@ -139,6 +139,9 @@ class Config: self.max_errors_args = {'short_name': 'E'} self.old_binaries_path: Path = Path('/app/deploy/global_data/oldBinaries/') self.old_binaries_path_args = {'help': 'Path to the directory containing the old fdb binaries'} + self.tls_plugin_path: Path = Path('/app/deploy/runtime/.tls_5_1/FDBLibTLS.so') + self.tls_plugin_path_args = {'help': 'Path to the tls plugin used for binaries < 5.2.0'} + self.disable_kaio: bool = False self.use_valgrind: bool = False self.use_valgrind_args = {'action': 'store_true'} self.buggify = BuggifyOption('random') diff --git a/contrib/TestHarness2/test_harness/run.py b/contrib/TestHarness2/test_harness/run.py index c5e948eb6d..f144ded0da 100644 --- a/contrib/TestHarness2/test_harness/run.py +++ b/contrib/TestHarness2/test_harness/run.py @@ -18,7 +18,7 @@ from functools import total_ordering from pathlib import Path from test_harness.version import Version from test_harness.config import config -from typing import List, Pattern, OrderedDict +from typing import Dict, List, Pattern, OrderedDict from test_harness.summarize import Summary, SummaryTree @@ -309,6 +309,7 @@ class TestRun: self.trace_format: str | None = config.trace_format if Version.of_binary(self.binary) < "6.1.0": self.trace_format = None + self.use_tls_plugin = Version.of_binary(self.binary) < "5.2.0" self.temp_path = config.run_dir / str(self.uid) # state for the run self.retryable_error: bool = False @@ -332,6 +333,7 @@ class TestRun: def run(self): command: List[str] = [] + env: Dict[str, str] = os.environ.copy() valgrind_file: Path | None = None if self.use_valgrind: command.append('valgrind') @@ -346,6 +348,11 @@ class TestRun: '-s', str(self.random_seed)] if self.trace_format is not None: command += ['--trace_format', self.trace_format] + if self.use_tls_plugin: + command += ['--tls_plugin', str(config.tls_plugin_path)] + env["FDB_TLS_PLUGIN"] = str(config.tls_plugin_path) + if config.disable_kaio: + command += ['--knob-disable-posix-kernel-aio=1'] if Version.of_binary(self.binary) >= '7.1.0': command += ['-fi', 'on' if self.fault_injection_enabled else 'off'] if self.restarting: @@ -361,7 +368,7 @@ class TestRun: resources = ResourceMonitor() resources.start() process = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, cwd=self.temp_path, - text=True) + text=True, env=env) did_kill = False timeout = 20 * config.kill_seconds if self.use_valgrind else config.kill_seconds err_out: str diff --git a/design/special-key-space.md b/design/special-key-space.md index 7cdcfe460d..be104915fe 100644 --- a/design/special-key-space.md +++ b/design/special-key-space.md @@ -32,10 +32,10 @@ public: explicit SKRExampleImpl(KeyRangeRef kr): SpecialKeyRangeReadImpl(kr) { // Our implementation is quite simple here, the key-value pairs are formatted as: // \xff\xff/example/ : - CountryToCapitalCity[LiteralStringRef("USA")] = LiteralStringRef("Washington, D.C."); - CountryToCapitalCity[LiteralStringRef("UK")] = LiteralStringRef("London"); - CountryToCapitalCity[LiteralStringRef("Japan")] = LiteralStringRef("Tokyo"); - CountryToCapitalCity[LiteralStringRef("China")] = LiteralStringRef("Beijing"); + CountryToCapitalCity["USA"_sr] = "Washington, D.C."_sr; + CountryToCapitalCity["UK"_sr] = "London"_sr; + CountryToCapitalCity["Japan"_sr] = "Tokyo"_sr; + CountryToCapitalCity["China"_sr] = "Beijing"_sr; } // Implement the getRange interface Future getRange(ReadYourWritesTransaction* ryw, @@ -58,7 +58,7 @@ private: }; // Instantiate the function object // In development, you should have a function object pointer in DatabaseContext(DatabaseContext.h) and initialize in DatabaseContext's constructor(NativeAPI.actor.cpp) -const KeyRangeRef exampleRange(LiteralStringRef("\xff\xff/example/"), LiteralStringRef("\xff\xff/example/\xff")); +const KeyRangeRef exampleRange("\xff\xff/example/"_sr, "\xff\xff/example/\xff"_sr); SKRExampleImpl exampleImpl(exampleRange); // Assuming the database handler is `cx`, register to special-key-space // In development, you should register all function objects in the constructor of DatabaseContext(NativeAPI.actor.cpp) @@ -67,16 +67,16 @@ cx->specialKeySpace->registerKeyRange(exampleRange, &exampleImpl); state ReadYourWritesTransaction tr(cx); // get Optional res1 = wait(tr.get("\xff\xff/example/Japan")); -ASSERT(res1.present() && res.getValue() == LiteralStringRef("Tokyo")); +ASSERT(res1.present() && res.getValue() == "Tokyo"_sr); // getRange // Note: for getRange(key1, key2), both key1 and key2 should prefixed with \xff\xff // something like getRange("normal_key", "\xff\xff/...") is not supported yet -RangeResult res2 = wait(tr.getRange(LiteralStringRef("\xff\xff/example/U"), LiteralStringRef("\xff\xff/example/U\xff"))); +RangeResult res2 = wait(tr.getRange("\xff\xff/example/U"_sr, "\xff\xff/example/U\xff"_sr)); // res2 should contain USA and UK ASSERT( res2.size() == 2 && - res2[0].value == LiteralStringRef("London") && - res2[1].value == LiteralStringRef("Washington, D.C.") + res2[0].value == "London"_sr && + res2[1].value == "Washington, D.C."_sr ); ``` diff --git a/documentation/sphinx/source/administration.rst b/documentation/sphinx/source/administration.rst index 424d9a6f2a..018ffd66c5 100644 --- a/documentation/sphinx/source/administration.rst +++ b/documentation/sphinx/source/administration.rst @@ -12,6 +12,7 @@ Administration configuration moving-a-cluster tls + authorization This document covers the administration of an existing FoundationDB cluster. We recommend you read this document before setting up a cluster for performance testing or production use. diff --git a/documentation/sphinx/source/authorization.rst b/documentation/sphinx/source/authorization.rst new file mode 100644 index 0000000000..ce25df1057 --- /dev/null +++ b/documentation/sphinx/source/authorization.rst @@ -0,0 +1,124 @@ +############# +Authorization +############# + +.. warning :: Authorization is currently experimental and is not recommended for use in production. + +Introduction +============ + +:ref:`Multi-tenant ` database implies a couple of new concepts that did not previously exist in FoundationDB. +The first is the concept of privilege levels: we have *data-plane clients* whose typical workload is limited to accessing a tenant keyspace. +On the other hand, we have *control-plane clients* or *administrators* who may read or update cluster-wide configurations through system keyspace. +These operations also include creation and deletion of tenants. +The second is access control: with multiple tenant keyspaces, it comes naturally that we would want to restrict database access of a client to a subset of them. + +Privilege Levels +---------------- + +Authorization feature extends FoundationDB's existing TLS policy to distinguish administrators from data-plane clients, +making TLS configuration a prerequisite for enabling authorization. +There are only two privilege levels: *trusted* versus *untrusted* clients. +Trusted clients are authorized to perform any operation that pre-authorization FoundationDB clients used to perform, including those accessing the system keyspace. +Untrusted clients may only request what is necessary to access tenant keyspaces for which they are authorized. +Untrusted clients are blocked from accessing anything in the system keyspace or issuing management operations that modify the cluster in any way. + +In order to be considered a trusted client, a client needs to be :ref:`configured with a valid chain of X.509 certificates and a private key `, +and its certificate chain must be trusted by the server. In other words, a client must successfully complete a mutual TLS authentication. +Additionally, if the server was configured with trusted IP subnets, i.e. run with one or more ``--trusted-subnet-SUBNET_NAME`` followed by a CIDR block describing the subnet, +then the client's IP as seen from the server must belong to at least one of the subnets. + +Choosing to respond with an empty certificate chain during `client authentication `_ marks the client as untrusted. +If the server specifies a list of trusted subnets and the client's server-facing IP is not part of any of the subnets, +then the client is untrusted even if it successfully completes a mutual TLS authentication. + +.. note:: Presenting a bad or untrusted certificate chain causes the server to break the client connection and eventually throttle the client. + It does not let the client connect untrusted. + +Access Control +-------------- + +To restrict untrusted client's database access to a subset of tenant keyspaces, authorization feature allows database administrators +to grant tenant-scoped access in the form of `JSON Web Tokens `_. +Token verification is performed against a set of named public keys written in `JWK Set `_ format. +A token's header part must contain the `key identifier `_ of the public key which shall be used to verify the token itself. +Below is the list of token fields recognized by FoundationDB. +Note that some of the fields are *recognized* by FoundationDB but not *actively used* in enforcing security, pending future implementation. +Those fields are marked as **NOT required**. + + +.. table:: JSON Web Token Fields supported by FoundationDB + :align: left + :widths: auto + + =============== =========== ======== ==================================================== ================================================================================ + Containing Part Field Name Required Purpose Reference + =============== =========== ======== ==================================================== ================================================================================ + Header ``typ`` Yes Type of JSON Web Signature. Must be ``JWT``. `RFC7519 Section 5.1 `_ + Header ``alg`` Yes Algorithm used to generate the signature. Only `RFC7515 Section 4.1.1 `_ + ``ES256`` and ``RS256`` are supported. + Must match the ``alg`` attribute of public key. + Header ``kid`` Yes Name of public key with which to verify the token. `RFC7515 Section 4.1.4 `_ + Must match the ``kid`` attribute of public key. + Claim ``exp`` Yes Timestamp after which token is not accepted. `RFC7519 Section 4.1.4 `_ + Claim ``nbf`` Yes Timestamp before which token is not accepted. `RFC7519 Section 4.1.5 `_ + Claim ``iat`` Yes Timestamp at which token was issued. `RFC7519 Section 4.1.6 `_ + Claim ``tenants`` Yes Tenants names for which token holder is authorized. N/A + Must be an array. + Claim ``iss`` No Issuer of the token. `RFC7519 Section 4.1.1 `_ + Claim ``sub`` No Subject of the token. `RFC7519 Section 4.1.2 `_ + Claim ``aud`` No Intended recipients of the token. Must be an array. `RFC7519 Section 4.1.3 `_ + Claim ``jti`` No String that uniquely identifies a token. `RFC7519 Section 4.1.7 `_ + =============== =========== ======== ==================================================== ================================================================================ + +Public keys with which to verify the token must be serialized in `JWK Set `_ format and stored in a file. +The location of the key set file must be passed as command line argument ``--authorization-public-key-file`` to the ``fdbserver`` executable. +Public keys in the set must be either `RSA `_ public keys +containing ``n`` and ``e`` parameters, each containing `Base64urlUInt `_-encoded modulus and exponent, +or `Elliptic Curve `_ public keys on a ``P-256`` curve, +where ``crv`` parameter is set to ``P-256`` and ``x`` and ``y`` parameters contain +`base64url `_-encoded affine coordinates. +In addition, each public key JSON object in set must contain ``kty`` (set to either ``EC`` or ``RSA``) field to indicate public key algorithm, +along with ``kid``, and ``alg`` fields to be compared against their token header counterparts. +Private keys are strongly recommended against being included in the public key set and, if found, are excluded from consideration. + +.. note:: By design, FoundationDB authorization feature does not support revocation of outstanding tokens. + Use extra caution in signing tokens with long token durations. + +Enabling Clients to use Authorization Tokens +============================================ + +In order to use an untrusted client with an authorization token, a client must be configured to trust the server's CA, +but must not be configured to use the client's own certificates and keys. +More concretely, the client's ``TLS_CA_FILE`` must include the server's root CA certificate, +but the client must not be configured with its own ``TLS_CERTIFICATE_FILE`` or ``TLS_KEY_FILE``, neither programmatically nor by environment variable. +Before performing a tenant data read or update, a client must set ``AUTHORIZATION_TOKEN`` transaction option with the token string as argument. +It is the client's responsibility to keep the token up-to-date, by timely assigning a new token to the transaction object. + +.. note:: The TLS authentication mode of an untrusted client is similar to how typical web browsers connect to TLS-enabled web services. + They authenticate the server using their bundle of trusted root CA certificates, + but they do not authenticate themselves to the server. + +Public Key Rotation +=================== + +FoundationDB's internal public key set automatically refreshes itself based on the key set file's latest content every ``PUBLIC_KEY_FILE_REFRESH_INTERVAL_SECONDS`` seconds. +The in-memory set of public keys does not update unless the key file holds a correct `JWK Set`_. + +Token Caching +============= + +In a single-threaded runtime environment such as FoundationDB, it is important not to let the main thread be overloaded with computationally expensive operations, +such as token signature verification. FoundationDB internally caches the tokens that are considered valid at the time of verification in a fixed-size cache, +whose size may be configured using ``TOKEN_CACHE_SIZE`` knob. + +.. note:: Token cache is independent of the active public key set. Once the token reaches the cache, it is valid until its expiration time, + regardless of any key rotation that takes place thereafter. + +Allowing Untrusted Clients to Access Tenant Data Without Tokens +=============================================================== + +Rolling out a public key distribution infrastructure and an authorization-enabled FoundationDB cluster in lockstep might not be feasible with large scale distributed systems. +To support incremental rollout, authorization feature introduces ``ALLOW_TOKENLESS_TENANT_ACCESS`` boolean knob, +which preserves the TLS-based privilege level policy without untrusted clients having to set authorization tokens to their transactions in order to access tenant data. +With this knob active, any authorization token assigned to the client transaction is simply ignored. diff --git a/documentation/sphinx/source/global-configuration.rst b/documentation/sphinx/source/global-configuration.rst index 1c5f94dc7d..663ad26eb4 100644 --- a/documentation/sphinx/source/global-configuration.rst +++ b/documentation/sphinx/source/global-configuration.rst @@ -82,7 +82,7 @@ Values must always be encoded according to the :ref:`api-python-tuple-layer`. // In GlobalConfig.actor.h extern const KeyRef myGlobalConfigKey; // In GlobalConfig.actor.cpp - const KeyRef myGlobalConfigKey = LiteralStringRef("config/key"); + const KeyRef myGlobalConfigKey = "config/key"_sr; // When you want to set the value.. Tuple value = Tuple::makeTuple((double)1.5); diff --git a/documentation/sphinx/source/release-notes/release-notes-710.rst b/documentation/sphinx/source/release-notes/release-notes-710.rst index 05a33625e8..9dad6e05af 100644 --- a/documentation/sphinx/source/release-notes/release-notes-710.rst +++ b/documentation/sphinx/source/release-notes/release-notes-710.rst @@ -2,6 +2,21 @@ Release Notes ############# +7.1.23 +====== +* Same as 7.1.22 release with AVX enabled. + +7.1.22 +====== +* Released with AVX disabled. +* Added new latency samples for GetValue, GetRange, QueueWait, and VersionWait in storage servers. `(PR #8215) `_ +* Fixed a rare partial data write for TLogs. `(PR #8210) `_ +* Added HTTP proxy support for backup agents. `(PR #8193) `_ +* Fixed a memory bug of secondary queries in index prefetch. `(PR #8195) `_, `(PR #8190) `_ +* Introduced STORAGE_SERVER_REBOOT_ON_IO_TIMEOUT knob to recreate SS at io_timeout errors. `(PR #8123) `_ +* Fixed two TLog stopped bugs and a CC leader replacement bug. `(PR #8081) `_ +* Added back RecoveryAvailable trace event for status's seconds_since_last_recovered field. `(PR #8068) `_ + 7.1.21 ====== * Same as 7.1.20 release with AVX enabled. diff --git a/documentation/sphinx/source/tenants.rst b/documentation/sphinx/source/tenants.rst index b631c55ba2..07bd7b2a42 100644 --- a/documentation/sphinx/source/tenants.rst +++ b/documentation/sphinx/source/tenants.rst @@ -2,6 +2,8 @@ Tenants ####### +.. _multi-tenancy: + .. warning :: Tenants are currently experimental and are not recommended for use in production. FoundationDB provides a feature called tenants that allow you to configure one or more named transaction domains in your cluster. A transaction domain is a key-space in which a transaction is allowed to operate, and no tenant operations are allowed to use keys outside the tenant key-space. Tenants can be useful for managing separate, unrelated use-cases and preventing them from interfering with each other. They can also be helpful for defining safe boundaries when moving a subset of data between clusters. diff --git a/documentation/tutorial/tutorial.actor.cpp b/documentation/tutorial/tutorial.actor.cpp index 9d980ff3d6..245e6d09e3 100644 --- a/documentation/tutorial/tutorial.actor.cpp +++ b/documentation/tutorial/tutorial.actor.cpp @@ -478,7 +478,7 @@ ACTOR Future fdbClient() { state Transaction tx(db); state std::string keyPrefix = "/tut/"; state Key startKey; - state KeyRef endKey = LiteralStringRef("/tut0"); + state KeyRef endKey = "/tut0"_sr; state int beginIdx = 0; loop { try { @@ -494,7 +494,7 @@ ACTOR Future fdbClient() { RangeResult range = wait(tx.getRange(KeyRangeRef(startKey, endKey), 100)); for (int i = 0; i < 10; ++i) { Key k = Key(keyPrefix + std::to_string(beginIdx + deterministicRandom()->randomInt(0, 100))); - tx.set(k, LiteralStringRef("foo")); + tx.set(k, "foo"_sr); } wait(tx.commit()); std::cout << "Committed\n"; diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 559cfc7383..bab4bb5f66 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -905,12 +905,12 @@ CSimpleOpt::SOption g_rgDBPauseOptions[] = { SO_END_OF_OPTIONS }; -const KeyRef exeAgent = LiteralStringRef("backup_agent"); -const KeyRef exeBackup = LiteralStringRef("fdbbackup"); -const KeyRef exeRestore = LiteralStringRef("fdbrestore"); -const KeyRef exeFastRestoreTool = LiteralStringRef("fastrestore_tool"); // must be lower case -const KeyRef exeDatabaseAgent = LiteralStringRef("dr_agent"); -const KeyRef exeDatabaseBackup = LiteralStringRef("fdbdr"); +const KeyRef exeAgent = "backup_agent"_sr; +const KeyRef exeBackup = "fdbbackup"_sr; +const KeyRef exeRestore = "fdbrestore"_sr; +const KeyRef exeFastRestoreTool = "fastrestore_tool"_sr; // must be lower case +const KeyRef exeDatabaseAgent = "dr_agent"_sr; +const KeyRef exeDatabaseBackup = "fdbdr"_sr; extern const char* getSourceVersion(); @@ -1351,7 +1351,7 @@ ProgramExe getProgramType(std::string programExe) { } #endif // For debugging convenience, remove .debug suffix if present. - if (StringRef(programExe).endsWith(LiteralStringRef(".debug"))) + if (StringRef(programExe).endsWith(".debug"_sr)) programExe = programExe.substr(0, programExe.size() - 6); // Check if backup agent @@ -2449,8 +2449,8 @@ ACTOR Future runFastRestoreTool(Database db, dbVersion, LockDB::True, randomUID, - LiteralStringRef(""), - LiteralStringRef(""))); + ""_sr, + ""_sr)); // TODO: Support addPrefix and removePrefix if (waitForDone) { // Wait for parallel restore to finish and unlock DB after that @@ -3089,7 +3089,7 @@ static void addKeyRange(std::string optionValue, StandaloneOptionArg(); // If the url starts with '/' then prepend "file://" for backwards compatibility - if (StringRef(destinationContainer).startsWith(LiteralStringRef("/"))) + if (StringRef(destinationContainer).startsWith("/"_sr)) destinationContainer = std::string("file://") + destinationContainer; modifyOptions.destURL = destinationContainer; break; @@ -3654,7 +3654,7 @@ int main(int argc, char* argv[]) { case OPT_RESTORECONTAINER: restoreContainer = args->OptionArg(); // If the url starts with '/' then prepend "file://" for backwards compatibility - if (StringRef(restoreContainer).startsWith(LiteralStringRef("/"))) + if (StringRef(restoreContainer).startsWith("/"_sr)) restoreContainer = std::string("file://") + restoreContainer; break; case OPT_DESCRIBE_DEEP: @@ -4323,19 +4323,19 @@ int main(int argc, char* argv[]) { char* demangled = abi::__cxa_demangle(i->first, NULL, NULL, NULL); if (demangled) { s = demangled; - if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::"))) - s = s.substr(LiteralStringRef("(anonymous namespace)::").size()); + if (StringRef(s).startsWith("(anonymous namespace)::"_sr)) + s = s.substr("(anonymous namespace)::"_sr.size()); free(demangled); } else s = i->first; #else s = i->first; - if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::"))) - s = s.substr(LiteralStringRef("class `anonymous namespace'::").size()); - else if (StringRef(s).startsWith(LiteralStringRef("class "))) - s = s.substr(LiteralStringRef("class ").size()); - else if (StringRef(s).startsWith(LiteralStringRef("struct "))) - s = s.substr(LiteralStringRef("struct ").size()); + if (StringRef(s).startsWith("class `anonymous namespace'::"_sr)) + s = s.substr("class `anonymous namespace'::"_sr.size()); + else if (StringRef(s).startsWith("class "_sr)) + s = s.substr("class "_sr.size()); + else if (StringRef(s).startsWith("struct "_sr)) + s = s.substr("struct "_sr.size()); #endif typeNames.emplace_back(s, i->first); diff --git a/fdbcli/AdvanceVersionCommand.actor.cpp b/fdbcli/AdvanceVersionCommand.actor.cpp index 223af2d8e5..d3ba08d675 100644 --- a/fdbcli/AdvanceVersionCommand.actor.cpp +++ b/fdbcli/AdvanceVersionCommand.actor.cpp @@ -31,7 +31,7 @@ namespace fdb_cli { -const KeyRef advanceVersionSpecialKey = LiteralStringRef("\xff\xff/management/min_required_commit_version"); +const KeyRef advanceVersionSpecialKey = "\xff\xff/management/min_required_commit_version"_sr; ACTOR Future advanceVersionCommandActor(Reference db, std::vector tokens) { if (tokens.size() != 2) { diff --git a/fdbcli/BlobRangeCommand.actor.cpp b/fdbcli/BlobRangeCommand.actor.cpp index 521384f587..38edaa8568 100644 --- a/fdbcli/BlobRangeCommand.actor.cpp +++ b/fdbcli/BlobRangeCommand.actor.cpp @@ -112,7 +112,7 @@ ACTOR Future blobRangeCommandActor(Database localDb, end = tokens[3]; } - if (end > LiteralStringRef("\xff")) { + if (end > "\xff"_sr) { // TODO is this something we want? fmt::print("Cannot blobbify system keyspace! Problematic End Key: {0}\n", tokens[3].printable()); return false; diff --git a/fdbcli/ConfigureCommand.actor.cpp b/fdbcli/ConfigureCommand.actor.cpp index f81ec2401c..26a3da9876 100644 --- a/fdbcli/ConfigureCommand.actor.cpp +++ b/fdbcli/ConfigureCommand.actor.cpp @@ -44,20 +44,20 @@ ACTOR Future configureCommandActor(Reference db, if (tokens.size() < 2) result = ConfigurationResult::NO_OPTIONS_PROVIDED; else { - if (tokens[startToken] == LiteralStringRef("FORCE")) { + if (tokens[startToken] == "FORCE"_sr) { force = true; startToken = 2; } state Optional conf; - if (tokens[startToken] == LiteralStringRef("auto")) { + if (tokens[startToken] == "auto"_sr) { // get cluster status state Reference tr = db->createTransaction(); if (!tr->isValid()) { StatusObject _s = wait(StatusClient::statusFetcher(localDb)); s = _s; } else { - state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); + state ThreadFuture> statusValueF = tr->get("\xff\xff/status/json"_sr); Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); if (!statusValue.present()) { fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); @@ -166,7 +166,7 @@ ACTOR Future configureCommandActor(Reference db, case ConfigurationResult::CONFLICTING_OPTIONS: case ConfigurationResult::UNKNOWN_OPTION: case ConfigurationResult::INCOMPLETE_CONFIGURATION: - printUsage(LiteralStringRef("configure")); + printUsage("configure"_sr); ret = false; break; case ConfigurationResult::INVALID_CONFIGURATION: @@ -259,7 +259,6 @@ ACTOR Future configureCommandActor(Reference db, fprintf(stderr, "Type `configure perpetual_storage_wiggle=1' to enable the perpetual wiggle, or `configure " "storage_migration_type=gradual' to set the gradual migration type.\n"); - ret = false; break; case ConfigurationResult::SUCCESS_WARN_ROCKSDB_EXPERIMENTAL: printf("Configuration changed\n"); diff --git a/fdbcli/ConsistencyCheckCommand.actor.cpp b/fdbcli/ConsistencyCheckCommand.actor.cpp index 2e14e71fcc..1f225d1dfe 100644 --- a/fdbcli/ConsistencyCheckCommand.actor.cpp +++ b/fdbcli/ConsistencyCheckCommand.actor.cpp @@ -30,7 +30,7 @@ namespace fdb_cli { -const KeyRef consistencyCheckSpecialKey = LiteralStringRef("\xff\xff/management/consistency_check_suspended"); +const KeyRef consistencyCheckSpecialKey = "\xff\xff/management/consistency_check_suspended"_sr; ACTOR Future consistencyCheckCommandActor(Reference tr, std::vector tokens, diff --git a/fdbcli/ConsistencyScanCommand.actor.cpp b/fdbcli/ConsistencyScanCommand.actor.cpp new file mode 100644 index 0000000000..c94258045d --- /dev/null +++ b/fdbcli/ConsistencyScanCommand.actor.cpp @@ -0,0 +1,122 @@ +/* + * ConsistencyScanCommand.actor.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fdbcli/fdbcli.actor.h" + +#include "fdbclient/FDBOptions.g.h" +#include "fdbclient/IClientApi.h" + +#include "flow/Arena.h" +#include "flow/FastRef.h" +#include "flow/ThreadHelper.actor.h" +#include "fdbclient/ConsistencyScanInterface.h" +#include "flow/actorcompiler.h" // This must be the last #include. + +namespace fdb_cli { + +ACTOR Future consistencyScanCommandActor(Database db, std::vector tokens) { + state Reference tr = makeReference(db); + // Here we do not proceed in a try-catch loop since the transaction is always supposed to succeed. + // If not, the outer loop catch block(fdbcli.actor.cpp) will handle the error and print out the error message + state int usageError = 0; + state ConsistencyScanInfo csInfo = ConsistencyScanInfo(); + tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + + // Get the exisiting consistencyScanInfo object if present + state Optional consistencyScanInfo = wait(ConsistencyScanInfo::getInfo(tr)); + wait(tr->commit()); + if (consistencyScanInfo.present()) + csInfo = ObjectReader::fromStringRef(consistencyScanInfo.get(), IncludeVersion()); + tr->reset(); + + if (tokens.size() == 1) { + printf("Consistency Scan Info: %s\n", csInfo.toString().c_str()); + } else if ((tokens.size() == 2) && tokencmp(tokens[1], "off")) { + csInfo.consistency_scan_enabled = false; + wait(ConsistencyScanInfo::setInfo(tr, csInfo)); + wait(tr->commit()); + } else if ((tokencmp(tokens[1], "on") && tokens.size() > 2)) { + csInfo.consistency_scan_enabled = true; + state std::vector::iterator t; + for (t = tokens.begin() + 2; t != tokens.end(); ++t) { + if (tokencmp(t->toString(), "restart")) { + if (++t != tokens.end()) { + if (tokencmp(t->toString(), "0")) { + csInfo.restart = false; + } else if (tokencmp(t->toString(), "1")) { + csInfo.restart = true; + } else { + usageError = 1; + } + } else { + usageError = 1; + } + } else if (tokencmp(t->toString(), "maxRate")) { + if (++t != tokens.end()) { + char* end; + csInfo.max_rate = std::strtod(t->toString().data(), &end); + if (!std::isspace(*end) && (*end != '\0')) { + fprintf(stderr, "ERROR: %s failed to parse.\n", t->toString().c_str()); + return false; + } + } else { + usageError = 1; + } + } else if (tokencmp(t->toString(), "targetInterval")) { + if (++t != tokens.end()) { + char* end; + csInfo.target_interval = std::strtod(t->toString().data(), &end); + if (!std::isspace(*end) && (*end != '\0')) { + fprintf(stderr, "ERROR: %s failed to parse.\n", t->toString().c_str()); + return false; + } + } else { + usageError = 1; + } + } else { + usageError = 1; + } + } + + if (!usageError) { + wait(ConsistencyScanInfo::setInfo(tr, csInfo)); + wait(tr->commit()); + } + } else { + usageError = 1; + } + + if (usageError) { + printUsage(tokens[0]); + return false; + } + return true; +} + +CommandFactory consistencyScanFactory( + "consistencyscan", + CommandHelp("consistencyscan ", + "enables or disables consistency scan", + "Calling this command with `on' enables the consistency scan process to run the scan with given " + "arguments and `off' will halt the scan. " + "Calling this command with no arguments will display if consistency scan is currently enabled.\n")); + +} // namespace fdb_cli \ No newline at end of file diff --git a/fdbcli/CoordinatorsCommand.actor.cpp b/fdbcli/CoordinatorsCommand.actor.cpp index b68d5ab3d3..4680c5393a 100644 --- a/fdbcli/CoordinatorsCommand.actor.cpp +++ b/fdbcli/CoordinatorsCommand.actor.cpp @@ -64,17 +64,26 @@ ACTOR Future changeCoordinators(Reference db, std::vectorstartsWith(nameTokenBegin)) { + if (tok->startsWith(nameTokenBegin) && new_cluster_description.empty()) { new_cluster_description = tok->substr(nameTokenBegin.size()); + auto next = tok - 1; std::copy(tok + 1, tokens.end(), tok); tokens.resize(tokens.size() - 1); - break; + tok = next; + } else if (tok->startsWith(noConfigDB)) { + disableConfigDB = true; + auto next = tok - 1; + std::copy(tok + 1, tokens.end(), tok); + tokens.resize(tokens.size() - 1); + tok = next; } } - state bool automatic = tokens.size() == 2 && tokens[1] == LiteralStringRef("auto"); + state bool automatic = tokens.size() == 2 && tokens[1] == "auto"_sr; state Reference tr = db->createTransaction(); loop { tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); @@ -83,6 +92,10 @@ ACTOR Future changeCoordinators(Reference db, std::vectorset(fdb_cli::clusterDescriptionSpecialKey, new_cluster_description); } + if (disableConfigDB) { + // All that matters is the key is set. + tr->set(fdb_cli::configDBSpecialKey, ""_sr); + } // if auto change, read the special key to retrieve the recommended config if (automatic) { // if previous read failed, retry, otherwise, use the same recommened config @@ -173,9 +186,10 @@ ACTOR Future changeCoordinators(Reference db, std::vector coordinatorsCommandActor(Reference db, std::vector tokens) { if (tokens.size() < 2) { diff --git a/fdbcli/DataDistributionCommand.actor.cpp b/fdbcli/DataDistributionCommand.actor.cpp index 7000bdf5c7..8b7690b009 100644 --- a/fdbcli/DataDistributionCommand.actor.cpp +++ b/fdbcli/DataDistributionCommand.actor.cpp @@ -108,8 +108,8 @@ Future setDDIgnoreRebalanceOff(Reference db, uint8_t DDIgnoreOp namespace fdb_cli { -const KeyRef ddModeSpecialKey = LiteralStringRef("\xff\xff/management/data_distribution/mode"); -const KeyRef ddIgnoreRebalanceSpecialKey = LiteralStringRef("\xff\xff/management/data_distribution/rebalance_ignored"); +const KeyRef ddModeSpecialKey = "\xff\xff/management/data_distribution/mode"_sr; +const KeyRef ddIgnoreRebalanceSpecialKey = "\xff\xff/management/data_distribution/rebalance_ignored"_sr; constexpr auto usage = "Usage: datadistribution |enable " ">\n"; @@ -127,7 +127,7 @@ ACTOR Future dataDistributionCommandActor(Reference db, std::ve printf("Data distribution is turned off.\n"); } else if (tokencmp(tokens[1], "disable")) { if (tokencmp(tokens[2], "ssfailure")) { - wait(success((setHealthyZone(db, LiteralStringRef("IgnoreSSFailures"), 0)))); + wait(success((setHealthyZone(db, "IgnoreSSFailures"_sr, 0)))); printf("Data distribution is disabled for storage server failures.\n"); } else if (tokencmp(tokens[2], "rebalance")) { wait(setDDIgnoreRebalanceOn(db, DDIgnore::REBALANCE_DISK | DDIgnore::REBALANCE_READ)); diff --git a/fdbcli/ExcludeCommand.actor.cpp b/fdbcli/ExcludeCommand.actor.cpp index db67bd8a6e..7c8b7217e0 100644 --- a/fdbcli/ExcludeCommand.actor.cpp +++ b/fdbcli/ExcludeCommand.actor.cpp @@ -227,22 +227,19 @@ ACTOR Future checkForCoordinators(Reference db, std::vector excludeCommandActor(Reference db, std::vector tokens, Future warn) { if (tokens.size() <= 1) { @@ -281,11 +278,11 @@ ACTOR Future excludeCommandActor(Reference db, std::vectorstartsWith(LocalityData::ExcludeLocalityPrefix) && t->toString().find(':') != std::string::npos) { diff --git a/fdbcli/FileConfigureCommand.actor.cpp b/fdbcli/FileConfigureCommand.actor.cpp index e35114c429..8cce2ec543 100644 --- a/fdbcli/FileConfigureCommand.actor.cpp +++ b/fdbcli/FileConfigureCommand.actor.cpp @@ -78,7 +78,7 @@ ACTOR Future fileConfigureCommandActor(Reference db, name + "=" + json_spirit::write_string(json_spirit::mValue(value.get_array()), json_spirit::Output_options::none); } else { - printUsage(LiteralStringRef("fileconfigure")); + printUsage("fileconfigure"_sr); return false; } } diff --git a/fdbcli/IncludeCommand.actor.cpp b/fdbcli/IncludeCommand.actor.cpp index a463772960..be55ac8476 100644 --- a/fdbcli/IncludeCommand.actor.cpp +++ b/fdbcli/IncludeCommand.actor.cpp @@ -92,8 +92,7 @@ ACTOR Future includeServers(Reference db, std::vectorclear(KeyRangeRef(addr.withSuffix(LiteralStringRef(":")), - addr.withSuffix(LiteralStringRef(";")))); + tr->clear(KeyRangeRef(addr.withSuffix(":"_sr), addr.withSuffix(";"_sr))); } } wait(safeThreadFutureToFuture(tr->commit())); @@ -112,9 +111,9 @@ ACTOR Future include(Reference db, std::vector token state bool failed = false; state bool all = false; for (auto t = tokens.begin() + 1; t != tokens.end(); ++t) { - if (*t == LiteralStringRef("all")) { + if (*t == "all"_sr) { all = true; - } else if (*t == LiteralStringRef("failed")) { + } else if (*t == "failed"_sr) { failed = true; } else if (t->startsWith(LocalityData::ExcludeLocalityPrefix) && t->toString().find(':') != std::string::npos) { // if the token starts with 'locality_' prefix. diff --git a/fdbcli/LockCommand.actor.cpp b/fdbcli/LockCommand.actor.cpp index 1ed988ee34..a2ac2c05cd 100644 --- a/fdbcli/LockCommand.actor.cpp +++ b/fdbcli/LockCommand.actor.cpp @@ -59,7 +59,7 @@ ACTOR Future lockDatabase(Reference db, UID id) { namespace fdb_cli { -const KeyRef lockSpecialKey = LiteralStringRef("\xff\xff/management/db_locked"); +const KeyRef lockSpecialKey = "\xff\xff/management/db_locked"_sr; ACTOR Future lockCommandActor(Reference db, std::vector tokens) { if (tokens.size() != 1) { diff --git a/fdbcli/MaintenanceCommand.actor.cpp b/fdbcli/MaintenanceCommand.actor.cpp index 487490e09f..b6dd8cc139 100644 --- a/fdbcli/MaintenanceCommand.actor.cpp +++ b/fdbcli/MaintenanceCommand.actor.cpp @@ -69,10 +69,10 @@ ACTOR Future printHealthyZone(Reference db) { namespace fdb_cli { -const KeyRangeRef maintenanceSpecialKeyRange = KeyRangeRef(LiteralStringRef("\xff\xff/management/maintenance/"), - LiteralStringRef("\xff\xff/management/maintenance0")); +const KeyRangeRef maintenanceSpecialKeyRange = + KeyRangeRef("\xff\xff/management/maintenance/"_sr, "\xff\xff/management/maintenance0"_sr); // The special key, if present, means data distribution is disabled for storage failures; -const KeyRef ignoreSSFailureSpecialKey = LiteralStringRef("\xff\xff/management/maintenance/IgnoreSSFailures"); +const KeyRef ignoreSSFailureSpecialKey = "\xff\xff/management/maintenance/IgnoreSSFailures"_sr; // add a zone to maintenance and specify the maintenance duration ACTOR Future setHealthyZone(Reference db, StringRef zoneId, double seconds, bool printWarning) { diff --git a/fdbcli/ProfileCommand.actor.cpp b/fdbcli/ProfileCommand.actor.cpp index c47a558258..52325d3de8 100644 --- a/fdbcli/ProfileCommand.actor.cpp +++ b/fdbcli/ProfileCommand.actor.cpp @@ -115,17 +115,13 @@ ACTOR Future profileCommandActor(Database db, return false; } // Hold the reference to the standalone's memory - state ThreadFuture kvsFuture = - tr->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), - LiteralStringRef("\xff\xff/worker_interfaces0")), - CLIENT_KNOBS->TOO_MANY); + state ThreadFuture kvsFuture = tr->getRange( + KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr), CLIENT_KNOBS->TOO_MANY); RangeResult kvs = wait(safeThreadFutureToFuture(kvsFuture)); ASSERT(!kvs.more); for (const auto& pair : kvs) { - auto ip_port = - (pair.key.endsWith(LiteralStringRef(":tls")) ? pair.key.removeSuffix(LiteralStringRef(":tls")) - : pair.key) - .removePrefix(LiteralStringRef("\xff\xff/worker_interfaces/")); + auto ip_port = (pair.key.endsWith(":tls"_sr) ? pair.key.removeSuffix(":tls"_sr) : pair.key) + .removePrefix("\xff\xff/worker_interfaces/"_sr); printf("%s\n", printable(ip_port).c_str()); } } else { diff --git a/fdbcli/SetClassCommand.actor.cpp b/fdbcli/SetClassCommand.actor.cpp index cd47e22f86..dc1f997e31 100644 --- a/fdbcli/SetClassCommand.actor.cpp +++ b/fdbcli/SetClassCommand.actor.cpp @@ -105,12 +105,10 @@ ACTOR Future setProcessClass(Reference db, KeyRef network_addre namespace fdb_cli { const KeyRangeRef processClassSourceSpecialKeyRange = - KeyRangeRef(LiteralStringRef("\xff\xff/configuration/process/class_source/"), - LiteralStringRef("\xff\xff/configuration/process/class_source0")); + KeyRangeRef("\xff\xff/configuration/process/class_source/"_sr, "\xff\xff/configuration/process/class_source0"_sr); const KeyRangeRef processClassTypeSpecialKeyRange = - KeyRangeRef(LiteralStringRef("\xff\xff/configuration/process/class_type/"), - LiteralStringRef("\xff\xff/configuration/process/class_type0")); + KeyRangeRef("\xff\xff/configuration/process/class_type/"_sr, "\xff\xff/configuration/process/class_type0"_sr); ACTOR Future setClassCommandActor(Reference db, std::vector tokens) { if (tokens.size() != 3 && tokens.size() != 1) { diff --git a/fdbcli/SnapshotCommand.actor.cpp b/fdbcli/SnapshotCommand.actor.cpp index 5bc7302f0c..7606101bba 100644 --- a/fdbcli/SnapshotCommand.actor.cpp +++ b/fdbcli/SnapshotCommand.actor.cpp @@ -40,7 +40,7 @@ ACTOR Future snapshotCommandActor(Reference db, std::vector statusCommandActor(Reference db, StatusObject _s = wait(StatusClient::statusFetcher(localDb)); s = _s; } else { - state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); + state ThreadFuture> statusValueF = tr->get("\xff\xff/status/json"_sr); Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); if (!statusValue.present()) { fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); diff --git a/fdbcli/ThrottleCommand.actor.cpp b/fdbcli/ThrottleCommand.actor.cpp index abff0e0475..057a83c78f 100644 --- a/fdbcli/ThrottleCommand.actor.cpp +++ b/fdbcli/ThrottleCommand.actor.cpp @@ -163,11 +163,11 @@ ACTOR Future throttleCommandActor(Reference db, std::vector tssQuarantine(Reference db, bool enable, UID tssId } if (enable) { - tr->set(tssQuarantineKeyFor(tssId), LiteralStringRef("")); + tr->set(tssQuarantineKeyFor(tssId), ""_sr); // remove server from TSS mapping when quarantine is enabled tssMapDB.erase(tr, ssi.tssPairID.get()); } else { @@ -112,19 +112,19 @@ namespace fdb_cli { ACTOR Future tssqCommandActor(Reference db, std::vector tokens) { if (tokens.size() == 2) { - if (tokens[1] != LiteralStringRef("list")) { + if (tokens[1] != "list"_sr) { printUsage(tokens[0]); return false; } else { wait(tssQuarantineList(db)); } } else if (tokens.size() == 3) { - if ((tokens[1] != LiteralStringRef("start") && tokens[1] != LiteralStringRef("stop")) || - (tokens[2].size() != 32) || !std::all_of(tokens[2].begin(), tokens[2].end(), &isxdigit)) { + if ((tokens[1] != "start"_sr && tokens[1] != "stop"_sr) || (tokens[2].size() != 32) || + !std::all_of(tokens[2].begin(), tokens[2].end(), &isxdigit)) { printUsage(tokens[0]); return false; } else { - bool enable = tokens[1] == LiteralStringRef("start"); + bool enable = tokens[1] == "start"_sr; UID tssId = UID::fromString(tokens[2].toString()); bool success = wait(tssQuarantine(db, enable, tssId)); return success; diff --git a/fdbcli/Util.actor.cpp b/fdbcli/Util.actor.cpp index 2d0e77d9fe..aed1133047 100644 --- a/fdbcli/Util.actor.cpp +++ b/fdbcli/Util.actor.cpp @@ -74,17 +74,15 @@ void addInterfacesFromKVs(RangeResult& kvs, return; } ClientLeaderRegInterface leaderInterf(workerInterf.address()); - StringRef ip_port = - (kv.key.endsWith(LiteralStringRef(":tls")) ? kv.key.removeSuffix(LiteralStringRef(":tls")) : kv.key) - .removePrefix(LiteralStringRef("\xff\xff/worker_interfaces/")); + StringRef ip_port = (kv.key.endsWith(":tls"_sr) ? kv.key.removeSuffix(":tls"_sr) : kv.key) + .removePrefix("\xff\xff/worker_interfaces/"_sr); (*address_interface)[ip_port] = std::make_pair(kv.value, leaderInterf); if (workerInterf.reboot.getEndpoint().addresses.secondaryAddress.present()) { Key full_ip_port2 = StringRef(workerInterf.reboot.getEndpoint().addresses.secondaryAddress.get().toString()); - StringRef ip_port2 = full_ip_port2.endsWith(LiteralStringRef(":tls")) - ? full_ip_port2.removeSuffix(LiteralStringRef(":tls")) - : full_ip_port2; + StringRef ip_port2 = + full_ip_port2.endsWith(":tls"_sr) ? full_ip_port2.removeSuffix(":tls"_sr) : full_ip_port2; (*address_interface)[ip_port2] = std::make_pair(kv.value, leaderInterf); } } @@ -99,8 +97,7 @@ ACTOR Future getWorkerInterfaces(Reference tr, } // Hold the reference to the standalone's memory state ThreadFuture kvsFuture = tr->getRange( - KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), LiteralStringRef("\xff\xff/worker_interfaces0")), - CLIENT_KNOBS->TOO_MANY); + KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr), CLIENT_KNOBS->TOO_MANY); state RangeResult kvs = wait(safeThreadFutureToFuture(kvsFuture)); ASSERT(!kvs.more); if (verify) { diff --git a/fdbcli/VersionEpochCommand.actor.cpp b/fdbcli/VersionEpochCommand.actor.cpp index 7d073e590d..a9dcd7e198 100644 --- a/fdbcli/VersionEpochCommand.actor.cpp +++ b/fdbcli/VersionEpochCommand.actor.cpp @@ -32,7 +32,7 @@ namespace fdb_cli { -const KeyRef versionEpochSpecialKey = LiteralStringRef("\xff\xff/management/version_epoch"); +const KeyRef versionEpochSpecialKey = "\xff\xff/management/version_epoch"_sr; struct VersionInfo { int64_t version; diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index ca357acde5..a2258bcdd5 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -44,6 +44,7 @@ #include "fdbclient/ThreadSafeTransaction.h" #include "flow/flow.h" +#include "flow/ApiVersion.h" #include "flow/ArgParseUtil.h" #include "flow/DeterministicRandom.h" #include "flow/FastRef.h" @@ -73,7 +74,6 @@ #include "flow/actorcompiler.h" // This must be the last #include. -#define FDB_API_VERSION 720 /* * While we could just use the MultiVersionApi instance directly, this #define allows us to swap in any other IClientApi * instance (e.g. from ThreadSafeApi) @@ -654,7 +654,7 @@ ACTOR Future checkStatus(Future f, StatusObject _s = wait(StatusClient::statusFetcher(localDb)); s = _s; } else { - state ThreadFuture> statusValueF = tr->get(LiteralStringRef("\xff\xff/status/json")); + state ThreadFuture> statusValueF = tr->get("\xff\xff/status/json"_sr); Optional statusValue = wait(safeThreadFutureToFuture(statusValueF)); if (!statusValue.present()) { fprintf(stderr, "ERROR: Failed to get status json from the cluster\n"); @@ -698,7 +698,7 @@ ACTOR Future createSnapshot(Database db, std::vector tokens) { for (int i = 1; i < tokens.size(); i++) { snapCmd = snapCmd.withSuffix(tokens[i]); if (i != tokens.size() - 1) { - snapCmd = snapCmd.withSuffix(LiteralStringRef(" ")); + snapCmd = snapCmd.withSuffix(" "_sr); } } try { @@ -889,7 +889,7 @@ struct CLIOptions { std::vector> knobs; // api version, using the latest version by default - int apiVersion = FDB_API_VERSION; + int apiVersion = ApiVersion::LATEST_VERSION; CLIOptions(int argc, char* argv[]) { program_name = argv[0]; @@ -938,12 +938,12 @@ struct CLIOptions { if (*endptr != '\0') { fprintf(stderr, "ERROR: invalid client version %s\n", args.OptionArg()); return 1; - } else if (apiVersion < 700 || apiVersion > FDB_API_VERSION) { + } else if (apiVersion < 700 || apiVersion > ApiVersion::LATEST_VERSION) { // multi-version fdbcli only available after 7.0 fprintf(stderr, "ERROR: api version %s is not supported. (Min: 700, Max: %d)\n", args.OptionArg(), - FDB_API_VERSION); + ApiVersion::LATEST_VERSION); return 1; } break; @@ -1328,13 +1328,10 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise, Reference cli(CLIOptions opt, LineNoise* plinenoise, Reference configureTenantCommandActor(Reference db, std::vec ACTOR Future consistencyCheckCommandActor(Reference tr, std::vector tokens, bool intrans); +// consistency scan command +ACTOR Future consistencyScanCommandActor(Database localDb, std::vector tokens); // coordinators command ACTOR Future coordinatorsCommandActor(Reference db, std::vector tokens); // createtenant command diff --git a/fdbclient/Atomic.cpp b/fdbclient/Atomic.cpp new file mode 100644 index 0000000000..02a56e0fb3 --- /dev/null +++ b/fdbclient/Atomic.cpp @@ -0,0 +1,45 @@ +/* + * Atomic.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fdbclient/Atomic.h" +#include "flow/Arena.h" +#include "flow/UnitTest.h" + +void forceLinkAtomicTests() {} + +TEST_CASE("/Atomic/DoAppendIfFits") { + Arena arena; + { + Value existingValue = ValueRef(arena, "existing"_sr); + Value otherOperand = ValueRef(arena, "other"_sr); + auto result = doAppendIfFits(existingValue, otherOperand, arena); + ASSERT(compare("existingother"_sr, result) == 0); + } + { + Value existingValue = makeString(CLIENT_KNOBS->VALUE_SIZE_LIMIT - 1, arena); + Value otherOperand = makeString(2, arena); + // Appended values cannot fit in result, should return existingValue + auto result = doAppendIfFits(existingValue, otherOperand, arena); + ASSERT(compare(existingValue, result) == 0); + } + return Void(); +} + +// TODO: Add more unit tests for atomic operations defined in Atomic.h diff --git a/fdbclient/BackupAgentBase.actor.cpp b/fdbclient/BackupAgentBase.actor.cpp index cb999a6e12..c72cb83afc 100644 --- a/fdbclient/BackupAgentBase.actor.cpp +++ b/fdbclient/BackupAgentBase.actor.cpp @@ -414,7 +414,7 @@ ACTOR Future readCommitted(Database cx, loop { try { state GetRangeLimits limits(GetRangeLimits::ROW_LIMIT_UNLIMITED, - (g_network->isSimulated() && !g_simulator.speedUpSimulation) + (g_network->isSimulated() && !g_simulator->speedUpSimulation) ? CLIENT_KNOBS->BACKUP_SIMULATED_LIMIT_BYTES : CLIENT_KNOBS->BACKUP_GET_RANGE_LIMIT_BYTES); @@ -493,7 +493,7 @@ ACTOR Future readCommitted(Database cx, loop { try { state GetRangeLimits limits(GetRangeLimits::ROW_LIMIT_UNLIMITED, - (g_network->isSimulated() && !g_simulator.speedUpSimulation) + (g_network->isSimulated() && !g_simulator->speedUpSimulation) ? CLIENT_KNOBS->BACKUP_SIMULATED_LIMIT_BYTES : CLIENT_KNOBS->BACKUP_GET_RANGE_LIMIT_BYTES); @@ -968,10 +968,9 @@ ACTOR Future cleanupLogMutations(Database cx, Value destUidValue, bool del .get(BackupAgentBase::keySourceStates) .get(currLogUid) .pack(DatabaseBackupAgent::keyStateStatus)); - state Future> foundBackupKey = - tr->get(Subspace(currLogUid.withPrefix(LiteralStringRef("uid->config/")) - .withPrefix(fileBackupPrefixRange.begin)) - .pack(LiteralStringRef("stateEnum"))); + state Future> foundBackupKey = tr->get( + Subspace(currLogUid.withPrefix("uid->config/"_sr).withPrefix(fileBackupPrefixRange.begin)) + .pack("stateEnum"_sr)); wait(success(foundDRKey) && success(foundBackupKey)); if (foundDRKey.get().present() && foundBackupKey.get().present()) { diff --git a/fdbclient/BackupContainerFileSystem.actor.cpp b/fdbclient/BackupContainerFileSystem.actor.cpp index b222153517..c446f4069e 100644 --- a/fdbclient/BackupContainerFileSystem.actor.cpp +++ b/fdbclient/BackupContainerFileSystem.actor.cpp @@ -1697,7 +1697,7 @@ ACTOR Future testBackupContainer(std::string url, // List of sizes to use to test edge cases on underlying file implementations state std::vector fileSizes = { 0 }; - if (StringRef(url).startsWith(LiteralStringRef("blob"))) { + if (StringRef(url).startsWith("blob"_sr)) { fileSizes.push_back(CLIENT_KNOBS->BLOBSTORE_MULTIPART_MIN_PART_SIZE); fileSizes.push_back(CLIENT_KNOBS->BLOBSTORE_MULTIPART_MIN_PART_SIZE + 10); } @@ -1705,8 +1705,8 @@ ACTOR Future testBackupContainer(std::string url, loop { state Version logStart = v; state int kvfiles = deterministicRandom()->randomInt(0, 3); - state Key begin = LiteralStringRef(""); - state Key end = LiteralStringRef(""); + state Key begin = ""_sr; + state Key end = ""_sr; state int blockSize = 3 * sizeof(uint32_t) + begin.size() + end.size() + 8; while (kvfiles > 0) { diff --git a/fdbclient/BackupContainerLocalDirectory.actor.cpp b/fdbclient/BackupContainerLocalDirectory.actor.cpp index 528910dabc..51abc24678 100644 --- a/fdbclient/BackupContainerLocalDirectory.actor.cpp +++ b/fdbclient/BackupContainerLocalDirectory.actor.cpp @@ -103,16 +103,15 @@ ACTOR static Future listFiles_impl(st // Remove .lnk files from results, they are a side effect of a backup that was *read* during simulation. See // openFile() above for more info on why they are created. if (g_network->isSimulated()) - files.erase( - std::remove_if(files.begin(), - files.end(), - [](std::string const& f) { return StringRef(f).endsWith(LiteralStringRef(".lnk")); }), - files.end()); + files.erase(std::remove_if(files.begin(), + files.end(), + [](std::string const& f) { return StringRef(f).endsWith(".lnk"_sr); }), + files.end()); for (const auto& f : files) { // Hide .part or .temp files. StringRef s(f); - if (!s.endsWith(LiteralStringRef(".part")) && !s.endsWith(LiteralStringRef(".temp"))) + if (!s.endsWith(".part"_sr) && !s.endsWith(".temp"_sr)) results.push_back({ f.substr(m_path.size() + 1), ::fileSize(f) }); } @@ -227,10 +226,10 @@ Future> BackupContainerLocalDirectory::readFile(const std: throw file_not_found(); } - if (g_simulator.getCurrentProcess()->uid == UID()) { + if (g_simulator->getCurrentProcess()->uid == UID()) { TraceEvent(SevError, "BackupContainerReadFileOnUnsetProcessID").log(); } - std::string uniquePath = fullPath + "." + g_simulator.getCurrentProcess()->uid.toString() + ".lnk"; + std::string uniquePath = fullPath + "." + g_simulator->getCurrentProcess()->uid.toString() + ".lnk"; unlink(uniquePath.c_str()); ASSERT(symlink(basename(path).c_str(), uniquePath.c_str()) == 0); fullPath = uniquePath; diff --git a/flow/BlobCipher.cpp b/fdbclient/BlobCipher.cpp similarity index 87% rename from flow/BlobCipher.cpp rename to fdbclient/BlobCipher.cpp index 123f63fef0..bed09f201f 100644 --- a/flow/BlobCipher.cpp +++ b/fdbclient/BlobCipher.cpp @@ -18,8 +18,9 @@ * limitations under the License. */ -#include "flow/BlobCipher.h" +#include "fdbclient/BlobCipher.h" +#include "fdbclient/Knobs.h" #include "flow/Arena.h" #include "flow/EncryptUtils.h" #include "flow/Knobs.h" @@ -27,6 +28,7 @@ #include "flow/FastRef.h" #include "flow/IRandom.h" #include "flow/ITrace.h" +#include "flow/Platform.h" #include "flow/flow.h" #include "flow/network.h" #include "flow/Trace.h" @@ -54,6 +56,42 @@ bool isEncryptHeaderAuthTokenModeValid(const EncryptAuthTokenMode mode) { } } // namespace +// BlobCipherMetrics methods + +BlobCipherMetrics::CounterSet::CounterSet(CounterCollection& cc, std::string name) + : encryptCPUTimeNS(name + "EncryptCPUTimeNS", cc), decryptCPUTimeNS(name + "DecryptCPUTimeNS", cc), + getCipherKeysLatency(name + "GetCipherKeysLatency", + UID(), + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE), + getLatestCipherKeysLatency(name + "GetLatestCipherKeysLatency", + UID(), + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE) {} + +BlobCipherMetrics::BlobCipherMetrics() + : cc("BlobCipher"), cipherKeyCacheHit("CipherKeyCacheHit", cc), cipherKeyCacheMiss("CipherKeyCacheMiss", cc), + cipherKeyCacheExpired("CipherKeyCacheExpired", cc), latestCipherKeyCacheHit("LatestCipherKeyCacheHit", cc), + latestCipherKeyCacheMiss("LatestCipherKeyCacheMiss", cc), + latestCipherKeyCacheNeedsRefresh("LatestCipherKeyCacheNeedsRefresh", cc), + getCipherKeysLatency("GetCipherKeysLatency", + UID(), + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE), + getLatestCipherKeysLatency("GetLatestCipherKeysLatency", + UID(), + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, + FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE), + counterSets({ CounterSet(cc, "TLog"), + CounterSet(cc, "KVMemory"), + CounterSet(cc, "KVRedwood"), + CounterSet(cc, "BlobGranule"), + CounterSet(cc, "Backup"), + CounterSet(cc, "Test") }) { + specialCounter(cc, "CacheSize", []() { return BlobCipherKeyCache::getInstance()->getSize(); }); + traceFuture = traceCounters("BlobCipherMetrics", UID(), FLOW_KNOBS->ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, &cc); +} + // BlobCipherKey class methods BlobCipherKey::BlobCipherKey(const EncryptCipherDomainId& domainId, @@ -144,17 +182,15 @@ void BlobCipherKey::reset() { // BlobKeyIdCache class methods -BlobCipherKeyIdCache::BlobCipherKeyIdCache() - : domainId(ENCRYPT_INVALID_DOMAIN_ID), latestBaseCipherKeyId(), latestRandomSalt() {} - -BlobCipherKeyIdCache::BlobCipherKeyIdCache(EncryptCipherDomainId dId) - : domainId(dId), latestBaseCipherKeyId(), latestRandomSalt() { +BlobCipherKeyIdCache::BlobCipherKeyIdCache(EncryptCipherDomainId dId, size_t* sizeStat) + : domainId(dId), latestBaseCipherKeyId(), latestRandomSalt(), sizeStat(sizeStat) { + ASSERT(sizeStat != nullptr); TraceEvent(SevInfo, "BlobCipher.KeyIdCacheInit").detail("DomainId", domainId); } BlobCipherKeyIdCacheKey BlobCipherKeyIdCache::getCacheKey(const EncryptCipherBaseKeyId& baseCipherKeyId, const EncryptCipherRandomSalt& salt) { - if (baseCipherKeyId == ENCRYPT_INVALID_CIPHER_KEY_ID || salt == ENCRYPT_INVALID_RANDOM_SALT) { + if (baseCipherKeyId == INVALID_ENCRYPT_CIPHER_KEY_ID || salt == INVALID_ENCRYPT_RANDOM_SALT) { throw encrypt_invalid_id(); } return std::make_pair(baseCipherKeyId, salt); @@ -164,9 +200,9 @@ Reference BlobCipherKeyIdCache::getLatestCipherKey() { if (!latestBaseCipherKeyId.present()) { return Reference(); } - ASSERT_NE(latestBaseCipherKeyId.get(), ENCRYPT_INVALID_CIPHER_KEY_ID); + ASSERT_NE(latestBaseCipherKeyId.get(), INVALID_ENCRYPT_CIPHER_KEY_ID); ASSERT(latestRandomSalt.present()); - ASSERT_NE(latestRandomSalt.get(), ENCRYPT_INVALID_RANDOM_SALT); + ASSERT_NE(latestRandomSalt.get(), INVALID_ENCRYPT_RANDOM_SALT); return getCipherByBaseCipherId(latestBaseCipherKeyId.get(), latestRandomSalt.get()); } @@ -185,7 +221,7 @@ Reference BlobCipherKeyIdCache::insertBaseCipherKey(const Encrypt int baseCipherLen, const int64_t refreshAt, const int64_t expireAt) { - ASSERT_GT(baseCipherId, ENCRYPT_INVALID_CIPHER_KEY_ID); + ASSERT_GT(baseCipherId, INVALID_ENCRYPT_CIPHER_KEY_ID); // BaseCipherKeys are immutable, given the routine invocation updates 'latestCipher', // ensure no key-tampering is done @@ -223,6 +259,7 @@ Reference BlobCipherKeyIdCache::insertBaseCipherKey(const Encrypt latestBaseCipherKeyId = baseCipherId; latestRandomSalt = cipherKey->getSalt(); + (*sizeStat)++; return cipherKey; } @@ -232,8 +269,8 @@ Reference BlobCipherKeyIdCache::insertBaseCipherKey(const Encrypt const EncryptCipherRandomSalt& salt, const int64_t refreshAt, const int64_t expireAt) { - ASSERT_NE(baseCipherId, ENCRYPT_INVALID_CIPHER_KEY_ID); - ASSERT_NE(salt, ENCRYPT_INVALID_RANDOM_SALT); + ASSERT_NE(baseCipherId, INVALID_ENCRYPT_CIPHER_KEY_ID); + ASSERT_NE(salt, INVALID_ENCRYPT_RANDOM_SALT); BlobCipherKeyIdCacheKey cacheKey = getCacheKey(baseCipherId, salt); @@ -267,6 +304,7 @@ Reference BlobCipherKeyIdCache::insertBaseCipherKey(const Encrypt Reference cipherKey = makeReference(domainId, baseCipherId, baseCipher, baseCipherLen, salt, refreshAt, expireAt); keyIdCache.emplace(cacheKey, cipherKey); + (*sizeStat)++; return cipherKey; } @@ -294,23 +332,23 @@ Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipher int baseCipherLen, const int64_t refreshAt, const int64_t expireAt) { - if (domainId == ENCRYPT_INVALID_DOMAIN_ID || baseCipherId == ENCRYPT_INVALID_CIPHER_KEY_ID) { + if (domainId == INVALID_ENCRYPT_DOMAIN_ID || baseCipherId == INVALID_ENCRYPT_CIPHER_KEY_ID) { throw encrypt_invalid_id(); } + Reference cipherKey; + try { auto domainItr = domainCacheMap.find(domainId); if (domainItr == domainCacheMap.end()) { // Add mapping to track new encryption domain - Reference keyIdCache = makeReference(domainId); - Reference cipherKey = - keyIdCache->insertBaseCipherKey(baseCipherId, baseCipher, baseCipherLen, refreshAt, expireAt); + Reference keyIdCache = makeReference(domainId, &size); + cipherKey = keyIdCache->insertBaseCipherKey(baseCipherId, baseCipher, baseCipherLen, refreshAt, expireAt); domainCacheMap.emplace(domainId, keyIdCache); - return cipherKey; } else { // Track new baseCipher keys Reference keyIdCache = domainItr->second; - return keyIdCache->insertBaseCipherKey(baseCipherId, baseCipher, baseCipherLen, refreshAt, expireAt); + cipherKey = keyIdCache->insertBaseCipherKey(baseCipherId, baseCipher, baseCipherLen, refreshAt, expireAt); } } catch (Error& e) { TraceEvent(SevWarn, "BlobCipher.InsertCipherKeyFailed") @@ -318,6 +356,7 @@ Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipher .detail("DomainId", domainId); throw; } + return cipherKey; } Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipherDomainId& domainId, @@ -327,8 +366,8 @@ Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipher const EncryptCipherRandomSalt& salt, const int64_t refreshAt, const int64_t expireAt) { - if (domainId == ENCRYPT_INVALID_DOMAIN_ID || baseCipherId == ENCRYPT_INVALID_CIPHER_KEY_ID || - salt == ENCRYPT_INVALID_RANDOM_SALT) { + if (domainId == INVALID_ENCRYPT_DOMAIN_ID || baseCipherId == INVALID_ENCRYPT_CIPHER_KEY_ID || + salt == INVALID_ENCRYPT_RANDOM_SALT) { throw encrypt_invalid_id(); } @@ -337,7 +376,7 @@ Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipher auto domainItr = domainCacheMap.find(domainId); if (domainItr == domainCacheMap.end()) { // Add mapping to track new encryption domain - Reference keyIdCache = makeReference(domainId); + Reference keyIdCache = makeReference(domainId, &size); cipherKey = keyIdCache->insertBaseCipherKey(baseCipherId, baseCipher, baseCipherLen, salt, refreshAt, expireAt); domainCacheMap.emplace(domainId, keyIdCache); @@ -354,12 +393,11 @@ Reference BlobCipherKeyCache::insertCipherKey(const EncryptCipher .detail("Salt", salt); throw; } - return cipherKey; } Reference BlobCipherKeyCache::getLatestCipherKey(const EncryptCipherDomainId& domainId) { - if (domainId == ENCRYPT_INVALID_DOMAIN_ID) { + if (domainId == INVALID_ENCRYPT_DOMAIN_ID) { TraceEvent(SevWarn, "BlobCipher.GetLatestCipherKeyInvalidID").detail("DomainId", domainId); throw encrypt_invalid_id(); } @@ -373,15 +411,20 @@ Reference BlobCipherKeyCache::getLatestCipherKey(const EncryptCip Reference cipherKey = keyIdCache->getLatestCipherKey(); // Ensure 'freshness' guarantees for the latestCipher - if (cipherKey.isValid() && cipherKey->needsRefresh()) { + if (cipherKey.isValid()) { + if (cipherKey->needsRefresh()) { #if BLOB_CIPHER_DEBUG - TraceEvent("SevDebug, BlobCipher.GetLatestNeedsRefresh") - .detail("DomainId", domainId) - .detail("Now", now()) - .detail("RefreshAt", cipherKey->getRefreshAtTS()); + TraceEvent("SevDebug, BlobCipher.GetLatestNeedsRefresh") + .detail("DomainId", domainId) + .detail("Now", now()) + .detail("RefreshAt", cipherKey->getRefreshAtTS()); #endif - - return Reference(); + ++BlobCipherMetrics::getInstance()->latestCipherKeyCacheNeedsRefresh; + return Reference(); + } + ++BlobCipherMetrics::getInstance()->latestCipherKeyCacheHit; + } else { + ++BlobCipherMetrics::getInstance()->latestCipherKeyCacheMiss; } return cipherKey; @@ -399,16 +442,21 @@ Reference BlobCipherKeyCache::getCipherKey(const EncryptCipherDom Reference cipherKey = keyIdCache->getCipherByBaseCipherId(baseCipherId, salt); // Ensure 'liveness' guarantees for the cipher - if (cipherKey.isValid() && cipherKey->isExpired()) { + if (cipherKey.isValid()) { + if (cipherKey->isExpired()) { #if BLOB_CIPHER_DEBUG - TraceEvent(SevDebug, "BlobCipher.GetCipherExpired") - .detail("DomainId", domainId) - .detail("BaseCipherId", baseCipherId) - .detail("Now", now()) - .detail("ExpireAt", cipherKey->getExpireAtTS()); + TraceEvent(SevDebug, "BlobCipher.GetCipherExpired") + .detail("DomainId", domainId) + .detail("BaseCipherId", baseCipherId) + .detail("Now", now()) + .detail("ExpireAt", cipherKey->getExpireAtTS()); #endif - - return Reference(); + ++BlobCipherMetrics::getInstance()->cipherKeyCacheExpired; + return Reference(); + } + ++BlobCipherMetrics::getInstance()->cipherKeyCacheHit; + } else { + ++BlobCipherMetrics::getInstance()->cipherKeyCacheMiss; } return cipherKey; @@ -421,6 +469,8 @@ void BlobCipherKeyCache::resetEncryptDomainId(const EncryptCipherDomainId domain } Reference keyIdCache = domainItr->second; + ASSERT(keyIdCache->getSize() <= size); + size -= keyIdCache->getSize(); keyIdCache->cleanup(); TraceEvent(SevInfo, "BlobCipher.ResetEncryptDomainId").detail("DomainId", domainId); } @@ -437,6 +487,7 @@ void BlobCipherKeyCache::cleanup() noexcept { } instance->domainCacheMap.clear(); + instance->size = 0; } std::vector> BlobCipherKeyCache::getAllCiphers(const EncryptCipherDomainId& domainId) { @@ -455,8 +506,10 @@ EncryptBlobCipherAes265Ctr::EncryptBlobCipherAes265Ctr(Reference Reference hCipherKey, const uint8_t* cipherIV, const int ivLen, - const EncryptAuthTokenMode mode) - : ctx(EVP_CIPHER_CTX_new()), textCipherKey(tCipherKey), headerCipherKey(hCipherKey), authTokenMode(mode) { + const EncryptAuthTokenMode mode, + BlobCipherMetrics::UsageType usageType) + : ctx(EVP_CIPHER_CTX_new()), textCipherKey(tCipherKey), headerCipherKey(hCipherKey), authTokenMode(mode), + usageType(usageType) { ASSERT(isEncryptHeaderAuthTokenModeValid(mode)); ASSERT_EQ(ivLen, AES_256_IV_LENGTH); memcpy(&iv[0], cipherIV, ivLen); @@ -465,8 +518,10 @@ EncryptBlobCipherAes265Ctr::EncryptBlobCipherAes265Ctr(Reference EncryptBlobCipherAes265Ctr::EncryptBlobCipherAes265Ctr(Reference tCipherKey, Reference hCipherKey, - const EncryptAuthTokenMode mode) - : ctx(EVP_CIPHER_CTX_new()), textCipherKey(tCipherKey), headerCipherKey(hCipherKey), authTokenMode(mode) { + const EncryptAuthTokenMode mode, + BlobCipherMetrics::UsageType usageType) + : ctx(EVP_CIPHER_CTX_new()), textCipherKey(tCipherKey), headerCipherKey(hCipherKey), authTokenMode(mode), + usageType(usageType) { ASSERT(isEncryptHeaderAuthTokenModeValid(mode)); deterministicRandom()->randomBytes(iv, AES_256_IV_LENGTH); init(); @@ -488,6 +543,10 @@ Reference EncryptBlobCipherAes265Ctr::encrypt(const uint8_t* plainte const int plaintextLen, BlobCipherEncryptHeader* header, Arena& arena) { + double startTime = 0.0; + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + startTime = timer_monotonic(); + } CODE_PROBE(true, "Encrypting data with BlobCipher"); memset(reinterpret_cast(header), 0, sizeof(BlobCipherEncryptHeader)); @@ -577,11 +636,18 @@ Reference EncryptBlobCipherAes265Ctr::encrypt(const uint8_t* plainte } encryptBuf->setLogicalSize(plaintextLen); + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + BlobCipherMetrics::counters(usageType).encryptCPUTimeNS += int64_t((timer_monotonic() - startTime) * 1e9); + } return encryptBuf; } Standalone EncryptBlobCipherAes265Ctr::encryptBlobGranuleChunk(const uint8_t* plaintext, const int plaintextLen) { + double startTime = 0.0; + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + startTime = timer_monotonic(); + } Standalone encrypted = makeString(plaintextLen); uint8_t* ciphertext = mutateString(encrypted); int bytes{ 0 }; @@ -605,6 +671,9 @@ Standalone EncryptBlobCipherAes265Ctr::encryptBlobGranuleChunk(const .detail("EncryptedBufLen", bytes + finalBytes); throw encrypt_ops_error(); } + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + BlobCipherMetrics::counters(usageType).encryptCPUTimeNS += int64_t((timer_monotonic() - startTime) * 1e9); + } return encrypted; } @@ -618,9 +687,10 @@ EncryptBlobCipherAes265Ctr::~EncryptBlobCipherAes265Ctr() { DecryptBlobCipherAes256Ctr::DecryptBlobCipherAes256Ctr(Reference tCipherKey, Reference hCipherKey, - const uint8_t* iv) + const uint8_t* iv, + BlobCipherMetrics::UsageType usageType) : ctx(EVP_CIPHER_CTX_new()), textCipherKey(tCipherKey), headerCipherKey(hCipherKey), - headerAuthTokenValidationDone(false), authTokensValidationDone(false) { + headerAuthTokenValidationDone(false), authTokensValidationDone(false), usageType(usageType) { if (ctx == nullptr) { throw encrypt_ops_error(); } @@ -754,6 +824,11 @@ Reference DecryptBlobCipherAes256Ctr::decrypt(const uint8_t* ciphert Arena& arena) { CODE_PROBE(true, "Decrypting data with BlobCipher"); + double startTime = 0.0; + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + startTime = timer_monotonic(); + } + verifyEncryptHeaderMetadata(header); if (header.flags.authTokenMode != ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE && !headerCipherKey.isValid()) { @@ -797,6 +872,9 @@ Reference DecryptBlobCipherAes256Ctr::decrypt(const uint8_t* ciphert } decrypted->setLogicalSize(ciphertextLen); + if (CLIENT_KNOBS->ENABLE_ENCRYPTION_CPU_TIME_LOGGING) { + BlobCipherMetrics::counters(usageType).decryptCPUTimeNS += int64_t((timer_monotonic() - startTime) * 1e9); + } return decrypted; } @@ -912,7 +990,7 @@ TEST_CASE("flow/BlobCipher") { cipherKeyCache->getLatestCipherKey(deterministicRandom()->randomInt(minDomainId, maxDomainId)); ASSERT(!latestKeyNonexists.isValid()); try { - cipherKeyCache->getLatestCipherKey(ENCRYPT_INVALID_DOMAIN_ID); + cipherKeyCache->getLatestCipherKey(INVALID_ENCRYPT_DOMAIN_ID); ASSERT(false); // shouldn't get here } catch (Error& e) { ASSERT_EQ(e.code(), error_code_encrypt_invalid_id); @@ -1017,8 +1095,12 @@ TEST_CASE("flow/BlobCipher") { { TraceEvent("NoneAuthMode.Start").log(); - EncryptBlobCipherAes265Ctr encryptor( - cipherKey, Reference(), iv, AES_256_IV_LENGTH, ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE); + EncryptBlobCipherAes265Ctr encryptor(cipherKey, + Reference(), + iv, + AES_256_IV_LENGTH, + ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE, + BlobCipherMetrics::TEST); BlobCipherEncryptHeader header; Reference encrypted = encryptor.encrypt(&orgData[0], bufLen, &header, arena); @@ -1038,7 +1120,8 @@ TEST_CASE("flow/BlobCipher") { header.cipherTextDetails.baseCipherId, header.cipherTextDetails.salt); ASSERT(tCipherKeyKey->isEqual(cipherKey)); - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, Reference(), &header.iv[0]); + DecryptBlobCipherAes256Ctr decryptor( + tCipherKeyKey, Reference(), &header.iv[0], BlobCipherMetrics::TEST); Reference decrypted = decryptor.decrypt(encrypted->begin(), bufLen, header, arena); ASSERT_EQ(decrypted->getLogicalSize(), bufLen); @@ -1053,7 +1136,8 @@ TEST_CASE("flow/BlobCipher") { headerCopy.flags.headerVersion += 1; try { encrypted = encryptor.encrypt(&orgData[0], bufLen, &header, arena); - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, Reference(), header.iv); + DecryptBlobCipherAes256Ctr decryptor( + tCipherKeyKey, Reference(), header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1069,7 +1153,8 @@ TEST_CASE("flow/BlobCipher") { headerCopy.flags.encryptMode += 1; try { encrypted = encryptor.encrypt(&orgData[0], bufLen, &header, arena); - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, Reference(), header.iv); + DecryptBlobCipherAes256Ctr decryptor( + tCipherKeyKey, Reference(), header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1085,7 +1170,8 @@ TEST_CASE("flow/BlobCipher") { memcpy(encrypted->begin(), &temp[0], bufLen); int tIdx = deterministicRandom()->randomInt(0, bufLen - 1); temp[tIdx] += 1; - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, Reference(), header.iv); + DecryptBlobCipherAes256Ctr decryptor( + tCipherKeyKey, Reference(), header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(&temp[0], bufLen, header, arena); } catch (Error& e) { // No authToken, hence, no corruption detection supported @@ -1099,8 +1185,12 @@ TEST_CASE("flow/BlobCipher") { { TraceEvent("SingleAuthMode.Start").log(); - EncryptBlobCipherAes265Ctr encryptor( - cipherKey, headerCipherKey, iv, AES_256_IV_LENGTH, ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + EncryptBlobCipherAes265Ctr encryptor(cipherKey, + headerCipherKey, + iv, + AES_256_IV_LENGTH, + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + BlobCipherMetrics::TEST); BlobCipherEncryptHeader header; Reference encrypted = encryptor.encrypt(&orgData[0], bufLen, &header, arena); @@ -1125,7 +1215,7 @@ TEST_CASE("flow/BlobCipher") { header.cipherHeaderDetails.baseCipherId, header.cipherHeaderDetails.salt); ASSERT(tCipherKeyKey->isEqual(cipherKey)); - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); Reference decrypted = decryptor.decrypt(encrypted->begin(), bufLen, header, arena); ASSERT_EQ(decrypted->getLogicalSize(), bufLen); @@ -1140,7 +1230,7 @@ TEST_CASE("flow/BlobCipher") { sizeof(BlobCipherEncryptHeader)); headerCopy.flags.headerVersion += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1156,7 +1246,7 @@ TEST_CASE("flow/BlobCipher") { sizeof(BlobCipherEncryptHeader)); headerCopy.flags.encryptMode += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1173,7 +1263,7 @@ TEST_CASE("flow/BlobCipher") { int hIdx = deterministicRandom()->randomInt(0, AUTH_TOKEN_SIZE - 1); headerCopy.singleAuthToken.authToken[hIdx] += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1189,7 +1279,7 @@ TEST_CASE("flow/BlobCipher") { memcpy(encrypted->begin(), &temp[0], bufLen); int tIdx = deterministicRandom()->randomInt(0, bufLen - 1); temp[tIdx] += 1; - DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKeyKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(&temp[0], bufLen, header, arena); } catch (Error& e) { if (e.code() != error_code_encrypt_header_authtoken_mismatch) { @@ -1204,8 +1294,12 @@ TEST_CASE("flow/BlobCipher") { { TraceEvent("MultiAuthMode.Start").log(); - EncryptBlobCipherAes265Ctr encryptor( - cipherKey, headerCipherKey, iv, AES_256_IV_LENGTH, ENCRYPT_HEADER_AUTH_TOKEN_MODE_MULTI); + EncryptBlobCipherAes265Ctr encryptor(cipherKey, + headerCipherKey, + iv, + AES_256_IV_LENGTH, + ENCRYPT_HEADER_AUTH_TOKEN_MODE_MULTI, + BlobCipherMetrics::TEST); BlobCipherEncryptHeader header; Reference encrypted = encryptor.encrypt(&orgData[0], bufLen, &header, arena); @@ -1231,7 +1325,7 @@ TEST_CASE("flow/BlobCipher") { header.cipherHeaderDetails.salt); ASSERT(tCipherKey->isEqual(cipherKey)); - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); Reference decrypted = decryptor.decrypt(encrypted->begin(), bufLen, header, arena); ASSERT_EQ(decrypted->getLogicalSize(), bufLen); @@ -1246,7 +1340,7 @@ TEST_CASE("flow/BlobCipher") { sizeof(BlobCipherEncryptHeader)); headerCopy.flags.headerVersion += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1262,7 +1356,7 @@ TEST_CASE("flow/BlobCipher") { sizeof(BlobCipherEncryptHeader)); headerCopy.flags.encryptMode += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1279,7 +1373,7 @@ TEST_CASE("flow/BlobCipher") { int hIdx = deterministicRandom()->randomInt(0, AUTH_TOKEN_SIZE - 1); headerCopy.multiAuthTokens.cipherTextAuthToken[hIdx] += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1296,7 +1390,7 @@ TEST_CASE("flow/BlobCipher") { hIdx = deterministicRandom()->randomInt(0, AUTH_TOKEN_SIZE - 1); headerCopy.multiAuthTokens.headerAuthToken[hIdx] += 1; try { - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(encrypted->begin(), bufLen, headerCopy, arena); ASSERT(false); // error expected } catch (Error& e) { @@ -1311,7 +1405,7 @@ TEST_CASE("flow/BlobCipher") { memcpy(encrypted->begin(), &temp[0], bufLen); int tIdx = deterministicRandom()->randomInt(0, bufLen - 1); temp[tIdx] += 1; - DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(tCipherKey, hCipherKey, header.iv, BlobCipherMetrics::TEST); decrypted = decryptor.decrypt(&temp[0], bufLen, header, arena); } catch (Error& e) { if (e.code() != error_code_encrypt_header_authtoken_mismatch) { diff --git a/fdbclient/BlobGranuleFiles.cpp b/fdbclient/BlobGranuleFiles.cpp index 0e402cedb1..5d700c5950 100644 --- a/fdbclient/BlobGranuleFiles.cpp +++ b/fdbclient/BlobGranuleFiles.cpp @@ -20,6 +20,7 @@ #include "fdbclient/BlobGranuleFiles.h" +#include "fdbclient/BlobCipher.h" #include "fdbclient/BlobGranuleCommon.h" #include "fdbclient/ClientKnobs.h" #include "fdbclient/CommitTransaction.h" @@ -27,7 +28,6 @@ #include "fdbclient/SystemData.h" // for allKeys unit test - could remove #include "flow/Arena.h" -#include "flow/BlobCipher.h" #include "flow/CompressionUtils.h" #include "flow/DeterministicRandom.h" #include "flow/IRandom.h" @@ -304,7 +304,8 @@ struct IndexBlockRef { eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin(), AES_256_IV_LENGTH, - ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + BlobCipherMetrics::BLOB_GRANULE); Value serializedBuff = ObjectWriter::toValue(block, IncludeVersion(ProtocolVersion::withBlobGranuleFile())); BlobCipherEncryptHeader header; buffer = encryptor.encrypt(serializedBuff.contents().begin(), serializedBuff.contents().size(), &header, arena) @@ -332,7 +333,8 @@ struct IndexBlockRef { validateEncryptionHeaderDetails(eKeys, header, cipherKeysCtx.ivRef); - DecryptBlobCipherAes256Ctr decryptor(eKeys.textCipherKey, eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin()); + DecryptBlobCipherAes256Ctr decryptor( + eKeys.textCipherKey, eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin(), BlobCipherMetrics::BLOB_GRANULE); StringRef decrypted = decryptor.decrypt(idxRef.buffer.begin(), idxRef.buffer.size(), header, arena)->toStringRef(); @@ -425,7 +427,8 @@ struct IndexBlobGranuleFileChunkRef { eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin(), AES_256_IV_LENGTH, - ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + BlobCipherMetrics::BLOB_GRANULE); BlobCipherEncryptHeader header; chunkRef.buffer = encryptor.encrypt(chunkRef.buffer.begin(), chunkRef.buffer.size(), &header, arena)->toStringRef(); @@ -454,7 +457,8 @@ struct IndexBlobGranuleFileChunkRef { validateEncryptionHeaderDetails(eKeys, header, cipherKeysCtx.ivRef); - DecryptBlobCipherAes256Ctr decryptor(eKeys.textCipherKey, eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin()); + DecryptBlobCipherAes256Ctr decryptor( + eKeys.textCipherKey, eKeys.headerCipherKey, cipherKeysCtx.ivRef.begin(), BlobCipherMetrics::BLOB_GRANULE); StringRef decrypted = decryptor.decrypt(chunkRef.buffer.begin(), chunkRef.buffer.size(), header, arena)->toStringRef(); @@ -1084,65 +1088,6 @@ ParsedDeltaBoundaryRef deltaAtVersion(const DeltaBoundaryRef& delta, Version beg } } -void applyDeltasSorted(const Standalone>& sortedDeltas, - bool startClear, - std::map& dataMap) { - if (sortedDeltas.empty() && !startClear) { - return; - } - - // sorted merge of 2 iterators - bool prevClear = startClear; - auto deltaIt = sortedDeltas.begin(); - auto snapshotIt = dataMap.begin(); - - while (deltaIt != sortedDeltas.end() && snapshotIt != dataMap.end()) { - if (deltaIt->key < snapshotIt->first) { - // Delta is lower than snapshot. Insert new row, if the delta is a set. Ignore point clear and noop - if (deltaIt->isSet()) { - snapshotIt = dataMap.insert(snapshotIt, { deltaIt->key, deltaIt->value }); - snapshotIt++; - } - prevClear = deltaIt->clearAfter; - deltaIt++; - } else if (snapshotIt->first < deltaIt->key) { - // Snapshot is lower than delta. Erase the current entry if the previous delta was a clearAfter - if (prevClear) { - snapshotIt = dataMap.erase(snapshotIt); - } else { - snapshotIt++; - } - } else { - // Delta and snapshot are for the same key. The delta is newer, so if it is a set, update the value, else if - // it's a clear, delete the value (ignore noop) - if (deltaIt->isSet()) { - snapshotIt->second = deltaIt->value; - } else if (deltaIt->isClear()) { - snapshotIt = dataMap.erase(snapshotIt); - } - if (!deltaIt->isClear()) { - snapshotIt++; - } - prevClear = deltaIt->clearAfter; - deltaIt++; - } - } - // Either we are out of deltas or out of snapshots. - // if snapshot remaining and prevClear last delta set, clear the rest of the map - if (prevClear && snapshotIt != dataMap.end()) { - CODE_PROBE(true, "last delta range cleared end of snapshot"); - dataMap.erase(snapshotIt, dataMap.end()); - } - // Apply remaining sets from delta, with no remaining snapshot - while (deltaIt != sortedDeltas.end()) { - if (deltaIt->isSet()) { - CODE_PROBE(true, "deltas past end of snapshot"); - snapshotIt = dataMap.insert(snapshotIt, { deltaIt->key, deltaIt->value }); - } - deltaIt++; - } -} - // The arena owns the BoundaryDeltaRef struct data but the StringRef pointers point to data in deltaData, to avoid extra // copying Standalone> loadChunkedDeltaFile(const Standalone& fileNameRef, @@ -2035,7 +1980,7 @@ struct KeyValueGen { sharedPrefix = sharedPrefix.substr(0, sharedPrefixLen) + "_"; targetValueLength = deterministicRandom()->randomExp(0, 12); allRange = KeyRangeRef(StringRef(sharedPrefix), - sharedPrefix.size() == 0 ? LiteralStringRef("\xff") : strinc(StringRef(sharedPrefix))); + sharedPrefix.size() == 0 ? "\xff"_sr : strinc(StringRef(sharedPrefix))); if (deterministicRandom()->coinflip()) { clearFrequency = 0.0; @@ -2210,7 +2155,6 @@ Standalone genSnapshot(KeyValueGen& kvGen, int targetDataBytes) while (totalDataBytes < targetDataBytes) { Optional key = kvGen.newKey(); if (!key.present()) { - CODE_PROBE(true, "snapshot unit test keyspace full"); break; } StringRef value = kvGen.value(); @@ -2355,9 +2299,9 @@ TEST_CASE("/blobgranule/files/snapshotFormatUnitTest") { } checkSnapshotEmpty(serialized, normalKeys.begin, data.front().key, kvGen.cipherKeys); - checkSnapshotEmpty(serialized, normalKeys.begin, LiteralStringRef("\x00"), kvGen.cipherKeys); + checkSnapshotEmpty(serialized, normalKeys.begin, "\x00"_sr, kvGen.cipherKeys); checkSnapshotEmpty(serialized, keyAfter(data.back().key), normalKeys.end, kvGen.cipherKeys); - checkSnapshotEmpty(serialized, LiteralStringRef("\xfe"), normalKeys.end, kvGen.cipherKeys); + checkSnapshotEmpty(serialized, "\xfe"_sr, normalKeys.end, kvGen.cipherKeys); fmt::print("Snapshot format test done!\n"); diff --git a/fdbclient/BlobGranuleReader.actor.cpp b/fdbclient/BlobGranuleReader.actor.cpp index 9b24380d2c..583da353f7 100644 --- a/fdbclient/BlobGranuleReader.actor.cpp +++ b/fdbclient/BlobGranuleReader.actor.cpp @@ -167,7 +167,7 @@ TEST_CASE("/fdbserver/blobgranule/isRangeCoveredByBlob") { } // check '' to \xff - { ASSERT(isRangeFullyCovered(KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff")), chunks) == false); } + { ASSERT(isRangeFullyCovered(KeyRangeRef(""_sr, "\xff"_sr), chunks) == false); } // check {key_a1, key_a9} { ASSERT(isRangeFullyCovered(KeyRangeRef("key_a1"_sr, "key_a9"_sr), chunks)); } diff --git a/fdbclient/BuildFlags.h.in b/fdbclient/BuildFlags.h.in index 6f94c540f8..b55c7e55dd 100644 --- a/fdbclient/BuildFlags.h.in +++ b/fdbclient/BuildFlags.h.in @@ -33,6 +33,9 @@ #define C_VERSION_MINOR 0 #endif +const char* kDate = __DATE__; +const char* kTime = __TIME__; + // FDB info. const std::string kGitHash = "@CURRENT_GIT_VERSION_WNL@"; const std::string kFdbVersion = "@FDB_VERSION@"; @@ -43,7 +46,7 @@ const std::string kArch = "@CMAKE_SYSTEM@"; const std::string kCompiler = "@CMAKE_CXX_COMPILER_ID@"; // Library versions. -const std::string kBoostVersion = "@Boost_LIB_VERSION@"; +const std::string kBoostVersion = BOOST_LIB_VERSION; // Build info and flags. const std::string kCMakeVersion = "@CMAKE_VERSION@"; @@ -61,6 +64,9 @@ std::string jsonBuildInformation() { json_spirit::mValue json; JSONDoc doc(json); + doc.create("build_date") = kDate; + doc.create("build_time") = kTime; + doc.create("git_hash") = kGitHash; doc.create("fdb_version") = kFdbVersion; diff --git a/fdbclient/ClientKnobs.cpp b/fdbclient/ClientKnobs.cpp index 7f543406b3..6b15c1aa4d 100644 --- a/fdbclient/ClientKnobs.cpp +++ b/fdbclient/ClientKnobs.cpp @@ -291,6 +291,8 @@ void ClientKnobs::initialize(Randomize randomize) { init( METACLUSTER_ASSIGNMENT_FIRST_CHOICE_DELAY, 1.0 ); if ( randomize && BUGGIFY ) METACLUSTER_ASSIGNMENT_FIRST_CHOICE_DELAY = deterministicRandom()->random01() * 60; init( METACLUSTER_ASSIGNMENT_AVAILABILITY_TIMEOUT, 10.0 ); if ( randomize && BUGGIFY ) METACLUSTER_ASSIGNMENT_AVAILABILITY_TIMEOUT = 1 + deterministicRandom()->random01() * 59; init( TENANT_ENTRY_CACHE_LIST_REFRESH_INTERVAL, 2 ); if( randomize && BUGGIFY ) TENANT_ENTRY_CACHE_LIST_REFRESH_INTERVAL = deterministicRandom()->randomInt(1, 10); + + init( ENABLE_ENCRYPTION_CPU_TIME_LOGGING, false ); // clang-format on } diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index 80d4a16cc9..97a41e743c 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -37,11 +37,11 @@ #include "flow/actorcompiler.h" // has to be last include -const Key DatabaseBackupAgent::keyAddPrefix = LiteralStringRef("add_prefix"); -const Key DatabaseBackupAgent::keyRemovePrefix = LiteralStringRef("remove_prefix"); -const Key DatabaseBackupAgent::keyRangeVersions = LiteralStringRef("range_versions"); -const Key DatabaseBackupAgent::keyCopyStop = LiteralStringRef("copy_stop"); -const Key DatabaseBackupAgent::keyDatabasesInSync = LiteralStringRef("databases_in_sync"); +const Key DatabaseBackupAgent::keyAddPrefix = "add_prefix"_sr; +const Key DatabaseBackupAgent::keyRemovePrefix = "remove_prefix"_sr; +const Key DatabaseBackupAgent::keyRangeVersions = "range_versions"_sr; +const Key DatabaseBackupAgent::keyCopyStop = "copy_stop"_sr; +const Key DatabaseBackupAgent::keyDatabasesInSync = "databases_in_sync"_sr; const int DatabaseBackupAgent::LATEST_DR_VERSION = 1; DatabaseBackupAgent::DatabaseBackupAgent() @@ -75,8 +75,7 @@ DatabaseBackupAgent::DatabaseBackupAgent(Database src) class DRConfig { public: DRConfig(UID uid = UID()) - : uid(uid), - configSpace(uidPrefixKey(LiteralStringRef("uid->config/").withPrefix(databaseBackupPrefixRange.begin), uid)) {} + : uid(uid), configSpace(uidPrefixKey("uid->config/"_sr.withPrefix(databaseBackupPrefixRange.begin), uid)) {} DRConfig(Reference task) : DRConfig(BinaryReader::fromStringRef(task->params[BackupAgentBase::keyConfigLogUid], Unversioned())) {} @@ -203,7 +202,7 @@ struct BackupRangeTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } ACTOR static Future _execute(Database cx, @@ -536,9 +535,9 @@ struct BackupRangeTaskFunc : TaskFuncBase { return Void(); } }; -StringRef BackupRangeTaskFunc::name = LiteralStringRef("dr_backup_range"); -const Key BackupRangeTaskFunc::keyAddBackupRangeTasks = LiteralStringRef("addBackupRangeTasks"); -const Key BackupRangeTaskFunc::keyBackupRangeBeginKey = LiteralStringRef("backupRangeBeginKey"); +StringRef BackupRangeTaskFunc::name = "dr_backup_range"_sr; +const Key BackupRangeTaskFunc::keyAddBackupRangeTasks = "addBackupRangeTasks"_sr; +const Key BackupRangeTaskFunc::keyBackupRangeBeginKey = "backupRangeBeginKey"_sr; REGISTER_TASKFUNC(BackupRangeTaskFunc); struct FinishFullBackupTaskFunc : TaskFuncBase { @@ -588,7 +587,7 @@ struct FinishFullBackupTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -606,7 +605,7 @@ struct FinishFullBackupTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef FinishFullBackupTaskFunc::name = LiteralStringRef("dr_finish_full_backup"); +StringRef FinishFullBackupTaskFunc::name = "dr_finish_full_backup"_sr; REGISTER_TASKFUNC(FinishFullBackupTaskFunc); struct EraseLogRangeTaskFunc : TaskFuncBase { @@ -683,7 +682,7 @@ struct EraseLogRangeTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } ACTOR static Future _finish(Reference tr, @@ -697,7 +696,7 @@ struct EraseLogRangeTaskFunc : TaskFuncBase { return Void(); } }; -StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("dr_erase_log_range"); +StringRef EraseLogRangeTaskFunc::name = "dr_erase_log_range"_sr; REGISTER_TASKFUNC(EraseLogRangeTaskFunc); struct CopyLogRangeTaskFunc : TaskFuncBase { @@ -958,7 +957,7 @@ struct CopyLogRangeTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } ACTOR static Future _finish(Reference tr, @@ -989,8 +988,8 @@ struct CopyLogRangeTaskFunc : TaskFuncBase { return Void(); } }; -StringRef CopyLogRangeTaskFunc::name = LiteralStringRef("dr_copy_log_range"); -const Key CopyLogRangeTaskFunc::keyNextBeginVersion = LiteralStringRef("nextBeginVersion"); +StringRef CopyLogRangeTaskFunc::name = "dr_copy_log_range"_sr; +const Key CopyLogRangeTaskFunc::keyNextBeginVersion = "nextBeginVersion"_sr; REGISTER_TASKFUNC(CopyLogRangeTaskFunc); struct CopyLogsTaskFunc : TaskFuncBase { @@ -1125,7 +1124,7 @@ struct CopyLogsTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -1143,7 +1142,7 @@ struct CopyLogsTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef CopyLogsTaskFunc::name = LiteralStringRef("dr_copy_logs"); +StringRef CopyLogsTaskFunc::name = "dr_copy_logs"_sr; REGISTER_TASKFUNC(CopyLogsTaskFunc); struct FinishedFullBackupTaskFunc : TaskFuncBase { @@ -1235,7 +1234,7 @@ struct FinishedFullBackupTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } ACTOR static Future _finish(Reference tr, @@ -1283,8 +1282,8 @@ struct FinishedFullBackupTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef FinishedFullBackupTaskFunc::name = LiteralStringRef("dr_finished_full_backup"); -const Key FinishedFullBackupTaskFunc::keyInsertTask = LiteralStringRef("insertTask"); +StringRef FinishedFullBackupTaskFunc::name = "dr_finished_full_backup"_sr; +const Key FinishedFullBackupTaskFunc::keyInsertTask = "insertTask"_sr; REGISTER_TASKFUNC(FinishedFullBackupTaskFunc); struct CopyDiffLogsTaskFunc : TaskFuncBase { @@ -1396,7 +1395,7 @@ struct CopyDiffLogsTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -1414,7 +1413,7 @@ struct CopyDiffLogsTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef CopyDiffLogsTaskFunc::name = LiteralStringRef("dr_copy_diff_logs"); +StringRef CopyDiffLogsTaskFunc::name = "dr_copy_diff_logs"_sr; REGISTER_TASKFUNC(CopyDiffLogsTaskFunc); // Skip unneeded EraseLogRangeTaskFunc in 5.1 @@ -1446,7 +1445,7 @@ struct SkipOldEraseLogRangeTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef SkipOldEraseLogRangeTaskFunc::name = LiteralStringRef("dr_skip_legacy_task"); +StringRef SkipOldEraseLogRangeTaskFunc::name = "dr_skip_legacy_task"_sr; REGISTER_TASKFUNC(SkipOldEraseLogRangeTaskFunc); REGISTER_TASKFUNC_ALIAS(SkipOldEraseLogRangeTaskFunc, db_erase_log_range); @@ -1652,7 +1651,7 @@ struct OldCopyLogRangeTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } ACTOR static Future _finish(Reference tr, @@ -1683,8 +1682,8 @@ struct OldCopyLogRangeTaskFunc : TaskFuncBase { return Void(); } }; -StringRef OldCopyLogRangeTaskFunc::name = LiteralStringRef("db_copy_log_range"); -const Key OldCopyLogRangeTaskFunc::keyNextBeginVersion = LiteralStringRef("nextBeginVersion"); +StringRef OldCopyLogRangeTaskFunc::name = "db_copy_log_range"_sr; +const Key OldCopyLogRangeTaskFunc::keyNextBeginVersion = "nextBeginVersion"_sr; REGISTER_TASKFUNC(OldCopyLogRangeTaskFunc); struct AbortOldBackupTaskFunc : TaskFuncBase { @@ -1753,7 +1752,7 @@ struct AbortOldBackupTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -1771,7 +1770,7 @@ struct AbortOldBackupTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef AbortOldBackupTaskFunc::name = LiteralStringRef("dr_abort_legacy_backup"); +StringRef AbortOldBackupTaskFunc::name = "dr_abort_legacy_backup"_sr; REGISTER_TASKFUNC(AbortOldBackupTaskFunc); REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_backup_range); REGISTER_TASKFUNC_ALIAS(AbortOldBackupTaskFunc, db_finish_full_backup); @@ -1918,7 +1917,7 @@ struct CopyDiffLogsUpgradeTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef CopyDiffLogsUpgradeTaskFunc::name = LiteralStringRef("db_copy_diff_logs"); +StringRef CopyDiffLogsUpgradeTaskFunc::name = "db_copy_diff_logs"_sr; REGISTER_TASKFUNC(CopyDiffLogsUpgradeTaskFunc); struct BackupRestorableTaskFunc : TaskFuncBase { @@ -2031,7 +2030,7 @@ struct BackupRestorableTaskFunc : TaskFuncBase { task, parentTask->params[Task::reservedTaskParamValidKey], task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -2049,7 +2048,7 @@ struct BackupRestorableTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef BackupRestorableTaskFunc::name = LiteralStringRef("dr_backup_restorable"); +StringRef BackupRestorableTaskFunc::name = "dr_backup_restorable"_sr; REGISTER_TASKFUNC(BackupRestorableTaskFunc); struct StartFullBackupTaskFunc : TaskFuncBase { @@ -2281,7 +2280,7 @@ struct StartFullBackupTaskFunc : TaskFuncBase { task->params[BackupAgentBase::keyConfigBackupRanges] = keyConfigBackupRanges; task->params[BackupAgentBase::keyTagName] = tagName; task->params[DatabaseBackupAgent::keyDatabasesInSync] = - backupAction == DatabaseBackupAgent::PreBackupAction::NONE ? LiteralStringRef("t") : LiteralStringRef("f"); + backupAction == DatabaseBackupAgent::PreBackupAction::NONE ? "t"_sr : "f"_sr; if (!waitFor) { return taskBucket->addTask(tr, @@ -2301,7 +2300,7 @@ struct StartFullBackupTaskFunc : TaskFuncBase { .get(logUid) .pack(BackupAgentBase::keyFolderId), task->params[BackupAgentBase::keyFolderId])); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -2319,7 +2318,7 @@ struct StartFullBackupTaskFunc : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef StartFullBackupTaskFunc::name = LiteralStringRef("dr_start_full_backup"); +StringRef StartFullBackupTaskFunc::name = "dr_start_full_backup"_sr; REGISTER_TASKFUNC(StartFullBackupTaskFunc); } // namespace dbBackup diff --git a/fdbclient/DatabaseConfiguration.cpp b/fdbclient/DatabaseConfiguration.cpp index 2f337af490..76fded095c 100644 --- a/fdbclient/DatabaseConfiguration.cpp +++ b/fdbclient/DatabaseConfiguration.cpp @@ -66,6 +66,16 @@ void parse(int* i, ValueRef const& v) { *i = atoi(v.toString().c_str()); } +void parse(int64_t* i, ValueRef const& v) { + // FIXME: Sanity checking + *i = atoll(v.toString().c_str()); +} + +void parse(double* i, ValueRef const& v) { + // FIXME: Sanity checking + *i = atof(v.toString().c_str()); +} + void parseReplicationPolicy(Reference* policy, ValueRef const& v) { BinaryReader reader(v, IncludeVersion()); serializeReplicationPolicy(reader, *policy); @@ -550,38 +560,38 @@ bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) { KeyRef ck = key.removePrefix(configKeysPrefix); int type; - if (ck == LiteralStringRef("initialized")) { + if (ck == "initialized"_sr) { initialized = true; - } else if (ck == LiteralStringRef("commit_proxies")) { + } else if (ck == "commit_proxies"_sr) { commitProxyCount = toInt(value); if (commitProxyCount == -1) overwriteProxiesCount(); - } else if (ck == LiteralStringRef("grv_proxies")) { + } else if (ck == "grv_proxies"_sr) { grvProxyCount = toInt(value); if (grvProxyCount == -1) overwriteProxiesCount(); - } else if (ck == LiteralStringRef("resolvers")) { + } else if (ck == "resolvers"_sr) { parse(&resolverCount, value); - } else if (ck == LiteralStringRef("logs")) { + } else if (ck == "logs"_sr) { parse(&desiredTLogCount, value); - } else if (ck == LiteralStringRef("log_replicas")) { + } else if (ck == "log_replicas"_sr) { parse(&tLogReplicationFactor, value); tLogWriteAntiQuorum = std::min(tLogWriteAntiQuorum, tLogReplicationFactor / 2); - } else if (ck == LiteralStringRef("log_anti_quorum")) { + } else if (ck == "log_anti_quorum"_sr) { parse(&tLogWriteAntiQuorum, value); if (tLogReplicationFactor > 0) { tLogWriteAntiQuorum = std::min(tLogWriteAntiQuorum, tLogReplicationFactor / 2); } - } else if (ck == LiteralStringRef("storage_replicas")) { + } else if (ck == "storage_replicas"_sr) { parse(&storageTeamSize, value); - } else if (ck == LiteralStringRef("tss_count")) { + } else if (ck == "tss_count"_sr) { parse(&desiredTSSCount, value); - } else if (ck == LiteralStringRef("log_version")) { + } else if (ck == "log_version"_sr) { parse((&type), value); type = std::max((int)TLogVersion::MIN_RECRUITABLE, type); type = std::min((int)TLogVersion::MAX_SUPPORTED, type); tLogVersion = (TLogVersion::Version)type; - } else if (ck == LiteralStringRef("log_engine")) { + } else if (ck == "log_engine"_sr) { parse((&type), value); tLogDataStoreType = (KeyValueStoreType::StoreType)type; // TODO: Remove this once Redwood works as a log engine @@ -592,62 +602,62 @@ bool DatabaseConfiguration::setInternal(KeyRef key, ValueRef value) { if (tLogDataStoreType == KeyValueStoreType::MEMORY_RADIXTREE) { tLogDataStoreType = KeyValueStoreType::SSD_BTREE_V2; } - } else if (ck == LiteralStringRef("log_spill")) { + } else if (ck == "log_spill"_sr) { parse((&type), value); tLogSpillType = (TLogSpillType::SpillType)type; - } else if (ck == LiteralStringRef("storage_engine")) { + } else if (ck == "storage_engine"_sr) { parse((&type), value); storageServerStoreType = (KeyValueStoreType::StoreType)type; - } else if (ck == LiteralStringRef("tss_storage_engine")) { + } else if (ck == "tss_storage_engine"_sr) { parse((&type), value); testingStorageServerStoreType = (KeyValueStoreType::StoreType)type; - } else if (ck == LiteralStringRef("auto_commit_proxies")) { + } else if (ck == "auto_commit_proxies"_sr) { parse(&autoCommitProxyCount, value); - } else if (ck == LiteralStringRef("auto_grv_proxies")) { + } else if (ck == "auto_grv_proxies"_sr) { parse(&autoGrvProxyCount, value); - } else if (ck == LiteralStringRef("auto_resolvers")) { + } else if (ck == "auto_resolvers"_sr) { parse(&autoResolverCount, value); - } else if (ck == LiteralStringRef("auto_logs")) { + } else if (ck == "auto_logs"_sr) { parse(&autoDesiredTLogCount, value); - } else if (ck == LiteralStringRef("storage_replication_policy")) { + } else if (ck == "storage_replication_policy"_sr) { parseReplicationPolicy(&storagePolicy, value); - } else if (ck == LiteralStringRef("log_replication_policy")) { + } else if (ck == "log_replication_policy"_sr) { parseReplicationPolicy(&tLogPolicy, value); - } else if (ck == LiteralStringRef("log_routers")) { + } else if (ck == "log_routers"_sr) { parse(&desiredLogRouterCount, value); - } else if (ck == LiteralStringRef("remote_logs")) { + } else if (ck == "remote_logs"_sr) { parse(&remoteDesiredTLogCount, value); - } else if (ck == LiteralStringRef("remote_log_replicas")) { + } else if (ck == "remote_log_replicas"_sr) { parse(&remoteTLogReplicationFactor, value); - } else if (ck == LiteralStringRef("remote_log_policy")) { + } else if (ck == "remote_log_policy"_sr) { parseReplicationPolicy(&remoteTLogPolicy, value); - } else if (ck == LiteralStringRef("backup_worker_enabled")) { + } else if (ck == "backup_worker_enabled"_sr) { parse((&type), value); backupWorkerEnabled = (type != 0); - } else if (ck == LiteralStringRef("usable_regions")) { + } else if (ck == "usable_regions"_sr) { parse(&usableRegions, value); - } else if (ck == LiteralStringRef("repopulate_anti_quorum")) { + } else if (ck == "repopulate_anti_quorum"_sr) { parse(&repopulateRegionAntiQuorum, value); - } else if (ck == LiteralStringRef("regions")) { + } else if (ck == "regions"_sr) { parse(®ions, value); - } else if (ck == LiteralStringRef("perpetual_storage_wiggle")) { + } else if (ck == "perpetual_storage_wiggle"_sr) { parse(&perpetualStorageWiggleSpeed, value); - } else if (ck == LiteralStringRef("perpetual_storage_wiggle_locality")) { + } else if (ck == "perpetual_storage_wiggle_locality"_sr) { if (!isValidPerpetualStorageWiggleLocality(value.toString())) { return false; } perpetualStorageWiggleLocality = value.toString(); - } else if (ck == LiteralStringRef("storage_migration_type")) { + } else if (ck == "storage_migration_type"_sr) { parse((&type), value); storageMigrationType = (StorageMigrationType::MigrationType)type; - } else if (ck == LiteralStringRef("tenant_mode")) { + } else if (ck == "tenant_mode"_sr) { tenantMode = TenantMode::fromValue(value); - } else if (ck == LiteralStringRef("proxies")) { + } else if (ck == "proxies"_sr) { overwriteProxiesCount(); - } else if (ck == LiteralStringRef("blob_granules_enabled")) { + } else if (ck == "blob_granules_enabled"_sr) { parse((&type), value); blobGranulesEnabled = (type != 0); - } else if (ck == LiteralStringRef("encryption_at_rest_mode")) { + } else if (ck == "encryption_at_rest_mode"_sr) { encryptionAtRestMode = EncryptionAtRestMode::fromValue(value); } else { return false; diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index f579ce51a5..de6765a156 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -90,7 +90,7 @@ std::string secondsToTimeFormat(int64_t seconds) { return format("%lld second(s)", seconds); } -const Key FileBackupAgent::keyLastRestorable = LiteralStringRef("last_restorable"); +const Key FileBackupAgent::keyLastRestorable = "last_restorable"_sr; // For convenience typedef FileBackupAgent::ERestoreState ERestoreState; @@ -98,19 +98,19 @@ typedef FileBackupAgent::ERestoreState ERestoreState; StringRef FileBackupAgent::restoreStateText(ERestoreState id) { switch (id) { case ERestoreState::UNITIALIZED: - return LiteralStringRef("unitialized"); + return "unitialized"_sr; case ERestoreState::QUEUED: - return LiteralStringRef("queued"); + return "queued"_sr; case ERestoreState::STARTING: - return LiteralStringRef("starting"); + return "starting"_sr; case ERestoreState::RUNNING: - return LiteralStringRef("running"); + return "running"_sr; case ERestoreState::COMPLETED: - return LiteralStringRef("completed"); + return "completed"_sr; case ERestoreState::ABORTED: - return LiteralStringRef("aborted"); + return "aborted"_sr; default: - return LiteralStringRef("Unknown"); + return "Unknown"_sr; } } @@ -162,7 +162,7 @@ public: return configSpace.pack(LiteralStringRef(__FUNCTION__)); } // Get the source container as a bare URL, without creating a container instance - KeyBackedProperty sourceContainerURL() { return configSpace.pack(LiteralStringRef("sourceContainer")); } + KeyBackedProperty sourceContainerURL() { return configSpace.pack("sourceContainer"_sr); } // Total bytes written by all log and range restore tasks. KeyBackedBinaryValue bytesWritten() { return configSpace.pack(LiteralStringRef(__FUNCTION__)); } @@ -775,8 +775,7 @@ ACTOR static Future abortFiveZeroBackup(FileBackupAgent* backupAgent, state Subspace statusSpace = backupAgent->subspace.get(BackupAgentBase::keyStates).get(uid.toString()); state Subspace globalConfig = backupAgent->subspace.get(BackupAgentBase::keyConfig).get(uid.toString()); - state Subspace newConfigSpace = - uidPrefixKey(LiteralStringRef("uid->config/").withPrefix(fileBackupPrefixRange.begin), uid); + state Subspace newConfigSpace = uidPrefixKey("uid->config/"_sr.withPrefix(fileBackupPrefixRange.begin), uid); Optional statusStr = wait(tr->get(statusSpace.pack(FileBackupAgent::keyStateStatus))); state EBackupState status = @@ -816,8 +815,6 @@ struct AbortFiveZeroBackupTask : TaskFuncBase { state FileBackupAgent backupAgent; state std::string tagName = task->params[BackupAgentBase::keyConfigBackupTag].toString(); - CODE_PROBE(true, "Canceling old backup task"); - TraceEvent(SevInfo, "FileBackupCancelOldTask") .detail("Task", task->params[Task::reservedTaskParamKeyType]) .detail("TagName", tagName); @@ -847,7 +844,7 @@ struct AbortFiveZeroBackupTask : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef AbortFiveZeroBackupTask::name = LiteralStringRef("abort_legacy_backup"); +StringRef AbortFiveZeroBackupTask::name = "abort_legacy_backup"_sr; REGISTER_TASKFUNC(AbortFiveZeroBackupTask); REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_diff_logs); REGISTER_TASKFUNC_ALIAS(AbortFiveZeroBackupTask, file_backup_log_range); @@ -902,8 +899,6 @@ struct AbortFiveOneBackupTask : TaskFuncBase { state BackupConfig config(task); state std::string tagName = wait(config.tag().getOrThrow(tr)); - CODE_PROBE(true, "Canceling 5.1 backup task"); - TraceEvent(SevInfo, "FileBackupCancelFiveOneTask") .detail("Task", task->params[Task::reservedTaskParamKeyType]) .detail("TagName", tagName); @@ -933,7 +928,7 @@ struct AbortFiveOneBackupTask : TaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef AbortFiveOneBackupTask::name = LiteralStringRef("abort_legacy_backup_5.2"); +StringRef AbortFiveOneBackupTask::name = "abort_legacy_backup_5.2"_sr; REGISTER_TASKFUNC(AbortFiveOneBackupTask); REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_write_range); REGISTER_TASKFUNC_ALIAS(AbortFiveOneBackupTask, file_backup_dispatch_ranges); @@ -972,7 +967,7 @@ ACTOR static Future addBackupTask(StringRef name, } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } // Clears the backup ID from "backupStartedKey" to pause backup workers. @@ -1389,7 +1384,7 @@ struct BackupRangeTaskFunc : BackupTaskFuncBase { return Void(); } }; -StringRef BackupRangeTaskFunc::name = LiteralStringRef("file_backup_write_range_5.2"); +StringRef BackupRangeTaskFunc::name = "file_backup_write_range_5.2"_sr; REGISTER_TASKFUNC(BackupRangeTaskFunc); struct BackupSnapshotDispatchTask : BackupTaskFuncBase { @@ -1960,7 +1955,7 @@ struct BackupSnapshotDispatchTask : BackupTaskFuncBase { return Void(); } }; -StringRef BackupSnapshotDispatchTask::name = LiteralStringRef("file_backup_dispatch_ranges_5.2"); +StringRef BackupSnapshotDispatchTask::name = "file_backup_dispatch_ranges_5.2"_sr; REGISTER_TASKFUNC(BackupSnapshotDispatchTask); struct BackupLogRangeTaskFunc : BackupTaskFuncBase { @@ -2199,7 +2194,7 @@ struct BackupLogRangeTaskFunc : BackupTaskFuncBase { } }; -StringRef BackupLogRangeTaskFunc::name = LiteralStringRef("file_backup_write_logs_5.2"); +StringRef BackupLogRangeTaskFunc::name = "file_backup_write_logs_5.2"_sr; REGISTER_TASKFUNC(BackupLogRangeTaskFunc); // This task stopped being used in 6.2, however the code remains here to handle upgrades. @@ -2274,7 +2269,7 @@ struct EraseLogRangeTaskFunc : BackupTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef EraseLogRangeTaskFunc::name = LiteralStringRef("file_backup_erase_logs_5.2"); +StringRef EraseLogRangeTaskFunc::name = "file_backup_erase_logs_5.2"_sr; REGISTER_TASKFUNC(EraseLogRangeTaskFunc); struct BackupLogsDispatchTask : BackupTaskFuncBase { @@ -2444,7 +2439,7 @@ struct BackupLogsDispatchTask : BackupTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef BackupLogsDispatchTask::name = LiteralStringRef("file_backup_dispatch_logs_5.2"); +StringRef BackupLogsDispatchTask::name = "file_backup_dispatch_logs_5.2"_sr; REGISTER_TASKFUNC(BackupLogsDispatchTask); struct FileBackupFinishedTask : BackupTaskFuncBase { @@ -2504,7 +2499,7 @@ struct FileBackupFinishedTask : BackupTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef FileBackupFinishedTask::name = LiteralStringRef("file_backup_finished_5.2"); +StringRef FileBackupFinishedTask::name = "file_backup_finished_5.2"_sr; REGISTER_TASKFUNC(FileBackupFinishedTask); struct BackupSnapshotManifest : BackupTaskFuncBase { @@ -2693,7 +2688,7 @@ struct BackupSnapshotManifest : BackupTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef BackupSnapshotManifest::name = LiteralStringRef("file_backup_write_snapshot_manifest_5.2"); +StringRef BackupSnapshotManifest::name = "file_backup_write_snapshot_manifest_5.2"_sr; REGISTER_TASKFUNC(BackupSnapshotManifest); Future BackupSnapshotDispatchTask::addSnapshotManifestTask(Reference tr, @@ -2879,7 +2874,7 @@ struct StartFullBackupTaskFunc : BackupTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef StartFullBackupTaskFunc::name = LiteralStringRef("file_backup_start_5.2"); +StringRef StartFullBackupTaskFunc::name = "file_backup_start_5.2"_sr; REGISTER_TASKFUNC(StartFullBackupTaskFunc); struct RestoreCompleteTaskFunc : RestoreTaskFuncBase { @@ -2926,7 +2921,7 @@ struct RestoreCompleteTaskFunc : RestoreTaskFuncBase { } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } static StringRef name; @@ -2946,7 +2941,7 @@ struct RestoreCompleteTaskFunc : RestoreTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef RestoreCompleteTaskFunc::name = LiteralStringRef("restore_complete"); +StringRef RestoreCompleteTaskFunc::name = "restore_complete"_sr; REGISTER_TASKFUNC(RestoreCompleteTaskFunc); struct RestoreFileTaskFuncBase : RestoreTaskFuncBase { @@ -3218,7 +3213,7 @@ struct RestoreRangeTaskFunc : RestoreFileTaskFuncBase { } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } static StringRef name; @@ -3238,7 +3233,7 @@ struct RestoreRangeTaskFunc : RestoreFileTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef RestoreRangeTaskFunc::name = LiteralStringRef("restore_range_data"); +StringRef RestoreRangeTaskFunc::name = "restore_range_data"_sr; REGISTER_TASKFUNC(RestoreRangeTaskFunc); // Decodes a mutation log key, which contains (hash, commitVersion, chunkNumber) and @@ -3532,7 +3527,7 @@ struct RestoreLogDataTaskFunc : RestoreFileTaskFuncBase { } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } Future execute(Database cx, @@ -3548,7 +3543,7 @@ struct RestoreLogDataTaskFunc : RestoreFileTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef RestoreLogDataTaskFunc::name = LiteralStringRef("restore_log_data"); +StringRef RestoreLogDataTaskFunc::name = "restore_log_data"_sr; REGISTER_TASKFUNC(RestoreLogDataTaskFunc); struct RestoreDispatchTaskFunc : RestoreTaskFuncBase { @@ -3916,7 +3911,7 @@ struct RestoreDispatchTaskFunc : RestoreTaskFuncBase { } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } Future execute(Database cx, @@ -3932,7 +3927,7 @@ struct RestoreDispatchTaskFunc : RestoreTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef RestoreDispatchTaskFunc::name = LiteralStringRef("restore_dispatch"); +StringRef RestoreDispatchTaskFunc::name = "restore_dispatch"_sr; REGISTER_TASKFUNC(RestoreDispatchTaskFunc); ACTOR Future restoreStatus(Reference tr, Key tagName) { @@ -4274,7 +4269,7 @@ struct StartFullRestoreTaskFunc : RestoreTaskFuncBase { } wait(waitFor->onSetAddTask(tr, taskBucket, task)); - return LiteralStringRef("OnSetAddTask"); + return "OnSetAddTask"_sr; } StringRef getName() const override { return name; }; @@ -4292,7 +4287,7 @@ struct StartFullRestoreTaskFunc : RestoreTaskFuncBase { return _finish(tr, tb, fb, task); }; }; -StringRef StartFullRestoreTaskFunc::name = LiteralStringRef("restore_start"); +StringRef StartFullRestoreTaskFunc::name = "restore_start"_sr; REGISTER_TASKFUNC(StartFullRestoreTaskFunc); } // namespace fileBackup @@ -4885,7 +4880,7 @@ public: tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); try { - tr->set(backupPausedKey, pause ? LiteralStringRef("1") : LiteralStringRef("0")); + tr->set(backupPausedKey, pause ? "1"_sr : "0"_sr); wait(tr->commit()); break; } catch (Error& e) { diff --git a/fdbclient/GlobalConfig.actor.cpp b/fdbclient/GlobalConfig.actor.cpp index 7e6dd1ec00..45db5aa45b 100644 --- a/fdbclient/GlobalConfig.actor.cpp +++ b/fdbclient/GlobalConfig.actor.cpp @@ -28,14 +28,14 @@ #include "flow/actorcompiler.h" // This must be the last #include. -const KeyRef fdbClientInfoTxnSampleRate = LiteralStringRef("config/fdb_client_info/client_txn_sample_rate"); -const KeyRef fdbClientInfoTxnSizeLimit = LiteralStringRef("config/fdb_client_info/client_txn_size_limit"); +const KeyRef fdbClientInfoTxnSampleRate = "config/fdb_client_info/client_txn_sample_rate"_sr; +const KeyRef fdbClientInfoTxnSizeLimit = "config/fdb_client_info/client_txn_size_limit"_sr; -const KeyRef transactionTagSampleRate = LiteralStringRef("config/transaction_tag_sample_rate"); -const KeyRef transactionTagSampleCost = LiteralStringRef("config/transaction_tag_sample_cost"); +const KeyRef transactionTagSampleRate = "config/transaction_tag_sample_rate"_sr; +const KeyRef transactionTagSampleCost = "config/transaction_tag_sample_cost"_sr; -const KeyRef samplingFrequency = LiteralStringRef("visibility/sampling/frequency"); -const KeyRef samplingWindow = LiteralStringRef("visibility/sampling/window"); +const KeyRef samplingFrequency = "visibility/sampling/frequency"_sr; +const KeyRef samplingWindow = "visibility/sampling/window"_sr; GlobalConfig::GlobalConfig(DatabaseContext* cx) : cx(cx), lastUpdate(0) {} @@ -62,7 +62,7 @@ void GlobalConfig::applyChanges(Transaction& tr, // Write version key to trigger update in cluster controller. tr.atomicOp(globalConfigVersionKey, - LiteralStringRef("0123456789\x00\x00\x00\x00"), // versionstamp + "0123456789\x00\x00\x00\x00"_sr, // versionstamp MutationRef::SetVersionstampedValue); } diff --git a/fdbclient/KeyRangeMap.actor.cpp b/fdbclient/KeyRangeMap.actor.cpp index 5ec7ffd713..a678c28e4a 100644 --- a/fdbclient/KeyRangeMap.actor.cpp +++ b/fdbclient/KeyRangeMap.actor.cpp @@ -246,7 +246,7 @@ static Future krmSetRangeCoalescing_(Transaction* tr, // Determine how far to extend this range at the beginning auto beginRange = keys[0].get(); bool hasBegin = beginRange.size() > 0 && beginRange[0].key.startsWith(mapPrefix); - Value beginValue = hasBegin ? beginRange[0].value : LiteralStringRef(""); + Value beginValue = hasBegin ? beginRange[0].value : ""_sr; state Key beginKey = withPrefix.begin; if (beginValue == value) { @@ -259,7 +259,7 @@ static Future krmSetRangeCoalescing_(Transaction* tr, bool hasEnd = endRange.size() >= 1 && endRange[0].key.startsWith(mapPrefix) && endRange[0].key <= withPrefix.end; bool hasNext = (endRange.size() == 2 && endRange[1].key.startsWith(mapPrefix)) || (endRange.size() == 1 && withPrefix.end < endRange[0].key && endRange[0].key.startsWith(mapPrefix)); - Value existingValue = hasEnd ? endRange[0].value : LiteralStringRef(""); + Value existingValue = hasEnd ? endRange[0].value : ""_sr; bool valueMatches = value == existingValue; KeyRange conflictRange = KeyRangeRef(hasBegin ? beginRange[0].key : mapPrefix, withPrefix.begin); @@ -317,20 +317,20 @@ Future krmSetRangeCoalescing(Reference const& t TEST_CASE("/keyrangemap/decoderange/aligned") { Arena arena; - Key prefix = LiteralStringRef("/prefix/"); - StringRef fullKeyA = StringRef(arena, LiteralStringRef("/prefix/a")); - StringRef fullKeyB = StringRef(arena, LiteralStringRef("/prefix/b")); - StringRef fullKeyC = StringRef(arena, LiteralStringRef("/prefix/c")); - StringRef fullKeyD = StringRef(arena, LiteralStringRef("/prefix/d")); + Key prefix = "/prefix/"_sr; + StringRef fullKeyA = StringRef(arena, "/prefix/a"_sr); + StringRef fullKeyB = StringRef(arena, "/prefix/b"_sr); + StringRef fullKeyC = StringRef(arena, "/prefix/c"_sr); + StringRef fullKeyD = StringRef(arena, "/prefix/d"_sr); - StringRef keyA = StringRef(arena, LiteralStringRef("a")); - StringRef keyB = StringRef(arena, LiteralStringRef("b")); - StringRef keyC = StringRef(arena, LiteralStringRef("c")); - StringRef keyD = StringRef(arena, LiteralStringRef("d")); - StringRef keyE = StringRef(arena, LiteralStringRef("e")); - StringRef keyAB = StringRef(arena, LiteralStringRef("ab")); - StringRef keyAC = StringRef(arena, LiteralStringRef("ac")); - StringRef keyCD = StringRef(arena, LiteralStringRef("cd")); + StringRef keyA = StringRef(arena, "a"_sr); + StringRef keyB = StringRef(arena, "b"_sr); + StringRef keyC = StringRef(arena, "c"_sr); + StringRef keyD = StringRef(arena, "d"_sr); + StringRef keyE = StringRef(arena, "e"_sr); + StringRef keyAB = StringRef(arena, "ab"_sr); + StringRef keyAC = StringRef(arena, "ac"_sr); + StringRef keyCD = StringRef(arena, "cd"_sr); // Fake getRange() call. RangeResult kv; @@ -369,20 +369,20 @@ TEST_CASE("/keyrangemap/decoderange/aligned") { TEST_CASE("/keyrangemap/decoderange/unaligned") { Arena arena; - Key prefix = LiteralStringRef("/prefix/"); - StringRef fullKeyA = StringRef(arena, LiteralStringRef("/prefix/a")); - StringRef fullKeyB = StringRef(arena, LiteralStringRef("/prefix/b")); - StringRef fullKeyC = StringRef(arena, LiteralStringRef("/prefix/c")); - StringRef fullKeyD = StringRef(arena, LiteralStringRef("/prefix/d")); + Key prefix = "/prefix/"_sr; + StringRef fullKeyA = StringRef(arena, "/prefix/a"_sr); + StringRef fullKeyB = StringRef(arena, "/prefix/b"_sr); + StringRef fullKeyC = StringRef(arena, "/prefix/c"_sr); + StringRef fullKeyD = StringRef(arena, "/prefix/d"_sr); - StringRef keyA = StringRef(arena, LiteralStringRef("a")); - StringRef keyB = StringRef(arena, LiteralStringRef("b")); - StringRef keyC = StringRef(arena, LiteralStringRef("c")); - StringRef keyD = StringRef(arena, LiteralStringRef("d")); - StringRef keyE = StringRef(arena, LiteralStringRef("e")); - StringRef keyAB = StringRef(arena, LiteralStringRef("ab")); - StringRef keyAC = StringRef(arena, LiteralStringRef("ac")); - StringRef keyCD = StringRef(arena, LiteralStringRef("cd")); + StringRef keyA = StringRef(arena, "a"_sr); + StringRef keyB = StringRef(arena, "b"_sr); + StringRef keyC = StringRef(arena, "c"_sr); + StringRef keyD = StringRef(arena, "d"_sr); + StringRef keyE = StringRef(arena, "e"_sr); + StringRef keyAB = StringRef(arena, "ab"_sr); + StringRef keyAC = StringRef(arena, "ac"_sr); + StringRef keyCD = StringRef(arena, "cd"_sr); // Fake getRange() call. RangeResult kv; diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 783676b512..d2019b9fc2 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -844,7 +844,7 @@ ACTOR Future> getClusterConnectionStringFromSt // equal to one of the previously issued requests, there is a bug // and we are breaking the promises we make with // commit_unknown_result (the transaction must no longer be in - // progress when receiving this error). + // progress when receiving commit_unknown_result). int n = connectionStrings.size() > 0 ? connectionStrings.size() - 1 : 0; // avoid underflow for (int i = 0; i < n; ++i) { ASSERT(currentKey.get() != connectionStrings.at(i)); @@ -872,12 +872,59 @@ ACTOR Future> getClusterConnectionStringFromSt } } +ACTOR Future verifyConfigurationDatabaseAlive(Database cx) { + state Backoff backoff; + loop { + try { + // Attempt to read a random value from the configuration + // database to make sure it is online. + state Reference configTr = + ISingleThreadTransaction::create(ISingleThreadTransaction::Type::PAXOS_CONFIG, cx); + Tuple tuple; + tuple.appendNull(); // config class + tuple << "test"_sr; + Optional serializedValue = wait(configTr->get(tuple.pack())); + TraceEvent("ChangeQuorumCheckerNewCoordinatorsOnline").log(); + return Void(); + } catch (Error& e) { + TraceEvent("ChangeQuorumCheckerNewCoordinatorsError").error(e); + if (e.code() == error_code_coordinators_changed) { + wait(backoff.onError()); + configTr->reset(); + } else { + wait(configTr->onError(e)); + } + } + } +} + +ACTOR Future resetPreviousCoordinatorsKey(Database cx) { + loop { + // When the change coordinators transaction succeeds, it uses the + // special key space error message to return a message to the client. + // This causes the underlying transaction to not be committed. In order + // to make sure we clear the previous coordinators key, we have to use + // a new transaction here. + state Reference clearTr = + ISingleThreadTransaction::create(ISingleThreadTransaction::Type::RYW, cx); + try { + clearTr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + clearTr->clear(previousCoordinatorsKey); + wait(clearTr->commit()); + return Void(); + } catch (Error& e2) { + wait(clearTr->onError(e2)); + } + } +} + } // namespace ACTOR Future> changeQuorumChecker(Transaction* tr, ClusterConnectionString* conn, - std::string newName) { - + std::string newName, + bool disableConfigDB) { + TraceEvent("ChangeQuorumCheckerStart").detail("NewConnectionString", conn->toString()); state Optional clusterConnectionStringOptional = wait(getClusterConnectionStringFromStorageServer(tr)); @@ -892,7 +939,7 @@ ACTOR Future> changeQuorumChecker(Transaction* tr, conn->hostnames = old.hostnames; conn->coords = old.coords; } - std::vector desiredCoordinators = wait(conn->tryResolveHostnames()); + state std::vector desiredCoordinators = wait(conn->tryResolveHostnames()); if (desiredCoordinators.size() != conn->hostnames.size() + conn->coords.size()) { TraceEvent("ChangeQuorumCheckerEarlyTermination") .detail("Reason", "One or more hostnames are unresolvable") @@ -909,6 +956,13 @@ ACTOR Future> changeQuorumChecker(Transaction* tr, std::sort(old.coords.begin(), old.coords.end()); if (conn->hostnames == old.hostnames && conn->coords == old.coords && old.clusterKeyName() == newName) { connectionStrings.clear(); + if (g_network->isSimulated() && g_simulator->configDBType == ConfigDBType::DISABLED) { + disableConfigDB = true; + } + if (!disableConfigDB) { + wait(verifyConfigurationDatabaseAlive(tr->getDatabase())); + } + wait(resetPreviousCoordinatorsKey(tr->getDatabase())); return CoordinatorsResult::SAME_NETWORK_ADDRESSES; } @@ -919,7 +973,7 @@ ACTOR Future> changeQuorumChecker(Transaction* tr, int i = 0; int protectedCount = 0; while ((protectedCount < ((desiredCoordinators.size() / 2) + 1)) && (i < desiredCoordinators.size())) { - auto process = g_simulator.getProcessByAddress(desiredCoordinators[i]); + auto process = g_simulator->getProcessByAddress(desiredCoordinators[i]); auto addresses = process->addresses; if (!process->isReliable()) { @@ -927,9 +981,9 @@ ACTOR Future> changeQuorumChecker(Transaction* tr, continue; } - g_simulator.protectedAddresses.insert(process->addresses.address); + g_simulator->protectedAddresses.insert(process->addresses.address); if (addresses.secondaryAddress.present()) { - g_simulator.protectedAddresses.insert(process->addresses.secondaryAddress.get()); + g_simulator->protectedAddresses.insert(process->addresses.secondaryAddress.get()); } TraceEvent("ProtectCoordinator").detail("Address", desiredCoordinators[i]).backtrace(); protectedCount++; @@ -958,6 +1012,9 @@ ACTOR Future> changeQuorumChecker(Transaction* tr, when(wait(waitForAll(leaderServers))) {} when(wait(delay(5.0))) { return CoordinatorsResult::COORDINATOR_UNREACHABLE; } } + TraceEvent("ChangeQuorumCheckerSetCoordinatorsKey") + .detail("CurrentCoordinators", old.toString()) + .detail("NewCoordinators", conn->toString()); tr->set(coordinatorsKey, conn->toString()); return Optional(); } @@ -1020,12 +1077,12 @@ ACTOR Future changeQuorum(Database cx, ReferenceisSimulated()) { for (int i = 0; i < (desiredCoordinators.size() / 2) + 1; i++) { - auto process = g_simulator.getProcessByAddress(desiredCoordinators[i]); + auto process = g_simulator->getProcessByAddress(desiredCoordinators[i]); ASSERT(process->isReliable() || process->rebooting); - g_simulator.protectedAddresses.insert(process->addresses.address); + g_simulator->protectedAddresses.insert(process->addresses.address); if (process->addresses.secondaryAddress.present()) { - g_simulator.protectedAddresses.insert(process->addresses.secondaryAddress.get()); + g_simulator->protectedAddresses.insert(process->addresses.secondaryAddress.get()); } TraceEvent("ProtectCoordinator").detail("Address", desiredCoordinators[i]).backtrace(); } @@ -1099,10 +1156,8 @@ struct AutoQuorumChange final : IQuorumChange { } ACTOR static Future getRedundancy(AutoQuorumChange* self, Transaction* tr) { - state Future> fStorageReplicas = - tr->get(LiteralStringRef("storage_replicas").withPrefix(configKeysPrefix)); - state Future> fLogReplicas = - tr->get(LiteralStringRef("log_replicas").withPrefix(configKeysPrefix)); + state Future> fStorageReplicas = tr->get("storage_replicas"_sr.withPrefix(configKeysPrefix)); + state Future> fLogReplicas = tr->get("log_replicas"_sr.withPrefix(configKeysPrefix)); wait(success(fStorageReplicas) && success(fLogReplicas)); int redundancy = std::min(atoi(fStorageReplicas.get().get().toString().c_str()), atoi(fLogReplicas.get().get().toString().c_str())); @@ -1264,10 +1319,7 @@ struct AutoQuorumChange final : IQuorumChange { std::map> currentCounts; std::map hardLimits; - std::vector fields({ LiteralStringRef("dcid"), - LiteralStringRef("data_hall"), - LiteralStringRef("zoneid"), - LiteralStringRef("machineid") }); + std::vector fields({ "dcid"_sr, "data_hall"_sr, "zoneid"_sr, "machineid"_sr }); for (auto field = fields.begin(); field != fields.end(); field++) { if (field->toString() == "zoneid") { @@ -1284,7 +1336,7 @@ struct AutoQuorumChange final : IQuorumChange { continue; } // Exclude faulty node due to machine assassination - if (g_network->isSimulated() && !g_simulator.getProcessByAddress(worker->address)->isReliable()) { + if (g_network->isSimulated() && !g_simulator->getProcessByAddress(worker->address)->isReliable()) { TraceEvent("AutoSelectCoordinators").detail("SkipUnreliableWorker", worker->address.toString()); continue; } @@ -1293,7 +1345,7 @@ struct AutoQuorumChange final : IQuorumChange { if (maxCounts[*field] == 0) { maxCounts[*field] = 1; } - auto value = worker->locality.get(*field).orDefault(LiteralStringRef("")); + auto value = worker->locality.get(*field).orDefault(""_sr); auto currentCount = currentCounts[*field][value]; if (currentCount >= maxCounts[*field]) { valid = false; @@ -1302,7 +1354,7 @@ struct AutoQuorumChange final : IQuorumChange { } if (valid) { for (auto field = fields.begin(); field != fields.end(); field++) { - auto value = worker->locality.get(*field).orDefault(LiteralStringRef("")); + auto value = worker->locality.get(*field).orDefault(""_sr); currentCounts[*field][value] += 1; } chosen.push_back(worker->address); @@ -1355,6 +1407,7 @@ ACTOR Future excludeServers(Database cx, std::vector ser state ReadYourWritesTransaction ryw(cx); loop { try { + ryw.setOption(FDBTransactionOptions::RAW_ACCESS); ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); ryw.set( SpecialKeySpace::getManagementApiCommandOptionSpecialKey(failed ? "failed" : "excluded", "force"), @@ -1417,6 +1470,7 @@ ACTOR Future excludeLocalities(Database cx, std::unordered_set includeServers(Database cx, std::vector ser state ReadYourWritesTransaction ryw(cx); loop { try { + ryw.setOption(FDBTransactionOptions::RAW_ACCESS); ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); for (auto& s : servers) { if (!s.isValid()) { @@ -1481,8 +1536,7 @@ ACTOR Future includeServers(Database cx, std::vector ser // This is why we now make two clears: first only of the ip // address, the second will delete all ports. if (s.isWholeMachine()) - ryw.clear(KeyRangeRef(addr.withSuffix(LiteralStringRef(":")), - addr.withSuffix(LiteralStringRef(";")))); + ryw.clear(KeyRangeRef(addr.withSuffix(":"_sr), addr.withSuffix(";"_sr))); } } TraceEvent("IncludeServersCommit").detail("Servers", describe(servers)).detail("Failed", failed); @@ -1562,6 +1616,7 @@ ACTOR Future includeLocalities(Database cx, std::vector local state ReadYourWritesTransaction ryw(cx); loop { try { + ryw.setOption(FDBTransactionOptions::RAW_ACCESS); ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); if (includeAll) { if (failed) { @@ -2061,9 +2116,7 @@ ACTOR Future lockDatabase(Transaction* tr, UID id) { } tr->atomicOp(databaseLockedKey, - BinaryWriter::toValue(id, Unversioned()) - .withPrefix(LiteralStringRef("0123456789")) - .withSuffix(LiteralStringRef("\x00\x00\x00\x00")), + BinaryWriter::toValue(id, Unversioned()).withPrefix("0123456789"_sr).withSuffix("\x00\x00\x00\x00"_sr), MutationRef::SetVersionstampedValue); tr->addWriteConflictRange(normalKeys); return Void(); @@ -2084,9 +2137,7 @@ ACTOR Future lockDatabase(Reference tr, UID id) } tr->atomicOp(databaseLockedKey, - BinaryWriter::toValue(id, Unversioned()) - .withPrefix(LiteralStringRef("0123456789")) - .withSuffix(LiteralStringRef("\x00\x00\x00\x00")), + BinaryWriter::toValue(id, Unversioned()).withPrefix("0123456789"_sr).withSuffix("\x00\x00\x00\x00"_sr), MutationRef::SetVersionstampedValue); tr->addWriteConflictRange(normalKeys); return Void(); @@ -2205,7 +2256,7 @@ ACTOR Future updateChangeFeed(Transaction* tr, Key rangeID, ChangeFeedStat } else if (status == ChangeFeedStatus::CHANGE_FEED_DESTROY) { if (val.present()) { if (g_network->isSimulated()) { - g_simulator.validationData.allDestroyedChangeFeedIDs.insert(rangeID.toString()); + g_simulator->validationData.allDestroyedChangeFeedIDs.insert(rangeID.toString()); } tr->set(rangeIDKey, changeFeedValue(std::get<0>(decodeChangeFeedValue(val.get())), @@ -2243,7 +2294,7 @@ ACTOR Future updateChangeFeed(Reference tr, } else if (status == ChangeFeedStatus::CHANGE_FEED_DESTROY) { if (val.present()) { if (g_network->isSimulated()) { - g_simulator.validationData.allDestroyedChangeFeedIDs.insert(rangeID.toString()); + g_simulator->validationData.allDestroyedChangeFeedIDs.insert(rangeID.toString()); } tr->set(rangeIDKey, changeFeedValue(std::get<0>(decodeChangeFeedValue(val.get())), @@ -2556,24 +2607,24 @@ TEST_CASE("/ManagementAPI/AutoQuorumChange/checkLocality") { auto dataHall = dataCenter + std::to_string(i / 2 % 2); auto rack = dataHall + std::to_string(i % 2); auto machineId = rack + std::to_string(i); - data.locality.set(LiteralStringRef("dcid"), StringRef(dataCenter)); - data.locality.set(LiteralStringRef("data_hall"), StringRef(dataHall)); - data.locality.set(LiteralStringRef("rack"), StringRef(rack)); - data.locality.set(LiteralStringRef("zoneid"), StringRef(rack)); - data.locality.set(LiteralStringRef("machineid"), StringRef(machineId)); + data.locality.set("dcid"_sr, StringRef(dataCenter)); + data.locality.set("data_hall"_sr, StringRef(dataHall)); + data.locality.set("rack"_sr, StringRef(rack)); + data.locality.set("zoneid"_sr, StringRef(rack)); + data.locality.set("machineid"_sr, StringRef(machineId)); data.address.ip = IPAddress(i); if (g_network->isSimulated()) { - g_simulator.newProcess("TestCoordinator", - data.address.ip, - data.address.port, - false, - 1, - data.locality, - ProcessClass(ProcessClass::CoordinatorClass, ProcessClass::CommandLineSource), - "", - "", - currentProtocolVersion()); + g_simulator->newProcess("TestCoordinator", + data.address.ip, + data.address.port, + false, + 1, + data.locality, + ProcessClass(ProcessClass::CoordinatorClass, ProcessClass::CommandLineSource), + "", + "", + currentProtocolVersion()); } workers.push_back(data); @@ -2586,10 +2637,7 @@ TEST_CASE("/ManagementAPI/AutoQuorumChange/checkLocality") { std::map> chosenValues; ASSERT(chosen.size() == 5); - std::vector fields({ LiteralStringRef("dcid"), - LiteralStringRef("data_hall"), - LiteralStringRef("zoneid"), - LiteralStringRef("machineid") }); + std::vector fields({ "dcid"_sr, "data_hall"_sr, "zoneid"_sr, "machineid"_sr }); for (auto worker = chosen.begin(); worker != chosen.end(); worker++) { ASSERT(worker->ip.toV4() < workers.size()); LocalityData data = workers[worker->ip.toV4()].locality; @@ -2598,10 +2646,10 @@ TEST_CASE("/ManagementAPI/AutoQuorumChange/checkLocality") { } } - ASSERT(chosenValues[LiteralStringRef("dcid")].size() == 2); - ASSERT(chosenValues[LiteralStringRef("data_hall")].size() == 4); - ASSERT(chosenValues[LiteralStringRef("zoneid")].size() == 5); - ASSERT(chosenValues[LiteralStringRef("machineid")].size() == 5); + ASSERT(chosenValues["dcid"_sr].size() == 2); + ASSERT(chosenValues["data_hall"_sr].size() == 4); + ASSERT(chosenValues["zoneid"_sr].size() == 5); + ASSERT(chosenValues["machineid"_sr].size() == 5); ASSERT(std::find(chosen.begin(), chosen.end(), workers[noAssignIndex].address) != chosen.end()); return Void(); diff --git a/fdbclient/MonitorLeader.actor.cpp b/fdbclient/MonitorLeader.actor.cpp index 977d465908..c856b68783 100644 --- a/fdbclient/MonitorLeader.actor.cpp +++ b/fdbclient/MonitorLeader.actor.cpp @@ -248,7 +248,7 @@ TEST_CASE("/fdbclient/MonitorLeader/ConnectionString/hostname") { hostnames.push_back(Hostname::parse(hn1 + ":" + port1)); hostnames.push_back(Hostname::parse(hn2 + ":" + port2)); - ClusterConnectionString cs(hostnames, LiteralStringRef("TestCluster:0")); + ClusterConnectionString cs(hostnames, "TestCluster:0"_sr); ASSERT(cs.hostnames.size() == 2); ASSERT(cs.coords.size() == 0); ASSERT(cs.toString() == connectionString); @@ -259,7 +259,7 @@ TEST_CASE("/fdbclient/MonitorLeader/ConnectionString/hostname") { hostnames.push_back(Hostname::parse(hn1 + ":" + port1)); hostnames.push_back(Hostname::parse(hn1 + ":" + port1)); try { - ClusterConnectionString cs(hostnames, LiteralStringRef("TestCluster:0")); + ClusterConnectionString cs(hostnames, "TestCluster:0"_sr); } catch (Error& e) { ASSERT(e.code() == error_code_connection_string_invalid); } @@ -367,7 +367,7 @@ TEST_CASE("/fdbclient/MonitorLeader/parseConnectionString/fuzz") { auto c = connectionString.begin(); while (c != connectionString.end()) { if (deterministicRandom()->random01() < 0.1) // Add whitespace character - output += deterministicRandom()->randomChoice(LiteralStringRef(" \t\n\r")); + output += deterministicRandom()->randomChoice(" \t\n\r"_sr); if (deterministicRandom()->random01() < 0.5) { // Add one of the input characters output += *c; ++c; @@ -378,7 +378,7 @@ TEST_CASE("/fdbclient/MonitorLeader/parseConnectionString/fuzz") { for (int i = 0; i < charCount; i++) { output += deterministicRandom()->randomChoice(LiteralStringRef("asdfzxcv123345:!@#$#$&()<\"\' \t")); } - output += deterministicRandom()->randomChoice(LiteralStringRef("\n\r")); + output += deterministicRandom()->randomChoice("\n\r"_sr); } } @@ -501,6 +501,7 @@ ACTOR Future monitorNominee(Key key, Optional* info) { loop { state Optional li; + wait(Future(Void())); // Make sure we weren't cancelled if (coord.hostname.present()) { wait(store(li, retryGetReplyFromHostname(GetLeaderRequest(key, info->present() ? info->get().changeID : UID()), @@ -861,6 +862,7 @@ ACTOR Future monitorProxiesOneGeneration( for (const auto& c : cs.coords) { clientLeaderServers.push_back(ClientLeaderRegInterface(c)); } + ASSERT(clientLeaderServers.size() > 0); deterministicRandom()->randomShuffle(clientLeaderServers); @@ -880,7 +882,7 @@ ACTOR Future monitorProxiesOneGeneration( bool upToDate = wait(connRecord->upToDate(storedConnectionString)); if (upToDate) { incorrectTime = Optional(); - } else if (allConnectionsFailed) { + } else if (allConnectionsFailed && storedConnectionString.getNumberOfCoordinators() > 0) { // Failed to connect to all coordinators from the current connection string, // so it is not possible to get any new updates from the cluster. It can be that // all the coordinators have changed, but the client missed that, because it had @@ -894,7 +896,7 @@ ACTOR Future monitorProxiesOneGeneration( info.intermediateConnRecord = connRecord; return info; } else { - req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("incorrect_cluster_file_contents")); + req.issues.push_back_deep(req.issues.arena(), "incorrect_cluster_file_contents"_sr); std::string connectionString = connRecord->getConnectionString().toString(); if (!incorrectTime.present()) { incorrectTime = now(); @@ -938,6 +940,7 @@ ACTOR Future monitorProxiesOneGeneration( .detail("OldConnStr", info.intermediateConnRecord->getConnectionString().toString()); info.intermediateConnRecord = connRecord->makeIntermediateRecord( ClusterConnectionString(rep.get().read().forward.get().toString())); + ASSERT(info.intermediateConnRecord->getConnectionString().getNumberOfCoordinators() > 0); return info; } if (connRecord != info.intermediateConnRecord) { @@ -963,7 +966,6 @@ ACTOR Future monitorProxiesOneGeneration( } else { CODE_PROBE(rep.getError().code() == error_code_failed_to_progress, "Coordinator cant talk to cluster controller"); - CODE_PROBE(rep.getError().code() == error_code_lookup_failed, "Coordinator hostname resolving failure"); TraceEvent("MonitorProxiesConnectFailed") .detail("Error", rep.getError().name()) .detail("Coordinator", clientLeaderServer.getAddressString()); @@ -984,6 +986,7 @@ ACTOR Future monitorProxies( Key traceLogGroup) { state MonitorLeaderInfo info(connRecord->get()); loop { + ASSERT(connRecord->get().isValid()); choose { when(MonitorLeaderInfo _info = wait(monitorProxiesOneGeneration( connRecord->get(), clientInfo, coordinator, info, supportedVersions, traceLogGroup))) { diff --git a/fdbclient/MultiVersionTransaction.actor.cpp b/fdbclient/MultiVersionTransaction.actor.cpp index 87fb19b51d..4334de4479 100644 --- a/fdbclient/MultiVersionTransaction.actor.cpp +++ b/fdbclient/MultiVersionTransaction.actor.cpp @@ -652,7 +652,7 @@ ThreadFuture DLDatabase::blobbifyRange(const KeyRangeRef& keyRange) { db, keyRange.begin.begin(), keyRange.begin.size(), keyRange.end.begin(), keyRange.end.size()); return toThreadFuture(api, f, [](FdbCApi::FDBFuture* f, FdbCApi* api) { - bool ret = false; + FdbCApi::fdb_bool_t ret = false; ASSERT(!api->futureGetBool(f, &ret)); return ret; }); @@ -667,7 +667,7 @@ ThreadFuture DLDatabase::unblobbifyRange(const KeyRangeRef& keyRange) { db, keyRange.begin.begin(), keyRange.begin.size(), keyRange.end.begin(), keyRange.end.size()); return toThreadFuture(api, f, [](FdbCApi::FDBFuture* f, FdbCApi* api) { - bool ret = false; + FdbCApi::fdb_bool_t ret = false; ASSERT(!api->futureGetBool(f, &ret)); return ret; }); @@ -2770,7 +2770,6 @@ ACTOR Future updateClusterSharedStateMapImpl(MultiVersionApi* self, state Reference tr = db->createTransaction(); loop { try { - tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED); state ThreadFuture> clusterIdFuture = tr->get("\xff\xff/cluster_id"_sr); Optional clusterIdVal = wait(safeThreadFutureToFuture(clusterIdFuture)); ASSERT(clusterIdVal.present()); @@ -3270,7 +3269,7 @@ struct AbortableTest { } }; -TEST_CASE("/fdbclient/multiversionclient/AbortableSingleAssignmentVar") { +TEST_CASE("fdbclient/multiversionclient/AbortableSingleAssignmentVar") { state volatile bool done = false; state THREAD_HANDLE thread = g_network->startThread(runSingleAssignmentVarTest, (void*)&done); @@ -3347,7 +3346,7 @@ struct DLTest { } }; -TEST_CASE("/fdbclient/multiversionclient/DLSingleAssignmentVar") { +TEST_CASE("fdbclient/multiversionclient/DLSingleAssignmentVar") { state volatile bool done = false; MultiVersionApi::api->callbackOnMainThread = true; @@ -3391,7 +3390,7 @@ struct MapTest { } }; -TEST_CASE("/fdbclient/multiversionclient/MapSingleAssignmentVar") { +TEST_CASE("fdbclient/multiversionclient/MapSingleAssignmentVar") { state volatile bool done = false; state THREAD_HANDLE thread = g_network->startThread(runSingleAssignmentVarTest, (void*)&done); @@ -3430,7 +3429,7 @@ struct FlatMapTest { } }; -TEST_CASE("/fdbclient/multiversionclient/FlatMapSingleAssignmentVar") { +TEST_CASE("fdbclient/multiversionclient/FlatMapSingleAssignmentVar") { state volatile bool done = false; state THREAD_HANDLE thread = g_network->startThread(runSingleAssignmentVarTest, (void*)&done); diff --git a/fdbclient/MutationLogReader.actor.cpp b/fdbclient/MutationLogReader.actor.cpp index 5919fdc66b..d6e3adc8dd 100644 --- a/fdbclient/MutationLogReader.actor.cpp +++ b/fdbclient/MutationLogReader.actor.cpp @@ -67,7 +67,7 @@ ACTOR Future PipelinedReader::getNext_impl(PipelinedReader* self, Database state Transaction tr(cx); state GetRangeLimits limits(GetRangeLimits::ROW_LIMIT_UNLIMITED, - (g_network->isSimulated() && !g_simulator.speedUpSimulation) + (g_network->isSimulated() && !g_simulator->speedUpSimulation) ? CLIENT_KNOBS->BACKUP_SIMULATED_LIMIT_BYTES : CLIENT_KNOBS->BACKUP_GET_RANGE_LIMIT_BYTES); @@ -179,7 +179,7 @@ ACTOR Future> MutationLogReader::getNext_impl(Mutatio namespace { // UNIT TESTS TEST_CASE("/fdbclient/mutationlogreader/VersionKeyRefConversion") { - Key prefix = LiteralStringRef("foos"); + Key prefix = "foos"_sr; ASSERT(keyRefToVersion(versionToKey(0, prefix), prefix.size()) == 0); ASSERT(keyRefToVersion(versionToKey(1, prefix), prefix.size()) == 1); diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 0c81e93342..e91e98c2af 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -155,8 +155,8 @@ NetworkOptions::NetworkOptions() supportedVersions(new ReferencedObject>>()), runLoopProfilingEnabled(false), primaryClient(true), disableBypass(false) {} -static const Key CLIENT_LATENCY_INFO_PREFIX = LiteralStringRef("client_latency/"); -static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = LiteralStringRef("client_latency_counter/"); +static const Key CLIENT_LATENCY_INFO_PREFIX = "client_latency/"_sr; +static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = "client_latency_counter/"_sr; void DatabaseContext::addTssMapping(StorageServerInterface const& ssi, StorageServerInterface const& tssi) { auto result = tssMapping.find(ssi.id()); @@ -170,14 +170,8 @@ void DatabaseContext::addTssMapping(StorageServerInterface const& ssi, StorageSe tssMetrics[tssi.id()] = metrics; tssMapping[ssi.id()] = tssi; } else { - if (result->second.id() == tssi.id()) { - metrics = tssMetrics[tssi.id()]; - } else { - CODE_PROBE(true, "SS now maps to new TSS! This will probably never happen in practice"); - tssMetrics.erase(result->second.id()); - metrics = makeReference(); - tssMetrics[tssi.id()] = metrics; - } + ASSERT(result->second.id() == tssi.id()); + metrics = tssMetrics[tssi.id()]; result->second = tssi; } @@ -604,7 +598,8 @@ ACTOR Future databaseLogger(DatabaseContext* cx) { loop { wait(delay(CLIENT_KNOBS->SYSTEM_MONITOR_INTERVAL, TaskPriority::FlushTrace)); - if (!g_network->isSimulated()) { + bool logTraces = !g_network->isSimulated() || BUGGIFY_WITH_PROB(0.01); + if (logTraces) { TraceEvent ev("TransactionMetrics", cx->dbId); ev.detail("Elapsed", (lastLogged == 0) ? 0 : now() - lastLogged) @@ -657,6 +652,19 @@ ACTOR Future databaseLogger(DatabaseContext* cx) { cx->bgLatencies.clear(); cx->bgGranulesPerRequest.clear(); + if (cx->usedAnyChangeFeeds && logTraces) { + TraceEvent feedEv("ChangeFeedClientMetrics", cx->dbId); + + feedEv.detail("Elapsed", (lastLogged == 0) ? 0 : now() - lastLogged) + .detail("Cluster", + cx->getConnectionRecord() + ? cx->getConnectionRecord()->getConnectionString().clusterKeyName().toString() + : "") + .detail("Internal", cx->internal); + + cx->ccFeed.logToTraceEvent(feedEv); + } + lastLogged = now(); } } @@ -1145,7 +1153,7 @@ ACTOR static Future handleTssMismatches(DatabaseContext* cx) { tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); if (quarantine) { - tr->set(tssQuarantineKeyFor(data.first), LiteralStringRef("")); + tr->set(tssQuarantineKeyFor(data.first), ""_sr); } else { tr->clear(serverTagKeyFor(data.first)); } @@ -1296,12 +1304,17 @@ struct SingleSpecialKeyImpl : SpecialKeyRangeReadImpl { }); } - SingleSpecialKeyImpl(KeyRef k, const std::function>(ReadYourWritesTransaction*)>& f) - : SpecialKeyRangeReadImpl(singleKeyRange(k)), k(k), f(f) {} + SingleSpecialKeyImpl(KeyRef k, + const std::function>(ReadYourWritesTransaction*)>& f, + bool supportsTenants = false) + : SpecialKeyRangeReadImpl(singleKeyRange(k)), k(k), f(f), tenantSupport(supportsTenants) {} + + bool supportsTenants() const override { return tenantSupport; }; private: Key k; std::function>(ReadYourWritesTransaction*)> f; + bool tenantSupport; }; class HealthMetricsRangeImpl : public SpecialKeyRangeAsyncImpl { @@ -1316,7 +1329,7 @@ static RangeResult healthMetricsToKVPairs(const HealthMetrics& metrics, KeyRange RangeResult result; if (CLIENT_BUGGIFY) return result; - if (kr.contains(LiteralStringRef("\xff\xff/metrics/health/aggregate")) && metrics.worstStorageDurabilityLag != 0) { + if (kr.contains("\xff\xff/metrics/health/aggregate"_sr) && metrics.worstStorageDurabilityLag != 0) { json_spirit::mObject statsObj; statsObj["batch_limited"] = metrics.batchLimited; statsObj["tps_limit"] = metrics.tpsLimit; @@ -1328,15 +1341,13 @@ static RangeResult healthMetricsToKVPairs(const HealthMetrics& metrics, KeyRange std::string statsString = json_spirit::write_string(json_spirit::mValue(statsObj), json_spirit::Output_options::raw_utf8); ValueRef bytes(result.arena(), statsString); - result.push_back(result.arena(), KeyValueRef(LiteralStringRef("\xff\xff/metrics/health/aggregate"), bytes)); + result.push_back(result.arena(), KeyValueRef("\xff\xff/metrics/health/aggregate"_sr, bytes)); } // tlog stats { int phase = 0; // Avoid comparing twice per loop iteration for (const auto& [uid, logStats] : metrics.tLogQueue) { - StringRef k{ - StringRef(uid.toString()).withPrefix(LiteralStringRef("\xff\xff/metrics/health/log/"), result.arena()) - }; + StringRef k{ StringRef(uid.toString()).withPrefix("\xff\xff/metrics/health/log/"_sr, result.arena()) }; if (phase == 0 && k >= kr.begin) { phase = 1; } @@ -1358,8 +1369,7 @@ static RangeResult healthMetricsToKVPairs(const HealthMetrics& metrics, KeyRange { int phase = 0; // Avoid comparing twice per loop iteration for (const auto& [uid, storageStats] : metrics.storageStats) { - StringRef k{ StringRef(uid.toString()) - .withPrefix(LiteralStringRef("\xff\xff/metrics/health/storage/"), result.arena()) }; + StringRef k{ StringRef(uid.toString()).withPrefix("\xff\xff/metrics/health/storage/"_sr, result.arena()) }; if (phase == 0 && k >= kr.begin) { phase = 1; } @@ -1385,10 +1395,9 @@ static RangeResult healthMetricsToKVPairs(const HealthMetrics& metrics, KeyRange ACTOR static Future healthMetricsGetRangeActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) { HealthMetrics metrics = wait(ryw->getDatabase()->getHealthMetrics( - /*detailed ("per process")*/ kr.intersects(KeyRangeRef(LiteralStringRef("\xff\xff/metrics/health/storage/"), - LiteralStringRef("\xff\xff/metrics/health/storage0"))) || - kr.intersects(KeyRangeRef(LiteralStringRef("\xff\xff/metrics/health/log/"), - LiteralStringRef("\xff\xff/metrics/health/log0"))))); + /*detailed ("per process")*/ kr.intersects( + KeyRangeRef("\xff\xff/metrics/health/storage/"_sr, "\xff\xff/metrics/health/storage0"_sr)) || + kr.intersects(KeyRangeRef("\xff\xff/metrics/health/log/"_sr, "\xff\xff/metrics/health/log0"_sr)))); return healthMetricsToKVPairs(metrics, kr); } @@ -1461,9 +1470,13 @@ DatabaseContext::DatabaseContext(ReferenceisSimulated() ? CLIENT_KNOBS->TENANT_CACHE_EVICTION_SIZE_SIM : CLIENT_KNOBS->TENANT_CACHE_EVICTION_SIZE; - getValueSubmitted.init(LiteralStringRef("NativeAPI.GetValueSubmitted")); - getValueCompleted.init(LiteralStringRef("NativeAPI.GetValueCompleted")); + getValueSubmitted.init("NativeAPI.GetValueSubmitted"_sr); + getValueCompleted.init("NativeAPI.GetValueCompleted"_sr); clientDBInfoMonitor = monitorClientDBInfoChange(this, clientInfo, &proxiesChangeTrigger); tssMismatchHandler = handleTssMismatches(this); @@ -1511,12 +1524,13 @@ DatabaseContext::DatabaseContext(Reference(ryw->getSpecialKeySpaceErrorMsg().get()); else return Optional(); - })); + }, + true)); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - KeyRangeRef(LiteralStringRef("options/"), LiteralStringRef("options0")) + KeyRangeRef("options/"_sr, "options0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, @@ -1538,31 +1552,31 @@ DatabaseContext::DatabaseContext(Reference( - KeyRangeRef(LiteralStringRef("in_progress_exclusion/"), LiteralStringRef("in_progress_exclusion0")) + KeyRangeRef("in_progress_exclusion/"_sr, "in_progress_exclusion0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::CONFIGURATION, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - KeyRangeRef(LiteralStringRef("process/class_type/"), LiteralStringRef("process/class_type0")) + KeyRangeRef("process/class_type/"_sr, "process/class_type0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::CONFIGURATION, SpecialKeySpace::IMPLTYPE::READONLY, std::make_unique( - KeyRangeRef(LiteralStringRef("process/class_source/"), LiteralStringRef("process/class_source0")) + KeyRangeRef("process/class_source/"_sr, "process/class_source0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - singleKeyRange(LiteralStringRef("db_locked")) + singleKeyRange("db_locked"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - singleKeyRange(LiteralStringRef("consistency_check_suspended")) + singleKeyRange("consistency_check_suspended"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::GLOBALCONFIG, @@ -1576,44 +1590,44 @@ DatabaseContext::DatabaseContext(Reference( - KeyRangeRef(LiteralStringRef("coordinators/"), LiteralStringRef("coordinators0")) + KeyRangeRef("coordinators/"_sr, "coordinators0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READONLY, std::make_unique( - singleKeyRange(LiteralStringRef("auto_coordinators")) + singleKeyRange("auto_coordinators"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - singleKeyRange(LiteralStringRef("min_required_commit_version")) + singleKeyRange("min_required_commit_version"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - singleKeyRange(LiteralStringRef("version_epoch")) + singleKeyRange("version_epoch"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - KeyRangeRef(LiteralStringRef("profiling/"), LiteralStringRef("profiling0")) + KeyRangeRef("profiling/"_sr, "profiling0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin)), /* deprecated */ 720); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - KeyRangeRef(LiteralStringRef("maintenance/"), LiteralStringRef("maintenance0")) + KeyRangeRef("maintenance/"_sr, "maintenance0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::IMPLTYPE::READWRITE, std::make_unique( - KeyRangeRef(LiteralStringRef("data_distribution/"), LiteralStringRef("data_distribution0")) + KeyRangeRef("data_distribution/"_sr, "data_distribution0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); registerSpecialKeysImpl( SpecialKeySpace::MODULE::ACTORLINEAGE, @@ -1637,20 +1651,18 @@ DatabaseContext::DatabaseContext(Reference(ddStatsRange)); - registerSpecialKeysImpl( - SpecialKeySpace::MODULE::METRICS, - SpecialKeySpace::IMPLTYPE::READONLY, - std::make_unique(KeyRangeRef(LiteralStringRef("\xff\xff/metrics/health/"), - LiteralStringRef("\xff\xff/metrics/health0")))); - registerSpecialKeysImpl( - SpecialKeySpace::MODULE::WORKERINTERFACE, - SpecialKeySpace::IMPLTYPE::READONLY, - std::make_unique(KeyRangeRef( - LiteralStringRef("\xff\xff/worker_interfaces/"), LiteralStringRef("\xff\xff/worker_interfaces0")))); + registerSpecialKeysImpl(SpecialKeySpace::MODULE::METRICS, + SpecialKeySpace::IMPLTYPE::READONLY, + std::make_unique( + KeyRangeRef("\xff\xff/metrics/health/"_sr, "\xff\xff/metrics/health0"_sr))); + registerSpecialKeysImpl(SpecialKeySpace::MODULE::WORKERINTERFACE, + SpecialKeySpace::IMPLTYPE::READONLY, + std::make_unique( + KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr))); registerSpecialKeysImpl(SpecialKeySpace::MODULE::STATUSJSON, SpecialKeySpace::IMPLTYPE::READONLY, std::make_unique( - LiteralStringRef("\xff\xff/status/json"), + "\xff\xff/status/json"_sr, [](ReadYourWritesTransaction* ryw) -> Future> { if (ryw->getDatabase().getPtr() && ryw->getDatabase()->getConnectionRecord()) { ++ryw->getDatabase()->transactionStatusRequests; @@ -1658,11 +1670,12 @@ DatabaseContext::DatabaseContext(Reference(); } - })); + }, + true)); registerSpecialKeysImpl(SpecialKeySpace::MODULE::CLUSTERFILEPATH, SpecialKeySpace::IMPLTYPE::READONLY, std::make_unique( - LiteralStringRef("\xff\xff/cluster_file_path"), + "\xff\xff/cluster_file_path"_sr, [](ReadYourWritesTransaction* ryw) -> Future> { try { if (ryw->getDatabase().getPtr() && @@ -1675,13 +1688,14 @@ DatabaseContext::DatabaseContext(Reference(); - })); + }, + true)); registerSpecialKeysImpl( SpecialKeySpace::MODULE::CONNECTIONSTRING, SpecialKeySpace::IMPLTYPE::READONLY, std::make_unique( - LiteralStringRef("\xff\xff/connection_string"), + "\xff\xff/connection_string"_sr, [](ReadYourWritesTransaction* ryw) -> Future> { try { if (ryw->getDatabase().getPtr() && ryw->getDatabase()->getConnectionRecord()) { @@ -1693,22 +1707,25 @@ DatabaseContext::DatabaseContext(Reference(); - })); - registerSpecialKeysImpl( - SpecialKeySpace::MODULE::CLUSTERID, - SpecialKeySpace::IMPLTYPE::READONLY, - std::make_unique( - LiteralStringRef("\xff\xff/cluster_id"), [](ReadYourWritesTransaction* ryw) -> Future> { - try { - if (ryw->getDatabase().getPtr()) { - return map(getClusterId(ryw->getDatabase()), - [](UID id) { return Optional(StringRef(id.toString())); }); - } - } catch (Error& e) { - return e; - } - return Optional(); - })); + }, + true)); + registerSpecialKeysImpl(SpecialKeySpace::MODULE::CLUSTERID, + SpecialKeySpace::IMPLTYPE::READONLY, + std::make_unique( + "\xff\xff/cluster_id"_sr, + [](ReadYourWritesTransaction* ryw) -> Future> { + try { + if (ryw->getDatabase().getPtr()) { + return map(getClusterId(ryw->getDatabase()), [](UID id) { + return Optional(StringRef(id.toString())); + }); + } + } catch (Error& e) { + return e; + } + return Optional(); + }, + true)); registerSpecialKeysImpl( SpecialKeySpace::MODULE::MANAGEMENT, @@ -1753,9 +1770,13 @@ DatabaseContext::DatabaseContext(const Error& err) transactionsProcessBehind("ProcessBehind", cc), transactionsThrottled("Throttled", cc), transactionsExpensiveClearCostEstCount("ExpensiveClearCostEstCount", cc), transactionGrvFullBatches("NumGrvFullBatches", cc), transactionGrvTimedOutBatches("NumGrvTimedOutBatches", cc), - transactionCommitVersionNotFoundForSS("CommitVersionNotFoundForSS", cc), latencies(1000), readLatencies(1000), - commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), bgLatencies(1000), - bgGranulesPerRequest(1000), sharedStatePtr(nullptr), transactionTracingSample(false), + transactionCommitVersionNotFoundForSS("CommitVersionNotFoundForSS", cc), usedAnyChangeFeeds(false), + ccFeed("ChangeFeedClientMetrics"), feedStreamStarts("FeedStreamStarts", ccFeed), + feedMergeStreamStarts("FeedMergeStreamStarts", ccFeed), feedErrors("FeedErrors", ccFeed), + feedNonRetriableErrors("FeedNonRetriableErrors", ccFeed), feedPops("FeedPops", ccFeed), + feedPopsFallback("FeedPopsFallback", ccFeed), latencies(1000), readLatencies(1000), commitLatencies(1000), + GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), bgLatencies(1000), bgGranulesPerRequest(1000), + sharedStatePtr(nullptr), transactionTracingSample(false), smoothMidShardSize(CLIENT_KNOBS->SHARD_STAT_SMOOTH_AMOUNT), connectToDatabaseEventCacheHolder(format("ConnectToDatabase/%s", dbId.toString().c_str())) {} @@ -2458,7 +2479,7 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu ASSERT(value.present()); Standalone> supportedVersions; - std::vector supportedVersionsStrings = value.get().splitAny(LiteralStringRef(";")); + std::vector supportedVersionsStrings = value.get().splitAny(";"_sr); for (StringRef versionString : supportedVersionsStrings) { #ifdef ADDRESS_SANITIZER __lsan_disable(); @@ -4651,13 +4672,12 @@ static Future tssStreamComparison(Request request, // FIXME: this code is pretty much identical to LoadBalance.h // TODO could add team check logic in if we added synchronous way to turn this into a fixed getRange request // and send it to the whole team and compare? I think it's fine to skip that for streaming though - CODE_PROBE(ssEndOfStream != tssEndOfStream, "SS or TSS stream finished early!"); // skip tss comparison if both are end of stream if ((!ssEndOfStream || !tssEndOfStream) && !TSS_doCompare(ssReply.get(), tssReply.get())) { CODE_PROBE(true, "TSS mismatch in stream comparison"); TraceEvent mismatchEvent( - (g_network->isSimulated() && g_simulator.tssMode == ISimulator::TSSMode::EnabledDropMutations) + (g_network->isSimulated() && g_simulator->tssMode == ISimulator::TSSMode::EnabledDropMutations) ? SevWarnAlways : SevError, TSS_mismatchTraceName(request)); @@ -4679,7 +4699,7 @@ static Future tssStreamComparison(Request request, // record a summarized trace event instead TraceEvent summaryEvent((g_network->isSimulated() && - g_simulator.tssMode == ISimulator::TSSMode::EnabledDropMutations) + g_simulator->tssMode == ISimulator::TSSMode::EnabledDropMutations) ? SevWarnAlways : SevError, TSS_mismatchTraceName(request)); @@ -5615,7 +5635,7 @@ void Transaction::addReadConflictRange(KeyRangeRef const& keys) { void Transaction::makeSelfConflicting() { BinaryWriter wr(Unversioned()); - wr.serializeBytes(LiteralStringRef("\xFF/SC/")); + wr.serializeBytes("\xFF/SC/"_sr); wr << deterministicRandom()->randomUniqueID(); auto r = singleKeyRange(wr.toValue(), tr.arena); tr.transaction.read_conflict_ranges.push_back(tr.arena, r); @@ -6116,30 +6136,46 @@ ACTOR Future> estimateCommitCosts(Referen // TODO: send the prefix as part of the commit request and ship it all the way // through to the storage servers void applyTenantPrefix(CommitTransactionRequest& req, Key tenantPrefix) { + VectorRef updatedMutations; + updatedMutations.reserve(req.arena, req.transaction.mutations.size()); for (auto& m : req.transaction.mutations) { + StringRef param1 = m.param1; + StringRef param2 = m.param2; if (m.param1 != metadataVersionKey) { - m.param1 = m.param1.withPrefix(tenantPrefix, req.arena); + param1 = m.param1.withPrefix(tenantPrefix, req.arena); if (m.type == MutationRef::ClearRange) { - m.param2 = m.param2.withPrefix(tenantPrefix, req.arena); + param2 = m.param2.withPrefix(tenantPrefix, req.arena); } else if (m.type == MutationRef::SetVersionstampedKey) { - uint8_t* key = mutateString(m.param1); - int* offset = reinterpret_cast(&key[m.param1.size() - 4]); + uint8_t* key = mutateString(param1); + int* offset = reinterpret_cast(&key[param1.size() - 4]); *offset += tenantPrefix.size(); } } + updatedMutations.push_back(req.arena, MutationRef(MutationRef::Type(m.type), param1, param2)); } + req.transaction.mutations = updatedMutations; - for (auto& rc : req.transaction.read_conflict_ranges) { + VectorRef updatedReadConflictRanges; + updatedReadConflictRanges.reserve(req.arena, req.transaction.read_conflict_ranges.size()); + for (auto const& rc : req.transaction.read_conflict_ranges) { if (rc.begin != metadataVersionKey) { - rc = rc.withPrefix(tenantPrefix, req.arena); + updatedReadConflictRanges.push_back(req.arena, rc.withPrefix(tenantPrefix, req.arena)); + } else { + updatedReadConflictRanges.push_back(req.arena, rc); } } + req.transaction.read_conflict_ranges = updatedReadConflictRanges; + VectorRef updatedWriteConflictRanges; + updatedWriteConflictRanges.reserve(req.arena, req.transaction.write_conflict_ranges.size()); for (auto& wc : req.transaction.write_conflict_ranges) { if (wc.begin != metadataVersionKey) { - wc = wc.withPrefix(tenantPrefix, req.arena); + updatedWriteConflictRanges.push_back(req.arena, wc.withPrefix(tenantPrefix, req.arena)); + } else { + updatedWriteConflictRanges.push_back(req.arena, wc); } } + req.transaction.write_conflict_ranges = updatedWriteConflictRanges; } ACTOR static Future tryCommit(Reference trState, @@ -6800,19 +6836,16 @@ ACTOR Future readVersionBatcher(DatabaseContext* cx, state Future timeout; state Optional debugID; state bool send_batch; - state Reference batchSizeDist = Histogram::getHistogram(LiteralStringRef("GrvBatcher"), - LiteralStringRef("ClientGrvBatchSize"), - Histogram::Unit::countLinear, - 0, - CLIENT_KNOBS->MAX_BATCH_SIZE * 2); + state Reference batchSizeDist = Histogram::getHistogram( + "GrvBatcher"_sr, "ClientGrvBatchSize"_sr, Histogram::Unit::countLinear, 0, CLIENT_KNOBS->MAX_BATCH_SIZE * 2); state Reference batchIntervalDist = - Histogram::getHistogram(LiteralStringRef("GrvBatcher"), - LiteralStringRef("ClientGrvBatchInterval"), + Histogram::getHistogram("GrvBatcher"_sr, + "ClientGrvBatchInterval"_sr, Histogram::Unit::microseconds, 0, CLIENT_KNOBS->GRV_BATCH_TIMEOUT * 1000000 * 2); - state Reference grvReplyLatencyDist = Histogram::getHistogram( - LiteralStringRef("GrvBatcher"), LiteralStringRef("ClientGrvReplyLatency"), Histogram::Unit::microseconds); + state Reference grvReplyLatencyDist = + Histogram::getHistogram("GrvBatcher"_sr, "ClientGrvReplyLatency"_sr, Histogram::Unit::microseconds); state double lastRequestTime = now(); state TransactionTagMap tags; @@ -7686,23 +7719,24 @@ ACTOR Future>> getBlobGranuleRangesActor(Trans if (tenantPrefix.present()) { state Standalone mappingPrefix = tenantPrefix.get().withPrefix(blobGranuleMappingKeys.begin); - // basically krmGetRange, but enable it to not use tenant without RAW_ACCESS by doing manual getRange with - // UseTenant::False + // basically krmGetRangeUnaligned, but enable it to not use tenant without RAW_ACCESS by doing manual + // getRange with UseTenant::False GetRangeLimits limits(2 * rangeLimit + 2); limits.minRows = 2; + RangeResult rawMapping = wait(getRange(self->trState, self->getReadVersion(), lastLessOrEqual(keyRange.begin.withPrefix(mappingPrefix)), - firstGreaterThan(keyRange.end.withPrefix(mappingPrefix)), + KeySelectorRef(keyRange.end.withPrefix(mappingPrefix), false, +2), limits, Reverse::False, UseTenant::False)); // strip off mapping prefix - blobGranuleMapping = krmDecodeRanges(mappingPrefix, currentRange, rawMapping); + blobGranuleMapping = krmDecodeRanges(mappingPrefix, currentRange, rawMapping, false); } else { wait(store( blobGranuleMapping, - krmGetRanges( + krmGetRangesUnaligned( self, blobGranuleMappingKeys.begin, currentRange, 1000, GetRangeLimits::BYTE_LIMIT_UNLIMITED))); } @@ -8119,7 +8153,7 @@ ACTOR Future verifyBlobRangeActor(Reference cx, KeyRan // Chunk up to smaller ranges than this limit. Must be smaller than BG_TOO_MANY_GRANULES to not hit the limit int batchCount = 0; for (auto& it : allRanges) { - if (it.begin != curRegion.end) { + if (it.begin > curRegion.end) { return invalidVersion; } @@ -8381,7 +8415,7 @@ Reference Transaction::createTrLogInfoProbabilistically(cons cx->globalConfig->get(fdbClientInfoTxnSampleRate, CLIENT_KNOBS->CSI_SAMPLING_PROBABILITY); if (((networkOptions.logClientInfo.present() && networkOptions.logClientInfo.get()) || BUGGIFY) && deterministicRandom()->random01() < clientSamplingProbability && - (!g_network->isSimulated() || !g_simulator.speedUpSimulation)) { + (!g_network->isSimulated() || !g_simulator->speedUpSimulation)) { return makeReference(TransactionLogInfo::DATABASE); } } @@ -8702,16 +8736,13 @@ ACTOR static Future rebootWorkerActor(DatabaseContext* cx, ValueRef add for (const auto& it : kvs) { ClientWorkerInterface workerInterf = BinaryReader::fromStringRef(it.value, IncludeVersion()); - Key primaryAddress = - it.key.endsWith(LiteralStringRef(":tls")) ? it.key.removeSuffix(LiteralStringRef(":tls")) : it.key; + Key primaryAddress = it.key.endsWith(":tls"_sr) ? it.key.removeSuffix(":tls"_sr) : it.key; workerInterfaces[primaryAddress] = workerInterf; // Also add mapping from a worker's second address(if present) to its interface if (workerInterf.reboot.getEndpoint().addresses.secondaryAddress.present()) { Key secondAddress = StringRef(workerInterf.reboot.getEndpoint().addresses.secondaryAddress.get().toString()); - secondAddress = secondAddress.endsWith(LiteralStringRef(":tls")) - ? secondAddress.removeSuffix(LiteralStringRef(":tls")) - : secondAddress; + secondAddress = secondAddress.endsWith(":tls"_sr) ? secondAddress.removeSuffix(":tls"_sr) : secondAddress; workerInterfaces[secondAddress] = workerInterf; } } @@ -9492,6 +9523,7 @@ ACTOR Future getChangeFeedStreamActor(Reference db, bool canReadPopped) { state Database cx(db); state Span span("NAPI:GetChangeFeedStream"_loc); + db->usedAnyChangeFeeds = true; results->endVersion = end; @@ -9543,7 +9575,7 @@ ACTOR Future getChangeFeedStreamActor(Reference db, if (useIdx >= 0) { chosenLocations[loc] = useIdx; loc++; - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.01)) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && BUGGIFY_WITH_PROB(0.01)) { // simulate as if we had to wait for all alternatives delayed, before the next one wait(delay(deterministicRandom()->random01())); } @@ -9567,7 +9599,10 @@ ACTOR Future getChangeFeedStreamActor(Reference db, loc = 0; } + ++db->feedStreamStarts; + if (locations.size() > 1) { + ++db->feedMergeStreamStarts; std::vector> interfs; for (int i = 0; i < locations.size(); i++) { interfs.emplace_back(locations[i].locations->getInterface(chosenLocations[i]), @@ -9590,6 +9625,7 @@ ACTOR Future getChangeFeedStreamActor(Reference db, results->streams.clear(); results->storageData.clear(); if (e.code() == error_code_change_feed_popped) { + ++db->feedNonRetriableErrors; CODE_PROBE(true, "getChangeFeedStreamActor got popped"); results->mutations.sendError(e); results->refresh.sendError(e); @@ -9609,18 +9645,29 @@ ACTOR Future getChangeFeedStreamActor(Reference db, if (e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed || e.code() == error_code_connection_failed || e.code() == error_code_unknown_change_feed || e.code() == error_code_broken_promise || e.code() == error_code_future_version || - e.code() == error_code_request_maybe_delivered) { + e.code() == error_code_request_maybe_delivered || + e.code() == error_code_storage_too_many_feed_streams) { + ++db->feedErrors; db->changeFeedCache.erase(rangeID); cx->invalidateCache(Key(), keys); - if (begin == lastBeginVersion) { + if (begin == lastBeginVersion || e.code() == error_code_storage_too_many_feed_streams) { // We didn't read anything since the last failure before failing again. - // Do exponential backoff, up to 1 second - sleepWithBackoff = std::min(1.0, sleepWithBackoff * 1.5); + // Back off quickly and exponentially, up to 1 second + sleepWithBackoff = std::min(2.0, sleepWithBackoff * 5); + sleepWithBackoff = std::max(0.1, sleepWithBackoff); } else { sleepWithBackoff = CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY; } + TraceEvent("ChangeFeedClientError") + .errorUnsuppressed(e) + .suppressFor(30.0) + .detail("AnyProgress", begin != lastBeginVersion); wait(delay(sleepWithBackoff)); } else { + if (e.code() != error_code_end_of_stream) { + ++db->feedNonRetriableErrors; + TraceEvent("ChangeFeedClientErrorNonRetryable").errorUnsuppressed(e).suppressFor(5.0); + } results->mutations.sendError(e); results->refresh.sendError(change_feed_cancelled()); results->streams.clear(); @@ -9747,6 +9794,7 @@ Future DatabaseContext::getOverlappingChangeFeeds(Ke } ACTOR static Future popChangeFeedBackup(Database cx, Key rangeID, Version version) { + ++cx->feedPopsFallback; state Transaction tr(cx); loop { try { @@ -9784,6 +9832,8 @@ ACTOR Future popChangeFeedMutationsActor(Reference db, Ke state Database cx(db); state Key rangeIDKey = rangeID.withPrefix(changeFeedPrefix); state Span span("NAPI:PopChangeFeedMutations"_loc); + db->usedAnyChangeFeeds = true; + ++db->feedPops; state KeyRange keys = wait(getChangeFeedRange(db, cx, rangeID)); @@ -9880,6 +9930,18 @@ ACTOR Future purgeBlobGranulesActor(Reference db, purgeRange = purgeRange.withPrefix(tenantEntry.prefix); } + // must be aligned to blob range(s) + state Future> beginPresent = tr.get(purgeRange.begin.withPrefix(blobRangeKeys.begin)); + state Future> endPresent = tr.get(purgeRange.end.withPrefix(blobRangeKeys.begin)); + wait(success(beginPresent) && success(endPresent)); + if (!beginPresent.get().present() || !endPresent.get().present()) { + TraceEvent("UnalignedPurge") + .detail("Range", range) + .detail("Version", purgeVersion) + .detail("Force", force); + throw unsupported_operation(); + } + Value purgeValue = blobGranulePurgeValueFor(purgeVersion, range, force); tr.atomicOp( addVersionStampAtEnd(blobGranulePurgeKeys.begin), purgeValue, MutationRef::SetVersionstampedKey); diff --git a/fdbclient/PaxosConfigTransaction.actor.cpp b/fdbclient/PaxosConfigTransaction.actor.cpp index 3e69df7227..b940aced7f 100644 --- a/fdbclient/PaxosConfigTransaction.actor.cpp +++ b/fdbclient/PaxosConfigTransaction.actor.cpp @@ -19,6 +19,7 @@ */ #include "fdbclient/DatabaseContext.h" +#include "fdbclient/MonitorLeader.h" #include "fdbclient/PaxosConfigTransaction.h" #include "flow/actorcompiler.h" // must be last include @@ -34,8 +35,9 @@ class CommitQuorum { Standalone> mutations; ConfigCommitAnnotation annotation; - ConfigTransactionCommitRequest getCommitRequest(ConfigGeneration generation) const { - return ConfigTransactionCommitRequest(generation, mutations, annotation); + ConfigTransactionCommitRequest getCommitRequest(ConfigGeneration generation, + CoordinatorsHash coordinatorsHash) const { + return ConfigTransactionCommitRequest(coordinatorsHash, generation, mutations, annotation); } void updateResult() { @@ -62,14 +64,16 @@ class CommitQuorum { ACTOR static Future addRequestActor(CommitQuorum* self, ConfigGeneration generation, + CoordinatorsHash coordinatorsHash, ConfigTransactionInterface cti) { try { if (cti.hostname.present()) { - wait(timeoutError(retryGetReplyFromHostname( - self->getCommitRequest(generation), cti.hostname.get(), WLTOKEN_CONFIGTXN_COMMIT), + wait(timeoutError(retryGetReplyFromHostname(self->getCommitRequest(generation, coordinatorsHash), + cti.hostname.get(), + WLTOKEN_CONFIGTXN_COMMIT), CLIENT_KNOBS->COMMIT_QUORUM_TIMEOUT)); } else { - wait(timeoutError(cti.commit.getReply(self->getCommitRequest(generation)), + wait(timeoutError(cti.commit.getReply(self->getCommitRequest(generation, coordinatorsHash)), CLIENT_KNOBS->COMMIT_QUORUM_TIMEOUT)); } ++self->successful; @@ -109,11 +113,11 @@ public: } void setTimestamp() { annotation.timestamp = now(); } size_t expectedSize() const { return annotation.expectedSize() + mutations.expectedSize(); } - Future commit(ConfigGeneration generation) { + Future commit(ConfigGeneration generation, CoordinatorsHash coordinatorsHash) { // Send commit message to all replicas, even those that did not return the used replica. // This way, slow replicas are kept up date. for (const auto& cti : ctis) { - actors.add(addRequestActor(this, generation, cti)); + actors.add(addRequestActor(this, generation, coordinatorsHash, cti)); } return result.getFuture(); } @@ -122,11 +126,13 @@ public: class GetGenerationQuorum { ActorCollection actors{ false }; + CoordinatorsHash coordinatorsHash{ 0 }; std::vector ctis; std::map> seenGenerations; Promise result; size_t totalRepliesReceived{ 0 }; size_t maxAgreement{ 0 }; + Future coordinatorsChangedFuture; Optional lastSeenLiveVersion; Future getGenerationFuture; @@ -137,14 +143,15 @@ class GetGenerationQuorum { if (cti.hostname.present()) { wait(timeoutError(store(reply, retryGetReplyFromHostname( - ConfigTransactionGetGenerationRequest{ self->lastSeenLiveVersion }, + ConfigTransactionGetGenerationRequest{ self->coordinatorsHash, + self->lastSeenLiveVersion }, cti.hostname.get(), WLTOKEN_CONFIGTXN_GETGENERATION)), CLIENT_KNOBS->GET_GENERATION_QUORUM_TIMEOUT)); } else { wait(timeoutError(store(reply, - cti.getGeneration.getReply( - ConfigTransactionGetGenerationRequest{ self->lastSeenLiveVersion })), + cti.getGeneration.getReply(ConfigTransactionGetGenerationRequest{ + self->coordinatorsHash, self->lastSeenLiveVersion })), CLIENT_KNOBS->GET_GENERATION_QUORUM_TIMEOUT)); } @@ -155,6 +162,14 @@ class GetGenerationQuorum { auto& replicas = self->seenGenerations[gen]; replicas.push_back(cti); self->maxAgreement = std::max(replicas.size(), self->maxAgreement); + // TraceEvent("ConfigTransactionGotGenerationReply") + // .detail("From", cti.getGeneration.getEndpoint().getPrimaryAddress()) + // .detail("TotalRepliesReceived", self->totalRepliesReceived) + // .detail("ReplyGeneration", gen.toString()) + // .detail("Replicas", replicas.size()) + // .detail("Coordinators", self->ctis.size()) + // .detail("MaxAgreement", self->maxAgreement) + // .detail("LastSeenLiveVersion", self->lastSeenLiveVersion); if (replicas.size() >= self->ctis.size() / 2 + 1 && !self->result.isSet()) { self->result.send(gen); } else if (self->maxAgreement + (self->ctis.size() - self->totalRepliesReceived) < @@ -200,8 +215,18 @@ class GetGenerationQuorum { } catch (Error& e) { if (e.code() == error_code_failed_to_reach_quorum) { CODE_PROBE(true, "Failed to reach quorum getting generation"); - wait(delayJittered( - std::clamp(0.005 * (1 << retries), 0.0, CLIENT_KNOBS->TIMEOUT_RETRY_UPPER_BOUND))); + if (self->coordinatorsChangedFuture.isReady()) { + throw coordinators_changed(); + } + wait(delayJittered(std::clamp( + 0.005 * (1 << std::min(retries, 30)), 0.0, CLIENT_KNOBS->TIMEOUT_RETRY_UPPER_BOUND))); + if (deterministicRandom()->random01() < 0.05) { + // Randomly inject a delay of at least the generation + // reply timeout, to try to prevent contention between + // clients. + wait(delay(CLIENT_KNOBS->GET_GENERATION_QUORUM_TIMEOUT * + (deterministicRandom()->random01() + 1.0))); + } ++retries; self->actors.clear(false); self->seenGenerations.clear(); @@ -217,9 +242,12 @@ class GetGenerationQuorum { public: GetGenerationQuorum() = default; - explicit GetGenerationQuorum(std::vector const& ctis, + explicit GetGenerationQuorum(CoordinatorsHash coordinatorsHash, + std::vector const& ctis, + Future coordinatorsChangedFuture, Optional const& lastSeenLiveVersion = {}) - : ctis(ctis), lastSeenLiveVersion(lastSeenLiveVersion) {} + : coordinatorsHash(coordinatorsHash), ctis(ctis), coordinatorsChangedFuture(coordinatorsChangedFuture), + lastSeenLiveVersion(lastSeenLiveVersion) {} Future getGeneration() { if (!getGenerationFuture.isValid()) { getGenerationFuture = getGenerationActor(this); @@ -240,12 +268,14 @@ public: }; class PaxosConfigTransactionImpl { + CoordinatorsHash coordinatorsHash{ 0 }; std::vector ctis; GetGenerationQuorum getGenerationQuorum; CommitQuorum commitQuorum; int numRetries{ 0 }; Optional dID; Database cx; + Future watchClusterFileFuture; ACTOR static Future> get(PaxosConfigTransactionImpl* self, Key key) { state ConfigKey configKey = ConfigKey::decodeKey(key); @@ -263,18 +293,19 @@ class PaxosConfigTransactionImpl { } wait(waitForAll(fs)); state Reference configNodes(new ConfigTransactionInfo(readReplicas)); - ConfigTransactionGetReply reply = - wait(timeoutError(basicLoadBalance(configNodes, - &ConfigTransactionInterface::get, - ConfigTransactionGetRequest{ generation, configKey }), - CLIENT_KNOBS->GET_KNOB_TIMEOUT)); + ConfigTransactionGetReply reply = wait(timeoutError( + basicLoadBalance(configNodes, + &ConfigTransactionInterface::get, + ConfigTransactionGetRequest{ self->coordinatorsHash, generation, configKey }), + CLIENT_KNOBS->GET_KNOB_TIMEOUT)); if (reply.value.present()) { return reply.value.get().toValue(); } else { return Optional{}; } } catch (Error& e) { - if (e.code() != error_code_timed_out && e.code() != error_code_broken_promise) { + if (e.code() != error_code_timed_out && e.code() != error_code_broken_promise && + e.code() != error_code_coordinators_changed) { throw; } self->reset(); @@ -283,58 +314,87 @@ class PaxosConfigTransactionImpl { } ACTOR static Future getConfigClasses(PaxosConfigTransactionImpl* self) { - state ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); - state std::vector readReplicas = self->getGenerationQuorum.getReadReplicas(); - std::vector> fs; - for (ConfigTransactionInterface& readReplica : readReplicas) { - if (readReplica.hostname.present()) { - fs.push_back(tryInitializeRequestStream( - &readReplica.getClasses, readReplica.hostname.get(), WLTOKEN_CONFIGTXN_GETCLASSES)); + loop { + try { + state ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); + state std::vector readReplicas = + self->getGenerationQuorum.getReadReplicas(); + std::vector> fs; + for (ConfigTransactionInterface& readReplica : readReplicas) { + if (readReplica.hostname.present()) { + fs.push_back(tryInitializeRequestStream( + &readReplica.getClasses, readReplica.hostname.get(), WLTOKEN_CONFIGTXN_GETCLASSES)); + } + } + wait(waitForAll(fs)); + state Reference configNodes(new ConfigTransactionInfo(readReplicas)); + ConfigTransactionGetConfigClassesReply reply = wait( + basicLoadBalance(configNodes, + &ConfigTransactionInterface::getClasses, + ConfigTransactionGetConfigClassesRequest{ self->coordinatorsHash, generation })); + RangeResult result; + result.reserve(result.arena(), reply.configClasses.size()); + for (const auto& configClass : reply.configClasses) { + result.push_back_deep(result.arena(), KeyValueRef(configClass, ""_sr)); + } + return result; + } catch (Error& e) { + if (e.code() != error_code_coordinators_changed) { + throw; + } + self->reset(); } } - wait(waitForAll(fs)); - state Reference configNodes(new ConfigTransactionInfo(readReplicas)); - ConfigTransactionGetConfigClassesReply reply = - wait(basicLoadBalance(configNodes, - &ConfigTransactionInterface::getClasses, - ConfigTransactionGetConfigClassesRequest{ generation })); - RangeResult result; - result.reserve(result.arena(), reply.configClasses.size()); - for (const auto& configClass : reply.configClasses) { - result.push_back_deep(result.arena(), KeyValueRef(configClass, ""_sr)); - } - return result; } ACTOR static Future getKnobs(PaxosConfigTransactionImpl* self, Optional configClass) { - state ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); - state std::vector readReplicas = self->getGenerationQuorum.getReadReplicas(); - std::vector> fs; - for (ConfigTransactionInterface& readReplica : readReplicas) { - if (readReplica.hostname.present()) { - fs.push_back(tryInitializeRequestStream( - &readReplica.getKnobs, readReplica.hostname.get(), WLTOKEN_CONFIGTXN_GETKNOBS)); + loop { + try { + state ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); + state std::vector readReplicas = + self->getGenerationQuorum.getReadReplicas(); + std::vector> fs; + for (ConfigTransactionInterface& readReplica : readReplicas) { + if (readReplica.hostname.present()) { + fs.push_back(tryInitializeRequestStream( + &readReplica.getKnobs, readReplica.hostname.get(), WLTOKEN_CONFIGTXN_GETKNOBS)); + } + } + wait(waitForAll(fs)); + state Reference configNodes(new ConfigTransactionInfo(readReplicas)); + ConfigTransactionGetKnobsReply reply = wait(basicLoadBalance( + configNodes, + &ConfigTransactionInterface::getKnobs, + ConfigTransactionGetKnobsRequest{ self->coordinatorsHash, generation, configClass })); + RangeResult result; + result.reserve(result.arena(), reply.knobNames.size()); + for (const auto& knobName : reply.knobNames) { + result.push_back_deep(result.arena(), KeyValueRef(knobName, ""_sr)); + } + return result; + } catch (Error& e) { + if (e.code() != error_code_coordinators_changed) { + throw; + } + self->reset(); } } - wait(waitForAll(fs)); - state Reference configNodes(new ConfigTransactionInfo(readReplicas)); - ConfigTransactionGetKnobsReply reply = - wait(basicLoadBalance(configNodes, - &ConfigTransactionInterface::getKnobs, - ConfigTransactionGetKnobsRequest{ generation, configClass })); - RangeResult result; - result.reserve(result.arena(), reply.knobNames.size()); - for (const auto& knobName : reply.knobNames) { - result.push_back_deep(result.arena(), KeyValueRef(knobName, ""_sr)); - } - return result; } ACTOR static Future commit(PaxosConfigTransactionImpl* self) { - ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); - self->commitQuorum.setTimestamp(); - wait(self->commitQuorum.commit(generation)); - return Void(); + loop { + try { + ConfigGeneration generation = wait(self->getGenerationQuorum.getGeneration()); + self->commitQuorum.setTimestamp(); + wait(self->commitQuorum.commit(generation, self->coordinatorsHash)); + return Void(); + } catch (Error& e) { + if (e.code() != error_code_coordinators_changed) { + throw; + } + self->reset(); + } + } } ACTOR static Future onError(PaxosConfigTransactionImpl* self, Error e) { @@ -350,6 +410,20 @@ class PaxosConfigTransactionImpl { throw e; } + // Returns when the cluster interface updates with a new connection string. + ACTOR static Future watchClusterFile(Database cx) { + state Future leaderMonitor = + monitorLeader(cx->getConnectionRecord(), cx->statusClusterInterface); + state std::string connectionString = cx->getConnectionRecord()->getConnectionString().toString(); + + loop { + wait(cx->statusClusterInterface->onChange()); + if (cx->getConnectionRecord()->getConnectionString().toString() != connectionString) { + return Void(); + } + } + } + public: Future getReadVersion() { return map(getGenerationQuorum.getGeneration(), [](auto const& gen) { return gen.committedVersion; }); @@ -395,7 +469,25 @@ public: void debugTransaction(UID dID) { this->dID = dID; } void reset() { - getGenerationQuorum = GetGenerationQuorum{ ctis }; + ctis.clear(); + // Re-read connection string. If the cluster file changed, this will + // return the updated value. + const ClusterConnectionString& cs = cx->getConnectionRecord()->getConnectionString(); + ctis.reserve(cs.hostnames.size() + cs.coords.size()); + for (const auto& h : cs.hostnames) { + ctis.emplace_back(h); + } + for (const auto& c : cs.coords) { + ctis.emplace_back(c); + } + coordinatorsHash = std::hash()(cx->getConnectionRecord()->getConnectionString().toString()); + if (!cx->statusLeaderMon.isValid() || cx->statusLeaderMon.isReady()) { + cx->statusClusterInterface = makeReference>>(); + cx->statusLeaderMon = watchClusterFile(cx); + } + getGenerationQuorum = GetGenerationQuorum{ + coordinatorsHash, ctis, cx->statusLeaderMon, getGenerationQuorum.getLastSeenLiveVersion() + }; commitQuorum = CommitQuorum{ ctis }; } @@ -416,21 +508,10 @@ public: Future commit() { return commit(this); } - PaxosConfigTransactionImpl(Database const& cx) : cx(cx) { - const ClusterConnectionString& cs = cx->getConnectionRecord()->getConnectionString(); - ctis.reserve(cs.hostnames.size() + cs.coords.size()); - for (const auto& h : cs.hostnames) { - ctis.emplace_back(h); - } - for (const auto& c : cs.coords) { - ctis.emplace_back(c); - } - getGenerationQuorum = GetGenerationQuorum{ ctis }; - commitQuorum = CommitQuorum{ ctis }; - } + PaxosConfigTransactionImpl(Database const& cx) : cx(cx) { reset(); } PaxosConfigTransactionImpl(std::vector const& ctis) - : ctis(ctis), getGenerationQuorum(ctis), commitQuorum(ctis) {} + : ctis(ctis), getGenerationQuorum(0, ctis, Future()), commitQuorum(ctis) {} }; Future PaxosConfigTransaction::getReadVersion() { diff --git a/fdbclient/RYWIterator.cpp b/fdbclient/RYWIterator.cpp index 949f164485..3966df3748 100644 --- a/fdbclient/RYWIterator.cpp +++ b/fdbclient/RYWIterator.cpp @@ -231,28 +231,28 @@ void testSnapshotCache() { WriteMap writes(&arena); Standalone> keys; - keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("d"), LiteralStringRef("doo"))); - keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("e"), LiteralStringRef("eoo"))); - keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("e\x00"), LiteralStringRef("zoo"))); - keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("f"), LiteralStringRef("foo"))); - cache.insert(KeyRangeRef(LiteralStringRef("d"), LiteralStringRef("f\x00")), keys); + keys.push_back_deep(keys.arena(), KeyValueRef("d"_sr, "doo"_sr)); + keys.push_back_deep(keys.arena(), KeyValueRef("e"_sr, "eoo"_sr)); + keys.push_back_deep(keys.arena(), KeyValueRef("e\x00"_sr, "zoo"_sr)); + keys.push_back_deep(keys.arena(), KeyValueRef("f"_sr, "foo"_sr)); + cache.insert(KeyRangeRef("d"_sr, "f\x00"_sr), keys); - cache.insert(KeyRangeRef(LiteralStringRef("g"), LiteralStringRef("h")), Standalone>()); + cache.insert(KeyRangeRef("g"_sr, "h"_sr), Standalone>()); Standalone> keys2; - keys2.push_back_deep(keys2.arena(), KeyValueRef(LiteralStringRef("k"), LiteralStringRef("koo"))); - keys2.push_back_deep(keys2.arena(), KeyValueRef(LiteralStringRef("l"), LiteralStringRef("loo"))); - cache.insert(KeyRangeRef(LiteralStringRef("j"), LiteralStringRef("m")), keys2); + keys2.push_back_deep(keys2.arena(), KeyValueRef("k"_sr, "koo"_sr)); + keys2.push_back_deep(keys2.arena(), KeyValueRef("l"_sr, "loo"_sr)); + cache.insert(KeyRangeRef("j"_sr, "m"_sr), keys2); - writes.mutate(LiteralStringRef("c"), MutationRef::SetValue, LiteralStringRef("c--"), true); - writes.clear(KeyRangeRef(LiteralStringRef("c\x00"), LiteralStringRef("e")), true); - writes.mutate(LiteralStringRef("c\x00"), MutationRef::SetValue, LiteralStringRef("c00--"), true); + writes.mutate("c"_sr, MutationRef::SetValue, "c--"_sr, true); + writes.clear(KeyRangeRef("c\x00"_sr, "e"_sr), true); + writes.mutate("c\x00"_sr, MutationRef::SetValue, "c00--"_sr, true); WriteMap::iterator it3(&writes); - writes.mutate(LiteralStringRef("d"), MutationRef::SetValue, LiteralStringRef("d--"), true); - writes.mutate(LiteralStringRef("e"), MutationRef::SetValue, LiteralStringRef("e++"), true); - writes.mutate(LiteralStringRef("i"), MutationRef::SetValue, LiteralStringRef("i--"), true); + writes.mutate("d"_sr, MutationRef::SetValue, "d--"_sr, true); + writes.mutate("e"_sr, MutationRef::SetValue, "e++"_sr, true); + writes.mutate("i"_sr, MutationRef::SetValue, "i--"_sr, true); - KeyRange searchKeys = KeyRangeRef(LiteralStringRef("a"), LiteralStringRef("z")); + KeyRange searchKeys = KeyRangeRef("a"_sr, "z"_sr); RYWIterator it(&cache, &writes); it.skip(searchKeys.begin); @@ -425,7 +425,7 @@ TEST_CASE("/fdbclient/WriteMap/emptiness") { Arena arena = Arena(); WriteMap writes = WriteMap(&arena); ASSERT(writes.empty()); - writes.mutate(LiteralStringRef("apple"), MutationRef::SetValue, LiteralStringRef("red"), true); + writes.mutate("apple"_sr, MutationRef::SetValue, "red"_sr, true); ASSERT(!writes.empty()); return Void(); } @@ -457,11 +457,11 @@ TEST_CASE("/fdbclient/WriteMap/clear") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("apple"), MutationRef::SetValue, LiteralStringRef("red"), true); + writes.mutate("apple"_sr, MutationRef::SetValue, "red"_sr, true); ASSERT(!writes.empty()); ASSERT(getWriteMapCount(&writes) == 3); - KeyRangeRef range = KeyRangeRef(LiteralStringRef("a"), LiteralStringRef("j")); + KeyRangeRef range = KeyRangeRef("a"_sr, "j"_sr); writes.clear(range, true); ASSERT(getWriteMapCount(&writes) == 3); @@ -474,22 +474,19 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00"), - MutationRef::SetVersionstampedKey, - LiteralStringRef("1"), - true); + writes.mutate("stamp:XXXXXXXX\x06\x00\x00\x00"_sr, MutationRef::SetVersionstampedKey, "1"_sr, true); ASSERT(!writes.empty()); ASSERT(getWriteMapCount(&writes) == 3); - writes.mutate(LiteralStringRef("stamp:ZZZZZZZZZZ"), MutationRef::AddValue, LiteralStringRef("2"), true); + writes.mutate("stamp:ZZZZZZZZZZ"_sr, MutationRef::AddValue, "2"_sr, true); ASSERT(getWriteMapCount(&writes) == 5); WriteMap::iterator it(&writes); it.skip(allKeys.begin); ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 0); + ASSERT(it.beginKey().compare(""_sr) == 0); + ASSERT(it.endKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -498,8 +495,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 0); + ASSERT(it.beginKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00"_sr) == 0); + ASSERT(it.endKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00\x00"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(it.is_conflict_range()); ASSERT(it.is_operation()); @@ -509,8 +506,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp:ZZZZZZZZZZ")) == 0); + ASSERT(it.beginKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00\x00"_sr) == 0); + ASSERT(it.endKey().compare("stamp:ZZZZZZZZZZ"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -519,8 +516,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp:ZZZZZZZZZZ")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp:ZZZZZZZZZZ\x00")) == 0); + ASSERT(it.beginKey().compare("stamp:ZZZZZZZZZZ"_sr) == 0); + ASSERT(it.endKey().compare("stamp:ZZZZZZZZZZ\x00"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(it.is_conflict_range()); ASSERT(it.is_operation()); @@ -530,8 +527,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp:ZZZZZZZZZZ\x00")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("\xff\xff")) == 0); + ASSERT(it.beginKey().compare("stamp:ZZZZZZZZZZ\x00"_sr) == 0); + ASSERT(it.endKey().compare("\xff\xff"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -550,22 +547,19 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("stamp"), - MutationRef::SetVersionstampedValue, - LiteralStringRef("XXXXXXXX\x00\x00\x00\x00\x00\x00"), - true); + writes.mutate("stamp"_sr, MutationRef::SetVersionstampedValue, "XXXXXXXX\x00\x00\x00\x00\x00\x00"_sr, true); ASSERT(!writes.empty()); ASSERT(getWriteMapCount(&writes) == 3); - writes.mutate(LiteralStringRef("stamp123"), MutationRef::AddValue, LiteralStringRef("1"), true); + writes.mutate("stamp123"_sr, MutationRef::AddValue, "1"_sr, true); ASSERT(getWriteMapCount(&writes) == 5); WriteMap::iterator it(&writes); it.skip(allKeys.begin); ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp")) == 0); + ASSERT(it.beginKey().compare(""_sr) == 0); + ASSERT(it.endKey().compare("stamp"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -574,8 +568,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp\x00")) == 0); + ASSERT(it.beginKey().compare("stamp"_sr) == 0); + ASSERT(it.endKey().compare("stamp\x00"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(it.is_conflict_range()); ASSERT(it.is_operation()); @@ -585,8 +579,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp\x00")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp123")) == 0); + ASSERT(it.beginKey().compare("stamp\x00"_sr) == 0); + ASSERT(it.endKey().compare("stamp123"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -595,8 +589,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp123")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("stamp123\x00")) == 0); + ASSERT(it.beginKey().compare("stamp123"_sr) == 0); + ASSERT(it.endKey().compare("stamp123\x00"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(it.is_conflict_range()); ASSERT(it.is_operation()); @@ -606,8 +600,8 @@ TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") { ++it; ASSERT(it.beginKey() < allKeys.end); - ASSERT(it.beginKey().compare(LiteralStringRef("stamp123\x00")) == 0); - ASSERT(it.endKey().compare(LiteralStringRef("\xff\xff")) == 0); + ASSERT(it.beginKey().compare("stamp123\x00"_sr) == 0); + ASSERT(it.endKey().compare("\xff\xff"_sr) == 0); ASSERT(!it.is_cleared_range()); ASSERT(!it.is_conflict_range()); ASSERT(!it.is_operation()); @@ -626,10 +620,10 @@ TEST_CASE("/fdbclient/WriteMap/addValue") { ASSERT(writes.empty()); ASSERT(getWriteMapCount(&writes) == 1); - writes.mutate(LiteralStringRef("apple123"), MutationRef::SetValue, LiteralStringRef("17"), true); + writes.mutate("apple123"_sr, MutationRef::SetValue, "17"_sr, true); ASSERT(getWriteMapCount(&writes) == 3); - writes.mutate(LiteralStringRef("apple123"), MutationRef::AddValue, LiteralStringRef("1"), true); + writes.mutate("apple123"_sr, MutationRef::AddValue, "1"_sr, true); ASSERT(getWriteMapCount(&writes) == 3); return Void(); diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index b5cf4027ee..7f7a6dc41d 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1544,7 +1544,7 @@ Future> ReadYourWritesTransaction::get(const Key& key, Snapshot return getDatabase()->specialKeySpace->get(this, key); } } else { - if (key == LiteralStringRef("\xff\xff/status/json")) { + if (key == "\xff\xff/status/json"_sr) { if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) { ++tr.getDatabase()->transactionStatusRequests; return getJSON(tr.getDatabase()); @@ -1553,7 +1553,7 @@ Future> ReadYourWritesTransaction::get(const Key& key, Snapshot } } - if (key == LiteralStringRef("\xff\xff/cluster_file_path")) { + if (key == "\xff\xff/cluster_file_path"_sr) { try { if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) { Optional output = StringRef(tr.getDatabase()->getConnectionRecord()->getLocation()); @@ -1565,7 +1565,7 @@ Future> ReadYourWritesTransaction::get(const Key& key, Snapshot return Optional(); } - if (key == LiteralStringRef("\xff\xff/connection_string")) { + if (key == "\xff\xff/connection_string"_sr) { try { if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) { Reference f = tr.getDatabase()->getConnectionRecord(); @@ -1627,7 +1627,7 @@ Future ReadYourWritesTransaction::getRange(KeySelector begin, return getDatabase()->specialKeySpace->getRange(this, begin, end, limits, reverse); } } else { - if (begin.getKey() == LiteralStringRef("\xff\xff/worker_interfaces")) { + if (begin.getKey() == "\xff\xff/worker_interfaces"_sr) { if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) { return getWorkerInterfaces(tr.getDatabase()->getConnectionRecord()); } else { @@ -1697,7 +1697,7 @@ Future ReadYourWritesTransaction::getMappedRange(KeySelector throw client_invalid_operation(); // Not support special keys. } } else { - if (begin.getKey() == LiteralStringRef("\xff\xff/worker_interfaces")) { + if (begin.getKey() == "\xff\xff/worker_interfaces"_sr) { throw client_invalid_operation(); // Not support special keys. } } @@ -2033,22 +2033,20 @@ RangeResult ReadYourWritesTransaction::getReadConflictRangeIntersecting(KeyRange if (kr.begin <= iter->begin() && iter->begin() < kr.end) { result.push_back(result.arena(), KeyValueRef(iter->begin().withPrefix(readConflictRangeKeysRange.begin, result.arena()), - iter->value() ? LiteralStringRef("1") : LiteralStringRef("0"))); + iter->value() ? "1"_sr : "0"_sr)); } } } else { - CoalescedKeyRefRangeMap readConflicts{ LiteralStringRef("0"), specialKeys.end }; + CoalescedKeyRefRangeMap readConflicts{ "0"_sr, specialKeys.end }; for (const auto& range : tr.readConflictRanges()) - readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()), "1"_sr); for (const auto& range : nativeReadRanges) - readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()), "1"_sr); for (const auto& f : tr.getExtraReadConflictRanges()) { if (f.isReady() && f.get().first < f.get().second) readConflicts.insert(KeyRangeRef(f.get().first, f.get().second) .withPrefix(readConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + "1"_sr); } auto beginIter = readConflicts.rangeContaining(kr.begin); if (beginIter->begin() != kr.begin) @@ -2066,7 +2064,7 @@ RangeResult ReadYourWritesTransaction::getWriteConflictRangeIntersecting(KeyRang RangeResult result; // Memory owned by result - CoalescedKeyRefRangeMap writeConflicts{ LiteralStringRef("0"), specialKeys.end }; + CoalescedKeyRefRangeMap writeConflicts{ "0"_sr, specialKeys.end }; if (!options.readYourWritesDisabled) { KeyRangeRef strippedWriteRangePrefix = kr.removePrefix(writeConflictRangeKeysRange.begin); @@ -2079,15 +2077,13 @@ RangeResult ReadYourWritesTransaction::getWriteConflictRangeIntersecting(KeyRang writeConflicts.insert( KeyRangeRef(it.beginKey().toArena(result.arena()), it.endKey().toArena(result.arena())) .withPrefix(writeConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + "1"_sr); } } else { for (const auto& range : tr.writeConflictRanges()) - writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), "1"_sr); for (const auto& range : nativeWriteRanges) - writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), "1"_sr); } for (const auto& k : versionStampKeys) { @@ -2107,8 +2103,7 @@ RangeResult ReadYourWritesTransaction::getWriteConflictRangeIntersecting(KeyRang } else { range = getVersionstampKeyRange(result.arena(), k, tr.getCachedReadVersion().orDefault(0), getMaxReadKey()); } - writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), - LiteralStringRef("1")); + writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()), "1"_sr); } auto beginIter = writeConflicts.rangeContaining(kr.begin); @@ -2154,13 +2149,13 @@ void ReadYourWritesTransaction::atomicOp(const KeyRef& key, const ValueRef& oper KeyRef k; if (!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedKey) { - k = key.withSuffix(LiteralStringRef("\x00\x00"), arena); + k = key.withSuffix("\x00\x00"_sr, arena); } else { k = KeyRef(arena, key); } ValueRef v; if (!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedValue) { - v = operand.withSuffix(LiteralStringRef("\x00\x00\x00\x00"), arena); + v = operand.withSuffix("\x00\x00\x00\x00"_sr, arena); } else { v = ValueRef(arena, operand); } @@ -2212,17 +2207,17 @@ void ReadYourWritesTransaction::set(const KeyRef& key, const ValueRef& value) { } else { // These three special keys are deprecated in 7.0 and an alternative C API is added // TODO : Rewrite related code using C api - if (key == LiteralStringRef("\xff\xff/reboot_worker")) { + if (key == "\xff\xff/reboot_worker"_sr) { BinaryReader::fromStringRef(value, IncludeVersion()) .reboot.send(RebootRequest()); return; } - if (key == LiteralStringRef("\xff\xff/suspend_worker")) { + if (key == "\xff\xff/suspend_worker"_sr) { BinaryReader::fromStringRef(value, IncludeVersion()) .reboot.send(RebootRequest(false, false, options.timeoutInSeconds)); return; } - if (key == LiteralStringRef("\xff\xff/reboot_and_check_worker")) { + if (key == "\xff\xff/reboot_and_check_worker"_sr) { BinaryReader::fromStringRef(value, IncludeVersion()) .reboot.send(RebootRequest(false, true)); return; diff --git a/fdbclient/S3BlobStore.actor.cpp b/fdbclient/S3BlobStore.actor.cpp index ce99e30ac8..3f5134f25a 100644 --- a/fdbclient/S3BlobStore.actor.cpp +++ b/fdbclient/S3BlobStore.actor.cpp @@ -109,7 +109,7 @@ bool S3BlobStoreEndpoint::BlobKnobs::set(StringRef name, int value) { TRY_PARAM(request_tries, rt); TRY_PARAM(request_timeout_min, rtom); // TODO: For backward compatibility because request_timeout was renamed to request_timeout_min - if (name == LiteralStringRef("request_timeout") || name == LiteralStringRef("rto")) { + if (name == "request_timeout"_sr || name == "rto"_sr) { request_timeout_min = value; return true; } @@ -187,7 +187,7 @@ std::string guessRegionFromDomain(std::string domain) { StringRef h(domain.c_str() + p); - if (!h.startsWith(LiteralStringRef("oss-"))) { + if (!h.startsWith("oss-"_sr)) { h.eat(service); // ignore s3 service } @@ -208,7 +208,7 @@ Reference S3BlobStoreEndpoint::fromString(const std::string try { StringRef t(url); StringRef prefix = t.eat("://"); - if (prefix != LiteralStringRef("blobstore")) + if (prefix != "blobstore"_sr) throw format("Invalid blobstore URL prefix '%s'", prefix.toString().c_str()); Optional proxyHost, proxyPort; @@ -261,7 +261,7 @@ Reference S3BlobStoreEndpoint::fromString(const std::string StringRef value = t.eat("&"); // Special case for header - if (name == LiteralStringRef("header")) { + if (name == "header"_sr) { StringRef originalValue = value; StringRef headerFieldName = value.eat(":"); StringRef headerFieldValue = value; @@ -282,7 +282,7 @@ Reference S3BlobStoreEndpoint::fromString(const std::string } // overwrite s3 region from parameter - if (name == LiteralStringRef("region")) { + if (name == "region"_sr) { region = value.toString(); continue; } @@ -1428,7 +1428,7 @@ void S3BlobStoreEndpoint::setV4AuthHeaders(std::string const& verb, if (headers.find("Content-MD5") != headers.end()) headersList.push_back({ "content-md5", trim_copy(headers["Content-MD5"]) + "\n" }); for (auto h : headers) { - if (StringRef(h.first).startsWith(LiteralStringRef("x-amz"))) + if (StringRef(h.first).startsWith("x-amz"_sr)) headersList.push_back({ to_lower_copy(h.first), trim_copy(h.second) + "\n" }); } std::sort(headersList.begin(), headersList.end()); @@ -1489,7 +1489,7 @@ void S3BlobStoreEndpoint::setAuthHeaders(std::string const& verb, std::string co msg.append("\n"); for (auto h : headers) { StringRef name = h.first; - if (name.startsWith(LiteralStringRef("x-amz")) || name.startsWith(LiteralStringRef("x-icloud"))) { + if (name.startsWith("x-amz"_sr) || name.startsWith("x-icloud"_sr)) { msg.append(h.first); msg.append(":"); msg.append(h.second); diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 80436c87d1..8f7474f961 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -137,6 +137,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "blob_manager", "blob_worker", "encrypt_key_proxy", + "consistency_scan", "storage_cache", "router", "coordinator" @@ -561,6 +562,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "unreachable_ratekeeper_worker", "unreachable_blobManager_worker", "unreachable_encryptKeyProxy_worker", + "unreachable_consistencyScan_worker", "unreadable_configuration", "full_replication_timeout", "client_issues", @@ -855,6 +857,19 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "aes_256_ctr" ]} }, + "consistency_scan_info":{ + "consistency_scan_enabled":false, + "restart":false, + "max_rate":0, + "target_interval":0, + "bytes_read_prev_round":0, + "last_round_start_datetime":"2022-04-20 00:05:05.123 +0000", + "last_round_finish_datetime":"1970-01-01 00:00:00.000 +0000", + "last_round_start_timestamp":1648857905.123, + "last_round_finish_timestamp":0, + "smoothed_round_seconds":1, + "finished_rounds":1 + }, "data":{ "least_operating_space_bytes_log_server":0, "average_partition_size_bytes":0, diff --git a/fdbclient/ServerKnobs.cpp b/fdbclient/ServerKnobs.cpp index 0462721816..7edd39b834 100644 --- a/fdbclient/ServerKnobs.cpp +++ b/fdbclient/ServerKnobs.cpp @@ -546,6 +546,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( ATTEMPT_RECRUITMENT_DELAY, 0.035 ); init( WAIT_FOR_DISTRIBUTOR_JOIN_DELAY, 1.0 ); init( WAIT_FOR_RATEKEEPER_JOIN_DELAY, 1.0 ); + init( WAIT_FOR_CONSISTENCYSCAN_JOIN_DELAY, 1.0 ); init( WAIT_FOR_BLOB_MANAGER_JOIN_DELAY, 1.0 ); init( WAIT_FOR_ENCRYPT_KEY_PROXY_JOIN_DELAY, 1.0 ); init( WORKER_FAILURE_TIME, 1.0 ); if( randomize && BUGGIFY ) WORKER_FAILURE_TIME = 10.0; @@ -556,6 +557,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( CHECK_REMOTE_HEALTH_INTERVAL, 60 ); init( FORCE_RECOVERY_CHECK_DELAY, 5.0 ); init( RATEKEEPER_FAILURE_TIME, 1.0 ); + init( CONSISTENCYSCAN_FAILURE_TIME, 1.0 ); init( BLOB_MANAGER_FAILURE_TIME, 1.0 ); init( REPLACE_INTERFACE_DELAY, 60.0 ); init( REPLACE_INTERFACE_CHECK_DELAY, 5.0 ); @@ -684,7 +686,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi bool buggifySmallBWLag = randomize && BUGGIFY; init( TARGET_BW_LAG, 50.0 ); if(buggifySmallBWLag) TARGET_BW_LAG = 10.0; - init( TARGET_BW_LAG_BATCH, 20.0 ); if(buggifySmallBWLag) TARGET_BW_LAG_BATCH = 4.0; + init( TARGET_BW_LAG_BATCH, 30.0 ); if(buggifySmallBWLag) TARGET_BW_LAG_BATCH = 4.0; init( TARGET_BW_LAG_UPDATE, 9.0 ); if(buggifySmallBWLag) TARGET_BW_LAG_UPDATE = 1.0; init( MIN_BW_HISTORY, 10 ); init( BW_ESTIMATION_INTERVAL, 10.0 ); if(buggifySmallBWLag) BW_ESTIMATION_INTERVAL = 2.0; @@ -738,6 +740,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( FETCH_KEYS_PARALLELISM_FULL, 6 ); init( FETCH_KEYS_LOWER_PRIORITY, 0 ); init( SERVE_FETCH_CHECKPOINT_PARALLELISM, 4 ); + init( CHANGE_FEED_DISK_READS_PARALLELISM, 1000 ); if( randomize && BUGGIFY ) CHANGE_FEED_DISK_READS_PARALLELISM = 20; init( BUGGIFY_BLOCK_BYTES, 10000 ); init( STORAGE_RECOVERY_VERSION_LAG_LIMIT, 2 * MAX_READ_TRANSACTION_LIFE_VERSIONS ); init( STORAGE_COMMIT_BYTES, 10000000 ); if( randomize && BUGGIFY ) STORAGE_COMMIT_BYTES = 2000000; @@ -776,6 +779,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( MAX_PARALLEL_QUICK_GET_VALUE, 50 ); if ( randomize && BUGGIFY ) MAX_PARALLEL_QUICK_GET_VALUE = deterministicRandom()->randomInt(1, 100); init( QUICK_GET_KEY_VALUES_LIMIT, 2000 ); init( QUICK_GET_KEY_VALUES_LIMIT_BYTES, 1e7 ); + init( STORAGE_FEED_QUERY_HARD_LIMIT, 100000 ); //Wait Failure init( MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS, 250 ); if( randomize && BUGGIFY ) MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS = 2; @@ -803,6 +807,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( PEER_TIMEOUT_PERCENTAGE_DEGRADATION_THRESHOLD, 0.1 ); init( PEER_DEGRADATION_CONNECTION_FAILURE_COUNT, 1 ); init( WORKER_HEALTH_REPORT_RECENT_DESTROYED_PEER, true ); + init( STORAGE_SERVER_REBOOT_ON_IO_TIMEOUT, false ); if ( randomize && BUGGIFY ) STORAGE_SERVER_REBOOT_ON_IO_TIMEOUT = true; // Test harness init( WORKER_POLL_DELAY, 1.0 ); @@ -815,6 +820,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi // Dynamic Knobs (implementation) init( COMPACTION_INTERVAL, isSimulated ? 5.0 : 300.0 ); + init( BROADCASTER_SELF_UPDATE_DELAY, 1.0 ); init( GET_COMMITTED_VERSION_TIMEOUT, 3.0 ); init( GET_SNAPSHOT_AND_CHANGES_TIMEOUT, 3.0 ); init( FETCH_CHANGES_TIMEOUT, 3.0 ); @@ -917,7 +923,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi // encrypt key proxy init( ENABLE_BLOB_GRANULE_COMPRESSION, false ); if ( randomize && BUGGIFY ) { ENABLE_BLOB_GRANULE_COMPRESSION = deterministicRandom()->coinflip(); } - init( BLOB_GRANULE_COMPRESSION_FILTER, "GZIP" ); if ( randomize && BUGGIFY ) { BLOB_GRANULE_COMPRESSION_FILTER = "NONE"; } + init( BLOB_GRANULE_COMPRESSION_FILTER, "NONE" ); // KMS connector type init( KMS_CONNECTOR_TYPE, "RESTKmsConnector" ); @@ -958,6 +964,8 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi init( BLOB_MANAGER_STATUS_EXP_BACKOFF_MAX, 5.0 ); 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_FULL_RESTORE_MODE, false ); init( BGCC_TIMEOUT, isSimulated ? 10.0 : 120.0 ); init( BGCC_MIN_INTERVAL, isSimulated ? 1.0 : 10.0 ); diff --git a/fdbclient/SimpleConfigTransaction.actor.cpp b/fdbclient/SimpleConfigTransaction.actor.cpp index cea49019bc..dba5d327b7 100644 --- a/fdbclient/SimpleConfigTransaction.actor.cpp +++ b/fdbclient/SimpleConfigTransaction.actor.cpp @@ -43,11 +43,13 @@ class SimpleConfigTransactionImpl { state ConfigTransactionGetGenerationReply reply; if (self->cti.hostname.present()) { wait(store(reply, - retryGetReplyFromHostname(ConfigTransactionGetGenerationRequest{}, + retryGetReplyFromHostname(ConfigTransactionGetGenerationRequest{ 0, Optional() }, self->cti.hostname.get(), WLTOKEN_CONFIGTXN_GETGENERATION))); } else { - wait(store(reply, retryBrokenPromise(self->cti.getGeneration, ConfigTransactionGetGenerationRequest{}))); + wait(store(reply, + retryBrokenPromise(self->cti.getGeneration, + ConfigTransactionGetGenerationRequest{ 0, Optional() }))); } if (self->dID.present()) { TraceEvent("SimpleConfigTransactionGotReadVersion", self->dID.get()) @@ -70,11 +72,12 @@ class SimpleConfigTransactionImpl { state ConfigTransactionGetReply reply; if (self->cti.hostname.present()) { wait(store(reply, - retryGetReplyFromHostname(ConfigTransactionGetRequest{ generation, configKey }, + retryGetReplyFromHostname(ConfigTransactionGetRequest{ 0, generation, configKey }, self->cti.hostname.get(), WLTOKEN_CONFIGTXN_GET))); } else { - wait(store(reply, retryBrokenPromise(self->cti.get, ConfigTransactionGetRequest{ generation, configKey }))); + wait(store(reply, + retryBrokenPromise(self->cti.get, ConfigTransactionGetRequest{ 0, generation, configKey }))); } if (self->dID.present()) { TraceEvent("SimpleConfigTransactionGotValue", self->dID.get()) @@ -95,13 +98,13 @@ class SimpleConfigTransactionImpl { state ConfigTransactionGetConfigClassesReply reply; if (self->cti.hostname.present()) { wait(store(reply, - retryGetReplyFromHostname(ConfigTransactionGetConfigClassesRequest{ generation }, + retryGetReplyFromHostname(ConfigTransactionGetConfigClassesRequest{ 0, generation }, self->cti.hostname.get(), WLTOKEN_CONFIGTXN_GETCLASSES))); } else { wait(store( reply, - retryBrokenPromise(self->cti.getClasses, ConfigTransactionGetConfigClassesRequest{ generation }))); + retryBrokenPromise(self->cti.getClasses, ConfigTransactionGetConfigClassesRequest{ 0, generation }))); } RangeResult result; for (const auto& configClass : reply.configClasses) { @@ -118,13 +121,13 @@ class SimpleConfigTransactionImpl { state ConfigTransactionGetKnobsReply reply; if (self->cti.hostname.present()) { wait(store(reply, - retryGetReplyFromHostname(ConfigTransactionGetKnobsRequest{ generation, configClass }, + retryGetReplyFromHostname(ConfigTransactionGetKnobsRequest{ 0, generation, configClass }, self->cti.hostname.get(), WLTOKEN_CONFIGTXN_GETKNOBS))); } else { - wait(store( - reply, - retryBrokenPromise(self->cti.getKnobs, ConfigTransactionGetKnobsRequest{ generation, configClass }))); + wait(store(reply, + retryBrokenPromise(self->cti.getKnobs, + ConfigTransactionGetKnobsRequest{ 0, generation, configClass }))); } RangeResult result; for (const auto& knobName : reply.knobNames) { @@ -137,6 +140,7 @@ class SimpleConfigTransactionImpl { if (!self->getGenerationFuture.isValid()) { self->getGenerationFuture = getGeneration(self); } + self->toCommit.coordinatorsHash = 0; wait(store(self->toCommit.generation, self->getGenerationFuture)); self->toCommit.annotation.timestamp = now(); if (self->cti.hostname.present()) { diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 719aff9fe8..8040974dc7 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -56,65 +56,46 @@ static bool isAlphaNumeric(const std::string& key) { } // namespace std::unordered_map SpecialKeySpace::moduleToBoundary = { - { SpecialKeySpace::MODULE::TRANSACTION, - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")) }, + { SpecialKeySpace::MODULE::TRANSACTION, KeyRangeRef("\xff\xff/transaction/"_sr, "\xff\xff/transaction0"_sr) }, { SpecialKeySpace::MODULE::WORKERINTERFACE, - KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), LiteralStringRef("\xff\xff/worker_interfaces0")) }, - { SpecialKeySpace::MODULE::STATUSJSON, singleKeyRange(LiteralStringRef("\xff\xff/status/json")) }, - { SpecialKeySpace::MODULE::CONNECTIONSTRING, singleKeyRange(LiteralStringRef("\xff\xff/connection_string")) }, - { SpecialKeySpace::MODULE::CLUSTERFILEPATH, singleKeyRange(LiteralStringRef("\xff\xff/cluster_file_path")) }, - { SpecialKeySpace::MODULE::METRICS, - KeyRangeRef(LiteralStringRef("\xff\xff/metrics/"), LiteralStringRef("\xff\xff/metrics0")) }, - { SpecialKeySpace::MODULE::MANAGEMENT, - KeyRangeRef(LiteralStringRef("\xff\xff/management/"), LiteralStringRef("\xff\xff/management0")) }, - { SpecialKeySpace::MODULE::ERRORMSG, singleKeyRange(LiteralStringRef("\xff\xff/error_message")) }, - { SpecialKeySpace::MODULE::CONFIGURATION, - KeyRangeRef(LiteralStringRef("\xff\xff/configuration/"), LiteralStringRef("\xff\xff/configuration0")) }, - { SpecialKeySpace::MODULE::GLOBALCONFIG, - KeyRangeRef(LiteralStringRef("\xff\xff/global_config/"), LiteralStringRef("\xff\xff/global_config0")) }, - { SpecialKeySpace::MODULE::TRACING, - KeyRangeRef(LiteralStringRef("\xff\xff/tracing/"), LiteralStringRef("\xff\xff/tracing0")) }, - { SpecialKeySpace::MODULE::ACTORLINEAGE, - KeyRangeRef(LiteralStringRef("\xff\xff/actor_lineage/"), LiteralStringRef("\xff\xff/actor_lineage0")) }, + KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr) }, + { SpecialKeySpace::MODULE::STATUSJSON, singleKeyRange("\xff\xff/status/json"_sr) }, + { SpecialKeySpace::MODULE::CONNECTIONSTRING, singleKeyRange("\xff\xff/connection_string"_sr) }, + { SpecialKeySpace::MODULE::CLUSTERFILEPATH, singleKeyRange("\xff\xff/cluster_file_path"_sr) }, + { SpecialKeySpace::MODULE::METRICS, KeyRangeRef("\xff\xff/metrics/"_sr, "\xff\xff/metrics0"_sr) }, + { SpecialKeySpace::MODULE::MANAGEMENT, KeyRangeRef("\xff\xff/management/"_sr, "\xff\xff/management0"_sr) }, + { SpecialKeySpace::MODULE::ERRORMSG, singleKeyRange("\xff\xff/error_message"_sr) }, + { SpecialKeySpace::MODULE::CONFIGURATION, KeyRangeRef("\xff\xff/configuration/"_sr, "\xff\xff/configuration0"_sr) }, + { SpecialKeySpace::MODULE::GLOBALCONFIG, KeyRangeRef("\xff\xff/global_config/"_sr, "\xff\xff/global_config0"_sr) }, + { SpecialKeySpace::MODULE::TRACING, KeyRangeRef("\xff\xff/tracing/"_sr, "\xff\xff/tracing0"_sr) }, + { SpecialKeySpace::MODULE::ACTORLINEAGE, KeyRangeRef("\xff\xff/actor_lineage/"_sr, "\xff\xff/actor_lineage0"_sr) }, { SpecialKeySpace::MODULE::ACTOR_PROFILER_CONF, - KeyRangeRef(LiteralStringRef("\xff\xff/actor_profiler_conf/"), - LiteralStringRef("\xff\xff/actor_profiler_conf0")) }, - { SpecialKeySpace::MODULE::CLUSTERID, singleKeyRange(LiteralStringRef("\xff\xff/cluster_id")) }, + KeyRangeRef("\xff\xff/actor_profiler_conf/"_sr, "\xff\xff/actor_profiler_conf0"_sr) }, + { SpecialKeySpace::MODULE::CLUSTERID, singleKeyRange("\xff\xff/cluster_id"_sr) }, }; std::unordered_map SpecialKeySpace::managementApiCommandToRange = { - { "exclude", - KeyRangeRef(LiteralStringRef("excluded/"), LiteralStringRef("excluded0")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, - { "failed", - KeyRangeRef(LiteralStringRef("failed/"), LiteralStringRef("failed0")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + { "exclude", KeyRangeRef("excluded/"_sr, "excluded0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + { "failed", KeyRangeRef("failed/"_sr, "failed0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "excludedlocality", - KeyRangeRef(LiteralStringRef("excluded_locality/"), LiteralStringRef("excluded_locality0")) + KeyRangeRef("excluded_locality/"_sr, "excluded_locality0"_sr) .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "failedlocality", - KeyRangeRef(LiteralStringRef("failed_locality/"), LiteralStringRef("failed_locality0")) + KeyRangeRef("failed_locality/"_sr, "failed_locality0"_sr) .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, - { "lock", singleKeyRange(LiteralStringRef("db_locked")).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + { "lock", singleKeyRange("db_locked"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "consistencycheck", - singleKeyRange(LiteralStringRef("consistency_check_suspended")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + singleKeyRange("consistency_check_suspended"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "coordinators", - KeyRangeRef(LiteralStringRef("coordinators/"), LiteralStringRef("coordinators0")) - .withPrefix(moduleToBoundary[MODULE::CONFIGURATION].begin) }, + KeyRangeRef("coordinators/"_sr, "coordinators0"_sr).withPrefix(moduleToBoundary[MODULE::CONFIGURATION].begin) }, { "advanceversion", - singleKeyRange(LiteralStringRef("min_required_commit_version")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, - { "versionepoch", - singleKeyRange(LiteralStringRef("version_epoch")).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, - { "profile", - KeyRangeRef(LiteralStringRef("profiling/"), LiteralStringRef("profiling0")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + singleKeyRange("min_required_commit_version"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + { "versionepoch", singleKeyRange("version_epoch"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + { "profile", KeyRangeRef("profiling/"_sr, "profiling0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "maintenance", - KeyRangeRef(LiteralStringRef("maintenance/"), LiteralStringRef("maintenance0")) - .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, + KeyRangeRef("maintenance/"_sr, "maintenance0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "datadistribution", - KeyRangeRef(LiteralStringRef("data_distribution/"), LiteralStringRef("data_distribution0")) + KeyRangeRef("data_distribution/"_sr, "data_distribution0"_sr) .withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "tenant", KeyRangeRef("tenant/"_sr, "tenant0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }, { "tenantmap", @@ -122,12 +103,8 @@ std::unordered_map SpecialKeySpace::managementApiCommandT }; std::unordered_map SpecialKeySpace::actorLineageApiCommandToRange = { - { "state", - KeyRangeRef(LiteralStringRef("state/"), LiteralStringRef("state0")) - .withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) }, - { "time", - KeyRangeRef(LiteralStringRef("time/"), LiteralStringRef("time0")) - .withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) } + { "state", KeyRangeRef("state/"_sr, "state0"_sr).withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) }, + { "time", KeyRangeRef("time/"_sr, "time0"_sr).withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) } }; std::set SpecialKeySpace::options = { "excluded/force", @@ -156,6 +133,11 @@ ACTOR Future moveKeySelectorOverRangeActor(const SpecialKeyRangeReadImpl* // never being called if KeySelector is already normalized ASSERT(ks->offset != 1); + // Throw error if module doesn't support tenants and we have a tenant + if (ryw->getTenant().present() && !skrImpl->supportsTenants()) { + throw illegal_tenant_access(); + } + state Key startKey(skrImpl->getKeyRange().begin); state Key endKey(skrImpl->getKeyRange().end); state RangeResult result; @@ -376,6 +358,21 @@ ACTOR Future SpecialKeySpace::getRangeAggregationActor(SpecialKeySp } state RangeMap::Ranges ranges = sks->getReadImpls().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + + // Check tenant legality separately from below iterations + // because it may be partially completed and returned + // before illegal range is checked due to the limits handler + if (ryw->getTenant().present()) { + for (auto iter : ranges) { + if (iter->value() == nullptr) { + continue; + } + if (!iter->value()->supportsTenants()) { + throw illegal_tenant_access(); + } + } + } + // TODO : workaround to write this two together to make the code compact // The issue here is boost::iterator_range<> doest not provide rbegin(), rend() iter = reverse ? ranges.end() : ranges.begin(); @@ -455,7 +452,7 @@ Future SpecialKeySpace::getRange(ReadYourWritesTransaction* ryw, if (!limits.isValid()) return range_limits_invalid(); if (limits.isReached()) { - CODE_PROBE(true, "read limit 0"); + CODE_PROBE(true, "Special Key Space range read limit 0"); return RangeResult(); } // make sure orEqual == false @@ -501,6 +498,9 @@ void SpecialKeySpace::set(ReadYourWritesTransaction* ryw, const KeyRef& key, con .detail("Value", value.toString()); throw special_keys_no_write_module_found(); } + if (!impl->supportsTenants() && ryw->getTenant().present()) { + throw illegal_tenant_access(); + } return impl->set(ryw, key, value); } @@ -518,6 +518,9 @@ void SpecialKeySpace::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& r TraceEvent(SevDebug, "SpecialKeySpaceNoWriteModuleFound").detail("Range", range); throw special_keys_no_write_module_found(); } + if (!begin->supportsTenants() && ryw->getTenant().present()) { + throw illegal_tenant_access(); + } return begin->clear(ryw, range); } @@ -527,6 +530,9 @@ void SpecialKeySpace::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) { auto impl = writeImpls[key]; if (impl == nullptr) throw special_keys_no_write_module_found(); + if (!impl->supportsTenants() && ryw->getTenant().present()) { + throw illegal_tenant_access(); + } return impl->clear(ryw, key); } @@ -538,8 +544,8 @@ bool validateSnakeCaseNaming(const KeyRef& k) { // Suffix can be \xff\xff or \x00 in single key range if (key.endsWith(specialKeys.begin)) key = key.removeSuffix(specialKeys.end); - else if (key.endsWith(LiteralStringRef("\x00"))) - key = key.removeSuffix(LiteralStringRef("\x00")); + else if (key.endsWith("\x00"_sr)) + key = key.removeSuffix("\x00"_sr); for (const char& c : key.toString()) { // only small letters, numbers, '/', '_' is allowed ASSERT((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '/' || c == '_'); @@ -614,6 +620,16 @@ ACTOR Future commitActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ++iter; } state std::vector::const_iterator it; + // Check validity of tenant support before iterating through + // module ptrs and potentially getting partial commits + if (ryw->getTenant().present()) { + for (it = writeModulePtrs.begin(); it != writeModulePtrs.end(); ++it) { + if (!(*it)->supportsTenants()) { + throw illegal_tenant_access(); + } + } + } + for (it = writeModulePtrs.begin(); it != writeModulePtrs.end(); ++it) { Optional msg = wait((*it)->commit(ryw)); if (msg.present()) { @@ -749,7 +765,7 @@ Future DDStatsRangeImpl::getRange(ReadYourWritesTransaction* ryw, } Key SpecialKeySpace::getManagementApiCommandOptionSpecialKey(const std::string& command, const std::string& option) { - Key prefix = LiteralStringRef("options/").withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin); + Key prefix = "options/"_sr.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin); auto pair = command + "/" + option; ASSERT(options.find(pair) != options.end()); return prefix.withSuffix(pair); @@ -880,11 +896,11 @@ void ExcludeServersRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& Key ExcludeServersRangeImpl::decode(const KeyRef& key) const { return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin) - .withPrefix(LiteralStringRef("\xff/conf/")); + .withPrefix("\xff/conf/"_sr); } Key ExcludeServersRangeImpl::encode(const KeyRef& key) const { - return key.removePrefix(LiteralStringRef("\xff/conf/")) + return key.removePrefix("\xff/conf/"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); } @@ -1123,11 +1139,11 @@ void FailedServersRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& k Key FailedServersRangeImpl::decode(const KeyRef& key) const { return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin) - .withPrefix(LiteralStringRef("\xff/conf/")); + .withPrefix("\xff/conf/"_sr); } Key FailedServersRangeImpl::encode(const KeyRef& key) const { - return key.removePrefix(LiteralStringRef("\xff/conf/")) + return key.removePrefix("\xff/conf/"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); } @@ -1289,8 +1305,7 @@ Future> ProcessClassRangeImpl::commit(ReadYourWritesTransa // validate class type ValueRef processClassType = entry.second.get(); ProcessClass processClass(processClassType.toString(), ProcessClass::DBSource); - if (processClass.classType() == ProcessClass::InvalidClass && - processClassType != LiteralStringRef("default")) { + if (processClass.classType() == ProcessClass::InvalidClass && processClassType != "default"_sr) { std::string error = "ERROR: \'" + processClassType.toString() + "\' is not a valid process class\n"; errorMsg = ManagementAPIError::toJsonString(false, "setclass", error); return errorMsg; @@ -1390,11 +1405,10 @@ ACTOR Future> lockDatabaseCommitActor(ReadYourWritesTransa throw database_locked(); } else if (!val.present()) { // lock database - ryw->getTransaction().atomicOp(databaseLockedKey, - BinaryWriter::toValue(uid, Unversioned()) - .withPrefix(LiteralStringRef("0123456789")) - .withSuffix(LiteralStringRef("\x00\x00\x00\x00")), - MutationRef::SetVersionstampedValue); + ryw->getTransaction().atomicOp( + databaseLockedKey, + BinaryWriter::toValue(uid, Unversioned()).withPrefix("0123456789"_sr).withSuffix("\x00\x00\x00\x00"_sr), + MutationRef::SetVersionstampedValue); ryw->getTransaction().addWriteConflictRange(normalKeys); } @@ -1648,7 +1662,7 @@ ACTOR Future coordinatorsGetRangeActor(ReadYourWritesTransaction* r state ClusterConnectionString cs = ryw->getDatabase()->getConnectionRecord()->getConnectionString(); state std::vector coordinator_processes = wait(cs.tryResolveHostnames()); RangeResult result; - Key cluster_decription_key = prefix.withSuffix(LiteralStringRef("cluster_description")); + Key cluster_decription_key = prefix.withSuffix("cluster_description"_sr); if (kr.contains(cluster_decription_key)) { result.push_back_deep(result.arena(), KeyValueRef(cluster_decription_key, cs.clusterKeyName())); } @@ -1663,7 +1677,7 @@ ACTOR Future coordinatorsGetRangeActor(ReadYourWritesTransaction* r processes_str += ","; processes_str += w.toString(); } - Key processes_key = prefix.withSuffix(LiteralStringRef("processes")); + Key processes_key = prefix.withSuffix("processes"_sr); if (kr.contains(processes_key)) { result.push_back_deep(result.arena(), KeyValueRef(processes_key, Value(processes_str))); } @@ -1685,7 +1699,7 @@ ACTOR static Future> coordinatorsCommitActor(ReadYourWrite state bool parse_error = false; // check update for coordinators - Key processes_key = LiteralStringRef("processes").withPrefix(kr.begin); + Key processes_key = "processes"_sr.withPrefix(kr.begin); auto processes_entry = ryw->getSpecialKeySpaceWriteMap()[processes_key]; if (processes_entry.first) { ASSERT(processes_entry.second.present()); // no clear should be seen here @@ -1725,7 +1739,7 @@ ACTOR static Future> coordinatorsCommitActor(ReadYourWrite std::string newName; // check update for cluster_description - Key cluster_decription_key = LiteralStringRef("cluster_description").withPrefix(kr.begin); + Key cluster_decription_key = "cluster_description"_sr.withPrefix(kr.begin); auto entry = ryw->getSpecialKeySpaceWriteMap()[cluster_decription_key]; if (entry.first) { // check valid description [a-zA-Z0-9_]+ @@ -1739,11 +1753,15 @@ ACTOR static Future> coordinatorsCommitActor(ReadYourWrite } } + auto configDBEntry = ryw->getSpecialKeySpaceWriteMap()["config_db"_sr.withPrefix(kr.begin)]; + TraceEvent(SevDebug, "SKSChangeCoordinatorsStart") .detail("NewConnectionString", conn.toString()) - .detail("Description", entry.first ? entry.second.get().toString() : ""); + .detail("Description", entry.first ? entry.second.get().toString() : "") + .detail("ConfigDBDisabled", configDBEntry.first); - Optional r = wait(changeQuorumChecker(&ryw->getTransaction(), &conn, newName)); + Optional r = + wait(changeQuorumChecker(&ryw->getTransaction(), &conn, newName, configDBEntry.first)); TraceEvent(SevDebug, "SKSChangeCoordinatorsFinish") .detail("Result", r.present() ? static_cast(r.get()) : -1); // -1 means success @@ -1966,7 +1984,7 @@ Future ClientProfilingImpl::getRange(ReadYourWritesTransaction* ryw KeyRef prefix = getKeyRange().begin; RangeResult result = RangeResult(); // client_txn_sample_rate - Key sampleRateKey = LiteralStringRef("client_txn_sample_rate").withPrefix(prefix); + Key sampleRateKey = "client_txn_sample_rate"_sr.withPrefix(prefix); ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS); @@ -1987,7 +2005,7 @@ Future ClientProfilingImpl::getRange(ReadYourWritesTransaction* ryw } } // client_txn_size_limit - Key txnSizeLimitKey = LiteralStringRef("client_txn_size_limit").withPrefix(prefix); + Key txnSizeLimitKey = "client_txn_size_limit"_sr.withPrefix(prefix); if (kr.contains(txnSizeLimitKey)) { auto entry = ryw->getSpecialKeySpaceWriteMap()[txnSizeLimitKey]; if (!ryw->readYourWritesDisabled() && entry.first) { @@ -2013,7 +2031,7 @@ Future> ClientProfilingImpl::commit(ReadYourWritesTransact Standalone> clears; // client_txn_sample_rate - Key sampleRateKey = LiteralStringRef("client_txn_sample_rate").withPrefix(getKeyRange().begin); + Key sampleRateKey = "client_txn_sample_rate"_sr.withPrefix(getKeyRange().begin); auto rateEntry = ryw->getSpecialKeySpaceWriteMap()[sampleRateKey]; if (rateEntry.first && rateEntry.second.present()) { @@ -2033,7 +2051,7 @@ Future> ClientProfilingImpl::commit(ReadYourWritesTransact } } // client_txn_size_limit - Key txnSizeLimitKey = LiteralStringRef("client_txn_size_limit").withPrefix(getKeyRange().begin); + Key txnSizeLimitKey = "client_txn_size_limit"_sr.withPrefix(getKeyRange().begin); auto sizeLimitEntry = ryw->getSpecialKeySpaceWriteMap()[txnSizeLimitKey]; if (sizeLimitEntry.first && sizeLimitEntry.second.present()) { std::string sizeLimitStr = sizeLimitEntry.second.get().toString(); @@ -2078,11 +2096,11 @@ void parse(StringRef& val, double& d) { } void parse(StringRef& val, WaitState& w) { - if (val == LiteralStringRef("disk") || val == LiteralStringRef("Disk")) { + if (val == "disk"_sr || val == "Disk"_sr) { w = WaitState::Disk; - } else if (val == LiteralStringRef("network") || val == LiteralStringRef("Network")) { + } else if (val == "network"_sr || val == "Network"_sr) { w = WaitState::Network; - } else if (val == LiteralStringRef("running") || val == LiteralStringRef("Running")) { + } else if (val == "running"_sr || val == "Running"_sr) { w = WaitState::Running; } else { throw std::range_error("failed to parse run state"); @@ -2482,7 +2500,7 @@ ACTOR static Future DataDistributionGetRangeActor(ReadYourWritesTra KeyRangeRef kr) { state RangeResult result; // dataDistributionModeKey - state Key modeKey = LiteralStringRef("mode").withPrefix(prefix); + state Key modeKey = "mode"_sr.withPrefix(prefix); ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS); @@ -2498,7 +2516,7 @@ ACTOR static Future DataDistributionGetRangeActor(ReadYourWritesTra } } // rebalanceDDIgnoreKey - state Key rebalanceIgnoredKey = LiteralStringRef("rebalance_ignored").withPrefix(prefix); + state Key rebalanceIgnoredKey = "rebalance_ignored"_sr.withPrefix(prefix); if (kr.contains(rebalanceIgnoredKey)) { auto entry = ryw->getSpecialKeySpaceWriteMap()[rebalanceIgnoredKey]; if (ryw->readYourWritesDisabled() || !entry.first) { @@ -2525,8 +2543,8 @@ Future> DataDistributionImpl::commit(ReadYourWritesTransac Optional msg; KeyRangeRef kr = getKeyRange(); - Key modeKey = LiteralStringRef("mode").withPrefix(kr.begin); - Key rebalanceIgnoredKey = LiteralStringRef("rebalance_ignored").withPrefix(kr.begin); + Key modeKey = "mode"_sr.withPrefix(kr.begin); + Key rebalanceIgnoredKey = "rebalance_ignored"_sr.withPrefix(kr.begin); auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(kr); for (auto iter = ranges.begin(); iter != ranges.end(); ++iter) { if (!iter->value().first) @@ -2713,11 +2731,11 @@ void ExcludedLocalitiesRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyR Key ExcludedLocalitiesRangeImpl::decode(const KeyRef& key) const { return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin) - .withPrefix(LiteralStringRef("\xff/conf/")); + .withPrefix("\xff/conf/"_sr); } Key ExcludedLocalitiesRangeImpl::encode(const KeyRef& key) const { - return key.removePrefix(LiteralStringRef("\xff/conf/")) + return key.removePrefix("\xff/conf/"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); } @@ -2742,11 +2760,11 @@ void FailedLocalitiesRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef Key FailedLocalitiesRangeImpl::decode(const KeyRef& key) const { return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin) - .withPrefix(LiteralStringRef("\xff/conf/")); + .withPrefix("\xff/conf/"_sr); } Key FailedLocalitiesRangeImpl::encode(const KeyRef& key) const { - return key.removePrefix(LiteralStringRef("\xff/conf/")) + return key.removePrefix("\xff/conf/"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); } diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index 11aa2cca2e..a720547b56 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -31,20 +31,20 @@ FDB_DEFINE_BOOLEAN_PARAM(AssignEmptyRange); FDB_DEFINE_BOOLEAN_PARAM(UnassignShard); -const KeyRef systemKeysPrefix = LiteralStringRef("\xff"); +const KeyRef systemKeysPrefix = "\xff"_sr; const KeyRangeRef normalKeys(KeyRef(), systemKeysPrefix); -const KeyRangeRef systemKeys(systemKeysPrefix, LiteralStringRef("\xff\xff")); -const KeyRangeRef nonMetadataSystemKeys(LiteralStringRef("\xff\x02"), LiteralStringRef("\xff\x03")); +const KeyRangeRef systemKeys(systemKeysPrefix, "\xff\xff"_sr); +const KeyRangeRef nonMetadataSystemKeys("\xff\x02"_sr, "\xff\x03"_sr); const KeyRangeRef allKeys = KeyRangeRef(normalKeys.begin, systemKeys.end); -const KeyRef afterAllKeys = LiteralStringRef("\xff\xff\x00"); -const KeyRangeRef specialKeys = KeyRangeRef(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); +const KeyRef afterAllKeys = "\xff\xff\x00"_sr; +const KeyRangeRef specialKeys = KeyRangeRef("\xff\xff"_sr, "\xff\xff\xff"_sr); // keyServersKeys.contains(k) iff k.startsWith(keyServersPrefix) -const KeyRangeRef keyServersKeys(LiteralStringRef("\xff/keyServers/"), LiteralStringRef("\xff/keyServers0")); +const KeyRangeRef keyServersKeys("\xff/keyServers/"_sr, "\xff/keyServers0"_sr); const KeyRef keyServersPrefix = keyServersKeys.begin; const KeyRef keyServersEnd = keyServersKeys.end; -const KeyRangeRef keyServersKeyServersKeys(LiteralStringRef("\xff/keyServers/\xff/keyServers/"), - LiteralStringRef("\xff/keyServers/\xff/keyServers0")); +const KeyRangeRef keyServersKeyServersKeys("\xff/keyServers/\xff/keyServers/"_sr, + "\xff/keyServers/\xff/keyServers0"_sr); const KeyRef keyServersKeyServersKey = keyServersKeyServersKeys.begin; // These constants are selected to be easily recognized during debugging. @@ -274,20 +274,17 @@ void decodeKeyServersValue(std::map const& tag_uid, } const KeyRangeRef conflictingKeysRange = - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/conflicting_keys/"), - LiteralStringRef("\xff\xff/transaction/conflicting_keys/\xff\xff")); -const ValueRef conflictingKeysTrue = LiteralStringRef("1"); -const ValueRef conflictingKeysFalse = LiteralStringRef("0"); + KeyRangeRef("\xff\xff/transaction/conflicting_keys/"_sr, "\xff\xff/transaction/conflicting_keys/\xff\xff"_sr); +const ValueRef conflictingKeysTrue = "1"_sr; +const ValueRef conflictingKeysFalse = "0"_sr; const KeyRangeRef readConflictRangeKeysRange = - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/read_conflict_range/"), - LiteralStringRef("\xff\xff/transaction/read_conflict_range/\xff\xff")); + KeyRangeRef("\xff\xff/transaction/read_conflict_range/"_sr, "\xff\xff/transaction/read_conflict_range/\xff\xff"_sr); -const KeyRangeRef writeConflictRangeKeysRange = - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/write_conflict_range/"), - LiteralStringRef("\xff\xff/transaction/write_conflict_range/\xff\xff")); +const KeyRangeRef writeConflictRangeKeysRange = KeyRangeRef("\xff\xff/transaction/write_conflict_range/"_sr, + "\xff\xff/transaction/write_conflict_range/\xff\xff"_sr); -const KeyRef clusterIdKey = LiteralStringRef("\xff/clusterId"); +const KeyRef clusterIdKey = "\xff/clusterId"_sr; const KeyRef checkpointPrefix = "\xff/checkpoint/"_sr; @@ -344,7 +341,7 @@ DataMoveMetaData decodeDataMoveValue(const ValueRef& value) { } // "\xff/cacheServer/[[UID]] := StorageServerInterface" -const KeyRangeRef storageCacheServerKeys(LiteralStringRef("\xff/cacheServer/"), LiteralStringRef("\xff/cacheServer0")); +const KeyRangeRef storageCacheServerKeys("\xff/cacheServer/"_sr, "\xff/cacheServer0"_sr); const KeyRef storageCacheServersPrefix = storageCacheServerKeys.begin; const KeyRef storageCacheServersEnd = storageCacheServerKeys.end; @@ -361,11 +358,11 @@ const Value storageCacheServerValue(const StorageServerInterface& ssi) { return ObjectWriter::toValue(ssi, IncludeVersion(protocolVersion)); } -const KeyRangeRef ddStatsRange = KeyRangeRef(LiteralStringRef("\xff\xff/metrics/data_distribution_stats/"), - LiteralStringRef("\xff\xff/metrics/data_distribution_stats/\xff\xff")); +const KeyRangeRef ddStatsRange = + KeyRangeRef("\xff\xff/metrics/data_distribution_stats/"_sr, "\xff\xff/metrics/data_distribution_stats/\xff\xff"_sr); // "\xff/storageCache/[[begin]]" := "[[vector]]" -const KeyRangeRef storageCacheKeys(LiteralStringRef("\xff/storageCache/"), LiteralStringRef("\xff/storageCache0")); +const KeyRangeRef storageCacheKeys("\xff/storageCache/"_sr, "\xff/storageCache0"_sr); const KeyRef storageCachePrefix = storageCacheKeys.begin; const Key storageCacheKey(const KeyRef& k) { @@ -427,7 +424,7 @@ const Key serverKeysKey(UID serverID, const KeyRef& key) { BinaryWriter wr(Unversioned()); wr.serializeBytes(serverKeysPrefix); wr << serverID; - wr.serializeBytes(LiteralStringRef("/")); + wr.serializeBytes("/"_sr); wr.serializeBytes(key); return wr.toValue(); } @@ -435,7 +432,7 @@ const Key serverKeysPrefixFor(UID serverID) { BinaryWriter wr(Unversioned()); wr.serializeBytes(serverKeysPrefix); wr << serverID; - wr.serializeBytes(LiteralStringRef("/")); + wr.serializeBytes("/"_sr); return wr.toValue(); } UID serverKeysDecodeServer(const KeyRef& key) { @@ -499,13 +496,13 @@ void decodeServerKeysValue(const ValueRef& value, bool& assigned, bool& emptyRan } } -const KeyRef cacheKeysPrefix = LiteralStringRef("\xff\x02/cacheKeys/"); +const KeyRef cacheKeysPrefix = "\xff\x02/cacheKeys/"_sr; const Key cacheKeysKey(uint16_t idx, const KeyRef& key) { BinaryWriter wr(Unversioned()); wr.serializeBytes(cacheKeysPrefix); wr << idx; - wr.serializeBytes(LiteralStringRef("/")); + wr.serializeBytes("/"_sr); wr.serializeBytes(key); return wr.toValue(); } @@ -513,7 +510,7 @@ const Key cacheKeysPrefixFor(uint16_t idx) { BinaryWriter wr(Unversioned()); wr.serializeBytes(cacheKeysPrefix); wr << idx; - wr.serializeBytes(LiteralStringRef("/")); + wr.serializeBytes("/"_sr); return wr.toValue(); } uint16_t cacheKeysDecodeIndex(const KeyRef& key) { @@ -526,9 +523,8 @@ KeyRef cacheKeysDecodeKey(const KeyRef& key) { return key.substr(cacheKeysPrefix.size() + sizeof(uint16_t) + 1); } -const KeyRef cacheChangeKey = LiteralStringRef("\xff\x02/cacheChangeKey"); -const KeyRangeRef cacheChangeKeys(LiteralStringRef("\xff\x02/cacheChangeKeys/"), - LiteralStringRef("\xff\x02/cacheChangeKeys0")); +const KeyRef cacheChangeKey = "\xff\x02/cacheChangeKey"_sr; +const KeyRangeRef cacheChangeKeys("\xff\x02/cacheChangeKeys/"_sr, "\xff\x02/cacheChangeKeys0"_sr); const KeyRef cacheChangePrefix = cacheChangeKeys.begin; const Key cacheChangeKeyFor(uint16_t idx) { BinaryWriter wr(Unversioned()); @@ -543,9 +539,9 @@ uint16_t cacheChangeKeyDecodeIndex(const KeyRef& key) { return idx; } -const KeyRangeRef tssMappingKeys(LiteralStringRef("\xff/tss/"), LiteralStringRef("\xff/tss0")); +const KeyRangeRef tssMappingKeys("\xff/tss/"_sr, "\xff/tss0"_sr); -const KeyRangeRef tssQuarantineKeys(LiteralStringRef("\xff/tssQ/"), LiteralStringRef("\xff/tssQ0")); +const KeyRangeRef tssQuarantineKeys("\xff/tssQ/"_sr, "\xff/tssQ0"_sr); const Key tssQuarantineKeyFor(UID serverID) { BinaryWriter wr(Unversioned()); @@ -561,22 +557,19 @@ UID decodeTssQuarantineKey(KeyRef const& key) { return serverID; } -const KeyRangeRef tssMismatchKeys(LiteralStringRef("\xff/tssMismatch/"), LiteralStringRef("\xff/tssMismatch0")); +const KeyRangeRef tssMismatchKeys("\xff/tssMismatch/"_sr, "\xff/tssMismatch0"_sr); -const KeyRangeRef serverMetadataKeys(LiteralStringRef("\xff/serverMetadata/"), - LiteralStringRef("\xff/serverMetadata0")); +const KeyRangeRef serverMetadataKeys("\xff/serverMetadata/"_sr, "\xff/serverMetadata0"_sr); -const KeyRangeRef serverTagKeys(LiteralStringRef("\xff/serverTag/"), LiteralStringRef("\xff/serverTag0")); +const KeyRangeRef serverTagKeys("\xff/serverTag/"_sr, "\xff/serverTag0"_sr); const KeyRef serverTagPrefix = serverTagKeys.begin; -const KeyRangeRef serverTagConflictKeys(LiteralStringRef("\xff/serverTagConflict/"), - LiteralStringRef("\xff/serverTagConflict0")); +const KeyRangeRef serverTagConflictKeys("\xff/serverTagConflict/"_sr, "\xff/serverTagConflict0"_sr); const KeyRef serverTagConflictPrefix = serverTagConflictKeys.begin; // serverTagHistoryKeys is the old tag a storage server uses before it is migrated to a different location. // For example, we can copy a SS file to a remote DC and start the SS there; // The new SS will need to consume the last bits of data from the old tag it is responsible for. -const KeyRangeRef serverTagHistoryKeys(LiteralStringRef("\xff/serverTagHistory/"), - LiteralStringRef("\xff/serverTagHistory0")); +const KeyRangeRef serverTagHistoryKeys("\xff/serverTagHistory/"_sr, "\xff/serverTagHistory0"_sr); const KeyRef serverTagHistoryPrefix = serverTagHistoryKeys.begin; const Key serverTagKeyFor(UID serverID) { @@ -661,8 +654,7 @@ const Key serverTagConflictKeyFor(Tag tag) { return wr.toValue(); } -const KeyRangeRef tagLocalityListKeys(LiteralStringRef("\xff/tagLocalityList/"), - LiteralStringRef("\xff/tagLocalityList0")); +const KeyRangeRef tagLocalityListKeys("\xff/tagLocalityList/"_sr, "\xff/tagLocalityList0"_sr); const KeyRef tagLocalityListPrefix = tagLocalityListKeys.begin; const Key tagLocalityListKeyFor(Optional dcID) { @@ -690,8 +682,7 @@ int8_t decodeTagLocalityListValue(ValueRef const& value) { return s; } -const KeyRangeRef datacenterReplicasKeys(LiteralStringRef("\xff\x02/datacenterReplicas/"), - LiteralStringRef("\xff\x02/datacenterReplicas0")); +const KeyRangeRef datacenterReplicasKeys("\xff\x02/datacenterReplicas/"_sr, "\xff\x02/datacenterReplicas0"_sr); const KeyRef datacenterReplicasPrefix = datacenterReplicasKeys.begin; const Key datacenterReplicasKeyFor(Optional dcID) { @@ -724,8 +715,7 @@ extern const KeyRangeRef tLogDatacentersKeys; extern const KeyRef tLogDatacentersPrefix; const Key tLogDatacentersKeyFor(Optional dcID); -const KeyRangeRef tLogDatacentersKeys(LiteralStringRef("\xff\x02/tLogDatacenters/"), - LiteralStringRef("\xff\x02/tLogDatacenters0")); +const KeyRangeRef tLogDatacentersKeys("\xff\x02/tLogDatacenters/"_sr, "\xff\x02/tLogDatacenters0"_sr); const KeyRef tLogDatacentersPrefix = tLogDatacentersKeys.begin; const Key tLogDatacentersKeyFor(Optional dcID) { @@ -741,10 +731,10 @@ Optional decodeTLogDatacentersKey(KeyRef const& key) { return dcID; } -const KeyRef primaryDatacenterKey = LiteralStringRef("\xff/primaryDatacenter"); +const KeyRef primaryDatacenterKey = "\xff/primaryDatacenter"_sr; // serverListKeys.contains(k) iff k.startsWith( serverListKeys.begin ) because '/'+1 == '0' -const KeyRangeRef serverListKeys(LiteralStringRef("\xff/serverList/"), LiteralStringRef("\xff/serverList0")); +const KeyRangeRef serverListKeys("\xff/serverList/"_sr, "\xff/serverList0"_sr); const KeyRef serverListPrefix = serverListKeys.begin; const Key serverListKeyFor(UID serverID) { @@ -800,11 +790,11 @@ SWVersion decodeSWVersionValue(ValueRef const& value) { } // processClassKeys.contains(k) iff k.startsWith( processClassKeys.begin ) because '/'+1 == '0' -const KeyRangeRef processClassKeys(LiteralStringRef("\xff/processClass/"), LiteralStringRef("\xff/processClass0")); +const KeyRangeRef processClassKeys("\xff/processClass/"_sr, "\xff/processClass0"_sr); const KeyRef processClassPrefix = processClassKeys.begin; -const KeyRef processClassChangeKey = LiteralStringRef("\xff/processClassChanges"); -const KeyRef processClassVersionKey = LiteralStringRef("\xff/processClassChangesVersion"); -const ValueRef processClassVersionValue = LiteralStringRef("1"); +const KeyRef processClassChangeKey = "\xff/processClassChanges"_sr; +const KeyRef processClassVersionKey = "\xff/processClassChangesVersion"_sr; +const ValueRef processClassVersionValue = "1"_sr; const Key processClassKeyFor(StringRef processID) { BinaryWriter wr(Unversioned()); @@ -840,23 +830,23 @@ ProcessClass decodeProcessClassValue(ValueRef const& value) { return s; } -const KeyRangeRef configKeys(LiteralStringRef("\xff/conf/"), LiteralStringRef("\xff/conf0")); +const KeyRangeRef configKeys("\xff/conf/"_sr, "\xff/conf0"_sr); const KeyRef configKeysPrefix = configKeys.begin; -const KeyRef perpetualStorageWiggleKey(LiteralStringRef("\xff/conf/perpetual_storage_wiggle")); -const KeyRef perpetualStorageWiggleLocalityKey(LiteralStringRef("\xff/conf/perpetual_storage_wiggle_locality")); -const KeyRef perpetualStorageWiggleIDPrefix( - LiteralStringRef("\xff/storageWiggleID/")); // withSuffix /primary or /remote -const KeyRef perpetualStorageWiggleStatsPrefix( - LiteralStringRef("\xff/storageWiggleStats/")); // withSuffix /primary or /remote +const KeyRef perpetualStorageWiggleKey("\xff/conf/perpetual_storage_wiggle"_sr); +const KeyRef perpetualStorageWiggleLocalityKey("\xff/conf/perpetual_storage_wiggle_locality"_sr); +const KeyRef perpetualStorageWiggleIDPrefix("\xff/storageWiggleID/"_sr); // withSuffix /primary or /remote +const KeyRef perpetualStorageWiggleStatsPrefix("\xff/storageWiggleStats/"_sr); // withSuffix /primary or /remote -const KeyRef triggerDDTeamInfoPrintKey(LiteralStringRef("\xff/triggerDDTeamInfoPrint")); +const KeyRef triggerDDTeamInfoPrintKey("\xff/triggerDDTeamInfoPrint"_sr); -const KeyRef encryptionAtRestModeConfKey(LiteralStringRef("\xff/conf/encryption_at_rest_mode")); +const KeyRef consistencyScanInfoKey = "\xff/consistencyScanInfo"_sr; -const KeyRangeRef excludedServersKeys(LiteralStringRef("\xff/conf/excluded/"), LiteralStringRef("\xff/conf/excluded0")); +const KeyRef encryptionAtRestModeConfKey("\xff/conf/encryption_at_rest_mode"_sr); + +const KeyRangeRef excludedServersKeys("\xff/conf/excluded/"_sr, "\xff/conf/excluded0"_sr); const KeyRef excludedServersPrefix = excludedServersKeys.begin; -const KeyRef excludedServersVersionKey = LiteralStringRef("\xff/conf/excluded"); +const KeyRef excludedServersVersionKey = "\xff/conf/excluded"_sr; AddressExclusion decodeExcludedServersKey(KeyRef const& key) { ASSERT(key.startsWith(excludedServersPrefix)); // Returns an invalid NetworkAddress if given an invalid key (within the prefix) @@ -871,10 +861,9 @@ std::string encodeExcludedServersKey(AddressExclusion const& addr) { return excludedServersPrefix.toString() + addr.toString(); } -const KeyRangeRef excludedLocalityKeys(LiteralStringRef("\xff/conf/excluded_locality/"), - LiteralStringRef("\xff/conf/excluded_locality0")); +const KeyRangeRef excludedLocalityKeys("\xff/conf/excluded_locality/"_sr, "\xff/conf/excluded_locality0"_sr); const KeyRef excludedLocalityPrefix = excludedLocalityKeys.begin; -const KeyRef excludedLocalityVersionKey = LiteralStringRef("\xff/conf/excluded_locality"); +const KeyRef excludedLocalityVersionKey = "\xff/conf/excluded_locality"_sr; std::string decodeExcludedLocalityKey(KeyRef const& key) { ASSERT(key.startsWith(excludedLocalityPrefix)); return key.removePrefix(excludedLocalityPrefix).toString(); @@ -883,9 +872,9 @@ std::string encodeExcludedLocalityKey(std::string const& locality) { return excludedLocalityPrefix.toString() + locality; } -const KeyRangeRef failedServersKeys(LiteralStringRef("\xff/conf/failed/"), LiteralStringRef("\xff/conf/failed0")); +const KeyRangeRef failedServersKeys("\xff/conf/failed/"_sr, "\xff/conf/failed0"_sr); const KeyRef failedServersPrefix = failedServersKeys.begin; -const KeyRef failedServersVersionKey = LiteralStringRef("\xff/conf/failed"); +const KeyRef failedServersVersionKey = "\xff/conf/failed"_sr; AddressExclusion decodeFailedServersKey(KeyRef const& key) { ASSERT(key.startsWith(failedServersPrefix)); // Returns an invalid NetworkAddress if given an invalid key (within the prefix) @@ -900,10 +889,9 @@ std::string encodeFailedServersKey(AddressExclusion const& addr) { return failedServersPrefix.toString() + addr.toString(); } -const KeyRangeRef failedLocalityKeys(LiteralStringRef("\xff/conf/failed_locality/"), - LiteralStringRef("\xff/conf/failed_locality0")); +const KeyRangeRef failedLocalityKeys("\xff/conf/failed_locality/"_sr, "\xff/conf/failed_locality0"_sr); const KeyRef failedLocalityPrefix = failedLocalityKeys.begin; -const KeyRef failedLocalityVersionKey = LiteralStringRef("\xff/conf/failed_locality"); +const KeyRef failedLocalityVersionKey = "\xff/conf/failed_locality"_sr; std::string decodeFailedLocalityKey(KeyRef const& key) { ASSERT(key.startsWith(failedLocalityPrefix)); return key.removePrefix(failedLocalityPrefix).toString(); @@ -912,20 +900,18 @@ std::string encodeFailedLocalityKey(std::string const& locality) { return failedLocalityPrefix.toString() + locality; } -// const KeyRangeRef globalConfigKeys( LiteralStringRef("\xff/globalConfig/"), LiteralStringRef("\xff/globalConfig0") ); +// const KeyRangeRef globalConfigKeys( "\xff/globalConfig/"_sr, "\xff/globalConfig0"_sr ); // const KeyRef globalConfigPrefix = globalConfigKeys.begin; -const KeyRangeRef globalConfigDataKeys(LiteralStringRef("\xff/globalConfig/k/"), - LiteralStringRef("\xff/globalConfig/k0")); +const KeyRangeRef globalConfigDataKeys("\xff/globalConfig/k/"_sr, "\xff/globalConfig/k0"_sr); const KeyRef globalConfigKeysPrefix = globalConfigDataKeys.begin; -const KeyRangeRef globalConfigHistoryKeys(LiteralStringRef("\xff/globalConfig/h/"), - LiteralStringRef("\xff/globalConfig/h0")); +const KeyRangeRef globalConfigHistoryKeys("\xff/globalConfig/h/"_sr, "\xff/globalConfig/h0"_sr); const KeyRef globalConfigHistoryPrefix = globalConfigHistoryKeys.begin; -const KeyRef globalConfigVersionKey = LiteralStringRef("\xff/globalConfig/v"); +const KeyRef globalConfigVersionKey = "\xff/globalConfig/v"_sr; -const KeyRangeRef workerListKeys(LiteralStringRef("\xff/worker/"), LiteralStringRef("\xff/worker0")); +const KeyRangeRef workerListKeys("\xff/worker/"_sr, "\xff/worker0"_sr); const KeyRef workerListPrefix = workerListKeys.begin; const Key workerListKeyFor(StringRef processID) { @@ -955,11 +941,10 @@ ProcessData decodeWorkerListValue(ValueRef const& value) { return s; } -const KeyRangeRef backupProgressKeys(LiteralStringRef("\xff\x02/backupProgress/"), - LiteralStringRef("\xff\x02/backupProgress0")); +const KeyRangeRef backupProgressKeys("\xff\x02/backupProgress/"_sr, "\xff\x02/backupProgress0"_sr); const KeyRef backupProgressPrefix = backupProgressKeys.begin; -const KeyRef backupStartedKey = LiteralStringRef("\xff\x02/backupStarted"); -extern const KeyRef backupPausedKey = LiteralStringRef("\xff\x02/backupPaused"); +const KeyRef backupStartedKey = "\xff\x02/backupStarted"_sr; +extern const KeyRef backupPausedKey = "\xff\x02/backupPaused"_sr; const Key backupProgressKeyFor(UID workerID) { BinaryWriter wr(Unversioned()); @@ -1002,98 +987,91 @@ std::vector> decodeBackupStartedValue(const ValueRef& va return ids; } -const KeyRef coordinatorsKey = LiteralStringRef("\xff/coordinators"); -const KeyRef logsKey = LiteralStringRef("\xff/logs"); -const KeyRef minRequiredCommitVersionKey = LiteralStringRef("\xff/minRequiredCommitVersion"); -const KeyRef versionEpochKey = LiteralStringRef("\xff/versionEpoch"); +const KeyRef previousCoordinatorsKey = "\xff/previousCoordinators"_sr; +const KeyRef coordinatorsKey = "\xff/coordinators"_sr; +const KeyRef logsKey = "\xff/logs"_sr; +const KeyRef minRequiredCommitVersionKey = "\xff/minRequiredCommitVersion"_sr; +const KeyRef versionEpochKey = "\xff/versionEpoch"_sr; -const KeyRef globalKeysPrefix = LiteralStringRef("\xff/globals"); -const KeyRef lastEpochEndKey = LiteralStringRef("\xff/globals/lastEpochEnd"); -const KeyRef lastEpochEndPrivateKey = LiteralStringRef("\xff\xff/globals/lastEpochEnd"); -const KeyRef killStorageKey = LiteralStringRef("\xff/globals/killStorage"); -const KeyRef killStoragePrivateKey = LiteralStringRef("\xff\xff/globals/killStorage"); -const KeyRef rebootWhenDurableKey = LiteralStringRef("\xff/globals/rebootWhenDurable"); -const KeyRef rebootWhenDurablePrivateKey = LiteralStringRef("\xff\xff/globals/rebootWhenDurable"); -const KeyRef primaryLocalityKey = LiteralStringRef("\xff/globals/primaryLocality"); -const KeyRef primaryLocalityPrivateKey = LiteralStringRef("\xff\xff/globals/primaryLocality"); -const KeyRef fastLoggingEnabled = LiteralStringRef("\xff/globals/fastLoggingEnabled"); -const KeyRef fastLoggingEnabledPrivateKey = LiteralStringRef("\xff\xff/globals/fastLoggingEnabled"); +const KeyRef globalKeysPrefix = "\xff/globals"_sr; +const KeyRef lastEpochEndKey = "\xff/globals/lastEpochEnd"_sr; +const KeyRef lastEpochEndPrivateKey = "\xff\xff/globals/lastEpochEnd"_sr; +const KeyRef killStorageKey = "\xff/globals/killStorage"_sr; +const KeyRef killStoragePrivateKey = "\xff\xff/globals/killStorage"_sr; +const KeyRef rebootWhenDurableKey = "\xff/globals/rebootWhenDurable"_sr; +const KeyRef rebootWhenDurablePrivateKey = "\xff\xff/globals/rebootWhenDurable"_sr; +const KeyRef primaryLocalityKey = "\xff/globals/primaryLocality"_sr; +const KeyRef primaryLocalityPrivateKey = "\xff\xff/globals/primaryLocality"_sr; +const KeyRef fastLoggingEnabled = "\xff/globals/fastLoggingEnabled"_sr; +const KeyRef fastLoggingEnabledPrivateKey = "\xff\xff/globals/fastLoggingEnabled"_sr; // Whenever configuration changes or DD related system keyspace is changed(e.g.., serverList), // actor must grab the moveKeysLockOwnerKey and update moveKeysLockWriteKey. // This prevents concurrent write to the same system keyspace. // When the owner of the DD related system keyspace changes, DD will reboot -const KeyRef moveKeysLockOwnerKey = LiteralStringRef("\xff/moveKeysLock/Owner"); -const KeyRef moveKeysLockWriteKey = LiteralStringRef("\xff/moveKeysLock/Write"); +const KeyRef moveKeysLockOwnerKey = "\xff/moveKeysLock/Owner"_sr; +const KeyRef moveKeysLockWriteKey = "\xff/moveKeysLock/Write"_sr; -const KeyRef dataDistributionModeKey = LiteralStringRef("\xff/dataDistributionMode"); +const KeyRef dataDistributionModeKey = "\xff/dataDistributionMode"_sr; const UID dataDistributionModeLock = UID(6345, 3425); // Keys to view and control tag throttling -const KeyRangeRef tagThrottleKeys = - KeyRangeRef(LiteralStringRef("\xff\x02/throttledTags/tag/"), LiteralStringRef("\xff\x02/throttledTags/tag0")); +const KeyRangeRef tagThrottleKeys = KeyRangeRef("\xff\x02/throttledTags/tag/"_sr, "\xff\x02/throttledTags/tag0"_sr); const KeyRef tagThrottleKeysPrefix = tagThrottleKeys.begin; -const KeyRef tagThrottleAutoKeysPrefix = LiteralStringRef("\xff\x02/throttledTags/tag/\x01"); -const KeyRef tagThrottleSignalKey = LiteralStringRef("\xff\x02/throttledTags/signal"); -const KeyRef tagThrottleAutoEnabledKey = LiteralStringRef("\xff\x02/throttledTags/autoThrottlingEnabled"); -const KeyRef tagThrottleLimitKey = LiteralStringRef("\xff\x02/throttledTags/manualThrottleLimit"); -const KeyRef tagThrottleCountKey = LiteralStringRef("\xff\x02/throttledTags/manualThrottleCount"); +const KeyRef tagThrottleAutoKeysPrefix = "\xff\x02/throttledTags/tag/\x01"_sr; +const KeyRef tagThrottleSignalKey = "\xff\x02/throttledTags/signal"_sr; +const KeyRef tagThrottleAutoEnabledKey = "\xff\x02/throttledTags/autoThrottlingEnabled"_sr; +const KeyRef tagThrottleLimitKey = "\xff\x02/throttledTags/manualThrottleLimit"_sr; +const KeyRef tagThrottleCountKey = "\xff\x02/throttledTags/manualThrottleCount"_sr; // Client status info prefix -const KeyRangeRef fdbClientInfoPrefixRange(LiteralStringRef("\xff\x02/fdbClientInfo/"), - LiteralStringRef("\xff\x02/fdbClientInfo0")); +const KeyRangeRef fdbClientInfoPrefixRange("\xff\x02/fdbClientInfo/"_sr, "\xff\x02/fdbClientInfo0"_sr); // See remaining fields in GlobalConfig.actor.h // ConsistencyCheck settings -const KeyRef fdbShouldConsistencyCheckBeSuspended = LiteralStringRef("\xff\x02/ConsistencyCheck/Suspend"); +const KeyRef fdbShouldConsistencyCheckBeSuspended = "\xff\x02/ConsistencyCheck/Suspend"_sr; // Request latency measurement key -const KeyRef latencyBandConfigKey = LiteralStringRef("\xff\x02/latencyBandConfig"); +const KeyRef latencyBandConfigKey = "\xff\x02/latencyBandConfig"_sr; // Keyspace to maintain wall clock to version map -const KeyRangeRef timeKeeperPrefixRange(LiteralStringRef("\xff\x02/timeKeeper/map/"), - LiteralStringRef("\xff\x02/timeKeeper/map0")); -const KeyRef timeKeeperVersionKey = LiteralStringRef("\xff\x02/timeKeeper/version"); -const KeyRef timeKeeperDisableKey = LiteralStringRef("\xff\x02/timeKeeper/disable"); +const KeyRangeRef timeKeeperPrefixRange("\xff\x02/timeKeeper/map/"_sr, "\xff\x02/timeKeeper/map0"_sr); +const KeyRef timeKeeperVersionKey = "\xff\x02/timeKeeper/version"_sr; +const KeyRef timeKeeperDisableKey = "\xff\x02/timeKeeper/disable"_sr; // Backup Log Mutation constant variables -const KeyRef backupEnabledKey = LiteralStringRef("\xff/backupEnabled"); -const KeyRangeRef backupLogKeys(LiteralStringRef("\xff\x02/blog/"), LiteralStringRef("\xff\x02/blog0")); -const KeyRangeRef applyLogKeys(LiteralStringRef("\xff\x02/alog/"), LiteralStringRef("\xff\x02/alog0")); +const KeyRef backupEnabledKey = "\xff/backupEnabled"_sr; +const KeyRangeRef backupLogKeys("\xff\x02/blog/"_sr, "\xff\x02/blog0"_sr); +const KeyRangeRef applyLogKeys("\xff\x02/alog/"_sr, "\xff\x02/alog0"_sr); // static_assert( backupLogKeys.begin.size() == backupLogPrefixBytes, "backupLogPrefixBytes incorrect" ); -const KeyRef backupVersionKey = LiteralStringRef("\xff/backupDataFormat"); -const ValueRef backupVersionValue = LiteralStringRef("4"); +const KeyRef backupVersionKey = "\xff/backupDataFormat"_sr; +const ValueRef backupVersionValue = "4"_sr; const int backupVersion = 4; // Log Range constant variables // \xff/logRanges/[16-byte UID][begin key] := serialize( make_pair([end key], [destination key prefix]), // IncludeVersion() ) -const KeyRangeRef logRangesRange(LiteralStringRef("\xff/logRanges/"), LiteralStringRef("\xff/logRanges0")); +const KeyRangeRef logRangesRange("\xff/logRanges/"_sr, "\xff/logRanges0"_sr); // Layer status metadata prefix -const KeyRangeRef layerStatusMetaPrefixRange(LiteralStringRef("\xff\x02/status/"), - LiteralStringRef("\xff\x02/status0")); +const KeyRangeRef layerStatusMetaPrefixRange("\xff\x02/status/"_sr, "\xff\x02/status0"_sr); // Backup agent status root -const KeyRangeRef backupStatusPrefixRange(LiteralStringRef("\xff\x02/backupstatus/"), - LiteralStringRef("\xff\x02/backupstatus0")); +const KeyRangeRef backupStatusPrefixRange("\xff\x02/backupstatus/"_sr, "\xff\x02/backupstatus0"_sr); // Restore configuration constant variables -const KeyRangeRef fileRestorePrefixRange(LiteralStringRef("\xff\x02/restore-agent/"), - LiteralStringRef("\xff\x02/restore-agent0")); +const KeyRangeRef fileRestorePrefixRange("\xff\x02/restore-agent/"_sr, "\xff\x02/restore-agent0"_sr); // Backup Agent configuration constant variables -const KeyRangeRef fileBackupPrefixRange(LiteralStringRef("\xff\x02/backup-agent/"), - LiteralStringRef("\xff\x02/backup-agent0")); +const KeyRangeRef fileBackupPrefixRange("\xff\x02/backup-agent/"_sr, "\xff\x02/backup-agent0"_sr); // DR Agent configuration constant variables -const KeyRangeRef databaseBackupPrefixRange(LiteralStringRef("\xff\x02/db-backup-agent/"), - LiteralStringRef("\xff\x02/db-backup-agent0")); +const KeyRangeRef databaseBackupPrefixRange("\xff\x02/db-backup-agent/"_sr, "\xff\x02/db-backup-agent0"_sr); // \xff\x02/sharedLogRangesConfig/destUidLookup/[keyRange] -const KeyRef destUidLookupPrefix = LiteralStringRef("\xff\x02/sharedLogRangesConfig/destUidLookup/"); +const KeyRef destUidLookupPrefix = "\xff\x02/sharedLogRangesConfig/destUidLookup/"_sr; // \xff\x02/sharedLogRangesConfig/backuplatestVersions/[destUid]/[logUid] -const KeyRef backupLatestVersionsPrefix = LiteralStringRef("\xff\x02/sharedLogRangesConfig/backupLatestVersions/"); +const KeyRef backupLatestVersionsPrefix = "\xff\x02/sharedLogRangesConfig/backupLatestVersions/"_sr; // Returns the encoded key comprised of begin key and log uid Key logRangesEncodeKey(KeyRef keyBegin, UID logUid) { @@ -1148,31 +1126,27 @@ Key uidPrefixKey(KeyRef keyPrefix, UID logUid) { // Apply mutations constant variables // \xff/applyMutationsEnd/[16-byte UID] := serialize( endVersion, Unversioned() ) // This indicates what is the highest version the mutation log can be applied -const KeyRangeRef applyMutationsEndRange(LiteralStringRef("\xff/applyMutationsEnd/"), - LiteralStringRef("\xff/applyMutationsEnd0")); +const KeyRangeRef applyMutationsEndRange("\xff/applyMutationsEnd/"_sr, "\xff/applyMutationsEnd0"_sr); // \xff/applyMutationsBegin/[16-byte UID] := serialize( beginVersion, Unversioned() ) -const KeyRangeRef applyMutationsBeginRange(LiteralStringRef("\xff/applyMutationsBegin/"), - LiteralStringRef("\xff/applyMutationsBegin0")); +const KeyRangeRef applyMutationsBeginRange("\xff/applyMutationsBegin/"_sr, "\xff/applyMutationsBegin0"_sr); // \xff/applyMutationsAddPrefix/[16-byte UID] := addPrefix -const KeyRangeRef applyMutationsAddPrefixRange(LiteralStringRef("\xff/applyMutationsAddPrefix/"), - LiteralStringRef("\xff/applyMutationsAddPrefix0")); +const KeyRangeRef applyMutationsAddPrefixRange("\xff/applyMutationsAddPrefix/"_sr, "\xff/applyMutationsAddPrefix0"_sr); // \xff/applyMutationsRemovePrefix/[16-byte UID] := removePrefix -const KeyRangeRef applyMutationsRemovePrefixRange(LiteralStringRef("\xff/applyMutationsRemovePrefix/"), - LiteralStringRef("\xff/applyMutationsRemovePrefix0")); +const KeyRangeRef applyMutationsRemovePrefixRange("\xff/applyMutationsRemovePrefix/"_sr, + "\xff/applyMutationsRemovePrefix0"_sr); -const KeyRangeRef applyMutationsKeyVersionMapRange(LiteralStringRef("\xff/applyMutationsKeyVersionMap/"), - LiteralStringRef("\xff/applyMutationsKeyVersionMap0")); -const KeyRangeRef applyMutationsKeyVersionCountRange(LiteralStringRef("\xff\x02/applyMutationsKeyVersionCount/"), - LiteralStringRef("\xff\x02/applyMutationsKeyVersionCount0")); +const KeyRangeRef applyMutationsKeyVersionMapRange("\xff/applyMutationsKeyVersionMap/"_sr, + "\xff/applyMutationsKeyVersionMap0"_sr); +const KeyRangeRef applyMutationsKeyVersionCountRange("\xff\x02/applyMutationsKeyVersionCount/"_sr, + "\xff\x02/applyMutationsKeyVersionCount0"_sr); -const KeyRef systemTuplesPrefix = LiteralStringRef("\xff/a/"); -const KeyRef metricConfChangeKey = LiteralStringRef("\x01TDMetricConfChanges\x00"); +const KeyRef systemTuplesPrefix = "\xff/a/"_sr; +const KeyRef metricConfChangeKey = "\x01TDMetricConfChanges\x00"_sr; -const KeyRangeRef metricConfKeys(LiteralStringRef("\x01TDMetricConf\x00\x01"), - LiteralStringRef("\x01TDMetricConf\x00\x02")); +const KeyRangeRef metricConfKeys("\x01TDMetricConf\x00\x01"_sr, "\x01TDMetricConf\x00\x02"_sr); const KeyRef metricConfPrefix = metricConfKeys.begin; /* @@ -1181,15 +1155,15 @@ const Key metricConfKey( KeyRef const& prefix, MetricNameRef const& name, KeyRef wr.serializeBytes( prefix ); wr.serializeBytes( metricConfPrefix ); wr.serializeBytes( name.type ); - wr.serializeBytes( LiteralStringRef("\x00\x01") ); + wr.serializeBytes( "\x00\x01"_sr ); wr.serializeBytes( name.name ); - wr.serializeBytes( LiteralStringRef("\x00\x01") ); + wr.serializeBytes( "\x00\x01"_sr ); wr.serializeBytes( name.address ); - wr.serializeBytes( LiteralStringRef("\x00\x01") ); + wr.serializeBytes( "\x00\x01"_sr ); wr.serializeBytes( name.id ); - wr.serializeBytes( LiteralStringRef("\x00\x01") ); + wr.serializeBytes( "\x00\x01"_sr ); wr.serializeBytes( key ); - wr.serializeBytes( LiteralStringRef("\x00") ); + wr.serializeBytes( "\x00"_sr ); return wr.toValue(); } @@ -1212,23 +1186,22 @@ std::pair decodeMetricConfKey( KeyRef const& prefix, KeyR } */ -const KeyRef maxUIDKey = LiteralStringRef("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"); +const KeyRef maxUIDKey = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"_sr; -const KeyRef databaseLockedKey = LiteralStringRef("\xff/dbLocked"); -const KeyRef databaseLockedKeyEnd = LiteralStringRef("\xff/dbLocked\x00"); -const KeyRef metadataVersionKey = LiteralStringRef("\xff/metadataVersion"); -const KeyRef metadataVersionKeyEnd = LiteralStringRef("\xff/metadataVersion\x00"); -const KeyRef metadataVersionRequiredValue = - LiteralStringRef("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); -const KeyRef mustContainSystemMutationsKey = LiteralStringRef("\xff/mustContainSystemMutations"); +const KeyRef databaseLockedKey = "\xff/dbLocked"_sr; +const KeyRef databaseLockedKeyEnd = "\xff/dbLocked\x00"_sr; +const KeyRef metadataVersionKey = "\xff/metadataVersion"_sr; +const KeyRef metadataVersionKeyEnd = "\xff/metadataVersion\x00"_sr; +const KeyRef metadataVersionRequiredValue = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_sr; +const KeyRef mustContainSystemMutationsKey = "\xff/mustContainSystemMutations"_sr; -const KeyRangeRef monitorConfKeys(LiteralStringRef("\xff\x02/monitorConf/"), LiteralStringRef("\xff\x02/monitorConf0")); +const KeyRangeRef monitorConfKeys("\xff\x02/monitorConf/"_sr, "\xff\x02/monitorConf0"_sr); -const KeyRef restoreRequestDoneKey = LiteralStringRef("\xff\x02/restoreRequestDone"); +const KeyRef restoreRequestDoneKey = "\xff\x02/restoreRequestDone"_sr; -const KeyRef healthyZoneKey = LiteralStringRef("\xff\x02/healthyZone"); -const StringRef ignoreSSFailuresZoneString = LiteralStringRef("IgnoreSSFailures"); -const KeyRef rebalanceDDIgnoreKey = LiteralStringRef("\xff\x02/rebalanceDDIgnored"); +const KeyRef healthyZoneKey = "\xff\x02/healthyZone"_sr; +const StringRef ignoreSSFailuresZoneString = "IgnoreSSFailures"_sr; +const KeyRef rebalanceDDIgnoreKey = "\xff\x02/rebalanceDDIgnored"_sr; const Value healthyZoneValue(StringRef const& zoneId, Version version) { BinaryWriter wr(IncludeVersion(ProtocolVersion::withHealthyZoneValue())); @@ -1245,16 +1218,15 @@ std::pair decodeHealthyZoneValue(ValueRef const& value) { return std::make_pair(zoneId, version); } -const KeyRangeRef testOnlyTxnStateStorePrefixRange(LiteralStringRef("\xff/TESTONLYtxnStateStore/"), - LiteralStringRef("\xff/TESTONLYtxnStateStore0")); +const KeyRangeRef testOnlyTxnStateStorePrefixRange("\xff/TESTONLYtxnStateStore/"_sr, "\xff/TESTONLYtxnStateStore0"_sr); -const KeyRef writeRecoveryKey = LiteralStringRef("\xff/writeRecovery"); -const ValueRef writeRecoveryKeyTrue = LiteralStringRef("1"); -const KeyRef snapshotEndVersionKey = LiteralStringRef("\xff/snapshotEndVersion"); +const KeyRef writeRecoveryKey = "\xff/writeRecovery"_sr; +const ValueRef writeRecoveryKeyTrue = "1"_sr; +const KeyRef snapshotEndVersionKey = "\xff/snapshotEndVersion"_sr; -const KeyRangeRef changeFeedKeys(LiteralStringRef("\xff\x02/feed/"), LiteralStringRef("\xff\x02/feed0")); +const KeyRangeRef changeFeedKeys("\xff\x02/feed/"_sr, "\xff\x02/feed0"_sr); const KeyRef changeFeedPrefix = changeFeedKeys.begin; -const KeyRef changeFeedPrivatePrefix = LiteralStringRef("\xff\xff\x02/feed/"); +const KeyRef changeFeedPrivatePrefix = "\xff\xff\x02/feed/"_sr; const Value changeFeedValue(KeyRangeRef const& range, Version popVersion, ChangeFeedStatus status) { BinaryWriter wr(IncludeVersion(ProtocolVersion::withChangeFeed())); @@ -1275,7 +1247,7 @@ std::tuple decodeChangeFeedValue(ValueRef c return std::make_tuple(range, version, status); } -const KeyRangeRef changeFeedDurableKeys(LiteralStringRef("\xff\xff/cf/"), LiteralStringRef("\xff\xff/cf0")); +const KeyRangeRef changeFeedDurableKeys("\xff\xff/cf/"_sr, "\xff\xff/cf0"_sr); const KeyRef changeFeedDurablePrefix = changeFeedDurableKeys.begin; const Value changeFeedDurableKey(Key const& feed, Version version) { @@ -1315,9 +1287,9 @@ const KeyRangeRef configClassKeys("\xff\xff/configClasses/"_sr, "\xff\xff/config // key to watch for changes in active blob ranges + KeyRangeMap of active blob ranges // Blob Manager + Worker stuff is all \xff\x02 to avoid Transaction State Store -const KeyRef blobRangeChangeKey = LiteralStringRef("\xff\x02/blobRangeChange"); -const KeyRangeRef blobRangeKeys(LiteralStringRef("\xff\x02/blobRange/"), LiteralStringRef("\xff\x02/blobRange0")); -const KeyRef blobManagerEpochKey = LiteralStringRef("\xff\x02/blobManagerEpoch"); +const KeyRef blobRangeChangeKey = "\xff\x02/blobRangeChange"_sr; +const KeyRangeRef blobRangeKeys("\xff\x02/blobRange/"_sr, "\xff\x02/blobRange0"_sr); +const KeyRef blobManagerEpochKey = "\xff\x02/blobManagerEpoch"_sr; const Value blobManagerEpochValueFor(int64_t epoch) { BinaryWriter wr(IncludeVersion(ProtocolVersion::withBlobGranule())); @@ -1333,21 +1305,19 @@ int64_t decodeBlobManagerEpochValue(ValueRef const& value) { } // blob granule data -const KeyRef blobRangeActive = LiteralStringRef("1"); +const KeyRef blobRangeActive = "1"_sr; const KeyRef blobRangeInactive = StringRef(); -const KeyRangeRef blobGranuleFileKeys(LiteralStringRef("\xff\x02/bgf/"), LiteralStringRef("\xff\x02/bgf0")); -const KeyRangeRef blobGranuleMappingKeys(LiteralStringRef("\xff\x02/bgm/"), LiteralStringRef("\xff\x02/bgm0")); -const KeyRangeRef blobGranuleLockKeys(LiteralStringRef("\xff\x02/bgl/"), LiteralStringRef("\xff\x02/bgl0")); -const KeyRangeRef blobGranuleSplitKeys(LiteralStringRef("\xff\x02/bgs/"), LiteralStringRef("\xff\x02/bgs0")); -const KeyRangeRef blobGranuleMergeKeys(LiteralStringRef("\xff\x02/bgmerge/"), LiteralStringRef("\xff\x02/bgmerge0")); -const KeyRangeRef blobGranuleMergeBoundaryKeys(LiteralStringRef("\xff\x02/bgmergebounds/"), - LiteralStringRef("\xff\x02/bgmergebounds0")); -const KeyRangeRef blobGranuleHistoryKeys(LiteralStringRef("\xff\x02/bgh/"), LiteralStringRef("\xff\x02/bgh0")); -const KeyRangeRef blobGranulePurgeKeys(LiteralStringRef("\xff\x02/bgp/"), LiteralStringRef("\xff\x02/bgp0")); -const KeyRangeRef blobGranuleForcePurgedKeys(LiteralStringRef("\xff\x02/bgpforce/"), - LiteralStringRef("\xff\x02/bgpforce0")); -const KeyRef blobGranulePurgeChangeKey = LiteralStringRef("\xff\x02/bgpChange"); +const KeyRangeRef blobGranuleFileKeys("\xff\x02/bgf/"_sr, "\xff\x02/bgf0"_sr); +const KeyRangeRef blobGranuleMappingKeys("\xff\x02/bgm/"_sr, "\xff\x02/bgm0"_sr); +const KeyRangeRef blobGranuleLockKeys("\xff\x02/bgl/"_sr, "\xff\x02/bgl0"_sr); +const KeyRangeRef blobGranuleSplitKeys("\xff\x02/bgs/"_sr, "\xff\x02/bgs0"_sr); +const KeyRangeRef blobGranuleMergeKeys("\xff\x02/bgmerge/"_sr, "\xff\x02/bgmerge0"_sr); +const KeyRangeRef blobGranuleMergeBoundaryKeys("\xff\x02/bgmergebounds/"_sr, "\xff\x02/bgmergebounds0"_sr); +const KeyRangeRef blobGranuleHistoryKeys("\xff\x02/bgh/"_sr, "\xff\x02/bgh0"_sr); +const KeyRangeRef blobGranulePurgeKeys("\xff\x02/bgp/"_sr, "\xff\x02/bgp0"_sr); +const KeyRangeRef blobGranuleForcePurgedKeys("\xff\x02/bgpforce/"_sr, "\xff\x02/bgpforce0"_sr); +const KeyRef blobGranulePurgeChangeKey = "\xff\x02/bgpChange"_sr; const uint8_t BG_FILE_TYPE_DELTA = 'D'; const uint8_t BG_FILE_TYPE_SNAPSHOT = 'S'; @@ -1625,7 +1595,7 @@ Standalone decodeBlobGranuleHistoryValue(const ValueRef return historyValue; } -const KeyRangeRef blobWorkerListKeys(LiteralStringRef("\xff\x02/bwList/"), LiteralStringRef("\xff\x02/bwList0")); +const KeyRangeRef blobWorkerListKeys("\xff\x02/bwList/"_sr, "\xff\x02/bwList0"_sr); const Key blobWorkerListKeyFor(UID workerID) { BinaryWriter wr(AssumeVersion(ProtocolVersion::withBlobGranule())); @@ -1652,7 +1622,7 @@ BlobWorkerInterface decodeBlobWorkerListValue(ValueRef const& value) { return interf; } -const KeyRangeRef storageQuotaKeys(LiteralStringRef("\xff/storageQuota/"), LiteralStringRef("\xff/storageQuota0")); +const KeyRangeRef storageQuotaKeys("\xff/storageQuota/"_sr, "\xff/storageQuota0"_sr); const KeyRef storageQuotaPrefix = storageQuotaKeys.begin; Key storageQuotaKey(StringRef tenantName) { diff --git a/fdbclient/TaskBucket.actor.cpp b/fdbclient/TaskBucket.actor.cpp index d5bab71971..382c5a4327 100644 --- a/fdbclient/TaskBucket.actor.cpp +++ b/fdbclient/TaskBucket.actor.cpp @@ -66,7 +66,7 @@ struct UnblockFutureTaskFunc : TaskFuncBase { return Void(); } }; -StringRef UnblockFutureTaskFunc::name = LiteralStringRef("UnblockFuture"); +StringRef UnblockFutureTaskFunc::name = "UnblockFuture"_sr; REGISTER_TASKFUNC(UnblockFutureTaskFunc); struct AddTaskFunc : TaskFuncBase { @@ -88,7 +88,7 @@ struct AddTaskFunc : TaskFuncBase { return Void(); }; }; -StringRef AddTaskFunc::name = LiteralStringRef("AddTask"); +StringRef AddTaskFunc::name = "AddTask"_sr; REGISTER_TASKFUNC(AddTaskFunc); struct IdleTaskFunc : TaskFuncBase { @@ -109,18 +109,18 @@ struct IdleTaskFunc : TaskFuncBase { return tb->finish(tr, task); }; }; -StringRef IdleTaskFunc::name = LiteralStringRef("idle"); +StringRef IdleTaskFunc::name = "idle"_sr; REGISTER_TASKFUNC(IdleTaskFunc); -Key Task::reservedTaskParamKeyType = LiteralStringRef("type"); -Key Task::reservedTaskParamKeyAddTask = LiteralStringRef("_add_task"); -Key Task::reservedTaskParamKeyDone = LiteralStringRef("done"); -Key Task::reservedTaskParamKeyPriority = LiteralStringRef("priority"); -Key Task::reservedTaskParamKeyFuture = LiteralStringRef("future"); -Key Task::reservedTaskParamKeyBlockID = LiteralStringRef("blockid"); -Key Task::reservedTaskParamKeyVersion = LiteralStringRef("version"); -Key Task::reservedTaskParamValidKey = LiteralStringRef("_validkey"); -Key Task::reservedTaskParamValidValue = LiteralStringRef("_validvalue"); +Key Task::reservedTaskParamKeyType = "type"_sr; +Key Task::reservedTaskParamKeyAddTask = "_add_task"_sr; +Key Task::reservedTaskParamKeyDone = "done"_sr; +Key Task::reservedTaskParamKeyPriority = "priority"_sr; +Key Task::reservedTaskParamKeyFuture = "future"_sr; +Key Task::reservedTaskParamKeyBlockID = "blockid"_sr; +Key Task::reservedTaskParamKeyVersion = "version"_sr; +Key Task::reservedTaskParamValidKey = "_validkey"_sr; +Key Task::reservedTaskParamValidValue = "_validvalue"_sr; // IMPORTANT: Task() must result in an EMPTY parameter set, so params should only // be set for non-default constructor arguments. To change this behavior look at all @@ -722,7 +722,7 @@ public: Reference taskBucket) { taskBucket->setOptions(tr); - Optional val = wait(tr->get(taskBucket->prefix.pack(LiteralStringRef("task_count")))); + Optional val = wait(tr->get(taskBucket->prefix.pack("task_count"_sr))); if (!val.present()) return 0; @@ -873,10 +873,10 @@ TaskBucket::TaskBucket(const Subspace& subspace, : cc("TaskBucket"), dispatchSlotChecksStarted("DispatchSlotChecksStarted", cc), dispatchErrors("DispatchErrors", cc), dispatchDoTasks("DispatchDoTasks", cc), dispatchEmptyTasks("DispatchEmptyTasks", cc), dispatchSlotChecksComplete("DispatchSlotChecksComplete", cc), dbgid(deterministicRandom()->randomUniqueID()), - prefix(subspace), active(prefix.get(LiteralStringRef("ac"))), pauseKey(prefix.pack(LiteralStringRef("pause"))), - available(prefix.get(LiteralStringRef("av"))), available_prioritized(prefix.get(LiteralStringRef("avp"))), - timeouts(prefix.get(LiteralStringRef("to"))), timeout(CLIENT_KNOBS->TASKBUCKET_TIMEOUT_VERSIONS), - system_access(sysAccess), priority_batch(priorityBatch), lockAware(lockAware) {} + prefix(subspace), active(prefix.get("ac"_sr)), pauseKey(prefix.pack("pause"_sr)), available(prefix.get("av"_sr)), + available_prioritized(prefix.get("avp"_sr)), timeouts(prefix.get("to"_sr)), + timeout(CLIENT_KNOBS->TASKBUCKET_TIMEOUT_VERSIONS), system_access(sysAccess), priority_batch(priorityBatch), + lockAware(lockAware) {} TaskBucket::~TaskBucket() {} @@ -919,9 +919,7 @@ Key TaskBucket::addTask(Reference tr, Reference for (auto& param : task->params) tr->set(taskSpace.pack(param.key), param.value); - tr->atomicOp(prefix.pack(LiteralStringRef("task_count")), - LiteralStringRef("\x01\x00\x00\x00\x00\x00\x00\x00"), - MutationRef::AddValue); + tr->atomicOp(prefix.pack("task_count"_sr), "\x01\x00\x00\x00\x00\x00\x00\x00"_sr, MutationRef::AddValue); return key; } @@ -995,9 +993,7 @@ Future TaskBucket::finish(Reference tr, Referen Tuple t = Tuple::makeTuple(task->timeoutVersion, task->key); - tr->atomicOp(prefix.pack(LiteralStringRef("task_count")), - LiteralStringRef("\xff\xff\xff\xff\xff\xff\xff\xff"), - MutationRef::AddValue); + tr->atomicOp(prefix.pack("task_count"_sr), "\xff\xff\xff\xff\xff\xff\xff\xff"_sr, MutationRef::AddValue); tr->clear(timeouts.range(t)); return Void(); @@ -1028,7 +1024,7 @@ Future TaskBucket::getTaskCount(Reference tr } Future TaskBucket::watchTaskCount(Reference tr) { - return tr->watch(prefix.pack(LiteralStringRef("task_count"))); + return tr->watch(prefix.pack("task_count"_sr)); } Future TaskBucket::debugPrintRange(Reference tr, Subspace subspace, Key msg) { @@ -1103,7 +1099,7 @@ public: Key key = StringRef(deterministicRandom()->randomUniqueID().toString()); taskFuture->addBlock(tr, key); auto task = makeReference(); - task->params[Task::reservedTaskParamKeyType] = LiteralStringRef("UnblockFuture"); + task->params[Task::reservedTaskParamKeyType] = "UnblockFuture"_sr; task->params[Task::reservedTaskParamKeyFuture] = taskFuture->key; task->params[Task::reservedTaskParamKeyBlockID] = key; onSetFutures.push_back(vectorFuture[i]->onSet(tr, taskBucket, task)); @@ -1217,7 +1213,7 @@ public: taskFuture->futureBucket->setOptions(tr); task->params[Task::reservedTaskParamKeyAddTask] = task->params[Task::reservedTaskParamKeyType]; - task->params[Task::reservedTaskParamKeyType] = LiteralStringRef("AddTask"); + task->params[Task::reservedTaskParamKeyType] = "AddTask"_sr; wait(onSet(tr, taskBucket, taskFuture, task)); return Void(); @@ -1282,14 +1278,14 @@ TaskFuture::TaskFuture(const Reference bucket, Key k) : futureBuck } prefix = futureBucket->prefix.get(key); - blocks = prefix.get(LiteralStringRef("bl")); - callbacks = prefix.get(LiteralStringRef("cb")); + blocks = prefix.get("bl"_sr); + callbacks = prefix.get("cb"_sr); } TaskFuture::~TaskFuture() {} void TaskFuture::addBlock(Reference tr, StringRef block_id) { - tr->set(blocks.pack(block_id), LiteralStringRef("")); + tr->set(blocks.pack(block_id), ""_sr); } Future TaskFuture::set(Reference tr, Reference taskBucket) { diff --git a/fdbclient/Tenant.cpp b/fdbclient/Tenant.cpp index 835dcb32b9..dd9c6c9796 100644 --- a/fdbclient/Tenant.cpp +++ b/fdbclient/Tenant.cpp @@ -21,20 +21,28 @@ #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/SystemData.h" #include "fdbclient/Tenant.h" +#include "fdbrpc/TenantInfo.h" +#include "flow/BooleanParam.h" #include "libb64/encode.h" #include "flow/ApiVersion.h" #include "flow/UnitTest.h" +FDB_DEFINE_BOOLEAN_PARAM(EnforceValidTenantId); + Key TenantMapEntry::idToPrefix(int64_t id) { int64_t swapped = bigEndian64(id); return StringRef(reinterpret_cast(&swapped), TENANT_PREFIX_SIZE); } -int64_t TenantMapEntry::prefixToId(KeyRef prefix) { +int64_t TenantMapEntry::prefixToId(KeyRef prefix, EnforceValidTenantId enforceValidTenantId) { ASSERT(prefix.size() == TENANT_PREFIX_SIZE); int64_t id = *reinterpret_cast(prefix.begin()); id = bigEndian64(id); - ASSERT(id >= 0); + if (enforceValidTenantId) { + ASSERT(id >= 0); + } else if (id < 0) { + return TenantInfo::INVALID_TENANT; + } return id; } diff --git a/fdbclient/ThreadSafeTransaction.cpp b/fdbclient/ThreadSafeTransaction.cpp index 856b8d6528..13736155e6 100644 --- a/fdbclient/ThreadSafeTransaction.cpp +++ b/fdbclient/ThreadSafeTransaction.cpp @@ -55,7 +55,7 @@ Reference ThreadSafeDatabase::openTenant(TenantNameRef tenantName) { } Reference ThreadSafeDatabase::createTransaction() { - auto type = isConfigDB ? ISingleThreadTransaction::Type::SIMPLE_CONFIG : ISingleThreadTransaction::Type::RYW; + auto type = isConfigDB ? ISingleThreadTransaction::Type::PAXOS_CONFIG : ISingleThreadTransaction::Type::RYW; return Reference(new ThreadSafeTransaction(db, type, Optional())); } @@ -171,7 +171,7 @@ ThreadFuture ThreadSafeDatabase::unblobbifyRange(const KeyRangeRef& keyRan KeyRange range = keyRange; return onMainThread([=]() -> Future { db->checkDeferredError(); - return db->blobbifyRange(range); + return db->unblobbifyRange(range); }); } @@ -224,7 +224,7 @@ ThreadSafeDatabase::~ThreadSafeDatabase() { } Reference ThreadSafeTenant::createTransaction() { - auto type = db->isConfigDB ? ISingleThreadTransaction::Type::SIMPLE_CONFIG : ISingleThreadTransaction::Type::RYW; + auto type = db->isConfigDB ? ISingleThreadTransaction::Type::PAXOS_CONFIG : ISingleThreadTransaction::Type::RYW; return Reference(new ThreadSafeTransaction(db->db, type, name)); } diff --git a/fdbclient/Tracing.actor.cpp b/fdbclient/Tracing.actor.cpp index 815e568acb..3100b208fc 100644 --- a/fdbclient/Tracing.actor.cpp +++ b/fdbclient/Tracing.actor.cpp @@ -497,7 +497,7 @@ TEST_CASE("/flow/Tracing/AddEvents") { auto arena = span1.arena; SmallVectorRef attrs; attrs.push_back(arena, KeyValueRef("foo"_sr, "bar"_sr)); - span1.addEvent(LiteralStringRef("read_version"), 1.0, attrs); + span1.addEvent("read_version"_sr, 1.0, attrs); ASSERT(span1.events[0].name.toString() == "read_version"); ASSERT(span1.events[0].time == 1.0); ASSERT(span1.events[0].attributes.begin()->key.toString() == "foo"); @@ -505,7 +505,7 @@ TEST_CASE("/flow/Tracing/AddEvents") { // Use helper method to add an OTELEventRef with no attributes to an OTELSpan Span span2("span_with_event"_loc); - span2.addEvent(StringRef(span2.arena, LiteralStringRef("commit_succeed")), 1234567.100); + span2.addEvent(StringRef(span2.arena, "commit_succeed"_sr), 1234567.100); ASSERT(span2.events[0].name.toString() == "commit_succeed"); ASSERT(span2.events[0].time == 1234567.100); ASSERT(span2.events[0].attributes.size() == 0); @@ -537,8 +537,8 @@ TEST_CASE("/flow/Tracing/AddAttributes") { IKnobCollection::getMutableGlobalKnobCollection().setKnob("tracing_span_attributes_enabled", KnobValueRef::create(bool{ true })); auto arena = span1.arena; - span1.addAttribute(StringRef(arena, LiteralStringRef("foo")), StringRef(arena, LiteralStringRef("bar"))); - span1.addAttribute(StringRef(arena, LiteralStringRef("operation")), StringRef(arena, LiteralStringRef("grv"))); + span1.addAttribute(StringRef(arena, "foo"_sr), StringRef(arena, "bar"_sr)); + span1.addAttribute(StringRef(arena, "operation"_sr), StringRef(arena, "grv"_sr)); ASSERT_EQ(span1.attributes.size(), 3); // Includes default attribute of "address" ASSERT(span1.attributes[1] == KeyValueRef("foo"_sr, "bar"_sr)); ASSERT(span1.attributes[2] == KeyValueRef("operation"_sr, "grv"_sr)); @@ -548,9 +548,9 @@ TEST_CASE("/flow/Tracing/AddAttributes") { deterministicRandom()->randomUInt64(), TraceFlags::sampled)); auto s2Arena = span2.arena; - span2.addAttribute(StringRef(s2Arena, LiteralStringRef("a")), StringRef(s2Arena, LiteralStringRef("1"))) - .addAttribute(StringRef(s2Arena, LiteralStringRef("b")), LiteralStringRef("2")) - .addAttribute(StringRef(s2Arena, LiteralStringRef("c")), LiteralStringRef("3")); + span2.addAttribute(StringRef(s2Arena, "a"_sr), StringRef(s2Arena, "1"_sr)) + .addAttribute(StringRef(s2Arena, "b"_sr), "2"_sr) + .addAttribute(StringRef(s2Arena, "c"_sr), "3"_sr); ASSERT_EQ(span2.attributes.size(), 4); // Includes default attribute of "address" ASSERT(span2.attributes[1] == KeyValueRef("a"_sr, "1"_sr)); @@ -718,7 +718,7 @@ TEST_CASE("/flow/Tracing/FastUDPMessagePackEncoding") { attrs.push_back(s3Arena, KeyValueRef("foo"_sr, "bar"_sr)); span3.addAttribute("operation"_sr, "grv"_sr) .addLink(SpanContext(UID(300, 301), 400, TraceFlags::sampled)) - .addEvent(StringRef(s3Arena, LiteralStringRef("event1")), 100.101, attrs); + .addEvent(StringRef(s3Arena, "event1"_sr), 100.101, attrs); tracer.serialize_span(span3, request); data = request.buffer.get(); ASSERT(data[0] == 0b10011100); // 12 element array. diff --git a/fdbclient/include/fdbclient/Atomic.h b/fdbclient/include/fdbclient/Atomic.h index 1d19150b04..61f948e38f 100644 --- a/fdbclient/include/fdbclient/Atomic.h +++ b/fdbclient/include/fdbclient/Atomic.h @@ -120,7 +120,7 @@ inline ValueRef doAppendIfFits(const Optional& existingValueOptional, if (!otherOperand.size()) return existingValue; if (existingValue.size() + otherOperand.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT) { - CODE_PROBE(true, "AppendIfFIts resulted in truncation"); + CODE_PROBE(true, "AppendIfFits resulted in truncation"); return existingValue; } diff --git a/fdbclient/include/fdbclient/BackupAgent.actor.h b/fdbclient/include/fdbclient/BackupAgent.actor.h index e0eb69ede6..70894b9ea9 100644 --- a/fdbclient/include/fdbclient/BackupAgent.actor.h +++ b/fdbclient/include/fdbclient/BackupAgent.actor.h @@ -621,7 +621,7 @@ class TagUidMap : public KeyBackedMap { Snapshot snapshot); public: - TagUidMap(const StringRef& prefix) : TagMap(LiteralStringRef("tag->uid/").withPrefix(prefix)), prefix(prefix) {} + TagUidMap(const StringRef& prefix) : TagMap("tag->uid/"_sr.withPrefix(prefix)), prefix(prefix) {} Future> getAll(Reference tr, Snapshot snapshot = Snapshot::False) { @@ -656,7 +656,7 @@ public: } TaskParams; KeyBackedConfig(StringRef prefix, UID uid = UID()) - : uid(uid), prefix(prefix), configSpace(uidPrefixKey(LiteralStringRef("uid->config/").withPrefix(prefix), uid)) {} + : uid(uid), prefix(prefix), configSpace(uidPrefixKey("uid->config/"_sr.withPrefix(prefix), uid)) {} KeyBackedConfig(StringRef prefix, Reference task) : KeyBackedConfig(prefix, TaskParams.uid().get(task)) {} diff --git a/flow/include/flow/BlobCipher.h b/fdbclient/include/fdbclient/BlobCipher.h similarity index 90% rename from flow/include/flow/BlobCipher.h rename to fdbclient/include/fdbclient/BlobCipher.h index 27cbbc6c5f..9c61661955 100644 --- a/flow/include/flow/BlobCipher.h +++ b/fdbclient/include/fdbclient/BlobCipher.h @@ -17,10 +17,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef FLOW_BLOB_CIPHER_H -#define FLOW_BLOB_CIPHER_H +#ifndef FDBCLIENT_BLOB_CIPHER_H +#define FDBCLIENT_BLOB_CIPHER_H #pragma once +#include "fdbrpc/Stats.h" #include "flow/Arena.h" #include "flow/EncryptUtils.h" #include "flow/FastRef.h" @@ -28,6 +29,7 @@ #include "flow/genericactors.actor.h" #include "flow/Knobs.h" #include "flow/network.h" +#include "flow/Platform.h" #include "flow/ProtocolVersion.h" #include "flow/serialize.h" @@ -50,6 +52,59 @@ #define AES_256_KEY_LENGTH 32 #define AES_256_IV_LENGTH 16 +class BlobCipherMetrics : public NonCopyable { +public: + static BlobCipherMetrics* getInstance() { + static BlobCipherMetrics* instance = nullptr; + if (instance == nullptr) { + instance = new BlobCipherMetrics; + } + return instance; + } + + // Order of this enum has to match initializer of counterSets. + enum UsageType : int { + TLOG = 0, + KV_MEMORY, + KV_REDWOOD, + BLOB_GRANULE, + BACKUP, + TEST, + MAX, + }; + + struct CounterSet { + Counter encryptCPUTimeNS; + Counter decryptCPUTimeNS; + LatencySample getCipherKeysLatency; + LatencySample getLatestCipherKeysLatency; + + CounterSet(CounterCollection& cc, std::string name); + }; + + static CounterSet& counters(UsageType t) { + ASSERT(t < UsageType::MAX); + return getInstance()->counterSets[int(t)]; + } + +private: + BlobCipherMetrics(); + + CounterCollection cc; + Future traceFuture; + +public: + Counter cipherKeyCacheHit; + Counter cipherKeyCacheMiss; + Counter cipherKeyCacheExpired; + Counter latestCipherKeyCacheHit; + Counter latestCipherKeyCacheMiss; + Counter latestCipherKeyCacheNeedsRefresh; + LatencySample getCipherKeysLatency; + LatencySample getLatestCipherKeysLatency; + std::array counterSets; +}; + // Encryption operations buffer management // Approach limits number of copies needed during encryption or decryption operations. // For encryption EncryptBuf is allocated using client supplied Arena and provided to AES library to capture @@ -85,9 +140,9 @@ private: #pragma pack(push, 1) // exact fit - no padding struct BlobCipherDetails { // Encryption domain boundary identifier. - EncryptCipherDomainId encryptDomainId = ENCRYPT_INVALID_DOMAIN_ID; + EncryptCipherDomainId encryptDomainId = INVALID_ENCRYPT_DOMAIN_ID; // BaseCipher encryption key identifier - EncryptCipherBaseKeyId baseCipherId = ENCRYPT_INVALID_CIPHER_KEY_ID; + EncryptCipherBaseKeyId baseCipherId = INVALID_ENCRYPT_CIPHER_KEY_ID; // Random salt EncryptCipherRandomSalt salt{}; @@ -324,8 +379,7 @@ using BlobCipherKeyIdCacheMapCItr = struct BlobCipherKeyIdCache : ReferenceCounted { public: - BlobCipherKeyIdCache(); - explicit BlobCipherKeyIdCache(EncryptCipherDomainId dId); + explicit BlobCipherKeyIdCache(EncryptCipherDomainId dId, size_t* sizeStat); BlobCipherKeyIdCacheKey getCacheKey(const EncryptCipherBaseKeyId& baseCipherId, const EncryptCipherRandomSalt& salt); @@ -378,11 +432,15 @@ public: // API returns list of all 'cached' cipherKeys std::vector> getAllCipherKeys(); + // Return number of cipher keys in the cahce. + size_t getSize() const { return keyIdCache.size(); } + private: EncryptCipherDomainId domainId; BlobCipherKeyIdCacheMap keyIdCache; Optional latestBaseCipherKeyId; Optional latestRandomSalt; + size_t* sizeStat; // pointer to the outer BlobCipherKeyCache size count. }; using BlobCipherDomainCacheMap = std::unordered_map>; @@ -447,10 +505,19 @@ public: // API enables dropping all 'cached' cipherKeys for a given encryption domain Id. // Useful to cleanup cache if an encryption domain gets removed/destroyed etc. - void resetEncryptDomainId(const EncryptCipherDomainId domainId); + // Total number of cipher keys in the cache. + size_t getSize() const { return size; } + static Reference getInstance() { + static bool cleanupRegistered = false; + if (!cleanupRegistered) { + // We try to avoid cipher keys appear in core dumps, so we clean them up before crash. + // TODO(yiwu): use of MADV_DONTDUMP instead of the crash handler. + registerCrashHandlerCallback(BlobCipherKeyCache::cleanup); + cleanupRegistered = true; + } if (g_network->isSimulated()) { return FlowSingleton::getInstance( []() { return makeReference(g_network->isSimulated()); }); @@ -466,6 +533,7 @@ public: private: BlobCipherDomainCacheMap domainCacheMap; + size_t size = 0; BlobCipherKeyCache() {} }; @@ -483,10 +551,12 @@ public: Reference hCipherKey, const uint8_t* iv, const int ivLen, - const EncryptAuthTokenMode mode); + const EncryptAuthTokenMode mode, + BlobCipherMetrics::UsageType usageType); EncryptBlobCipherAes265Ctr(Reference tCipherKey, Reference hCipherKey, - const EncryptAuthTokenMode mode); + const EncryptAuthTokenMode mode, + BlobCipherMetrics::UsageType usageType); ~EncryptBlobCipherAes265Ctr(); Reference encrypt(const uint8_t* plaintext, @@ -501,6 +571,7 @@ private: Reference headerCipherKey; EncryptAuthTokenMode authTokenMode; uint8_t iv[AES_256_IV_LENGTH]; + BlobCipherMetrics::UsageType usageType; void init(); }; @@ -512,7 +583,8 @@ class DecryptBlobCipherAes256Ctr final : NonCopyable, public ReferenceCounted tCipherKey, Reference hCipherKey, - const uint8_t* iv); + const uint8_t* iv, + BlobCipherMetrics::UsageType usageType); ~DecryptBlobCipherAes256Ctr(); Reference decrypt(const uint8_t* ciphertext, @@ -531,6 +603,7 @@ private: Reference headerCipherKey; bool headerAuthTokenValidationDone; bool authTokensValidationDone; + BlobCipherMetrics::UsageType usageType; void verifyEncryptHeaderMetadata(const BlobCipherEncryptHeader& header); void verifyAuthTokens(const uint8_t* ciphertext, @@ -567,4 +640,4 @@ StringRef computeAuthToken(const uint8_t* payload, const int keyLen, Arena& arena); -#endif // FLOW_BLOB_CIPHER_H \ No newline at end of file +#endif // FDBCLIENT_BLOB_CIPHER_H \ No newline at end of file diff --git a/fdbclient/include/fdbclient/BlobGranuleCommon.h b/fdbclient/include/fdbclient/BlobGranuleCommon.h index e0589877c1..394e5fadd0 100644 --- a/fdbclient/include/fdbclient/BlobGranuleCommon.h +++ b/fdbclient/include/fdbclient/BlobGranuleCommon.h @@ -22,10 +22,10 @@ #define FDBCLIENT_BLOBGRANULECOMMON_H #pragma once +#include "fdbclient/BlobCipher.h" #include "fdbclient/CommitTransaction.h" #include "fdbclient/FDBTypes.h" -#include "flow/BlobCipher.h" #include "flow/EncryptUtils.h" #include "flow/IRandom.h" #include "flow/serialize.h" @@ -276,4 +276,15 @@ struct BlobGranuleHistoryValue { } }; +// A manifest to assist full fdb restore from blob granule files +struct BlobManifest { + constexpr static FileIdentifier file_identifier = 298872; + VectorRef rows; + + template + void serialize(Ar& ar) { + serializer(ar, rows); + } +}; + #endif diff --git a/fdbclient/include/fdbclient/ClientKnobs.h b/fdbclient/include/fdbclient/ClientKnobs.h index 3f42d93d03..492c1ed44d 100644 --- a/fdbclient/include/fdbclient/ClientKnobs.h +++ b/fdbclient/include/fdbclient/ClientKnobs.h @@ -286,6 +286,9 @@ public: double METACLUSTER_ASSIGNMENT_AVAILABILITY_TIMEOUT; int TENANT_ENTRY_CACHE_LIST_REFRESH_INTERVAL; // How often the TenantEntryCache is refreshed + // Encryption-at-rest + bool ENABLE_ENCRYPTION_CPU_TIME_LOGGING; + ClientKnobs(Randomize randomize); void initialize(Randomize randomize); }; diff --git a/fdbclient/include/fdbclient/ClientVersion.h b/fdbclient/include/fdbclient/ClientVersion.h index fe3068affc..c395a69cc2 100644 --- a/fdbclient/include/fdbclient/ClientVersion.h +++ b/fdbclient/include/fdbclient/ClientVersion.h @@ -37,7 +37,7 @@ struct ClientVersionRef { ClientVersionRef(StringRef clientVersion, StringRef sourceVersion, StringRef protocolVersion) : clientVersion(clientVersion), sourceVersion(sourceVersion), protocolVersion(protocolVersion) {} ClientVersionRef(StringRef versionString) { - std::vector parts = versionString.splitAny(LiteralStringRef(",")); + std::vector parts = versionString.splitAny(","_sr); if (parts.size() != 3) { initUnknown(); return; @@ -48,9 +48,9 @@ struct ClientVersionRef { } void initUnknown() { - clientVersion = LiteralStringRef("Unknown"); - sourceVersion = LiteralStringRef("Unknown"); - protocolVersion = LiteralStringRef("Unknown"); + clientVersion = "Unknown"_sr; + sourceVersion = "Unknown"_sr; + protocolVersion = "Unknown"_sr; } template diff --git a/fdbclient/include/fdbclient/CommitProxyInterface.h b/fdbclient/include/fdbclient/CommitProxyInterface.h index 7fc516dcdd..1614aeacf0 100644 --- a/fdbclient/include/fdbclient/CommitProxyInterface.h +++ b/fdbclient/include/fdbclient/CommitProxyInterface.h @@ -25,17 +25,17 @@ #include #include +#include "fdbclient/CommitTransaction.h" #include "fdbclient/EncryptKeyProxyInterface.h" #include "fdbclient/FDBTypes.h" -#include "fdbclient/StorageServerInterface.h" -#include "fdbclient/CommitTransaction.h" -#include "fdbclient/TagThrottle.actor.h" #include "fdbclient/GlobalConfig.h" +#include "fdbclient/GrvProxyInterface.h" +#include "fdbclient/StorageServerInterface.h" +#include "fdbclient/TagThrottle.actor.h" #include "fdbclient/VersionVector.h" #include "fdbrpc/Stats.h" #include "fdbrpc/TimedRequest.h" -#include "GrvProxyInterface.h" struct CommitProxyInterface { constexpr static FileIdentifier file_identifier = 8954922; diff --git a/fdbclient/include/fdbclient/CommitTransaction.h b/fdbclient/include/fdbclient/CommitTransaction.h index dc26df4fa4..ab044549dc 100644 --- a/fdbclient/include/fdbclient/CommitTransaction.h +++ b/fdbclient/include/fdbclient/CommitTransaction.h @@ -22,10 +22,10 @@ #define FLOW_FDBCLIENT_COMMITTRANSACTION_H #pragma once +#include "fdbclient/BlobCipher.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/Knobs.h" #include "fdbclient/Tracing.h" -#include "flow/BlobCipher.h" // The versioned message has wire format : -1, version, messages static const int32_t VERSION_HEADER = -1; @@ -141,8 +141,9 @@ struct MutationRef { MutationRef encrypt(const std::unordered_map>& cipherKeys, const EncryptCipherDomainId& domainId, - Arena& arena) const { - ASSERT_NE(domainId, ENCRYPT_INVALID_DOMAIN_ID); + Arena& arena, + BlobCipherMetrics::UsageType usageType) const { + ASSERT_NE(domainId, INVALID_ENCRYPT_DOMAIN_ID); auto textCipherItr = cipherKeys.find(domainId); auto headerCipherItr = cipherKeys.find(ENCRYPT_HEADER_DOMAIN_ID); ASSERT(textCipherItr != cipherKeys.end() && textCipherItr->second.isValid()); @@ -155,7 +156,8 @@ struct MutationRef { headerCipherItr->second, iv, AES_256_IV_LENGTH, - ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + usageType); BlobCipherEncryptHeader* header = new (arena) BlobCipherEncryptHeader; StringRef headerRef(reinterpret_cast(header), sizeof(BlobCipherEncryptHeader)); StringRef payload = @@ -164,19 +166,21 @@ struct MutationRef { } MutationRef encryptMetadata(const std::unordered_map>& cipherKeys, - Arena& arena) const { - return encrypt(cipherKeys, SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, arena); + Arena& arena, + BlobCipherMetrics::UsageType usageType) const { + return encrypt(cipherKeys, SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, arena, usageType); } MutationRef decrypt(const std::unordered_map>& cipherKeys, Arena& arena, + BlobCipherMetrics::UsageType usageType, StringRef* buf = nullptr) const { const BlobCipherEncryptHeader* header = encryptionHeader(); auto textCipherItr = cipherKeys.find(header->cipherTextDetails); auto headerCipherItr = cipherKeys.find(header->cipherHeaderDetails); ASSERT(textCipherItr != cipherKeys.end() && textCipherItr->second.isValid()); ASSERT(headerCipherItr != cipherKeys.end() && headerCipherItr->second.isValid()); - DecryptBlobCipherAes256Ctr cipher(textCipherItr->second, headerCipherItr->second, header->iv); + DecryptBlobCipherAes256Ctr cipher(textCipherItr->second, headerCipherItr->second, header->iv, usageType); StringRef plaintext = cipher.decrypt(param2.begin(), param2.size(), *header, arena)->toStringRef(); if (buf != nullptr) { *buf = plaintext; diff --git a/fdbclient/include/fdbclient/ConfigKnobs.h b/fdbclient/include/fdbclient/ConfigKnobs.h index 536bca16f3..168e2fed16 100644 --- a/fdbclient/include/fdbclient/ConfigKnobs.h +++ b/fdbclient/include/fdbclient/ConfigKnobs.h @@ -25,6 +25,8 @@ #include "fdbclient/FDBTypes.h" +typedef uint64_t CoordinatorsHash; + /* * KnobValueRefs are stored in the configuration database, and in local configuration files. They are created from * ParsedKnobValue objects, so it is assumed that the value type is correct for the corresponding knob name diff --git a/fdbclient/include/fdbclient/ConfigTransactionInterface.h b/fdbclient/include/fdbclient/ConfigTransactionInterface.h index b42f653c27..dad60f2d04 100644 --- a/fdbclient/include/fdbclient/ConfigTransactionInterface.h +++ b/fdbclient/include/fdbclient/ConfigTransactionInterface.h @@ -65,16 +65,18 @@ struct ConfigTransactionGetGenerationReply { struct ConfigTransactionGetGenerationRequest { static constexpr FileIdentifier file_identifier = 138941; + CoordinatorsHash coordinatorsHash{ 0 }; // A hint to catch up lagging nodes: Optional lastSeenLiveVersion; ReplyPromise reply; ConfigTransactionGetGenerationRequest() = default; - explicit ConfigTransactionGetGenerationRequest(Optional const& lastSeenLiveVersion) - : lastSeenLiveVersion(lastSeenLiveVersion) {} + explicit ConfigTransactionGetGenerationRequest(CoordinatorsHash coordinatorsHash, + Optional const& lastSeenLiveVersion) + : coordinatorsHash(coordinatorsHash), lastSeenLiveVersion(lastSeenLiveVersion) {} template void serialize(Ar& ar) { - serializer(ar, lastSeenLiveVersion, reply); + serializer(ar, coordinatorsHash, lastSeenLiveVersion, reply); } }; @@ -92,39 +94,43 @@ struct ConfigTransactionGetReply { struct ConfigTransactionGetRequest { static constexpr FileIdentifier file_identifier = 923040; + CoordinatorsHash coordinatorsHash{ 0 }; ConfigGeneration generation; ConfigKey key; ReplyPromise reply; ConfigTransactionGetRequest() = default; - explicit ConfigTransactionGetRequest(ConfigGeneration generation, ConfigKey key) - : generation(generation), key(key) {} + explicit ConfigTransactionGetRequest(CoordinatorsHash coordinatorsHash, ConfigGeneration generation, ConfigKey key) + : coordinatorsHash(coordinatorsHash), generation(generation), key(key) {} template void serialize(Ar& ar) { - serializer(ar, generation, key, reply); + serializer(ar, coordinatorsHash, generation, key, reply); } }; struct ConfigTransactionCommitRequest { static constexpr FileIdentifier file_identifier = 103841; Arena arena; + CoordinatorsHash coordinatorsHash{ 0 }; ConfigGeneration generation{ ::invalidVersion, ::invalidVersion }; VectorRef mutations; ConfigCommitAnnotationRef annotation; ReplyPromise reply; ConfigTransactionCommitRequest() = default; - explicit ConfigTransactionCommitRequest(ConfigGeneration generation, + explicit ConfigTransactionCommitRequest(CoordinatorsHash coordinatorsHash, + ConfigGeneration generation, VectorRef mutations, ConfigCommitAnnotationRef annotation) - : generation(generation), mutations(arena, mutations), annotation(arena, annotation) {} + : coordinatorsHash(coordinatorsHash), generation(generation), mutations(arena, mutations), + annotation(arena, annotation) {} size_t expectedSize() const { return mutations.expectedSize() + annotation.expectedSize(); } template void serialize(Ar& ar) { - serializer(ar, generation, mutations, annotation, reply, arena); + serializer(ar, coordinatorsHash, generation, mutations, annotation, reply, arena); } }; @@ -144,15 +150,17 @@ struct ConfigTransactionGetConfigClassesReply { struct ConfigTransactionGetConfigClassesRequest { static constexpr FileIdentifier file_identifier = 7163400; + CoordinatorsHash coordinatorsHash{ 0 }; ConfigGeneration generation; ReplyPromise reply; ConfigTransactionGetConfigClassesRequest() = default; - explicit ConfigTransactionGetConfigClassesRequest(ConfigGeneration generation) : generation(generation) {} + explicit ConfigTransactionGetConfigClassesRequest(CoordinatorsHash coordinatorsHash, ConfigGeneration generation) + : coordinatorsHash(coordinatorsHash), generation(generation) {} template void serialize(Ar& ar) { - serializer(ar, generation); + serializer(ar, coordinatorsHash, generation); } }; @@ -171,17 +179,20 @@ struct ConfigTransactionGetKnobsReply { struct ConfigTransactionGetKnobsRequest { static constexpr FileIdentifier file_identifier = 987410; + CoordinatorsHash coordinatorsHash{ 0 }; ConfigGeneration generation; Optional configClass; ReplyPromise reply; ConfigTransactionGetKnobsRequest() = default; - explicit ConfigTransactionGetKnobsRequest(ConfigGeneration generation, Optional configClass) - : generation(generation), configClass(configClass) {} + explicit ConfigTransactionGetKnobsRequest(CoordinatorsHash coordinatorsHash, + ConfigGeneration generation, + Optional configClass) + : coordinatorsHash(coordinatorsHash), generation(generation), configClass(configClass) {} template void serialize(Ar& ar) { - serializer(ar, generation, configClass, reply); + serializer(ar, coordinatorsHash, generation, configClass, reply); } }; diff --git a/fdbclient/include/fdbclient/ConsistencyScanInterface.h b/fdbclient/include/fdbclient/ConsistencyScanInterface.h new file mode 100644 index 0000000000..a5ef1e3dd0 --- /dev/null +++ b/fdbclient/include/fdbclient/ConsistencyScanInterface.h @@ -0,0 +1,189 @@ +/* + * ConsistencyScanInterface.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2019 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FDBCLIENT_CONSISTENCYSCANINTERFACE_H +#define FDBCLIENT_CONSISTENCYSCANINTERFACE_H + +#include "fdbclient/CommitProxyInterface.h" +#include "fdbclient/DatabaseConfiguration.h" +#include "fdbclient/FDBTypes.h" +#include "fdbclient/RunTransaction.actor.h" +#include "fdbrpc/fdbrpc.h" +#include "fdbrpc/Locality.h" + +struct ConsistencyScanInterface { + constexpr static FileIdentifier file_identifier = 4983265; + RequestStream> waitFailure; + RequestStream haltConsistencyScan; + struct LocalityData locality; + UID myId; + + ConsistencyScanInterface() {} + explicit ConsistencyScanInterface(const struct LocalityData& l, UID id) : locality(l), myId(id) {} + + void initEndpoints() {} + UID id() const { return myId; } + NetworkAddress address() const { return waitFailure.getEndpoint().getPrimaryAddress(); } + bool operator==(const ConsistencyScanInterface& r) const { return id() == r.id(); } + bool operator!=(const ConsistencyScanInterface& r) const { return !(*this == r); } + + template + void serialize(Archive& ar) { + serializer(ar, waitFailure, haltConsistencyScan, locality, myId); + } +}; + +struct HaltConsistencyScanRequest { + constexpr static FileIdentifier file_identifier = 2323417; + UID requesterID; + ReplyPromise reply; + + HaltConsistencyScanRequest() {} + explicit HaltConsistencyScanRequest(UID uid) : requesterID(uid) {} + + template + void serialize(Ar& ar) { + serializer(ar, requesterID, reply); + } +}; + +// consistency scan configuration and metrics +struct ConsistencyScanInfo { + constexpr static FileIdentifier file_identifier = 732125; + bool consistency_scan_enabled = false; + bool restart = false; + int64_t max_rate = 0; + int64_t target_interval = CLIENT_KNOBS->CONSISTENCY_CHECK_ONE_ROUND_TARGET_COMPLETION_TIME; + int64_t bytes_read_prev_round = 0; + KeyRef progress_key = KeyRef(); + + // Round Metrics - one round of complete validation across all SSs + // Start and finish are in epoch seconds + double last_round_start = 0; + double last_round_finish = 0; + TimerSmoother smoothed_round_duration; + int finished_rounds = 0; + + ConsistencyScanInfo() : smoothed_round_duration(20.0 * 60) {} + ConsistencyScanInfo(bool enabled, bool r, uint64_t rate, uint64_t interval) + : consistency_scan_enabled(enabled), restart(r), max_rate(rate), target_interval(interval), + smoothed_round_duration(20.0 * 60) {} + + template + void serialize(Ar& ar) { + double round_total; + if (!ar.isDeserializing) { + round_total = smoothed_round_duration.getTotal(); + } + serializer(ar, + consistency_scan_enabled, + restart, + max_rate, + target_interval, + bytes_read_prev_round, + last_round_start, + last_round_finish, + round_total, + finished_rounds); + if (ar.isDeserializing) { + smoothed_round_duration.reset(round_total); + } + } + + static Future setInfo(Reference tr, ConsistencyScanInfo info) { + tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::LOCK_AWARE); + tr->set(consistencyScanInfoKey, ObjectWriter::toValue(info, IncludeVersion())); + return Void(); + } + + static Future setInfo(Database cx, ConsistencyScanInfo info) { + return runRYWTransaction( + cx, [=](Reference tr) -> Future { return setInfo(tr, info); }); + } + + static Future> getInfo(Reference tr) { + tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE); + return tr->get(consistencyScanInfoKey); + } + + static Future> getInfo(Database cx) { + return runRYWTransaction( + cx, [=](Reference tr) -> Future> { return getInfo(tr); }); + } + + StatusObject toJSON() const { + StatusObject result; + result["consistency_scan_enabled"] = consistency_scan_enabled; + result["restart"] = restart; + result["max_rate"] = max_rate; + result["target_interval"] = target_interval; + result["bytes_read_prev_round"] = bytes_read_prev_round; + result["last_round_start_datetime"] = epochsToGMTString(last_round_start); + result["last_round_finish_datetime"] = epochsToGMTString(last_round_finish); + result["last_round_start_timestamp"] = last_round_start; + result["last_round_finish_timestamp"] = last_round_finish; + result["smoothed_round_seconds"] = smoothed_round_duration.smoothTotal(); + result["finished_rounds"] = finished_rounds; + return result; + } + + std::string toString() const { + return format("consistency_scan_enabled = %d, restart = %d, max_rate = %ld, target_interval = %ld", + consistency_scan_enabled, + restart, + max_rate, + target_interval); + } +}; + +Future getVersion(Database const& cx); +Future getKeyServers( + Database const& cx, + Promise>>> const& keyServersPromise, + KeyRangeRef const& kr, + bool const& performQuiescentChecks); +Future getKeyLocations(Database const& cx, + std::vector>> const& shards, + Promise>> const& keyLocationPromise, + bool const& performQuiescentChecks); +Future checkDataConsistency(Database const& cx, + VectorRef const& keyLocations, + DatabaseConfiguration const& configuration, + std::map 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); + +#endif // FDBCLIENT_CONSISTENCYSCANINTERFACE_H \ No newline at end of file diff --git a/fdbclient/include/fdbclient/CoordinationInterface.h b/fdbclient/include/fdbclient/CoordinationInterface.h index cc8042976d..c5d53d9ad7 100644 --- a/fdbclient/include/fdbclient/CoordinationInterface.h +++ b/fdbclient/include/fdbclient/CoordinationInterface.h @@ -86,6 +86,8 @@ public: std::vector coords; std::vector hostnames; + size_t getNumberOfCoordinators() const { return coords.size() + hostnames.size(); } + bool operator==(const ClusterConnectionString& other) const noexcept { return key == other.key && keyDesc == other.keyDesc && coords == other.coords && hostnames == other.hostnames; } diff --git a/fdbclient/include/fdbclient/DatabaseContext.h b/fdbclient/include/fdbclient/DatabaseContext.h index d86058b3b8..8897972bc3 100644 --- a/fdbclient/include/fdbclient/DatabaseContext.h +++ b/fdbclient/include/fdbclient/DatabaseContext.h @@ -545,6 +545,16 @@ public: Counter transactionGrvTimedOutBatches; Counter transactionCommitVersionNotFoundForSS; + // Change Feed metrics. Omit change feed metrics from logging if not used + bool usedAnyChangeFeeds; + CounterCollection ccFeed; + Counter feedStreamStarts; + Counter feedMergeStreamStarts; + Counter feedErrors; + Counter feedNonRetriableErrors; + Counter feedPops; + Counter feedPopsFallback; + ContinuousSample latencies, readLatencies, commitLatencies, GRVLatencies, mutationsPerCommit, bytesPerCommit, bgLatencies, bgGranulesPerRequest; diff --git a/fdbclient/include/fdbclient/EncryptKeyProxyInterface.h b/fdbclient/include/fdbclient/EncryptKeyProxyInterface.h index 3a88a855e0..5f4d56eb96 100644 --- a/fdbclient/include/fdbclient/EncryptKeyProxyInterface.h +++ b/fdbclient/include/fdbclient/EncryptKeyProxyInterface.h @@ -147,7 +147,7 @@ struct EKPGetBaseCipherKeysRequestInfo { EncryptCipherDomainNameRef domainName; EKPGetBaseCipherKeysRequestInfo() - : domainId(ENCRYPT_INVALID_DOMAIN_ID), baseCipherId(ENCRYPT_INVALID_CIPHER_KEY_ID) {} + : domainId(INVALID_ENCRYPT_DOMAIN_ID), baseCipherId(INVALID_ENCRYPT_CIPHER_KEY_ID) {} EKPGetBaseCipherKeysRequestInfo(const EncryptCipherDomainId dId, const EncryptCipherBaseKeyId bCId, StringRef name, @@ -205,7 +205,7 @@ struct EKPGetLatestCipherKeysRequestInfo { // {domainId, cipherBaseId} tuple EncryptCipherDomainNameRef domainName; - EKPGetLatestCipherKeysRequestInfo() : domainId(ENCRYPT_INVALID_DOMAIN_ID) {} + EKPGetLatestCipherKeysRequestInfo() : domainId(INVALID_ENCRYPT_DOMAIN_ID) {} EKPGetLatestCipherKeysRequestInfo(const EncryptCipherDomainId dId, StringRef name, Arena& arena) : domainId(dId), domainName(StringRef(arena, name)) {} diff --git a/fdbclient/include/fdbclient/EventTypes.actor.h b/fdbclient/include/fdbclient/EventTypes.actor.h index 39a75e09dc..dc946ce42e 100644 --- a/fdbclient/include/fdbclient/EventTypes.actor.h +++ b/fdbclient/include/fdbclient/EventTypes.actor.h @@ -26,7 +26,7 @@ #define FDBCLIENT_EVENTTYPES_ACTOR_G_H #include "fdbclient/EventTypes.actor.g.h" #elif !defined(FDBCLIENT_EVENTTYPES_ACTOR_H) -#define FDBCLIENT_EVENTTYPESS_ACTOR_H +#define FDBCLIENT_EVENTTYPES_ACTOR_H #include "flow/flow.h" #include "flow/TDMetric.actor.h" diff --git a/fdbclient/include/fdbclient/FDBTypes.h b/fdbclient/include/fdbclient/FDBTypes.h index 7c4f82b4ce..89ddacad2d 100644 --- a/fdbclient/include/fdbclient/FDBTypes.h +++ b/fdbclient/include/fdbclient/FDBTypes.h @@ -41,6 +41,7 @@ typedef StringRef KeyRef; typedef StringRef ValueRef; typedef int64_t Generation; typedef UID SpanID; +typedef uint64_t CoordinatorsHash; enum { tagLocalitySpecial = -1, // tag with this locality means it is invalidTag (id=0), txsTag (id=1), or cacheTag (id=2) @@ -519,7 +520,7 @@ using MappedRangeResult = Standalone; enum { invalidVersion = -1, latestVersion = -2, MAX_VERSION = std::numeric_limits::max() }; inline Key keyAfter(const KeyRef& key) { - if (key == LiteralStringRef("\xff\xff")) + if (key == "\xff\xff"_sr) return key; Standalone r; @@ -532,7 +533,7 @@ inline Key keyAfter(const KeyRef& key) { return r; } inline KeyRef keyAfter(const KeyRef& key, Arena& arena) { - if (key == LiteralStringRef("\xff\xff")) + if (key == "\xff\xff"_sr) return key; uint8_t* t = new (arena) uint8_t[key.size() + 1]; memcpy(t, key.begin(), key.size()); @@ -947,17 +948,17 @@ struct TLogVersion { } static ErrorOr FromStringRef(StringRef s) { - if (s == LiteralStringRef("2")) + if (s == "2"_sr) return V2; - if (s == LiteralStringRef("3")) + if (s == "3"_sr) return V3; - if (s == LiteralStringRef("4")) + if (s == "4"_sr) return V4; - if (s == LiteralStringRef("5")) + if (s == "5"_sr) return V5; - if (s == LiteralStringRef("6")) + if (s == "6"_sr) return V6; - if (s == LiteralStringRef("7")) + if (s == "7"_sr) return V7; return default_error_or(); } @@ -1007,9 +1008,9 @@ struct TLogSpillType { } static ErrorOr FromStringRef(StringRef s) { - if (s == LiteralStringRef("1")) + if (s == "1"_sr) return VALUE; - if (s == LiteralStringRef("2")) + if (s == "2"_sr) return REFERENCE; return default_error_or(); } diff --git a/fdbclient/include/fdbclient/GenericManagementAPI.actor.h b/fdbclient/include/fdbclient/GenericManagementAPI.actor.h index 5af792d231..21ebbd3a3c 100644 --- a/fdbclient/include/fdbclient/GenericManagementAPI.actor.h +++ b/fdbclient/include/fdbclient/GenericManagementAPI.actor.h @@ -249,7 +249,7 @@ Future> getWorkers(Reference tr, // Accepts a full configuration in key/value format (from buildConfiguration) ACTOR template Future changeConfig(Reference db, std::map m, bool force) { - state StringRef initIdKey = LiteralStringRef("\xff/init_id"); + state StringRef initIdKey = "\xff/init_id"_sr; state Reference tr = db->createTransaction(); if (!m.size()) { @@ -505,8 +505,8 @@ Future changeConfig(Reference db, std::mapatomicOp(databaseLockedKey, BinaryWriter::toValue(locked.get(), Unversioned()) - .withPrefix(LiteralStringRef("0123456789")) - .withSuffix(LiteralStringRef("\x00\x00\x00\x00")), + .withPrefix("0123456789"_sr) + .withSuffix("\x00\x00\x00\x00"_sr), MutationRef::SetVersionstampedValue); } @@ -650,7 +650,7 @@ Future changeConfig(Reference db, std::vector const& modes, Optional const& conf, bool force) { - if (modes.size() && modes[0] == LiteralStringRef("auto") && conf.present()) { + if (modes.size() && modes[0] == "auto"_sr && conf.present()) { return autoConfig(db, conf.get()); } diff --git a/fdbclient/include/fdbclient/GetEncryptCipherKeys.actor.h b/fdbclient/include/fdbclient/GetEncryptCipherKeys.actor.h index b809870735..e82cd363c3 100644 --- a/fdbclient/include/fdbclient/GetEncryptCipherKeys.actor.h +++ b/fdbclient/include/fdbclient/GetEncryptCipherKeys.actor.h @@ -24,8 +24,10 @@ #elif !defined(FDBCLIENT_GETCIPHERKEYS_ACTOR_H) #define FDBCLIENT_GETCIPHERKEYS_ACTOR_H +#include "fdbclient/BlobCipher.h" #include "fdbclient/EncryptKeyProxyInterface.h" -#include "flow/BlobCipher.h" +#include "fdbrpc/Stats.h" +#include "flow/Knobs.h" #include "flow/IRandom.h" #include @@ -88,7 +90,8 @@ Future getUncachedLatestEncryptCipherKeys(Refer ACTOR template Future>> getLatestEncryptCipherKeys( Reference const> db, - std::unordered_map domains) { + std::unordered_map domains, + BlobCipherMetrics::UsageType usageType) { state Reference cipherKeyCache = BlobCipherKeyCache::getInstance(); state std::unordered_map> cipherKeys; state EKPGetLatestBaseCipherKeysRequest request; @@ -112,6 +115,7 @@ Future>> getL return cipherKeys; } // Fetch any uncached cipher keys. + state double startTime = now(); loop choose { when(EKPGetLatestBaseCipherKeysReply reply = wait(getUncachedLatestEncryptCipherKeys(db, request))) { // Insert base cipher keys into cache and construct result. @@ -140,6 +144,9 @@ Future>> getL // In case encryptKeyProxy has changed, retry the request. when(wait(onEncryptKeyProxyChange(db))) {} } + double elapsed = now() - startTime; + BlobCipherMetrics::getInstance()->getLatestCipherKeysLatency.addMeasurement(elapsed); + BlobCipherMetrics::counters(usageType).getLatestCipherKeysLatency.addMeasurement(elapsed); return cipherKeys; } @@ -178,7 +185,8 @@ using BaseCipherIndex = std::pair ACTOR template Future>> getEncryptCipherKeys( Reference const> db, - std::unordered_set cipherDetails) { + std::unordered_set cipherDetails, + BlobCipherMetrics::UsageType usageType) { state Reference cipherKeyCache = BlobCipherKeyCache::getInstance(); state std::unordered_map> cipherKeys; state std::unordered_set> uncachedBaseCipherIds; @@ -207,6 +215,7 @@ Future>> getEncry id.first /*domainId*/, id.second /*baseCipherId*/, StringRef() /*domainName*/, request.arena); } // Fetch any uncached cipher keys. + state double startTime = now(); loop choose { when(EKPGetBaseCipherKeysByIdsReply reply = wait(getUncachedEncryptCipherKeys(db, request))) { std::unordered_map> baseCipherKeys; @@ -242,6 +251,9 @@ Future>> getEncry // In case encryptKeyProxy has changed, retry the request. when(wait(onEncryptKeyProxyChange(db))) {} } + double elapsed = now() - startTime; + BlobCipherMetrics::getInstance()->getCipherKeysLatency.addMeasurement(elapsed); + BlobCipherMetrics::counters(usageType).getCipherKeysLatency.addMeasurement(elapsed); return cipherKeys; } @@ -253,12 +265,13 @@ struct TextAndHeaderCipherKeys { ACTOR template Future getLatestEncryptCipherKeysForDomain(Reference const> db, EncryptCipherDomainId domainId, - EncryptCipherDomainNameRef domainName) { - std::unordered_map domains; + EncryptCipherDomainName domainName, + BlobCipherMetrics::UsageType usageType) { + std::unordered_map domains; domains[domainId] = domainName; - domains[ENCRYPT_HEADER_DOMAIN_ID] = FDB_DEFAULT_ENCRYPT_DOMAIN_NAME; + domains[ENCRYPT_HEADER_DOMAIN_ID] = FDB_ENCRYPT_HEADER_DOMAIN_NAME; std::unordered_map> cipherKeys = - wait(getLatestEncryptCipherKeys(db, domains)); + wait(getLatestEncryptCipherKeys(db, domains, usageType)); ASSERT(cipherKeys.count(domainId) > 0); ASSERT(cipherKeys.count(ENCRYPT_HEADER_DOMAIN_ID) > 0); TextAndHeaderCipherKeys result{ cipherKeys.at(domainId), cipherKeys.at(ENCRYPT_HEADER_DOMAIN_ID) }; @@ -268,15 +281,19 @@ Future getLatestEncryptCipherKeysForDomain(Reference -Future getLatestSystemEncryptCipherKeys(const Reference const>& db) { - return getLatestEncryptCipherKeysForDomain(db, SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME); +Future getLatestSystemEncryptCipherKeys(const Reference const>& db, + BlobCipherMetrics::UsageType usageType) { + return getLatestEncryptCipherKeysForDomain( + db, SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME, usageType); } ACTOR template -Future getEncryptCipherKeys(Reference const> db, BlobCipherEncryptHeader header) { +Future getEncryptCipherKeys(Reference const> db, + BlobCipherEncryptHeader header, + BlobCipherMetrics::UsageType usageType) { std::unordered_set cipherDetails{ header.cipherTextDetails, header.cipherHeaderDetails }; std::unordered_map> cipherKeys = - wait(getEncryptCipherKeys(db, cipherDetails)); + wait(getEncryptCipherKeys(db, cipherDetails, usageType)); ASSERT(cipherKeys.count(header.cipherTextDetails) > 0); ASSERT(cipherKeys.count(header.cipherHeaderDetails) > 0); TextAndHeaderCipherKeys result{ cipherKeys.at(header.cipherTextDetails), diff --git a/fdbclient/include/fdbclient/ManagementAPI.actor.h b/fdbclient/include/fdbclient/ManagementAPI.actor.h index 1268185b1b..c0725324c8 100644 --- a/fdbclient/include/fdbclient/ManagementAPI.actor.h +++ b/fdbclient/include/fdbclient/ManagementAPI.actor.h @@ -57,7 +57,8 @@ struct IQuorumChange : ReferenceCounted { // Change to use the given set of coordination servers ACTOR Future> changeQuorumChecker(Transaction* tr, ClusterConnectionString* conn, - std::string newName); + std::string newName, + bool disableConfigDB); ACTOR Future changeQuorum(Database cx, Reference change); Reference autoQuorumChange(int desired = -1); Reference nameQuorumChange(std::string const& name, Reference const& other); diff --git a/fdbclient/include/fdbclient/Metacluster.h b/fdbclient/include/fdbclient/Metacluster.h index 99abed564b..7f07286ae4 100644 --- a/fdbclient/include/fdbclient/Metacluster.h +++ b/fdbclient/include/fdbclient/Metacluster.h @@ -20,7 +20,7 @@ #ifndef FDBCLIENT_METACLUSTER_H #define FDBCLIENT_METACLUSTER_H -#include "CoordinationInterface.h" +#include "fdbclient/CoordinationInterface.h" #include "json_spirit/json_spirit_value.h" #pragma once @@ -180,4 +180,4 @@ struct MetaclusterMetadata { static KeyBackedObjectProperty& metaclusterRegistration(); }; -#endif \ No newline at end of file +#endif diff --git a/fdbclient/include/fdbclient/MultiVersionTransaction.h b/fdbclient/include/fdbclient/MultiVersionTransaction.h index 067518b6c4..0f4e2860f8 100644 --- a/fdbclient/include/fdbclient/MultiVersionTransaction.h +++ b/fdbclient/include/fdbclient/MultiVersionTransaction.h @@ -368,7 +368,7 @@ struct FdbCApi : public ThreadSafeReferenceCounted { fdb_error_t (*futureGetDatabase)(FDBFuture* f, FDBDatabase** outDb); fdb_error_t (*futureGetInt64)(FDBFuture* f, int64_t* outValue); fdb_error_t (*futureGetUInt64)(FDBFuture* f, uint64_t* outValue); - fdb_error_t (*futureGetBool)(FDBFuture* f, bool* outValue); + fdb_error_t (*futureGetBool)(FDBFuture* f, fdb_bool_t* outValue); fdb_error_t (*futureGetError)(FDBFuture* f); fdb_error_t (*futureGetKey)(FDBFuture* f, uint8_t const** outKey, int* outKeyLength); fdb_error_t (*futureGetValue)(FDBFuture* f, fdb_bool_t* outPresent, uint8_t const** outValue, int* outValueLength); diff --git a/fdbclient/include/fdbclient/ReadYourWrites.h b/fdbclient/include/fdbclient/ReadYourWrites.h index 969b17c5af..0d47457de8 100644 --- a/fdbclient/include/fdbclient/ReadYourWrites.h +++ b/fdbclient/include/fdbclient/ReadYourWrites.h @@ -20,7 +20,7 @@ #ifndef FDBCLIENT_READYOURWRITES_H #define FDBCLIENT_READYOURWRITES_H -#include "Status.h" +#include "fdbclient/Status.h" #pragma once #include "fdbclient/NativeAPI.actor.h" diff --git a/fdbclient/include/fdbclient/ServerKnobs.h b/fdbclient/include/fdbclient/ServerKnobs.h index d8cb369ab8..a3d59000ff 100644 --- a/fdbclient/include/fdbclient/ServerKnobs.h +++ b/fdbclient/include/fdbclient/ServerKnobs.h @@ -457,6 +457,7 @@ public: double ATTEMPT_RECRUITMENT_DELAY; double WAIT_FOR_DISTRIBUTOR_JOIN_DELAY; double WAIT_FOR_RATEKEEPER_JOIN_DELAY; + double WAIT_FOR_CONSISTENCYSCAN_JOIN_DELAY; double WAIT_FOR_BLOB_MANAGER_JOIN_DELAY; double WAIT_FOR_ENCRYPT_KEY_PROXY_JOIN_DELAY; double WORKER_FAILURE_TIME; @@ -470,6 +471,7 @@ public: double CHECK_REMOTE_HEALTH_INTERVAL; // Remote DC health refresh interval. double FORCE_RECOVERY_CHECK_DELAY; double RATEKEEPER_FAILURE_TIME; + double CONSISTENCYSCAN_FAILURE_TIME; double BLOB_MANAGER_FAILURE_TIME; double REPLACE_INTERFACE_DELAY; double REPLACE_INTERFACE_CHECK_DELAY; @@ -693,6 +695,7 @@ public: int FETCH_KEYS_PARALLELISM_FULL; int FETCH_KEYS_LOWER_PRIORITY; int SERVE_FETCH_CHECKPOINT_PARALLELISM; + int CHANGE_FEED_DISK_READS_PARALLELISM; int BUGGIFY_BLOCK_BYTES; int64_t STORAGE_RECOVERY_VERSION_LAG_LIMIT; double STORAGE_DURABILITY_LAG_REJECT_THRESHOLD; @@ -731,6 +734,7 @@ public: int CHECKPOINT_TRANSFER_BLOCK_BYTES; int QUICK_GET_KEY_VALUES_LIMIT; int QUICK_GET_KEY_VALUES_LIMIT_BYTES; + int STORAGE_FEED_QUERY_HARD_LIMIT; // Wait Failure int MAX_OUTSTANDING_WAIT_FAILURE_REQUESTS; @@ -764,6 +768,12 @@ public: bool WORKER_HEALTH_REPORT_RECENT_DESTROYED_PEER; // When enabled, the worker's health monitor also report any recent // destroyed peers who are part of the transaction system to // cluster controller. + bool STORAGE_SERVER_REBOOT_ON_IO_TIMEOUT; // When enabled, storage server's worker will crash on io_timeout error; + // this allows fdbmonitor to restart the worker and recreate the same SS. + // When SS can be temporarily throttled by infrastructure, e.g, k8s, + // Enabling this can reduce toil of manually restarting the SS. + // Enable with caution: If io_timeout is caused by disk failure, we won't + // want to restart the SS, which increases risk of data corruption. // Test harness double WORKER_POLL_DELAY; @@ -777,6 +787,7 @@ public: // Dynamic Knobs (implementation) double COMPACTION_INTERVAL; + double BROADCASTER_SELF_UPDATE_DELAY; double GET_COMMITTED_VERSION_TIMEOUT; double GET_SNAPSHOT_AND_CHANGES_TIMEOUT; double FETCH_CHANGES_TIMEOUT; @@ -934,6 +945,8 @@ public: int BLOB_MANAGER_CONCURRENT_MERGE_CHECKS; double BGCC_TIMEOUT; double BGCC_MIN_INTERVAL; + bool BLOB_MANIFEST_BACKUP; + bool BLOB_FULL_RESTORE_MODE; // Blob metadata int64_t BLOB_METADATA_CACHE_TTL; diff --git a/fdbclient/include/fdbclient/SpecialKeySpace.actor.h b/fdbclient/include/fdbclient/SpecialKeySpace.actor.h index 75cae1fc47..d2ce7f5cf9 100644 --- a/fdbclient/include/fdbclient/SpecialKeySpace.actor.h +++ b/fdbclient/include/fdbclient/SpecialKeySpace.actor.h @@ -60,6 +60,8 @@ public: // TODO : give this function a more descriptive name virtual bool isAsync() const { return false; } + virtual bool supportsTenants() const { return false; } + virtual ~SpecialKeyRangeReadImpl() {} protected: @@ -301,6 +303,7 @@ public: Future getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr, GetRangeLimits limitsHint) const override; + bool supportsTenants() const override { return true; }; }; class ReadConflictRangeImpl : public SpecialKeyRangeReadImpl { @@ -309,6 +312,7 @@ public: Future getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr, GetRangeLimits limitsHint) const override; + bool supportsTenants() const override { return true; }; }; class WriteConflictRangeImpl : public SpecialKeyRangeReadImpl { @@ -317,6 +321,7 @@ public: Future getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr, GetRangeLimits limitsHint) const override; + bool supportsTenants() const override { return true; }; }; class DDStatsRangeImpl : public SpecialKeyRangeAsyncImpl { diff --git a/fdbclient/include/fdbclient/SystemData.h b/fdbclient/include/fdbclient/SystemData.h index a17c5e5ac7..068d1d9d37 100644 --- a/fdbclient/include/fdbclient/SystemData.h +++ b/fdbclient/include/fdbclient/SystemData.h @@ -28,7 +28,7 @@ #include "fdbclient/BlobGranuleCommon.h" #include "fdbclient/BlobWorkerInterface.h" // TODO move the functions that depend on this out of here and into BlobWorkerInterface.h to remove this depdendency #include "fdbclient/StorageServerInterface.h" -#include "Tenant.h" +#include "fdbclient/Tenant.h" // Don't warn on constants being defined in this file. #pragma clang diagnostic push @@ -163,6 +163,9 @@ extern const KeyRef cacheChangePrefix; const Key cacheChangeKeyFor(uint16_t idx); uint16_t cacheChangeKeyDecodeIndex(const KeyRef& key); +// For persisting the consistency scan configuration and metrics +extern const KeyRef consistencyScanInfoKey; + // "\xff/tss/[[serverId]]" := "[[tssId]]" extern const KeyRangeRef tssMappingKeys; @@ -377,6 +380,12 @@ std::vector> decodeBackupStartedValue(const ValueRef& va // 1 = Send a signal to pause/already paused. extern const KeyRef backupPausedKey; +// "\xff/previousCoordinators" = "[[ClusterConnectionString]]" +// Set to the encoded structure of the cluster's previous set of coordinators. +// Changed when performing quorumChange. +// See "CoordinationInterface.h" struct ClusterConnectionString for more details +extern const KeyRef previousCoordinatorsKey; + // "\xff/coordinators" = "[[ClusterConnectionString]]" // Set to the encoded structure of the cluster's current set of coordinators. // Changed when performing quorumChange. diff --git a/fdbclient/include/fdbclient/TagThrottle.actor.h b/fdbclient/include/fdbclient/TagThrottle.actor.h index 020fcea568..d663c02f02 100644 --- a/fdbclient/include/fdbclient/TagThrottle.actor.h +++ b/fdbclient/include/fdbclient/TagThrottle.actor.h @@ -264,9 +264,9 @@ Future getValidAutoEnabled(Reference tr) { tr->reset(); wait(delay(CLIENT_KNOBS->DEFAULT_BACKOFF)); continue; - } else if (value.get() == LiteralStringRef("1")) { + } else if (value.get() == "1"_sr) { result = true; - } else if (value.get() == LiteralStringRef("0")) { + } else if (value.get() == "0"_sr) { result = false; } else { TraceEvent(SevWarnAlways, "InvalidAutoTagThrottlingValue").detail("Value", value.get()); @@ -331,8 +331,7 @@ getThrottledTags(Reference db, int limit, ContainsRecommended containsRecomm template void signalThrottleChange(Reference tr) { - tr->atomicOp( - tagThrottleSignalKey, LiteralStringRef("XXXXXXXXXX\x00\x00\x00\x00"), MutationRef::SetVersionstampedValue); + tr->atomicOp(tagThrottleSignalKey, "XXXXXXXXXX\x00\x00\x00\x00"_sr, MutationRef::SetVersionstampedValue); } ACTOR template @@ -583,8 +582,7 @@ Future enableAuto(Reference db, bool enabled) { state typename DB::TransactionT::template FutureT> valueF = tr->get(tagThrottleAutoEnabledKey); Optional value = wait(safeThreadFutureToFuture(valueF)); - if (!value.present() || (enabled && value.get() != LiteralStringRef("1")) || - (!enabled && value.get() != LiteralStringRef("0"))) { + if (!value.present() || (enabled && value.get() != "1"_sr) || (!enabled && value.get() != "0"_sr)) { tr->set(tagThrottleAutoEnabledKey, LiteralStringRef(enabled ? "1" : "0")); signalThrottleChange(tr); diff --git a/fdbclient/include/fdbclient/Tenant.h b/fdbclient/include/fdbclient/Tenant.h index d75629da10..b6341550aa 100644 --- a/fdbclient/include/fdbclient/Tenant.h +++ b/fdbclient/include/fdbclient/Tenant.h @@ -27,6 +27,7 @@ #include "fdbclient/VersionedMap.h" #include "fdbclient/KeyBackedTypes.h" #include "fdbrpc/TenantInfo.h" +#include "flow/BooleanParam.h" #include "flow/flat_buffers.h" typedef StringRef TenantNameRef; @@ -64,11 +65,13 @@ enum class TenantLockState { UNLOCKED, READ_ONLY, LOCKED }; constexpr int TENANT_PREFIX_SIZE = sizeof(int64_t); +FDB_DECLARE_BOOLEAN_PARAM(EnforceValidTenantId); + struct TenantMapEntry { constexpr static FileIdentifier file_identifier = 12247338; static Key idToPrefix(int64_t id); - static int64_t prefixToId(KeyRef prefix); + static int64_t prefixToId(KeyRef prefix, EnforceValidTenantId enforceTenantId = EnforceValidTenantId::True); static std::string tenantStateToString(TenantState tenantState); static TenantState stringToTenantState(std::string stateStr); diff --git a/fdbclient/include/fdbclient/Tuple.h b/fdbclient/include/fdbclient/Tuple.h index 9d5594ccba..b9de705d91 100644 --- a/fdbclient/include/fdbclient/Tuple.h +++ b/fdbclient/include/fdbclient/Tuple.h @@ -71,7 +71,9 @@ struct Tuple { size_t size() const { return offsets.size(); } void reserve(size_t cap) { offsets.reserve(cap); } void clear() { - data.clear(); + // Make a new Standalone to use different memory so that + // previously returned objects from pack() are valid. + data = Standalone>(); offsets.clear(); } // Return a Tuple encoded raw string. diff --git a/fdbrpc/AsyncFileNonDurable.actor.cpp b/fdbrpc/AsyncFileNonDurable.actor.cpp index f7b41c5ae2..559c7cfce3 100644 --- a/fdbrpc/AsyncFileNonDurable.actor.cpp +++ b/fdbrpc/AsyncFileNonDurable.actor.cpp @@ -24,7 +24,7 @@ std::map> AsyncFileNonDurable::filesBeingDeleted; ACTOR Future sendOnProcess(ISimulator::ProcessInfo* process, Promise promise, TaskPriority taskID) { - wait(g_simulator.onProcess(process, taskID)); + wait(g_simulator->onProcess(process, taskID)); promise.send(Void()); return Void(); } @@ -33,7 +33,7 @@ ACTOR Future sendErrorOnProcess(ISimulator::ProcessInfo* process, Promise promise, Error e, TaskPriority taskID) { - wait(g_simulator.onProcess(process, taskID)); + wait(g_simulator->onProcess(process, taskID)); promise.sendError(e); return Void(); } diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index b88f8b60e7..e53f7dd5e5 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -48,7 +48,7 @@ TEST_CASE("/flow/actorcompiler/lineNumbers") { } break; } - ASSERT(LiteralStringRef(__FILE__).endsWith(LiteralStringRef("FlowTests.actor.cpp"))); + ASSERT(LiteralStringRef(__FILE__).endsWith("FlowTests.actor.cpp"_sr)); return Void(); } diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 27a184b76c..8bb476efc9 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -299,12 +299,12 @@ public: ~TransportData(); void initMetrics() { - bytesSent.init(LiteralStringRef("Net2.BytesSent")); - countPacketsReceived.init(LiteralStringRef("Net2.CountPacketsReceived")); - countPacketsGenerated.init(LiteralStringRef("Net2.CountPacketsGenerated")); - countConnEstablished.init(LiteralStringRef("Net2.CountConnEstablished")); - countConnClosedWithError.init(LiteralStringRef("Net2.CountConnClosedWithError")); - countConnClosedWithoutError.init(LiteralStringRef("Net2.CountConnClosedWithoutError")); + bytesSent.init("Net2.BytesSent"_sr); + countPacketsReceived.init("Net2.CountPacketsReceived"_sr); + countPacketsGenerated.init("Net2.CountPacketsGenerated"_sr); + countConnEstablished.init("Net2.CountConnEstablished"_sr); + countConnClosedWithError.init("Net2.CountConnClosedWithError"_sr); + countConnClosedWithoutError.init("Net2.CountConnClosedWithoutError"_sr); } Reference getPeer(NetworkAddress const& address); @@ -1136,9 +1136,10 @@ static void scanPackets(TransportData* transport, if (checksumEnabled) { bool isBuggifyEnabled = false; if (g_network->isSimulated() && !isStableConnection && - g_network->now() - g_simulator.lastConnectionFailure > g_simulator.connectionFailuresDisableDuration && + g_network->now() - g_simulator->lastConnectionFailure > + g_simulator->connectionFailuresDisableDuration && BUGGIFY_WITH_PROB(0.0001)) { - g_simulator.lastConnectionFailure = g_network->now(); + g_simulator->lastConnectionFailure = g_network->now(); isBuggifyEnabled = true; TraceEvent(SevInfo, "BitsFlip").log(); int flipBits = 32 - (int)floor(log2(deterministicRandom()->randomUInt32())); @@ -1588,7 +1589,7 @@ FlowTransport::FlowTransport(uint64_t transportId, int maxWellKnownEndpoints, IP : self(new TransportData(transportId, maxWellKnownEndpoints, allowList)) { self->multiVersionCleanup = multiVersionCleanupWorker(self); if (g_network->isSimulated()) { - for (auto const& p : g_simulator.authKeys) { + for (auto const& p : g_simulator->authKeys) { self->publicKeys.emplace(p.first, p.second.toPublic()); } } diff --git a/fdbrpc/HTTP.actor.cpp b/fdbrpc/HTTP.actor.cpp index 5fde809e2e..cf0bf6e157 100644 --- a/fdbrpc/HTTP.actor.cpp +++ b/fdbrpc/HTTP.actor.cpp @@ -100,14 +100,14 @@ PacketBuffer* writeRequestHeader(std::string const& verb, writer.serializeBytes(verb); writer.serializeBytes(" ", 1); writer.serializeBytes(resource); - writer.serializeBytes(LiteralStringRef(" HTTP/1.1\r\n")); + writer.serializeBytes(" HTTP/1.1\r\n"_sr); for (auto h : headers) { writer.serializeBytes(h.first); - writer.serializeBytes(LiteralStringRef(": ")); + writer.serializeBytes(": "_sr); writer.serializeBytes(h.second); - writer.serializeBytes(LiteralStringRef("\r\n")); + writer.serializeBytes("\r\n"_sr); } - writer.serializeBytes(LiteralStringRef("\r\n")); + writer.serializeBytes("\r\n"_sr); return writer.finish(); } diff --git a/fdbrpc/Locality.cpp b/fdbrpc/Locality.cpp index bd4c3a9461..724b539ffb 100644 --- a/fdbrpc/Locality.cpp +++ b/fdbrpc/Locality.cpp @@ -21,13 +21,13 @@ #include "fdbrpc/Locality.h" const UID LocalityData::UNSET_ID = UID(0x0ccb4e0feddb5583, 0x010f6b77d9d10ece); -const StringRef LocalityData::keyProcessId = LiteralStringRef("processid"); -const StringRef LocalityData::keyZoneId = LiteralStringRef("zoneid"); -const StringRef LocalityData::keyDcId = LiteralStringRef("dcid"); -const StringRef LocalityData::keyMachineId = LiteralStringRef("machineid"); -const StringRef LocalityData::keyDataHallId = LiteralStringRef("data_hall"); -const StringRef LocalityData::ExcludeLocalityKeyMachineIdPrefix = LiteralStringRef("locality_machineid:"); -const StringRef LocalityData::ExcludeLocalityPrefix = LiteralStringRef("locality_"); +const StringRef LocalityData::keyProcessId = "processid"_sr; +const StringRef LocalityData::keyZoneId = "zoneid"_sr; +const StringRef LocalityData::keyDcId = "dcid"_sr; +const StringRef LocalityData::keyMachineId = "machineid"_sr; +const StringRef LocalityData::keyDataHallId = "data_hall"_sr; +const StringRef LocalityData::ExcludeLocalityKeyMachineIdPrefix = "locality_machineid:"_sr; +const StringRef LocalityData::ExcludeLocalityPrefix = "locality_"_sr; ProcessClass::Fitness ProcessClass::machineClassFitness(ClusterRole role) const { switch (role) { @@ -240,6 +240,24 @@ ProcessClass::Fitness ProcessClass::machineClassFitness(ClusterRole role) const default: return ProcessClass::WorstFit; } + case ProcessClass::ConsistencyScan: + switch (_class) { + case ProcessClass::ConsistencyScanClass: + return ProcessClass::BestFit; + case ProcessClass::StatelessClass: + return ProcessClass::GoodFit; + case ProcessClass::UnsetClass: + return ProcessClass::UnsetFit; + case ProcessClass::MasterClass: + return ProcessClass::OkayFit; + case ProcessClass::CoordinatorClass: + case ProcessClass::TesterClass: + case ProcessClass::StorageCacheClass: + case ProcessClass::BlobWorkerClass: + return ProcessClass::NeverAssign; + default: + return ProcessClass::WorstFit; + } case ProcessClass::BlobManager: switch (_class) { case ProcessClass::BlobManagerClass: diff --git a/fdbrpc/ReplicationUtils.cpp b/fdbrpc/ReplicationUtils.cpp index cd84eea44f..bd55708614 100644 --- a/fdbrpc/ReplicationUtils.cpp +++ b/fdbrpc/ReplicationUtils.cpp @@ -493,10 +493,10 @@ Reference createTestLocalityMap(std::vector& indexes, serverValue = dcLoop + szLoop * 10 + rackLoop * 100 + slotLoop * 1000; slotText = format(".%d", slotLoop); LocalityData data; - data.set(LiteralStringRef("dc"), StringRef(dcText)); - data.set(LiteralStringRef("sz"), StringRef(dcText + szText)); - data.set(LiteralStringRef("rack"), StringRef(dcText + szText + rackText)); - data.set(LiteralStringRef("zoneid"), StringRef(dcText + szText + rackText + slotText)); + data.set("dc"_sr, StringRef(dcText)); + data.set("sz"_sr, StringRef(dcText + szText)); + data.set("rack"_sr, StringRef(dcText + szText + rackText)); + data.set("zoneid"_sr, StringRef(dcText + szText + rackText + slotText)); for (int independentLoop = 0; independentLoop < independentItems; independentLoop++) { independentName = format("indiv%02d", independentLoop + 1); for (int totalLoop = 0; totalLoop < independentTotal; totalLoop++) { @@ -520,10 +520,10 @@ Reference createTestLocalityMap(std::vector& indexes, serverValue = (dcLoop + 2) + szLoop * 10 + rackLoop * 100 + slotLoop * 1000; slotText = format(".%d", slotLoop); LocalityData data; - data.set(LiteralStringRef("dc"), StringRef(dcText)); - data.set(LiteralStringRef("az"), StringRef(dcText + szText)); - data.set(LiteralStringRef("rack"), StringRef(dcText + szText + rackText)); - data.set(LiteralStringRef("zoneid"), StringRef(dcText + szText + rackText + slotText)); + data.set("dc"_sr, StringRef(dcText)); + data.set("az"_sr, StringRef(dcText + szText)); + data.set("rack"_sr, StringRef(dcText + szText + rackText)); + data.set("zoneid"_sr, StringRef(dcText + szText + rackText + slotText)); for (int independentLoop = 0; independentLoop < independentItems; independentLoop++) { independentName = format("indiv%02d", independentLoop); for (int totalLoop = 0; totalLoop < independentTotal; totalLoop++) { diff --git a/fdbrpc/TokenCache.actor.cpp b/fdbrpc/TokenCache.actor.cpp index 2ab2f8d3ef..afa1fecf10 100644 --- a/fdbrpc/TokenCache.actor.cpp +++ b/fdbrpc/TokenCache.actor.cpp @@ -287,11 +287,18 @@ TEST_CASE("/fdbrpc/authz/TokenCache/BadTokens") { [](Arena&, IRandom&, authz::jwt::TokenRef& token) { token.issuedAtUnixTime.reset(); }, "NoIssuedAt", }, - { [](Arena& arena, IRandom&, authz::jwt::TokenRef& token) { token.tenants.reset(); }, "NoTenants", }, + { + [](Arena& arena, IRandom&, authz::jwt::TokenRef& token) { + StringRef* newTenants = new (arena) StringRef[1]; + *newTenants = token.tenants.get()[0].substr(1); + token.tenants = VectorRef(newTenants, 1); + }, + "UnmatchedTenant", + }, }; auto const pubKeyName = "somePublicKey"_sr; auto privateKey = mkcert::makeEcP256(); @@ -301,20 +308,31 @@ TEST_CASE("/fdbrpc/authz/TokenCache/BadTokens") { auto& rng = *deterministicRandom(); auto validTokenSpec = authz::jwt::makeRandomTokenSpec(arena, rng, authz::Algorithm::ES256); validTokenSpec.keyId = pubKeyName; - for (auto i = 0; i < numBadMutations; i++) { + for (auto i = 0; i <= numBadMutations; i++) { FlowTransport::transport().addPublicKey(pubKeyName, privateKey.toPublic()); auto publicKeyClearGuard = ScopeExit([pubKeyName]() { FlowTransport::transport().removePublicKey(pubKeyName); }); - auto [mutationFn, mutationDesc] = badMutations[i]; + auto signedToken = StringRef(); auto tmpArena = Arena(); - auto mutatedTokenSpec = validTokenSpec; - mutationFn(tmpArena, rng, mutatedTokenSpec); - auto signedToken = authz::jwt::signToken(tmpArena, mutatedTokenSpec, privateKey); - if (TokenCache::instance().validate(validTokenSpec.tenants.get()[0], signedToken)) { - fmt::print("Unexpected successful validation at mutation {}, token spec: {}\n", - mutationDesc, - mutatedTokenSpec.toStringRef(tmpArena).toStringView()); - ASSERT(false); + if (i < numBadMutations) { + auto [mutationFn, mutationDesc] = badMutations[i]; + auto mutatedTokenSpec = validTokenSpec; + mutationFn(tmpArena, rng, mutatedTokenSpec); + signedToken = authz::jwt::signToken(tmpArena, mutatedTokenSpec, privateKey); + if (TokenCache::instance().validate(validTokenSpec.tenants.get()[0], signedToken)) { + fmt::print("Unexpected successful validation at mutation {}, token spec: {}\n", + mutationDesc, + mutatedTokenSpec.toStringRef(tmpArena).toStringView()); + ASSERT(false); + } + } else { + // squeeze in a bad signature case that does not fit into mutation interface + signedToken = authz::jwt::signToken(tmpArena, validTokenSpec, privateKey); + signedToken = signedToken.substr(0, signedToken.size() - 1); + if (TokenCache::instance().validate(validTokenSpec.tenants.get()[0], signedToken)) { + fmt::print("Unexpected successful validation with a token with truncated signature part\n"); + ASSERT(false); + } } } } diff --git a/fdbrpc/TokenSign.cpp b/fdbrpc/TokenSign.cpp index 30ba6f6726..fd5826f872 100644 --- a/fdbrpc/TokenSign.cpp +++ b/fdbrpc/TokenSign.cpp @@ -53,12 +53,15 @@ namespace { // test-only constants for generating random tenant/key names +constexpr int MinIssuerNameLen = 16; constexpr int MaxIssuerNameLenPlus1 = 25; +constexpr int MinTenantNameLen = 8; constexpr int MaxTenantNameLenPlus1 = 17; +constexpr int MinKeyNameLen = 10; constexpr int MaxKeyNameLenPlus1 = 21; -StringRef genRandomAlphanumStringRef(Arena& arena, IRandom& rng, int maxLenPlusOne) { - const auto len = rng.randomInt(1, maxLenPlusOne); +StringRef genRandomAlphanumStringRef(Arena& arena, IRandom& rng, int minLen, int maxLenPlusOne) { + const auto len = rng.randomInt(minLen, maxLenPlusOne); auto strRaw = new (arena) uint8_t[len]; for (auto i = 0; i < len; i++) strRaw[i] = (uint8_t)rng.randomAlphaNumeric(); @@ -193,7 +196,7 @@ TokenRef makeRandomTokenSpec(Arena& arena, IRandom& rng) { token.expiresAt = timer_monotonic() * (0.5 + rng.random01()); const auto numTenants = rng.randomInt(1, 3); for (auto i = 0; i < numTenants; i++) { - token.tenants.push_back(arena, genRandomAlphanumStringRef(arena, rng, MaxTenantNameLenPlus1)); + token.tenants.push_back(arena, genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1)); } return token; } @@ -506,14 +509,14 @@ bool verifyToken(StringRef signedToken, PublicKey publicKey) { TokenRef makeRandomTokenSpec(Arena& arena, IRandom& rng, Algorithm alg) { auto ret = TokenRef{}; ret.algorithm = alg; - ret.keyId = genRandomAlphanumStringRef(arena, rng, MaxKeyNameLenPlus1); - ret.issuer = genRandomAlphanumStringRef(arena, rng, MaxIssuerNameLenPlus1); - ret.subject = genRandomAlphanumStringRef(arena, rng, MaxIssuerNameLenPlus1); - ret.tokenId = genRandomAlphanumStringRef(arena, rng, 31); + ret.keyId = genRandomAlphanumStringRef(arena, rng, MinKeyNameLen, MaxKeyNameLenPlus1); + ret.issuer = genRandomAlphanumStringRef(arena, rng, MinIssuerNameLen, MaxIssuerNameLenPlus1); + ret.subject = genRandomAlphanumStringRef(arena, rng, MinIssuerNameLen, MaxIssuerNameLenPlus1); + ret.tokenId = genRandomAlphanumStringRef(arena, rng, 16, 31); auto numAudience = rng.randomInt(1, 5); auto aud = new (arena) StringRef[numAudience]; for (auto i = 0; i < numAudience; i++) - aud[i] = genRandomAlphanumStringRef(arena, rng, MaxTenantNameLenPlus1); + aud[i] = genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1); ret.audience = VectorRef(aud, numAudience); ret.issuedAtUnixTime = g_network->timer(); ret.notBeforeUnixTime = ret.issuedAtUnixTime.get(); @@ -521,7 +524,7 @@ TokenRef makeRandomTokenSpec(Arena& arena, IRandom& rng, Algorithm alg) { auto numTenants = rng.randomInt(1, 3); auto tenants = new (arena) StringRef[numTenants]; for (auto i = 0; i < numTenants; i++) - tenants[i] = genRandomAlphanumStringRef(arena, rng, MaxTenantNameLenPlus1); + tenants[i] = genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1); ret.tenants = VectorRef(tenants, numTenants); return ret; } @@ -537,12 +540,13 @@ TEST_CASE("/fdbrpc/TokenSign/FlatBuffer") { auto privateKey = mkcert::makeEcP256(); auto& rng = *deterministicRandom(); auto tokenSpec = authz::flatbuffers::makeRandomTokenSpec(arena, rng); - auto keyName = genRandomAlphanumStringRef(arena, rng, MaxKeyNameLenPlus1); + auto keyName = genRandomAlphanumStringRef(arena, rng, MinKeyNameLen, MaxKeyNameLenPlus1); auto signedToken = authz::flatbuffers::signToken(arena, tokenSpec, keyName, privateKey); const auto verifyExpectOk = authz::flatbuffers::verifyToken(signedToken, privateKey.toPublic()); ASSERT(verifyExpectOk); // try tampering with signed token by adding one more tenant - tokenSpec.tenants.push_back(arena, genRandomAlphanumStringRef(arena, rng, MaxTenantNameLenPlus1)); + tokenSpec.tenants.push_back(arena, + genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1)); auto writer = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, IncludeVersion()); writer.serialize(tokenSpec); signedToken.token = writer.toStringRef(); @@ -586,7 +590,8 @@ TEST_CASE("/fdbrpc/TokenSign/JWT") { ASSERT(optSig.get() == parsedToken.signature); } // try tampering with signed token by adding one more tenant - tokenSpec.tenants.get().push_back(arena, genRandomAlphanumStringRef(arena, rng, MaxTenantNameLenPlus1)); + tokenSpec.tenants.get().push_back( + arena, genRandomAlphanumStringRef(arena, rng, MinTenantNameLen, MaxTenantNameLenPlus1)); auto tamperedTokenPart = makeTokenPart(arena, tokenSpec); auto tamperedTokenString = fmt::format("{}.{}", tamperedTokenPart.toString(), signaturePart.toString()); const auto verifyExpectFail = authz::jwt::verifyToken(StringRef(tamperedTokenString), privateKey.toPublic()); @@ -623,69 +628,73 @@ TEST_CASE("/fdbrpc/TokenSign/JWT/ToStringRef") { return Void(); } +// This unit test takes too long to run in RandomUnitTests.toml +// FIXME: Move this to benchmark to flowbench +/* TEST_CASE("/fdbrpc/TokenSign/bench") { - auto keyTypes = std::array{ "EC"_sr, "RSA"_sr }; - for (auto kty : keyTypes) { - constexpr auto repeat = 5; - constexpr auto numSamples = 10000; - fmt::print("=== {} keys case\n", kty.toString()); - auto key = kty == "EC"_sr ? mkcert::makeEcP256() : mkcert::makeRsa4096Bit(); - auto pubKey = key.toPublic(); - auto& rng = *deterministicRandom(); - auto arena = Arena(); - auto jwtSpecs = new (arena) authz::jwt::TokenRef[numSamples]; - auto fbSpecs = new (arena) authz::flatbuffers::TokenRef[numSamples]; - auto jwts = new (arena) StringRef[numSamples]; - auto fbs = new (arena) StringRef[numSamples]; - for (auto i = 0; i < numSamples; i++) { - jwtSpecs[i] = authz::jwt::makeRandomTokenSpec( - arena, rng, kty == "EC"_sr ? authz::Algorithm::ES256 : authz::Algorithm::RS256); - fbSpecs[i] = authz::flatbuffers::makeRandomTokenSpec(arena, rng); - } - { - auto const jwtSignBegin = timer_monotonic(); - for (auto i = 0; i < numSamples; i++) { - jwts[i] = authz::jwt::signToken(arena, jwtSpecs[i], key); - } - auto const jwtSignEnd = timer_monotonic(); - fmt::print("JWT Sign : {:.2f} OPS\n", numSamples / (jwtSignEnd - jwtSignBegin)); - } - { - auto const jwtVerifyBegin = timer_monotonic(); - for (auto rep = 0; rep < repeat; rep++) { - for (auto i = 0; i < numSamples; i++) { - auto verifyOk = authz::jwt::verifyToken(jwts[i], pubKey); - ASSERT(verifyOk); - } - } - auto const jwtVerifyEnd = timer_monotonic(); - fmt::print("JWT Verify : {:.2f} OPS\n", repeat * numSamples / (jwtVerifyEnd - jwtVerifyBegin)); - } - { - auto tmpArena = Arena(); - auto const fbSignBegin = timer_monotonic(); - for (auto i = 0; i < numSamples; i++) { - auto fbToken = authz::flatbuffers::signToken(tmpArena, fbSpecs[i], "defaultKey"_sr, key); - auto wr = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, Unversioned()); - wr.serialize(fbToken); - fbs[i] = wr.toStringRef(); - } - auto const fbSignEnd = timer_monotonic(); - fmt::print("FlatBuffers Sign : {:.2f} OPS\n", numSamples / (fbSignEnd - fbSignBegin)); - } - { - auto const fbVerifyBegin = timer_monotonic(); - for (auto rep = 0; rep < repeat; rep++) { - for (auto i = 0; i < numSamples; i++) { - auto signedToken = ObjectReader::fromStringRef>( - fbs[i], Unversioned()); - auto verifyOk = authz::flatbuffers::verifyToken(signedToken, pubKey); - ASSERT(verifyOk); - } - } - auto const fbVerifyEnd = timer_monotonic(); - fmt::print("FlatBuffers Verify : {:.2f} OPS\n", repeat * numSamples / (fbVerifyEnd - fbVerifyBegin)); - } - } - return Void(); + auto keyTypes = std::array{ "EC"_sr, "RSA"_sr }; + for (auto kty : keyTypes) { + constexpr auto repeat = 5; + constexpr auto numSamples = 10000; + fmt::print("=== {} keys case\n", kty.toString()); + auto key = kty == "EC"_sr ? mkcert::makeEcP256() : mkcert::makeRsa4096Bit(); + auto pubKey = key.toPublic(); + auto& rng = *deterministicRandom(); + auto arena = Arena(); + auto jwtSpecs = new (arena) authz::jwt::TokenRef[numSamples]; + auto fbSpecs = new (arena) authz::flatbuffers::TokenRef[numSamples]; + auto jwts = new (arena) StringRef[numSamples]; + auto fbs = new (arena) StringRef[numSamples]; + for (auto i = 0; i < numSamples; i++) { + jwtSpecs[i] = authz::jwt::makeRandomTokenSpec( + arena, rng, kty == "EC"_sr ? authz::Algorithm::ES256 : authz::Algorithm::RS256); + fbSpecs[i] = authz::flatbuffers::makeRandomTokenSpec(arena, rng); + } + { + auto const jwtSignBegin = timer_monotonic(); + for (auto i = 0; i < numSamples; i++) { + jwts[i] = authz::jwt::signToken(arena, jwtSpecs[i], key); + } + auto const jwtSignEnd = timer_monotonic(); + fmt::print("JWT Sign : {:.2f} OPS\n", numSamples / (jwtSignEnd - jwtSignBegin)); + } + { + auto const jwtVerifyBegin = timer_monotonic(); + for (auto rep = 0; rep < repeat; rep++) { + for (auto i = 0; i < numSamples; i++) { + auto verifyOk = authz::jwt::verifyToken(jwts[i], pubKey); + ASSERT(verifyOk); + } + } + auto const jwtVerifyEnd = timer_monotonic(); + fmt::print("JWT Verify : {:.2f} OPS\n", repeat * numSamples / (jwtVerifyEnd - jwtVerifyBegin)); + } + { + auto tmpArena = Arena(); + auto const fbSignBegin = timer_monotonic(); + for (auto i = 0; i < numSamples; i++) { + auto fbToken = authz::flatbuffers::signToken(tmpArena, fbSpecs[i], "defaultKey"_sr, key); + auto wr = ObjectWriter([&arena](size_t len) { return new (arena) uint8_t[len]; }, Unversioned()); + wr.serialize(fbToken); + fbs[i] = wr.toStringRef(); + } + auto const fbSignEnd = timer_monotonic(); + fmt::print("FlatBuffers Sign : {:.2f} OPS\n", numSamples / (fbSignEnd - fbSignBegin)); + } + { + auto const fbVerifyBegin = timer_monotonic(); + for (auto rep = 0; rep < repeat; rep++) { + for (auto i = 0; i < numSamples; i++) { + auto signedToken = ObjectReader::fromStringRef>( + fbs[i], Unversioned()); + auto verifyOk = authz::flatbuffers::verifyToken(signedToken, pubKey); + ASSERT(verifyOk); + } + } + auto const fbVerifyEnd = timer_monotonic(); + fmt::print("FlatBuffers Verify : {:.2f} OPS\n", repeat * numSamples / (fbVerifyEnd - fbVerifyBegin)); + } + } + return Void(); } +*/ diff --git a/fdbrpc/actorFuzz.py b/fdbrpc/actorFuzz.py index dc83b7dbaa..0b6ca8f99e 100755 --- a/fdbrpc/actorFuzz.py +++ b/fdbrpc/actorFuzz.py @@ -439,7 +439,7 @@ testCaseCount = 30 outputFile = open("ActorFuzz.actor.cpp", "wt") print(header, file=outputFile) print('// THIS FILE WAS GENERATED BY actorFuzz.py; DO NOT MODIFY IT DIRECTLY\n', file=outputFile) -print('#include "ActorFuzz.h"\n', file=outputFile) +print('#include "fdbrpc/ActorFuzz.h"\n', file=outputFile) print('#ifndef WIN32\n', file=outputFile) actors = [randomActor(i) for i in range(testCaseCount)] diff --git a/fdbrpc/dsltest.actor.cpp b/fdbrpc/dsltest.actor.cpp index fa33301145..b1e557e028 100644 --- a/fdbrpc/dsltest.actor.cpp +++ b/fdbrpc/dsltest.actor.cpp @@ -643,9 +643,9 @@ void arenaTest() { { Arena arena; VectorRef test; - test.push_back(arena, StringRef(arena, LiteralStringRef("Hello"))); - test.push_back(arena, StringRef(arena, LiteralStringRef(", "))); - test.push_back(arena, StringRef(arena, LiteralStringRef("World!"))); + test.push_back(arena, StringRef(arena, "Hello"_sr)); + test.push_back(arena, StringRef(arena, ", "_sr)); + test.push_back(arena, StringRef(arena, "World!"_sr)); for (auto i = test.begin(); i != test.end(); ++i) for (auto j = i->begin(); j != i->end(); ++j) @@ -1200,8 +1200,8 @@ void dsltest() { actorTest1(true); actorTest2(true); actorTest3(true); - // if (g_network == &g_simulator) - // g_simulator.run( actorTest4(true) ); + // if (g_network == g_simulator) + // g_simulator->run( actorTest4(true) ); actorTest5(); actorTest6(); actorTest7(); diff --git a/fdbrpc/genericactors.actor.cpp b/fdbrpc/genericactors.actor.cpp index 8eb9ba49b1..887c68990b 100644 --- a/fdbrpc/genericactors.actor.cpp +++ b/fdbrpc/genericactors.actor.cpp @@ -27,8 +27,8 @@ ACTOR Future disableConnectionFailuresAfter(double time, std::string context) { if (g_network->isSimulated()) { wait(delayUntil(time)); - g_simulator.connectionFailuresDisableDuration = 1e6; - g_simulator.speedUpSimulation = true; + g_simulator->connectionFailuresDisableDuration = 1e6; + g_simulator->speedUpSimulation = true; TraceEvent(SevWarnAlways, ("DisableConnectionFailures_" + context).c_str()); } return Void(); diff --git a/fdbrpc/include/fdbrpc/AsyncFileCached.actor.h b/fdbrpc/include/fdbrpc/AsyncFileCached.actor.h index b79d8530ad..1c8f3e58c2 100644 --- a/fdbrpc/include/fdbrpc/AsyncFileCached.actor.h +++ b/fdbrpc/include/fdbrpc/AsyncFileCached.actor.h @@ -73,7 +73,7 @@ struct EvictablePageCache : ReferenceCounted { explicit EvictablePageCache(int pageSize, int64_t maxSize) : pageSize(pageSize), maxPages(maxSize / pageSize), cacheEvictionType(evictionPolicyStringToEnum(FLOW_KNOBS->CACHE_EVICTION_POLICY)) { - cacheEvictions.init(LiteralStringRef("EvictablePageCache.CacheEvictions")); + cacheEvictions.init("EvictablePageCache.CacheEvictions"_sr); } void allocate(EvictablePage* page) { @@ -303,25 +303,25 @@ private: : filename(filename), uncached(uncached), length(length), prevLength(length), pageCache(pageCache), currentTruncate(Void()), currentTruncateSize(0), rateControl(nullptr) { if (!g_network->isSimulated()) { - countFileCacheWrites.init(LiteralStringRef("AsyncFile.CountFileCacheWrites"), filename); - countFileCacheReads.init(LiteralStringRef("AsyncFile.CountFileCacheReads"), filename); - countFileCacheWritesBlocked.init(LiteralStringRef("AsyncFile.CountFileCacheWritesBlocked"), filename); - countFileCacheReadsBlocked.init(LiteralStringRef("AsyncFile.CountFileCacheReadsBlocked"), filename); - countFileCachePageReadsHit.init(LiteralStringRef("AsyncFile.CountFileCachePageReadsHit"), filename); - countFileCachePageReadsMissed.init(LiteralStringRef("AsyncFile.CountFileCachePageReadsMissed"), filename); - countFileCachePageReadsMerged.init(LiteralStringRef("AsyncFile.CountFileCachePageReadsMerged"), filename); - countFileCacheFinds.init(LiteralStringRef("AsyncFile.CountFileCacheFinds"), filename); - countFileCacheReadBytes.init(LiteralStringRef("AsyncFile.CountFileCacheReadBytes"), filename); + countFileCacheWrites.init("AsyncFile.CountFileCacheWrites"_sr, filename); + countFileCacheReads.init("AsyncFile.CountFileCacheReads"_sr, filename); + countFileCacheWritesBlocked.init("AsyncFile.CountFileCacheWritesBlocked"_sr, filename); + countFileCacheReadsBlocked.init("AsyncFile.CountFileCacheReadsBlocked"_sr, filename); + countFileCachePageReadsHit.init("AsyncFile.CountFileCachePageReadsHit"_sr, filename); + countFileCachePageReadsMissed.init("AsyncFile.CountFileCachePageReadsMissed"_sr, filename); + countFileCachePageReadsMerged.init("AsyncFile.CountFileCachePageReadsMerged"_sr, filename); + countFileCacheFinds.init("AsyncFile.CountFileCacheFinds"_sr, filename); + countFileCacheReadBytes.init("AsyncFile.CountFileCacheReadBytes"_sr, filename); - countCacheWrites.init(LiteralStringRef("AsyncFile.CountCacheWrites")); - countCacheReads.init(LiteralStringRef("AsyncFile.CountCacheReads")); - countCacheWritesBlocked.init(LiteralStringRef("AsyncFile.CountCacheWritesBlocked")); - countCacheReadsBlocked.init(LiteralStringRef("AsyncFile.CountCacheReadsBlocked")); - countCachePageReadsHit.init(LiteralStringRef("AsyncFile.CountCachePageReadsHit")); - countCachePageReadsMissed.init(LiteralStringRef("AsyncFile.CountCachePageReadsMissed")); - countCachePageReadsMerged.init(LiteralStringRef("AsyncFile.CountCachePageReadsMerged")); - countCacheFinds.init(LiteralStringRef("AsyncFile.CountCacheFinds")); - countCacheReadBytes.init(LiteralStringRef("AsyncFile.CountCacheReadBytes")); + countCacheWrites.init("AsyncFile.CountCacheWrites"_sr); + countCacheReads.init("AsyncFile.CountCacheReads"_sr); + countCacheWritesBlocked.init("AsyncFile.CountCacheWritesBlocked"_sr); + countCacheReadsBlocked.init("AsyncFile.CountCacheReadsBlocked"_sr); + countCachePageReadsHit.init("AsyncFile.CountCachePageReadsHit"_sr); + countCachePageReadsMissed.init("AsyncFile.CountCachePageReadsMissed"_sr); + countCachePageReadsMerged.init("AsyncFile.CountCachePageReadsMerged"_sr); + countCacheFinds.init("AsyncFile.CountCacheFinds"_sr); + countCacheReadBytes.init("AsyncFile.CountCacheReadBytes"_sr); } } diff --git a/fdbrpc/include/fdbrpc/AsyncFileEIO.actor.h b/fdbrpc/include/fdbrpc/AsyncFileEIO.actor.h index bc7865b485..b3f2889450 100644 --- a/fdbrpc/include/fdbrpc/AsyncFileEIO.actor.h +++ b/fdbrpc/include/fdbrpc/AsyncFileEIO.actor.h @@ -279,11 +279,11 @@ private: AsyncFileEIO(int fd, int flags, std::string const& filename) : fd(fd), flags(flags), err(new ErrorInfo), filename(filename) { if (!g_network->isSimulated()) { - countFileLogicalWrites.init(LiteralStringRef("AsyncFile.CountFileLogicalWrites"), filename); - countFileLogicalReads.init(LiteralStringRef("AsyncFile.CountFileLogicalReads"), filename); + countFileLogicalWrites.init("AsyncFile.CountFileLogicalWrites"_sr, filename); + countFileLogicalReads.init("AsyncFile.CountFileLogicalReads"_sr, filename); - countLogicalWrites.init(LiteralStringRef("AsyncFile.CountLogicalWrites")); - countLogicalReads.init(LiteralStringRef("AsyncFile.CountLogicalReads")); + countLogicalWrites.init("AsyncFile.CountLogicalWrites"_sr); + countLogicalReads.init("AsyncFile.CountLogicalReads"_sr); } } diff --git a/fdbrpc/include/fdbrpc/AsyncFileKAIO.actor.h b/fdbrpc/include/fdbrpc/AsyncFileKAIO.actor.h index 09320ae659..40a84e6d9e 100644 --- a/fdbrpc/include/fdbrpc/AsyncFileKAIO.actor.h +++ b/fdbrpc/include/fdbrpc/AsyncFileKAIO.actor.h @@ -38,7 +38,7 @@ #include #include "fdbrpc/linux_kaio.h" #include "flow/Knobs.h" -#include "flow/Histogram.h" +#include "fdbrpc/Stats.h" #include "flow/UnitTest.h" #include "crc32/crc32c.h" #include "flow/genericactors.actor.h" @@ -48,14 +48,6 @@ // /data/v7/fdb/ #define KAIO_LOGGING 0 -struct AsyncFileKAIOMetrics { - Reference readLatencyDist; - Reference writeLatencyDist; - Reference syncLatencyDist; -} g_asyncFileKAIOMetrics; - -Future g_asyncFileKAIOHistogramLogger; - DESCR struct SlowAioSubmit { int64_t submitDuration; // ns int64_t truncateDuration; // ns @@ -66,6 +58,25 @@ DESCR struct SlowAioSubmit { class AsyncFileKAIO final : public IAsyncFile, public ReferenceCounted { public: + struct AsyncFileKAIOMetrics { + LatencySample readLatencySample = { "AsyncFileKAIOReadLatency", + UID(), + FLOW_KNOBS->KAIO_LATENCY_LOGGING_INTERVAL, + FLOW_KNOBS->KAIO_LATENCY_SAMPLE_SIZE }; + LatencySample writeLatencySample = { "AsyncFileKAIOWriteLatency", + UID(), + FLOW_KNOBS->KAIO_LATENCY_LOGGING_INTERVAL, + FLOW_KNOBS->KAIO_LATENCY_SAMPLE_SIZE }; + LatencySample syncLatencySample = { "AsyncFileKAIOSyncLatency", + UID(), + FLOW_KNOBS->KAIO_LATENCY_LOGGING_INTERVAL, + FLOW_KNOBS->KAIO_LATENCY_SAMPLE_SIZE }; + }; + + static AsyncFileKAIOMetrics& getMetrics() { + static AsyncFileKAIOMetrics metrics; + return metrics; + } #if KAIO_LOGGING private: @@ -180,12 +191,12 @@ public: static void init(Reference ev, double ioTimeout) { ASSERT(!FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO); if (!g_network->isSimulated()) { - ctx.countAIOSubmit.init(LiteralStringRef("AsyncFile.CountAIOSubmit")); - ctx.countAIOCollect.init(LiteralStringRef("AsyncFile.CountAIOCollect")); - ctx.submitMetric.init(LiteralStringRef("AsyncFile.Submit")); - ctx.countPreSubmitTruncate.init(LiteralStringRef("AsyncFile.CountPreAIOSubmitTruncate")); - ctx.preSubmitTruncateBytes.init(LiteralStringRef("AsyncFile.PreAIOSubmitTruncateBytes")); - ctx.slowAioSubmitMetric.init(LiteralStringRef("AsyncFile.SlowAIOSubmit")); + ctx.countAIOSubmit.init("AsyncFile.CountAIOSubmit"_sr); + ctx.countAIOCollect.init("AsyncFile.CountAIOCollect"_sr); + ctx.submitMetric.init("AsyncFile.Submit"_sr); + ctx.countPreSubmitTruncate.init("AsyncFile.CountPreAIOSubmitTruncate"_sr); + ctx.preSubmitTruncateBytes.init("AsyncFile.PreAIOSubmitTruncateBytes"_sr); + ctx.slowAioSubmitMetric.init("AsyncFile.SlowAIOSubmit"_sr); } int rc = io_setup(FLOW_KNOBS->MAX_OUTSTANDING, &ctx.iocx); @@ -353,7 +364,7 @@ public: #endif KAIOLogEvent(logFile, id, OpLogEntry::SYNC, OpLogEntry::START); - double start_time = now(); + double start_time = timer(); Future fsync = throwErrorIfFailed( Reference::addRef(this), @@ -365,7 +376,7 @@ public: fsync = map(fsync, [=](Void r) mutable { KAIOLogEvent(logFile, id, OpLogEntry::SYNC, OpLogEntry::COMPLETE); - g_asyncFileKAIOMetrics.syncLatencyDist->sampleSeconds(now() - start_time); + getMetrics().syncLatencySample.addMeasurement(timer() - start_time); return r; }); @@ -404,6 +415,7 @@ public: int64_t previousTruncateBytes = ctx.preSubmitTruncateBytes; int64_t largestTruncate = 0; + double start = timer(); for (int i = 0; i < n; i++) { auto io = ctx.queue.top(); @@ -411,7 +423,7 @@ public: ctx.queue.pop(); toStart[i] = io; - io->startTime = now(); + io->startTime = start; if (ctx.ioTimeout > 0) { ctx.appendToRequestList(io); @@ -636,27 +648,17 @@ private: : failed(false), fd(fd), flags(flags), filename(filename) { ASSERT(!FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO); if (!g_network->isSimulated()) { - countFileLogicalWrites.init(LiteralStringRef("AsyncFile.CountFileLogicalWrites"), filename); - countFileLogicalReads.init(LiteralStringRef("AsyncFile.CountFileLogicalReads"), filename); - countLogicalWrites.init(LiteralStringRef("AsyncFile.CountLogicalWrites")); - countLogicalReads.init(LiteralStringRef("AsyncFile.CountLogicalReads")); - if (!g_asyncFileKAIOHistogramLogger.isValid()) { - auto& metrics = g_asyncFileKAIOMetrics; - metrics.readLatencyDist = Reference(new Histogram( - Reference(), "AsyncFileKAIO", "ReadLatency", Histogram::Unit::microseconds)); - metrics.writeLatencyDist = Reference(new Histogram( - Reference(), "AsyncFileKAIO", "WriteLatency", Histogram::Unit::microseconds)); - metrics.syncLatencyDist = Reference(new Histogram( - Reference(), "AsyncFileKAIO", "SyncLatency", Histogram::Unit::microseconds)); - g_asyncFileKAIOHistogramLogger = histogramLogger(FLOW_KNOBS->DISK_METRIC_LOGGING_INTERVAL); - } + countFileLogicalWrites.init("AsyncFile.CountFileLogicalWrites"_sr, filename); + countFileLogicalReads.init("AsyncFile.CountFileLogicalReads"_sr, filename); + countLogicalWrites.init("AsyncFile.CountLogicalWrites"_sr); + countLogicalReads.init("AsyncFile.CountLogicalReads"_sr); } #if KAIO_LOGGING logFile = nullptr; // TODO: Don't do this hacky investigation-specific thing StringRef fname(filename); - if (fname.endsWith(LiteralStringRef(".sqlite")) || fname.endsWith(LiteralStringRef(".sqlite-wal"))) { + if (fname.endsWith(".sqlite"_sr) || fname.endsWith(".sqlite-wal"_sr)) { std::string logFileName = basename(filename); while (logFileName.find("/") != std::string::npos) logFileName = logFileName.substr(logFileName.find("/") + 1); @@ -735,6 +737,8 @@ private: break; } + double currentTime = timer(); + ++ctx.countAIOCollect; // printf("io_getevents: collected %d/%d in %f us (%d queued)\n", n, ctx.outstanding, (timer()-before)*1e6, // ctx.queue.size()); @@ -753,7 +757,6 @@ private: ctx.outstanding -= n; if (ctx.ioTimeout > 0) { - double currentTime = now(); while (ctx.submittedRequestList && currentTime - ctx.submittedRequestList->startTime > ctx.ioTimeout) { ctx.submittedRequestList->timeout(ctx.timeoutWarnOnly); ctx.removeFromRequestList(ctx.submittedRequestList); @@ -769,13 +772,12 @@ private: ctx.removeFromRequestList(iob); } - auto& metrics = g_asyncFileKAIOMetrics; switch (iob->aio_lio_opcode) { case IO_CMD_PREAD: - metrics.readLatencyDist->sampleSeconds(now() - iob->startTime); + getMetrics().readLatencySample.addMeasurement(currentTime - iob->startTime); break; case IO_CMD_PWRITE: - metrics.writeLatencyDist->sampleSeconds(now() - iob->startTime); + getMetrics().writeLatencySample.addMeasurement(currentTime - iob->startTime); break; } @@ -783,19 +785,6 @@ private: } } } - - ACTOR static Future histogramLogger(double interval) { - state double currentTime; - loop { - currentTime = now(); - wait(delay(interval)); - double elapsed = now() - currentTime; - auto& metrics = g_asyncFileKAIOMetrics; - metrics.readLatencyDist->writeToLog(elapsed); - metrics.writeLatencyDist->writeToLog(elapsed); - metrics.syncLatencyDist->writeToLog(elapsed); - } - } }; #if KAIO_LOGGING diff --git a/fdbrpc/include/fdbrpc/AsyncFileNonDurable.actor.h b/fdbrpc/include/fdbrpc/AsyncFileNonDurable.actor.h index 4b548afbf6..c01e7f27df 100644 --- a/fdbrpc/include/fdbrpc/AsyncFileNonDurable.actor.h +++ b/fdbrpc/include/fdbrpc/AsyncFileNonDurable.actor.h @@ -48,7 +48,7 @@ ACTOR Future sendErrorOnProcess(ISimulator::ProcessInfo* process, ACTOR template Future sendErrorOnShutdown(Future in) { choose { - when(wait(success(g_simulator.getCurrentProcess()->shutdownSignal.getFuture()))) { + when(wait(success(g_simulator->getCurrentProcess()->shutdownSignal.getFuture()))) { throw io_error().asInjectedFault(); } when(T rep = wait(in)) { return rep; } @@ -64,14 +64,14 @@ public: explicit AsyncFileDetachable(Reference file) : file(file) { shutdown = doShutdown(this); } ACTOR Future doShutdown(AsyncFileDetachable* self) { - wait(success(g_simulator.getCurrentProcess()->shutdownSignal.getFuture())); + wait(success(g_simulator->getCurrentProcess()->shutdownSignal.getFuture())); self->file = Reference(); return Void(); } ACTOR static Future> open(Future> wrappedFile) { choose { - when(wait(success(g_simulator.getCurrentProcess()->shutdownSignal.getFuture()))) { + when(wait(success(g_simulator->getCurrentProcess()->shutdownSignal.getFuture()))) { throw io_error().asInjectedFault(); } when(Reference f = wait(wrappedFile)) { return makeReference(f); } @@ -82,31 +82,31 @@ public: void delref() override { ReferenceCounted::delref(); } Future read(void* data, int length, int64_t offset) override { - if (!file.getPtr() || g_simulator.getCurrentProcess()->shutdownSignal.getFuture().isReady()) + if (!file.getPtr() || g_simulator->getCurrentProcess()->shutdownSignal.getFuture().isReady()) return io_error().asInjectedFault(); return sendErrorOnShutdown(file->read(data, length, offset)); } Future write(void const* data, int length, int64_t offset) override { - if (!file.getPtr() || g_simulator.getCurrentProcess()->shutdownSignal.getFuture().isReady()) + if (!file.getPtr() || g_simulator->getCurrentProcess()->shutdownSignal.getFuture().isReady()) return io_error().asInjectedFault(); return sendErrorOnShutdown(file->write(data, length, offset)); } Future truncate(int64_t size) override { - if (!file.getPtr() || g_simulator.getCurrentProcess()->shutdownSignal.getFuture().isReady()) + if (!file.getPtr() || g_simulator->getCurrentProcess()->shutdownSignal.getFuture().isReady()) return io_error().asInjectedFault(); return sendErrorOnShutdown(file->truncate(size)); } Future sync() override { - if (!file.getPtr() || g_simulator.getCurrentProcess()->shutdownSignal.getFuture().isReady()) + if (!file.getPtr() || g_simulator->getCurrentProcess()->shutdownSignal.getFuture().isReady()) return io_error().asInjectedFault(); return sendErrorOnShutdown(file->sync()); } Future size() const override { - if (!file.getPtr() || g_simulator.getCurrentProcess()->shutdownSignal.getFuture().isReady()) + if (!file.getPtr() || g_simulator->getCurrentProcess()->shutdownSignal.getFuture().isReady()) return io_error().asInjectedFault(); return sendErrorOnShutdown(file->size()); } @@ -214,12 +214,12 @@ public: Future> wrappedFile, Reference diskParameters, bool aio) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); state Future shutdown = success(currentProcess->shutdownSignal.getFuture()); - //TraceEvent("AsyncFileNonDurableOpenBegin").detail("Filename", filename).detail("Addr", g_simulator.getCurrentProcess()->address); - wait(g_simulator.onMachine(currentProcess)); + //TraceEvent("AsyncFileNonDurableOpenBegin").detail("Filename", filename).detail("Addr", g_simulator->getCurrentProcess()->address); + wait(g_simulator->onMachine(currentProcess)); try { wait(success(wrappedFile) || shutdown); @@ -237,7 +237,7 @@ public: //TraceEvent("AsyncFileNonDurableOpenWaitOnDelete2").detail("Filename", filename); if (shutdown.isReady()) throw io_error().asInjectedFault(); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); } state Reference nonDurableFile( @@ -252,7 +252,7 @@ public: //TraceEvent("AsyncFileNonDurableOpenComplete").detail("Filename", filename); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return nonDurableFile; } catch (Error& e) { @@ -260,8 +260,8 @@ public: std::string currentFilename = (wrappedFile.isReady() && !wrappedFile.isError()) ? wrappedFile.get()->getFilename() : actualFilename; currentProcess->machine->openFiles.erase(currentFilename); - //TraceEvent("AsyncFileNonDurableOpenError").errorUnsuppressed(e).detail("Filename", filename).detail("Address", currentProcess->address).detail("Addr", g_simulator.getCurrentProcess()->address); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + //TraceEvent("AsyncFileNonDurableOpenError").errorUnsuppressed(e).detail("Filename", filename).detail("Address", currentProcess->address).detail("Addr", g_simulator->getCurrentProcess()->address); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } @@ -290,7 +290,7 @@ public: // Removes a file from the openFiles map static void removeOpenFile(std::string filename, AsyncFileNonDurable* file) { - auto& openFiles = g_simulator.getCurrentProcess()->machine->openFiles; + auto& openFiles = g_simulator->getCurrentProcess()->machine->openFiles; auto iter = openFiles.find(filename); @@ -425,24 +425,24 @@ private: debugFileCheck("AsyncFileNonDurableRead", self->filename, data, offset, length); - // if(g_simulator.getCurrentProcess()->rebooting) + // if(g_simulator->getCurrentProcess()->rebooting) //TraceEvent("AsyncFileNonDurable_ReadEnd", self->id).detail("Filename", self->filename); return readFuture.get(); } ACTOR Future read(AsyncFileNonDurable* self, void* data, int length, int64_t offset) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { state int rep = wait(self->onRead(self, data, length, offset)); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return rep; } catch (Error& e) { state Error err = e; - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } @@ -457,12 +457,12 @@ private: int length, int64_t offset) { state Standalone dataCopy(StringRef((uint8_t*)data, length)); - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); state double delayDuration = - g_simulator.speedUpSimulation ? 0.0001 : (deterministicRandom()->random01() * self->maxWriteDelay); + g_simulator->speedUpSimulation ? 0.0001 : (deterministicRandom()->random01() * self->maxWriteDelay); state Future startSyncFuture = self->startSyncPromise.getFuture(); @@ -475,7 +475,7 @@ private: self->getModificationsAndInsert(offset, length, true, writeEnded); self->minSizeAfterPendingModifications = std::max(self->minSizeAfterPendingModifications, offset + length); - if (BUGGIFY_WITH_PROB(0.001) && !g_simulator.speedUpSimulation) + if (BUGGIFY_WITH_PROB(0.001) && !g_simulator->speedUpSimulation) priorModifications.push_back( delay(deterministicRandom()->random01() * FLOW_KNOBS->MAX_PRIOR_MODIFICATION_DELAY) || self->killed.getFuture()); @@ -629,12 +629,12 @@ private: Promise truncateStarted, Future> ownFuture, int64_t size) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); state double delayDuration = - g_simulator.speedUpSimulation ? 0.0001 : (deterministicRandom()->random01() * self->maxWriteDelay); + g_simulator->speedUpSimulation ? 0.0001 : (deterministicRandom()->random01() * self->maxWriteDelay); state Future startSyncFuture = self->startSyncPromise.getFuture(); try { @@ -773,18 +773,18 @@ private: } ACTOR Future sync(AsyncFileNonDurable* self, bool durable) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { wait(self->onSync(self, durable)); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return Void(); } catch (Error& e) { state Error err = e; - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } @@ -806,32 +806,33 @@ private: } ACTOR static Future size(AsyncFileNonDurable const* self) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { state int64_t rep = wait(onSize(self)); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return rep; } catch (Error& e) { state Error err = e; - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } // Finishes all outstanding actors on an AsyncFileNonDurable and then deletes it ACTOR Future closeFile(AsyncFileNonDurable* self) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); state std::string filename = self->filename; - g_simulator.getMachineByNetworkAddress(self->openedAddress)->deletingOrClosingFiles.insert(self->getFilename()); + g_simulator->getMachineByNetworkAddress(self->openedAddress) + ->deletingOrClosingFiles.insert(self->getFilename()); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { // Make sure all writes have gone through. Promise startSyncPromise = self->startSyncPromise; @@ -854,8 +855,8 @@ private: wait(self->killComplete.getFuture()); // Remove this file from the filesBeingDeleted map so that new files can be created with this filename - g_simulator.getMachineByNetworkAddress(self->openedAddress)->closingFiles.erase(self->getFilename()); - g_simulator.getMachineByNetworkAddress(self->openedAddress) + g_simulator->getMachineByNetworkAddress(self->openedAddress)->closingFiles.erase(self->getFilename()); + g_simulator->getMachineByNetworkAddress(self->openedAddress) ->deletingOrClosingFiles.erase(self->getFilename()); AsyncFileNonDurable::filesBeingDeleted.erase(self->filename); //TraceEvent("AsyncFileNonDurable_FinishDelete", self->id).detail("Filename", self->filename); diff --git a/fdbrpc/include/fdbrpc/LoadBalance.actor.h b/fdbrpc/include/fdbrpc/LoadBalance.actor.h index d8e15d927f..7d5e17b118 100644 --- a/fdbrpc/include/fdbrpc/LoadBalance.actor.h +++ b/fdbrpc/include/fdbrpc/LoadBalance.actor.h @@ -142,7 +142,7 @@ Future tssComparison(Req req, if (!TSS_doCompare(src.get(), tss.get().get())) { CODE_PROBE(true, "TSS Mismatch"); state TraceEvent mismatchEvent( - (g_network->isSimulated() && g_simulator.tssMode == ISimulator::TSSMode::EnabledDropMutations) + (g_network->isSimulated() && g_simulator->tssMode == ISimulator::TSSMode::EnabledDropMutations) ? SevWarnAlways : SevError, TSS_mismatchTraceName(req)); @@ -206,7 +206,7 @@ Future tssComparison(Req req, // record a summarized trace event instead TraceEvent summaryEvent((g_network->isSimulated() && - g_simulator.tssMode == ISimulator::TSSMode::EnabledDropMutations) + g_simulator->tssMode == ISimulator::TSSMode::EnabledDropMutations) ? SevWarnAlways : SevError, TSS_mismatchTraceName(req)); diff --git a/fdbrpc/include/fdbrpc/Locality.h b/fdbrpc/include/fdbrpc/Locality.h index 3e8d07f6f4..20269b6d05 100644 --- a/fdbrpc/include/fdbrpc/Locality.h +++ b/fdbrpc/include/fdbrpc/Locality.h @@ -43,6 +43,7 @@ struct ProcessClass { DataDistributorClass, CoordinatorClass, RatekeeperClass, + ConsistencyScanClass, StorageCacheClass, BackupClass, GrvProxyClass, @@ -72,6 +73,7 @@ struct ProcessClass { ClusterController, DataDistributor, Ratekeeper, + ConsistencyScan, BlobManager, BlobWorker, StorageCache, @@ -110,6 +112,7 @@ public: else if (s=="data_distributor") _class = DataDistributorClass; else if (s=="coordinator") _class = CoordinatorClass; else if (s=="ratekeeper") _class = RatekeeperClass; + else if (s=="consistency_scan") _class = ConsistencyScanClass; else if (s=="blob_manager") _class = BlobManagerClass; else if (s=="blob_worker") _class = BlobWorkerClass; else if (s=="storage_cache") _class = StorageCacheClass; @@ -140,6 +143,7 @@ public: else if (classStr=="data_distributor") _class = DataDistributorClass; else if (classStr=="coordinator") _class = CoordinatorClass; else if (classStr=="ratekeeper") _class = RatekeeperClass; + else if (classStr=="consistency_scan") _class = ConsistencyScanClass; else if (classStr=="blob_manager") _class = BlobManagerClass; else if (classStr=="blob_worker") _class = BlobWorkerClass; else if (classStr=="storage_cache") _class = StorageCacheClass; @@ -180,6 +184,7 @@ public: case DataDistributorClass: return "data_distributor"; case CoordinatorClass: return "coordinator"; case RatekeeperClass: return "ratekeeper"; + case ConsistencyScanClass: return "consistency_scan"; case BlobManagerClass: return "blob_manager"; case BlobWorkerClass: return "blob_worker"; case StorageCacheClass: return "storage_cache"; diff --git a/fdbrpc/include/fdbrpc/ReplicationPolicy.h b/fdbrpc/include/fdbrpc/ReplicationPolicy.h index acb0550a68..20e1638c81 100644 --- a/fdbrpc/include/fdbrpc/ReplicationPolicy.h +++ b/fdbrpc/include/fdbrpc/ReplicationPolicy.h @@ -254,19 +254,19 @@ void serializeReplicationPolicy(Ar& ar, Reference& policy) { StringRef name; serializer(ar, name); - if (name == LiteralStringRef("One")) { + if (name == "One"_sr) { PolicyOne* pointer = new PolicyOne(); pointer->serialize(ar); policy = Reference(pointer); - } else if (name == LiteralStringRef("Across")) { + } else if (name == "Across"_sr) { PolicyAcross* pointer = new PolicyAcross(0, "", Reference()); pointer->serialize(ar); policy = Reference(pointer); - } else if (name == LiteralStringRef("And")) { + } else if (name == "And"_sr) { PolicyAnd* pointer = new PolicyAnd{}; pointer->serialize(ar); policy = Reference(pointer); - } else if (name == LiteralStringRef("None")) { + } else if (name == "None"_sr) { policy = Reference(); } else { TraceEvent(SevError, "SerializingInvalidPolicyType").detail("PolicyName", name); diff --git a/fdbrpc/include/fdbrpc/WellKnownEndpoints.h b/fdbrpc/include/fdbrpc/WellKnownEndpoints.h index e986a65f33..b5d4beb17b 100644 --- a/fdbrpc/include/fdbrpc/WellKnownEndpoints.h +++ b/fdbrpc/include/fdbrpc/WellKnownEndpoints.h @@ -49,7 +49,8 @@ enum WellKnownEndpoints { WLTOKEN_CONFIGFOLLOWER_ROLLFORWARD, // 21 WLTOKEN_CONFIGFOLLOWER_GETCOMMITTEDVERSION, // 22 WLTOKEN_PROCESS, // 23 - WLTOKEN_RESERVED_COUNT // 24 + WLTOKEN_CONFIGFOLLOWER_LOCK, // 24 + WLTOKEN_RESERVED_COUNT // 25 }; static_assert(WLTOKEN_PROTOCOL_INFO == diff --git a/fdbrpc/include/fdbrpc/simulator.h b/fdbrpc/include/fdbrpc/simulator.h index da9a607962..5228ff7b92 100644 --- a/fdbrpc/include/fdbrpc/simulator.h +++ b/fdbrpc/include/fdbrpc/simulator.h @@ -186,6 +186,8 @@ public: return false; case ProcessClass::RatekeeperClass: return false; + case ProcessClass::ConsistencyScanClass: + return false; case ProcessClass::BlobManagerClass: return false; case ProcessClass::StorageCacheClass: @@ -534,9 +536,7 @@ private: bool allSwapsDisabled; }; -// Quickly make existing code work that expects g_simulator to be of class type (not a pointer) -extern ISimulator* g_pSimulator; -#define g_simulator (*g_pSimulator) +extern ISimulator* g_simulator; void startNewSimulator(bool printSimTime); diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 0e75b59976..9124a92880 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -55,24 +55,24 @@ #include "flow/FaultInjection.h" #include "flow/actorcompiler.h" // This must be the last #include. -ISimulator* g_pSimulator = nullptr; +ISimulator* g_simulator = nullptr; thread_local ISimulator::ProcessInfo* ISimulator::currentProcess = nullptr; ISimulator::ISimulator() : desiredCoordinators(1), physicalDatacenters(1), processesPerMachine(0), listenersPerProcess(1), usableRegions(1), - allowLogSetKills(true), tssMode(TSSMode::Disabled), isStopped(false), lastConnectionFailure(0), - connectionFailuresDisableDuration(0), speedUpSimulation(false), backupAgents(BackupAgentType::WaitForType), - drAgents(BackupAgentType::WaitForType), allSwapsDisabled(false) {} + allowLogSetKills(true), tssMode(TSSMode::Disabled), configDBType(ConfigDBType::DISABLED), isStopped(false), + lastConnectionFailure(0), connectionFailuresDisableDuration(0), speedUpSimulation(false), + backupAgents(BackupAgentType::WaitForType), drAgents(BackupAgentType::WaitForType), allSwapsDisabled(false) {} ISimulator::~ISimulator() = default; bool simulator_should_inject_fault(const char* context, const char* file, int line, int error_code) { if (!g_network->isSimulated() || !faultInjectionActivated) return false; - auto p = g_simulator.getCurrentProcess(); + auto p = g_simulator->getCurrentProcess(); if (p->fault_injection_p2 && deterministicRandom()->random01() < p->fault_injection_p2 && - !g_simulator.speedUpSimulation) { + !g_simulator->speedUpSimulation) { uint32_t h1 = line + (p->fault_injection_r >> 32); if (h1 < p->fault_injection_p1 * std::numeric_limits::max()) { @@ -154,13 +154,13 @@ struct SimClogging { double tnow = now(); double t = tnow + (stableConnection ? 0.1 : 1.0) * halfLatency(); - if (!g_simulator.speedUpSimulation && !stableConnection) + if (!g_simulator->speedUpSimulation && !stableConnection) t += clogPairLatency[pair]; - if (!g_simulator.speedUpSimulation && !stableConnection && clogPairUntil.count(pair)) + if (!g_simulator->speedUpSimulation && !stableConnection && clogPairUntil.count(pair)) t = std::max(t, clogPairUntil[pair]); - if (!g_simulator.speedUpSimulation && !stableConnection && clogRecvUntil.count(to.ip)) + if (!g_simulator->speedUpSimulation && !stableConnection && clogRecvUntil.count(to.ip)) t = std::max(t, clogRecvUntil[to.ip]); return t - tnow; @@ -192,7 +192,7 @@ private: double halfLatency() const { double a = deterministicRandom()->random01(); const double pFast = 0.999; - if (a <= pFast || g_simulator.speedUpSimulation) { + if (a <= pFast || g_simulator->speedUpSimulation) { a = a / pFast; return 0.5 * (FLOW_KNOBS->MIN_NETWORK_LATENCY * (1 - a) + FLOW_KNOBS->FAST_NETWORK_LATENCY / pFast * a); // 0.5ms average @@ -363,7 +363,7 @@ private: ACTOR static Future sender(Sim2Conn* self) { loop { wait(self->writtenBytes.onChange()); // takes place on peer! - ASSERT(g_simulator.getCurrentProcess() == self->peerProcess); + ASSERT(g_simulator->getCurrentProcess() == self->peerProcess); wait(delay(.002 * deterministicRandom()->random01())); self->sentBytes.set(self->writtenBytes.get()); // or possibly just some sometimes... } @@ -371,41 +371,41 @@ private: ACTOR static Future receiver(Sim2Conn* self) { loop { if (self->sentBytes.get() != self->receivedBytes.get()) - wait(g_simulator.onProcess(self->peerProcess)); + wait(g_simulator->onProcess(self->peerProcess)); while (self->sentBytes.get() == self->receivedBytes.get()) wait(self->sentBytes.onChange()); - ASSERT(g_simulator.getCurrentProcess() == self->peerProcess); + ASSERT(g_simulator->getCurrentProcess() == self->peerProcess); state int64_t pos = deterministicRandom()->random01() < .5 ? self->sentBytes.get() : deterministicRandom()->randomInt64(self->receivedBytes.get(), self->sentBytes.get() + 1); wait(delay(g_clogging.getSendDelay( self->process->address, self->peerProcess->address, self->isStableConnection()))); - wait(g_simulator.onProcess(self->process)); - ASSERT(g_simulator.getCurrentProcess() == self->process); + wait(g_simulator->onProcess(self->process)); + ASSERT(g_simulator->getCurrentProcess() == self->process); wait(delay(g_clogging.getRecvDelay( self->process->address, self->peerProcess->address, self->isStableConnection()))); - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); if (self->stopReceive.isReady()) { wait(Future(Never())); } self->receivedBytes.set(pos); wait(Future(Void())); // Prior notification can delete self and cancel this actor - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); } } ACTOR static Future whenReadable(Sim2Conn* self) { try { loop { if (self->readBytes.get() != self->receivedBytes.get()) { - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); return Void(); } wait(self->receivedBytes.onChange()); self->rollRandomClose(); } } catch (Error& e) { - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); throw; } } @@ -415,20 +415,20 @@ private: if (!self->peer) return Void(); if (self->peer->availableSendBufferForPeer() > 0) { - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); return Void(); } try { wait(self->peer->receivedBytes.onChange()); - ASSERT(g_simulator.getCurrentProcess() == self->peerProcess); + ASSERT(g_simulator->getCurrentProcess() == self->peerProcess); } catch (Error& e) { if (e.code() != error_code_broken_promise) throw; } - wait(g_simulator.onProcess(self->process)); + wait(g_simulator->onProcess(self->process)); } } catch (Error& e) { - ASSERT(g_simulator.getCurrentProcess() == self->process); + ASSERT(g_simulator->getCurrentProcess() == self->process); throw; } } @@ -436,9 +436,9 @@ private: void rollRandomClose() { // make sure connections between parenta and their childs are not closed if (!stableConnection && - now() - g_simulator.lastConnectionFailure > g_simulator.connectionFailuresDisableDuration && + now() - g_simulator->lastConnectionFailure > g_simulator->connectionFailuresDisableDuration && deterministicRandom()->random01() < .00001) { - g_simulator.lastConnectionFailure = now(); + g_simulator->lastConnectionFailure = now(); double a = deterministicRandom()->random01(), b = deterministicRandom()->random01(); CODE_PROBE(true, "Simulated connection failure", probe::context::sim2, probe::assert::simOnly); TraceEvent("ConnectionFailure", dbgid) @@ -460,7 +460,7 @@ private: } ACTOR static Future trackLeakedConnection(Sim2Conn* self) { - wait(g_simulator.onProcess(self->process)); + wait(g_simulator->onProcess(self->process)); if (self->process->address.isPublic()) { wait(delay(FLOW_KNOBS->CONNECTION_MONITOR_IDLE_TIMEOUT * FLOW_KNOBS->CONNECTION_MONITOR_IDLE_TIMEOUT * 1.5 + FLOW_KNOBS->CONNECTION_MONITOR_LOOP_TIME * 2.1 + FLOW_KNOBS->CONNECTION_MONITOR_TIMEOUT)); @@ -517,7 +517,7 @@ public: int mode, Reference diskParameters = makeReference(25000, 150000000), bool delayOnWrite = true) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); if (++openCount >= 6000) { @@ -527,15 +527,15 @@ public: if (openCount == 4000) { TraceEvent(SevWarnAlways, "DisableConnectionFailures_TooManyFiles").log(); - g_simulator.speedUpSimulation = true; - g_simulator.connectionFailuresDisableDuration = 1e6; + g_simulator->speedUpSimulation = true; + g_simulator->connectionFailuresDisableDuration = 1e6; } // Filesystems on average these days seem to start to have limits of around 255 characters for a // filename. We add ".part" below, so we need to stay under 250. ASSERT(basename(filename).size() < 250); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { wait(delay(FLOW_KNOBS->MIN_OPEN_TIME + deterministicRandom()->random01() * (FLOW_KNOBS->MAX_OPEN_TIME - FLOW_KNOBS->MIN_OPEN_TIME))); @@ -561,11 +561,11 @@ public: platform::makeTemporary(open_filename.c_str()); SimpleFile* simpleFile = new SimpleFile(h, diskParameters, delayOnWrite, filename, open_filename, flags); state Reference file = Reference(simpleFile); - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return file; } catch (Error& e) { state Error err = e; - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } @@ -779,7 +779,7 @@ private: if (self->flags & OPEN_ATOMIC_WRITE_AND_CREATE) { self->flags &= ~OPEN_ATOMIC_WRITE_AND_CREATE; - auto& machineCache = g_simulator.getCurrentProcess()->machine->openFiles; + auto& machineCache = g_simulator->getCurrentProcess()->machine->openFiles; std::string sourceFilename = self->filename + ".part"; if (machineCache.count(sourceFilename)) { @@ -863,7 +863,7 @@ private: PromiseStream> nextConnection; ACTOR static void incoming(Reference self, double seconds, Reference conn) { - wait(g_simulator.onProcess(self->process)); + wait(g_simulator->onProcess(self->process)); wait(delay(seconds)); if (((Sim2Conn*)conn.getPtr())->isPeerGone() && deterministicRandom()->random01() < 0.5) return; @@ -881,7 +881,7 @@ private: NetworkAddress address; }; -#define g_sim2 ((Sim2&)g_simulator) +#define g_sim2 ((Sim2&)(*g_simulator)) class Sim2 final : public ISimulator, public INetworkConnections { public: @@ -1101,7 +1101,7 @@ public: SimThreadArgs(THREAD_FUNC_RETURN (*func)(void*), void* arg) : func(func), arg(arg) { ASSERT(g_network->isSimulated()); - currentProcess = g_simulator.getCurrentProcess(); + currentProcess = g_simulator->getCurrentProcess(); } }; @@ -1175,18 +1175,18 @@ public: // This is a _rudimentary_ simulation of the untrustworthiness of non-durable deletes and the possibility of // rebooting during a durable one. It isn't perfect: for example, on real filesystems testing // for the existence of a non-durably deleted file BEFORE a reboot will show that it apparently doesn't exist. - if (g_simulator.getCurrentProcess()->machine->openFiles.count(filename)) { - g_simulator.getCurrentProcess()->machine->openFiles.erase(filename); - g_simulator.getCurrentProcess()->machine->deletingOrClosingFiles.insert(filename); + if (g_simulator->getCurrentProcess()->machine->openFiles.count(filename)) { + g_simulator->getCurrentProcess()->machine->openFiles.erase(filename); + g_simulator->getCurrentProcess()->machine->deletingOrClosingFiles.insert(filename); } if (mustBeDurable || deterministicRandom()->random01() < 0.5) { - state ISimulator::ProcessInfo* currentProcess = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* currentProcess = g_simulator->getCurrentProcess(); state TaskPriority currentTaskID = g_network->getCurrentTask(); TraceEvent(SevDebug, "Sim2DeleteFileImpl") .detail("CurrentProcess", currentProcess->toString()) .detail("Filename", filename) .detail("Durable", mustBeDurable); - wait(g_simulator.onMachine(currentProcess)); + wait(g_simulator->onMachine(currentProcess)); try { wait(::delay(0.05 * deterministicRandom()->random01())); if (!currentProcess->rebooting) { @@ -1195,11 +1195,11 @@ public: wait(::delay(0.05 * deterministicRandom()->random01())); CODE_PROBE(true, "Simulated durable delete", probe::context::sim2, probe::assert::simOnly); } - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); return Void(); } catch (Error& e) { state Error err = e; - wait(g_simulator.onProcess(currentProcess, currentTaskID)); + wait(g_simulator->onProcess(currentProcess, currentTaskID)); throw err; } } else { @@ -1293,8 +1293,8 @@ public: m->machine = &machine; machine.processes.push_back(m); currentlyRebootingProcesses.erase(addresses.address); - m->excluded = g_simulator.isExcluded(NetworkAddress(ip, port, true, false)); - m->cleared = g_simulator.isCleared(addresses.address); + m->excluded = g_simulator->isExcluded(NetworkAddress(ip, port, true, false)); + m->cleared = g_simulator->isCleared(addresses.address); m->protocolVersion = protocol; m->setGlobal(enTDMetrics, (flowGlobalType)&m->tdmetrics); @@ -2378,8 +2378,8 @@ class UDPSimSocket : public IUDPSocket, ReferenceCounted { public: UDPSimSocket(NetworkAddress const& localAddress, Optional const& peerAddress) - : id(deterministicRandom()->randomUniqueID()), process(g_simulator.getCurrentProcess()), peerAddress(peerAddress), - actors(false), _localAddress(localAddress) { + : id(deterministicRandom()->randomUniqueID()), process(g_simulator->getCurrentProcess()), + peerAddress(peerAddress), actors(false), _localAddress(localAddress) { g_sim2.addressMap.emplace(_localAddress, process); ASSERT(process->boundUDPSockets.find(localAddress) == process->boundUDPSockets.end()); process->boundUDPSockets.emplace(localAddress, this); @@ -2482,7 +2482,7 @@ public: Future> Sim2::createUDPSocket(NetworkAddress toAddr) { NetworkAddress localAddress; - auto process = g_simulator.getCurrentProcess(); + auto process = g_simulator->getCurrentProcess(); if (process->address.ip.isV6()) { IPAddress::IPAddressStore store = process->address.ip.toV6(); uint16_t* ipParts = (uint16_t*)store.data(); @@ -2500,7 +2500,7 @@ Future> Sim2::createUDPSocket(NetworkAddress toAddr) { Future> Sim2::createUDPSocket(bool isV6) { NetworkAddress localAddress; - auto process = g_simulator.getCurrentProcess(); + auto process = g_simulator->getCurrentProcess(); if (process->address.ip.isV6() == isV6) { localAddress = process->address; } else { @@ -2522,8 +2522,8 @@ Future> Sim2::createUDPSocket(bool isV6) { void startNewSimulator(bool printSimTime) { ASSERT(!g_network); - g_network = g_pSimulator = new Sim2(printSimTime); - g_simulator.connectionFailuresDisableDuration = deterministicRandom()->random01() < 0.5 ? 0 : 1e6; + g_network = g_simulator = new Sim2(printSimTime); + g_simulator->connectionFailuresDisableDuration = deterministicRandom()->random01() < 0.5 ? 0 : 1e6; } ACTOR void doReboot(ISimulator::ProcessInfo* p, ISimulator::KillType kt) { @@ -2585,7 +2585,7 @@ ACTOR void doReboot(ISimulator::ProcessInfo* p, ISimulator::KillType kt) { p->rebooting = true; if ((kt == ISimulator::RebootAndDelete) || (kt == ISimulator::RebootProcessAndDelete)) { p->cleared = true; - g_simulator.clearAddress(p->address); + g_simulator->clearAddress(p->address); } p->shutdownSignal.send(kt); } catch (Error& e) { @@ -2597,10 +2597,10 @@ ACTOR void doReboot(ISimulator::ProcessInfo* p, ISimulator::KillType kt) { // Simulates delays for performing operations on disk Future waitUntilDiskReady(Reference diskParameters, int64_t size, bool sync) { - if (g_simulator.getCurrentProcess()->failedDisk) { + if (g_simulator->getCurrentProcess()->failedDisk) { return Never(); } - if (g_simulator.connectionFailuresDisableDuration > 1e4) + if (g_simulator->connectionFailuresDisableDuration > 1e4) return delay(0.0001); if (diskParameters->nextOperation < now()) @@ -2648,14 +2648,14 @@ int sf_open(const char* filename, int flags, int convFlags, int mode) { Future> Sim2FileSystem::open(const std::string& filename, int64_t flags, int64_t mode) { ASSERT((flags & IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE) || !(flags & IAsyncFile::OPEN_CREATE) || StringRef(filename).endsWith( - LiteralStringRef(".fdb-lock"))); // We don't use "ordinary" non-atomic file creation right now except for - // folder locking, and we don't have code to simulate its unsafeness. + ".fdb-lock"_sr)); // We don't use "ordinary" non-atomic file creation right now except for + // folder locking, and we don't have code to simulate its unsafeness. if ((flags & IAsyncFile::OPEN_EXCLUSIVE)) ASSERT(flags & IAsyncFile::OPEN_CREATE); if (flags & IAsyncFile::OPEN_UNCACHED) { - auto& machineCache = g_simulator.getCurrentProcess()->machine->openFiles; + auto& machineCache = g_simulator->getCurrentProcess()->machine->openFiles; std::string actualFilename = filename; if (flags & IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE) { actualFilename = filename + ".part"; diff --git a/fdbrpc/sim_validation.cpp b/fdbrpc/sim_validation.cpp index 4c9b7536d6..6fff16afd3 100644 --- a/fdbrpc/sim_validation.cpp +++ b/fdbrpc/sim_validation.cpp @@ -51,13 +51,13 @@ void debug_advanceVersion(UID id, int64_t version, const char* suffix) { } void debug_advanceMinCommittedVersion(UID id, int64_t version) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return; debug_advanceVersion(id, version, "min"); } void debug_advanceMaxCommittedVersion(UID id, int64_t version) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return; debug_advanceVersion(id, version, "max"); } @@ -67,7 +67,7 @@ bool debug_checkPartRestoredVersion(UID id, std::string context, std::string minormax, Severity sev = SevError) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; if (disabledMachines.count(id)) return false; @@ -88,33 +88,33 @@ bool debug_checkPartRestoredVersion(UID id, } bool debug_checkRestoredVersion(UID id, int64_t version, std::string context, Severity sev) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; return debug_checkPartRestoredVersion(id, version, context, "min", sev) || debug_checkPartRestoredVersion(id, version, context, "max", sev); } void debug_removeVersions(UID id) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return; validationData.erase(id.toString() + "min"); validationData.erase(id.toString() + "max"); } bool debug_versionsExist(UID id) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; return validationData.count(id.toString() + "min") != 0 || validationData.count(id.toString() + "max") != 0; } bool debug_checkMinRestoredVersion(UID id, int64_t version, std::string context, Severity sev) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; return debug_checkPartRestoredVersion(id, version, context, "min", sev); } bool debug_checkMaxRestoredVersion(UID id, int64_t version, std::string context, Severity sev) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; return debug_checkPartRestoredVersion(id, version, context, "max", sev); } @@ -129,13 +129,13 @@ void debug_setCheckRelocationDuration(bool check) { checkRelocationDuration = check; } void debug_advanceVersionTimestamp(int64_t version, double t) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return; timedVersionsValidationData[version] = t; } bool debug_checkVersionTime(int64_t version, double t, std::string context, Severity sev) { - if (!g_network->isSimulated() || !g_simulator.extraDatabases.empty()) + if (!g_network->isSimulated() || !g_simulator->extraDatabases.empty()) return false; if (!timedVersionsValidationData.count(version)) { TraceEvent(SevWarn, (context + "UnknownTime").c_str()) diff --git a/fdbrpc/tests/AuthzTlsTest.actor.cpp b/fdbrpc/tests/AuthzTlsTest.actor.cpp index 3cf7c5528e..54dedb05a7 100644 --- a/fdbrpc/tests/AuthzTlsTest.actor.cpp +++ b/fdbrpc/tests/AuthzTlsTest.actor.cpp @@ -21,12 +21,17 @@ #ifndef _WIN32 #include #include +#include +#include #include +#include #include #include #include #include +#include #include "flow/Arena.h" +#include "flow/Error.h" #include "flow/MkCert.h" #include "flow/ScopeExit.h" #include "flow/TLSConfig.actor.h" @@ -36,46 +41,64 @@ std::FILE* outp = stdout; +enum ChainLength : int { + NO_TLS = std::numeric_limits::min(), +}; + template -void log(Args&&... args) { +void logRaw(Args&&... args) { auto buf = fmt::memory_buffer{}; fmt::format_to(std::back_inserter(buf), std::forward(args)...); - fmt::print(outp, "{}\n", std::string_view(buf.data(), buf.size())); + fmt::print(outp, "{}", std::string_view(buf.data(), buf.size())); +} + +template +void logWithPrefix(const char* prefix, Args&&... args) { + auto buf = fmt::memory_buffer{}; + fmt::format_to(std::back_inserter(buf), std::forward(args)...); + fmt::print(outp, "{}{}\n", prefix, std::string_view(buf.data(), buf.size())); } template void logc(Args&&... args) { - auto buf = fmt::memory_buffer{}; - fmt::format_to(std::back_inserter(buf), "[CLIENT] "); - fmt::format_to(std::back_inserter(buf), std::forward(args)...); - fmt::print(outp, "{}\n", std::string_view(buf.data(), buf.size())); + logWithPrefix("[CLIENT] ", std::forward(args)...); } template void logs(Args&&... args) { - auto buf = fmt::memory_buffer{}; - fmt::format_to(std::back_inserter(buf), "[SERVER] "); - fmt::format_to(std::back_inserter(buf), std::forward(args)...); - fmt::print(outp, "{}\n", std::string_view(buf.data(), buf.size())); + logWithPrefix("[SERVER] ", std::forward(args)...); } template void logm(Args&&... args) { - auto buf = fmt::memory_buffer{}; - fmt::format_to(std::back_inserter(buf), "[ MAIN ] "); - fmt::format_to(std::back_inserter(buf), std::forward(args)...); - fmt::print(outp, "{}\n", std::string_view(buf.data(), buf.size())); + logWithPrefix("[ MAIN ] ", std::forward(args)...); +} + +std::string drainPipe(int pipeFd) { + int readRc = 0; + std::string ret; + char buf[PIPE_BUF]; + while ((readRc = ::read(pipeFd, buf, PIPE_BUF)) > 0) { + ret.append(buf, readRc); + } + if (readRc != 0) { + logm("Unexpected error draining pipe: {}", strerror(errno)); + throw std::runtime_error("pipe read error"); + } + return ret; } struct TLSCreds { + bool noTls = false; std::string certBytes; std::string keyBytes; std::string caBytes; }; -TLSCreds makeCreds(int chainLen, mkcert::ESide side) { - if (chainLen == 0) - return {}; +TLSCreds makeCreds(ChainLength chainLen, mkcert::ESide side) { + if (chainLen == 0 || chainLen == NO_TLS) { + return TLSCreds{ chainLen == NO_TLS, "", "", "" }; + } auto arena = Arena(); auto ret = TLSCreds{}; auto specs = mkcert::makeCertChainSpec(arena, std::labs(chainLen), side); @@ -97,9 +120,10 @@ TLSCreds makeCreds(int chainLen, mkcert::ESide side) { } enum class Result : int { - TRUSTED = 0, + ERROR = 0, + TRUSTED, UNTRUSTED, - ERROR, + TIMEOUT, }; template <> @@ -112,11 +136,43 @@ struct fmt::formatter { return fmt::format_to(ctx.out(), "TRUSTED"); else if (r == Result::UNTRUSTED) return fmt::format_to(ctx.out(), "UNTRUSTED"); + else if (r == Result::TIMEOUT) + return fmt::format_to(ctx.out(), "TIMEOUT"); else return fmt::format_to(ctx.out(), "ERROR"); } }; +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } + + template + auto format(ChainLength value, FormatContext& ctx) -> decltype(ctx.out()) { + if (value == NO_TLS) + return fmt::format_to(ctx.out(), "NO_TLS"); + else + return fmt::format_to(ctx.out(), "{}", static_cast>(value)); + } +}; + +template <> +struct fmt::formatter>> { + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } + + template + auto format(const std::vector>& entries, FormatContext& ctx) + -> decltype(ctx.out()) { + fmt::format_to(ctx.out(), "["); + bool first = true; + for (const auto& entry : entries) { + fmt::format_to(ctx.out(), "{}{{ {}, {} }}", (first ? "" : ", "), entry.first, entry.second); + first = false; + } + return fmt::format_to(ctx.out(), "]"); + } +}; + ACTOR template Future stopNetworkAfter(Future what) { T t = wait(what); @@ -165,25 +221,27 @@ struct SessionProbeReceiver final : NetworkMessageReceiver { bool isPublic() const override { return true; } }; -Future runServer(Future listenFuture, const Endpoint& endpoint, int addrPipe, int completionPipe) { +void runServer(const Endpoint& endpoint, int addrPipe, int completionPipe) { auto realAddr = FlowTransport::transport().getLocalAddresses().address; logs("Listening at {}", realAddr.toString()); logs("Endpoint token is {}", endpoint.token.toString()); + static_assert(std::is_trivially_destructible_v, + "NetworkAddress cannot be directly put on wire; need proper (de-)serialization"); // below writes/reads would block, but this is good enough for a test. if (sizeof(realAddr) != ::write(addrPipe, &realAddr, sizeof(realAddr))) { logs("Failed to write server addr to pipe: {}", strerror(errno)); - return Void(); + return; } if (sizeof(endpoint.token) != ::write(addrPipe, &endpoint.token, sizeof(endpoint.token))) { logs("Failed to write server endpoint to pipe: {}", strerror(errno)); - return Void(); + return; } auto done = false; if (sizeof(done) != ::read(completionPipe, &done, sizeof(done))) { logs("Failed to read completion flag from pipe: {}", strerror(errno)); - return Void(); + return; } - return Void(); + return; } ACTOR Future waitAndPrintResponse(Future response, Result* rc) { @@ -192,8 +250,13 @@ ACTOR Future waitAndPrintResponse(Future response, Result* rc logc("Probe response: trusted={} peerAddress={}", info.isPeerTrusted, info.peerAddress.toString()); *rc = info.isPeerTrusted ? Result::TRUSTED : Result::UNTRUSTED; } catch (Error& err) { - logc("Error: {}", err.what()); - *rc = Result::ERROR; + if (err.code() != error_code_operation_cancelled) { + logc("Unexpected error: {}", err.what()); + *rc = Result::ERROR; + } else { + logc("Timed out"); + *rc = Result::TIMEOUT; + } } return Void(); } @@ -201,9 +264,12 @@ ACTOR Future waitAndPrintResponse(Future response, Result* rc template int runHost(TLSCreds creds, int addrPipe, int completionPipe, Result expect) { auto tlsConfig = TLSConfig(IsServer ? TLSEndpointType::SERVER : TLSEndpointType::CLIENT); - tlsConfig.setCertificateBytes(creds.certBytes); - tlsConfig.setCABytes(creds.caBytes); - tlsConfig.setKeyBytes(creds.keyBytes); + bool const noTls = creds.noTls; + if (!noTls) { + tlsConfig.setCertificateBytes(creds.certBytes); + tlsConfig.setCABytes(creds.caBytes); + tlsConfig.setKeyBytes(creds.keyBytes); + } g_network = newNet2(tlsConfig); openTraceFile(NetworkAddress(), 10 << 20, @@ -213,19 +279,21 @@ int runHost(TLSCreds creds, int addrPipe, int completionPipe, Result expect) { FlowTransport::createInstance(!IsServer, 1, WLTOKEN_RESERVED_COUNT); auto& transport = FlowTransport::transport(); if constexpr (IsServer) { - auto addr = NetworkAddress::parse("127.0.0.1:0:tls"); + auto addr = NetworkAddress::parse(noTls ? "127.0.0.1:0" : "127.0.0.1:0:tls"); + auto endpoint = Endpoint(); + auto receiver = SessionProbeReceiver(); + auto listenFuture = transport.bind(addr, addr); + transport.addEndpoint(endpoint, &receiver, TaskPriority::ReadSocket); auto thread = std::thread([]() { g_network->run(); flushTraceFileVoid(); }); - auto endpoint = Endpoint(); - auto receiver = SessionProbeReceiver(); - transport.addEndpoint(endpoint, &receiver, TaskPriority::ReadSocket); - runServer(transport.bind(addr, addr), endpoint, addrPipe, completionPipe); + runServer(endpoint, addrPipe, completionPipe); auto cleanupGuard = ScopeExit([&thread]() { g_network->stop(); thread.join(); }); + return 0; } else { auto dest = Endpoint(); auto& serverAddr = dest.addresses.address; @@ -233,26 +301,27 @@ int runHost(TLSCreds creds, int addrPipe, int completionPipe, Result expect) { logc("Failed to read server addr from pipe: {}", strerror(errno)); return 1; } + if (noTls) + serverAddr.flags &= ~NetworkAddress::FLAG_TLS; + else + serverAddr.flags |= NetworkAddress::FLAG_TLS; auto& token = dest.token; if (sizeof(token) != ::read(addrPipe, &token, sizeof(token))) { logc("Failed to read server endpoint token from pipe: {}", strerror(errno)); return 2; } - logc("Server address is {}", serverAddr.toString()); + logc("Server address is {}{}", serverAddr.toString(), noTls ? " (TLS suffix removed)" : ""); logc("Server endpoint token is {}", token.toString()); auto sessionProbeReq = SessionProbeRequest{}; transport.sendUnreliable(SerializeSource(sessionProbeReq), dest, true /*openConnection*/); logc("Request is sent"); - auto probeResponse = sessionProbeReq.reply.getFuture(); - auto result = Result::TRUSTED; - auto timeout = delay(5); - auto complete = waitAndPrintResponse(probeResponse, &result); - auto f = stopNetworkAfter(complete || timeout); auto rc = 0; - g_network->run(); - if (!complete.isReady()) { - logc("Error: Probe request timed out"); - rc = 3; + auto result = Result::ERROR; + { + auto timeout = delay(expect == Result::TIMEOUT ? 0.5 : 5); + auto complete = waitAndPrintResponse(sessionProbeReq.reply.getFuture(), &result); + auto f = stopNetworkAfter(complete || timeout); + g_network->run(); } auto done = true; if (sizeof(done) != ::write(completionPipe, &done, sizeof(done))) { @@ -269,19 +338,48 @@ int runHost(TLSCreds creds, int addrPipe, int completionPipe, Result expect) { } return rc; } - return 0; } -int runTlsTest(int serverChainLen, int clientChainLen) { - log("==== BEGIN TESTCASE ===="); +Result getExpectedResult(ChainLength serverChainLen, ChainLength clientChainLen) { auto expect = Result::ERROR; if (serverChainLen > 0) { - if (clientChainLen > 0) + if (clientChainLen == NO_TLS || clientChainLen < 0) { + expect = Result::TIMEOUT; + } else if (clientChainLen > 0) { expect = Result::TRUSTED; - else if (clientChainLen == 0) + } else if (clientChainLen == 0) { expect = Result::UNTRUSTED; + } + } else if (serverChainLen == NO_TLS && clientChainLen == NO_TLS) { + expect = Result::TRUSTED; + } else { + expect = Result::TIMEOUT; } - log("Cert chain length: server={} client={}", serverChainLen, clientChainLen); + return expect; +} + +bool waitPid(pid_t subProcPid, const char* procName) { + auto status = int{}; + auto pid = ::waitpid(subProcPid, &status, 0); + if (pid < 0) { + logm("{} subprocess waitpid() failed with {}", procName, strerror(errno)); + return false; + } else { + if (status != 0) { + logm("{} subprocess had error: rc={}", procName, status); + return false; + } else { + logm("{} subprocess waitpid() OK", procName); + return true; + } + } +} + +int runTlsTest(ChainLength serverChainLen, ChainLength clientChainLen) { + logm("==== BEGIN TESTCASE ===="); + auto const expect = getExpectedResult(serverChainLen, clientChainLen); + using namespace std::literals::string_literals; + logm("Cert chain length: server={} client={}", serverChainLen, clientChainLen); auto arena = Arena(); auto serverCreds = makeCreds(serverChainLen, mkcert::ESide::Server); auto clientCreds = makeCreds(clientChainLen, mkcert::ESide::Client); @@ -289,65 +387,123 @@ int runTlsTest(int serverChainLen, int clientChainLen) { std::swap(serverCreds.caBytes, clientCreds.caBytes); auto clientPid = pid_t{}; auto serverPid = pid_t{}; - int addrPipe[2]; - int completionPipe[2]; - if (::pipe(addrPipe) || ::pipe(completionPipe)) { + int addrPipe[2], completionPipe[2], serverStdoutPipe[2], clientStdoutPipe[2]; + if (::pipe(addrPipe) || ::pipe(completionPipe) || ::pipe(serverStdoutPipe) || ::pipe(clientStdoutPipe)) { logm("Pipe open failed: {}", strerror(errno)); return 1; } - auto pipeCleanup = ScopeExit([&addrPipe, &completionPipe]() { - ::close(addrPipe[0]); - ::close(addrPipe[1]); - ::close(completionPipe[0]); - ::close(completionPipe[1]); - }); - serverPid = fork(); - if (serverPid == 0) { - _exit(runHost(std::move(serverCreds), addrPipe[1], completionPipe[0], expect)); - } - clientPid = fork(); - if (clientPid == 0) { - _exit(runHost(std::move(clientCreds), addrPipe[0], completionPipe[1], expect)); - } - auto pid = pid_t{}; - auto status = int{}; - pid = waitpid(clientPid, &status, 0); auto ok = true; - if (pid < 0) { - logm("waitpid() for client failed with {}", strerror(errno)); - ok = false; - } else { - if (status != 0) { - logm("Client error: rc={}", status); - ok = false; - } else { - logm("Client OK"); + { + serverPid = fork(); + if (serverPid == -1) { + logm("fork() for server subprocess failed: {}", strerror(errno)); + return 1; + } else if (serverPid == 0) { + // server subprocess + ::close(addrPipe[0]); // close address-in pipe (server writes its own address for client) + ::close( + completionPipe[1]); // close completion-flag-out pipe (server awaits/reads completion flag from client) + ::close(clientStdoutPipe[0]); + ::close(clientStdoutPipe[1]); + ::close(serverStdoutPipe[0]); + auto pipeCleanup = ScopeExit([&addrPipe, &completionPipe]() { + ::close(addrPipe[1]); + ::close(completionPipe[0]); + }); + if (-1 == ::dup2(serverStdoutPipe[1], STDOUT_FILENO)) { + logs("Failed to redirect server stdout to pipe: {}", strerror(errno)); + ::close(serverStdoutPipe[1]); + return 1; + } + _exit(runHost(std::move(serverCreds), addrPipe[1], completionPipe[0], expect)); } - } - pid = waitpid(serverPid, &status, 0); - if (pid < 0) { - logm("waitpid() for server failed with {}", strerror(errno)); - ok = false; - } else { - if (status != 0) { - logm("Server error: rc={}", status); - ok = false; - } else { - logm("Server OK"); + auto serverProcCleanup = ScopeExit([&ok, serverPid]() { + if (!waitPid(serverPid, "Server")) + ok = false; + }); + clientPid = fork(); + if (clientPid == -1) { + logm("fork() for client subprocess failed: {}", strerror(errno)); + return 1; + } else if (clientPid == 0) { + ::close(addrPipe[1]); + ::close(completionPipe[0]); + ::close(serverStdoutPipe[0]); + ::close(serverStdoutPipe[1]); + ::close(clientStdoutPipe[0]); + auto pipeCleanup = ScopeExit([&addrPipe, &completionPipe]() { + ::close(addrPipe[0]); + ::close(completionPipe[1]); + }); + if (-1 == ::dup2(clientStdoutPipe[1], STDOUT_FILENO)) { + logs("Failed to redirect client stdout to pipe: {}", strerror(errno)); + ::close(clientStdoutPipe[1]); + return 1; + } + _exit(runHost(std::move(clientCreds), addrPipe[0], completionPipe[1], expect)); } + auto clientProcCleanup = ScopeExit([&ok, clientPid]() { + if (!waitPid(clientPid, "Client")) + ok = false; + }); } - log(ok ? "OK" : "FAILED"); - return 0; + // main process + ::close(addrPipe[0]); + ::close(addrPipe[1]); + ::close(completionPipe[0]); + ::close(completionPipe[1]); + ::close(serverStdoutPipe[1]); + ::close(clientStdoutPipe[1]); + auto pipeCleanup = ScopeExit([&]() { + ::close(serverStdoutPipe[0]); + ::close(clientStdoutPipe[0]); + }); + std::string const clientStdout = drainPipe(clientStdoutPipe[0]); + logm("/// Begin Client STDOUT ///"); + logRaw(clientStdout); + logm("/// End Client STDOUT ///"); + std::string const serverStdout = drainPipe(serverStdoutPipe[0]); + logm("/// Begin Server STDOUT ///"); + logRaw(serverStdout); + logm("/// End Server STDOUT ///"); + logm(ok ? "OK" : "FAILED"); + return !ok; } -int main() { - std::pair inputs[] = { { 3, 2 }, { 4, 0 }, { 1, 3 }, { 1, 0 }, { 2, 0 }, { 3, 3 }, { 3, 0 } }; +int main(int argc, char** argv) { + unsigned seed = std::time(nullptr); + if (argc > 1) + seed = std::stoul(argv[1]); + std::srand(seed); + logm("Seed: {}", seed); + auto categoryToValue = [](int category) -> ChainLength { + if (category == 2 || category == -2) { + return static_cast(category + std::rand() % 3); + } else { + return static_cast(category); + } + }; + std::vector> inputs; + std::vector categories{ 0, NO_TLS, 1, -1, 2, -2 }; + for (auto lhs : categories) { + for (auto rhs : categories) { + auto input = std::pair(categoryToValue(lhs), categoryToValue(rhs)); + inputs.push_back(input); + } + } + std::vector> failed; for (auto input : inputs) { auto [serverChainLen, clientChainLen] = input; - if (auto rc = runTlsTest(serverChainLen, clientChainLen)) - return rc; + if (runTlsTest(serverChainLen, clientChainLen)) + failed.push_back({ serverChainLen, clientChainLen }); + } + if (!failed.empty()) { + logm("Test Failed: {}/{} cases: {}", failed.size(), inputs.size(), failed); + return 1; + } else { + logm("Test OK: {}/{} cases passed", inputs.size(), inputs.size()); + return 0; } - return 0; } #else // _WIN32 diff --git a/fdbrpc/tests/CMakeLists.txt b/fdbrpc/tests/CMakeLists.txt index fa2afa5e89..d01bed6f17 100644 --- a/fdbrpc/tests/CMakeLists.txt +++ b/fdbrpc/tests/CMakeLists.txt @@ -4,5 +4,6 @@ if(NOT WIN32) if(NOT OPEN_FOR_IDE) add_test(NAME authorization_tls_unittest COMMAND $) + set_tests_properties(authorization_tls_unittest PROPERTIES TIMEOUT 120) endif() endif() diff --git a/fdbserver/ApplyMetadataMutation.cpp b/fdbserver/ApplyMetadataMutation.cpp index 6971b93af2..2353e4326b 100644 --- a/fdbserver/ApplyMetadataMutation.cpp +++ b/fdbserver/ApplyMetadataMutation.cpp @@ -83,8 +83,8 @@ public: uid_applyMutationsData(proxyCommitData_.firstProxy ? &proxyCommitData_.uid_applyMutationsData : nullptr), commit(proxyCommitData_.commit), cx(proxyCommitData_.cx), committedVersion(&proxyCommitData_.committedVersion), storageCache(&proxyCommitData_.storageCache), tag_popped(&proxyCommitData_.tag_popped), - tssMapping(&proxyCommitData_.tssMapping), tenantMap(&proxyCommitData_.tenantMap), initialCommit(initialCommit_), - dbInfo(proxyCommitData_.db) {} + tssMapping(&proxyCommitData_.tssMapping), tenantMap(&proxyCommitData_.tenantMap), + tenantIdIndex(&proxyCommitData_.tenantIdIndex), initialCommit(initialCommit_), dbInfo(proxyCommitData_.db) {} ApplyMetadataMutationsImpl(const SpanContext& spanContext_, ResolverData& resolverData_, @@ -134,6 +134,7 @@ private: std::unordered_map* tssMapping = nullptr; std::map* tenantMap = nullptr; + std::unordered_map* tenantIdIndex = nullptr; // true if the mutations were already written to the txnStateStore as part of recovery bool initialCommit = false; @@ -168,7 +169,7 @@ private: } else { ASSERT(cipherKeys != nullptr); Arena arena; - toCommit->writeTypedMessage(m.encryptMetadata(*cipherKeys, arena)); + toCommit->writeTypedMessage(m.encryptMetadata(*cipherKeys, arena, BlobCipherMetrics::TLOG)); } } @@ -327,7 +328,8 @@ private: } void checkSetConfigKeys(MutationRef m) { - if (!m.param1.startsWith(configKeysPrefix) && m.param1 != coordinatorsKey) { + if (!m.param1.startsWith(configKeysPrefix) && m.param1 != coordinatorsKey && + m.param1 != previousCoordinatorsKey) { return; } if (Optional(m.param2) != @@ -343,7 +345,8 @@ private: TraceEvent("MutationRequiresRestart", dbgid) .detail("M", m) .detail("PrevValue", t.orDefault("(none)"_sr)) - .detail("ToCommit", toCommit != nullptr); + .detail("ToCommit", toCommit != nullptr) + .detail("InitialCommit", initialCommit); confChange = true; } } @@ -657,13 +660,21 @@ private: void checkSetTenantMapPrefix(MutationRef m) { KeyRef prefix = TenantMetadata::tenantMap().subspace.begin; if (m.param1.startsWith(prefix)) { + TenantName tenantName = m.param1.removePrefix(prefix); + TenantMapEntry tenantEntry = TenantMapEntry::decode(m.param2); + if (tenantMap) { ASSERT(version != invalidVersion); - TenantName tenantName = m.param1.removePrefix(prefix); - TenantMapEntry tenantEntry = TenantMapEntry::decode(m.param2); - TraceEvent("CommitProxyInsertTenant", dbgid).detail("Tenant", tenantName).detail("Version", version); + TraceEvent("CommitProxyInsertTenant", dbgid) + .detail("Tenant", tenantName) + .detail("Id", tenantEntry.id) + .detail("Version", version); + (*tenantMap)[tenantName] = tenantEntry; + if (tenantIdIndex) { + (*tenantIdIndex)[tenantEntry.id] = tenantName; + } } if (!initialCommit) { @@ -1070,6 +1081,17 @@ private: auto startItr = tenantMap->lower_bound(startTenant); auto endItr = tenantMap->lower_bound(endTenant); + + if (tenantIdIndex) { + // Iterate over iterator-range and remove entries from TenantIdName map + // TODO: O(n) operation, optimize cpu + auto itr = startItr; + while (itr != endItr) { + tenantIdIndex->erase(itr->second.id); + itr++; + } + } + tenantMap->erase(startItr, endItr); } @@ -1116,6 +1138,9 @@ private: if (initialCommit) { return; } + if (range.contains(previousCoordinatorsKey)) { + txnStateStore->clear(singleKeyRange(previousCoordinatorsKey)); + } if (range.contains(coordinatorsKey)) { txnStateStore->clear(singleKeyRange(coordinatorsKey)); } diff --git a/fdbserver/BackupProgress.actor.cpp b/fdbserver/BackupProgress.actor.cpp index 1055cae265..e5719dd007 100644 --- a/fdbserver/BackupProgress.actor.cpp +++ b/fdbserver/BackupProgress.actor.cpp @@ -185,7 +185,7 @@ TEST_CASE("/BackupProgress/Unfinished") { const Tag tag1(tagLocalityLogRouter, 0); epochInfos.insert({ epoch1, ILogSystem::EpochTagsVersionsInfo(1, begin1, end1) }); BackupProgress progress(UID(0, 0), epochInfos); - progress.setBackupStartedValue(Optional(LiteralStringRef("1"))); + progress.setBackupStartedValue(Optional("1"_sr)); std::map, std::map> unfinished = progress.getUnfinishedBackup(); diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 3828f16df1..82cf1b1acf 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -20,6 +20,7 @@ #include "fdbclient/BackupAgent.actor.h" #include "fdbclient/BackupContainer.h" +#include "fdbclient/BlobCipher.h" #include "fdbclient/DatabaseContext.h" #include "fdbclient/CommitProxyInterface.h" #include "fdbclient/SystemData.h" @@ -79,7 +80,7 @@ struct VersionedMessage { // In case the mutation is encrypted, get the decrypted mutation and also update message to point to // the decrypted mutation. // We use dedicated arena for decrypt buffer, as the other arena is used to count towards backup lock bytes. - *m = m->decrypt(cipherKeys, decryptArena, &message); + *m = m->decrypt(cipherKeys, decryptArena, BlobCipherMetrics::BACKUP, &message); } return normalKeys.contains(m->param1) || m->param1 == metadataVersionKey; } @@ -780,7 +781,7 @@ ACTOR Future saveMutationsToFile(BackupData* self, // Fetch cipher keys if any of the messages are encrypted. if (!cipherDetails.empty()) { std::unordered_map> getCipherKeysResult = - wait(getEncryptCipherKeys(self->db, cipherDetails)); + wait(getEncryptCipherKeys(self->db, cipherDetails, BlobCipherMetrics::BLOB_GRANULE)); cipherKeys = getCipherKeysResult; } @@ -1068,7 +1069,7 @@ ACTOR static Future monitorWorkerPause(BackupData* self) { tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); Optional value = wait(tr->get(backupPausedKey)); - bool paused = value.present() && value.get() == LiteralStringRef("1"); + bool paused = value.present() && value.get() == "1"_sr; if (self->paused.get() != paused) { TraceEvent(paused ? "BackupWorkerPaused" : "BackupWorkerResumed", self->myId).log(); self->paused.set(paused); diff --git a/fdbserver/BlobGranuleServerCommon.actor.cpp b/fdbserver/BlobGranuleServerCommon.actor.cpp index 9ffa83aab7..750eb1592d 100644 --- a/fdbserver/BlobGranuleServerCommon.actor.cpp +++ b/fdbserver/BlobGranuleServerCommon.actor.cpp @@ -131,7 +131,7 @@ ACTOR Future getForcePurgedState(Transaction* tr, KeyRange key ASSERT(values[0].value != values[1].value); return ForcedPurgeState::SomePurged; } else { - return values[0].value == LiteralStringRef("1") ? ForcedPurgeState::AllPurged : ForcedPurgeState::NonePurged; + return values[0].value == "1"_sr ? ForcedPurgeState::AllPurged : ForcedPurgeState::NonePurged; } } @@ -155,7 +155,7 @@ void GranuleFiles::getFiles(Version beginVersion, int64_t& deltaBytesCounter, bool summarize) const { BlobFileIndex dummyIndex; // for searching - + ASSERT(!snapshotFiles.empty()); // if beginVersion == 0 or we can collapse, find the latest snapshot <= readVersion auto snapshotF = snapshotFiles.end(); if (beginVersion == 0 || canCollapse) { diff --git a/fdbserver/BlobManager.actor.cpp b/fdbserver/BlobManager.actor.cpp index ce372b2c12..2a0b36c58f 100644 --- a/fdbserver/BlobManager.actor.cpp +++ b/fdbserver/BlobManager.actor.cpp @@ -25,6 +25,7 @@ #include #include +#include "fdbclient/ServerKnobs.h" #include "fdbrpc/simulator.h" #include "fmt/format.h" #include "fdbclient/BackupContainerFileSystem.h" @@ -1944,6 +1945,7 @@ ACTOR Future maybeSplitRange(Reference bmData, for (auto it = splitPoints.boundaries.begin(); it != splitPoints.boundaries.end(); it++) { bmData->mergeBoundaries[it->first] = it->second; } + break; } catch (Error& e) { if (e.code() == error_code_operation_cancelled) { @@ -2611,7 +2613,7 @@ ACTOR Future granuleMergeChecker(Reference bmData) { double sleepTime = SERVER_KNOBS->BG_MERGE_CANDIDATE_DELAY_SECONDS; // Check more frequently if speedUpSimulation is set. This may - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { sleepTime = std::min(5.0, sleepTime); } // start delay at the start of the loop, to account for time spend in calculation @@ -3288,7 +3290,7 @@ ACTOR Future loadForcePurgedRanges(Reference bmData) { // Add the mappings to our in memory key range map for (int rangeIdx = 0; rangeIdx < results.size() - 1; rangeIdx++) { - if (results[rangeIdx].value == LiteralStringRef("1")) { + if (results[rangeIdx].value == "1"_sr) { Key rangeStartKey = results[rangeIdx].key.removePrefix(blobGranuleForcePurgedKeys.begin); Key rangeEndKey = results[rangeIdx + 1].key.removePrefix(blobGranuleForcePurgedKeys.begin); // note: if the old owner is dead, we handle this in rangeAssigner @@ -3455,6 +3457,10 @@ ACTOR Future recoverBlobManager(Reference bmData) { // Once we acknowledge the existing blob workers, we can go ahead and recruit new ones bmData->startRecruiting.trigger(); + bmData->initBStore(); + if (isFullRestoreMode()) + wait(loadManifest(bmData->db, bmData->bstore)); + state Reference tr = makeReference(bmData->db); // set up force purge keys if not done already @@ -3468,7 +3474,7 @@ ACTOR Future recoverBlobManager(Reference bmData) { break; } wait(checkManagerLock(tr, bmData)); - wait(krmSetRange(tr, blobGranuleForcePurgedKeys.begin, normalKeys, LiteralStringRef("0"))); + wait(krmSetRange(tr, blobGranuleForcePurgedKeys.begin, normalKeys, "0"_sr)); wait(tr->commit()); tr->reset(); break; @@ -3766,7 +3772,7 @@ ACTOR Future chaosRangeMover(Reference bmData) { loop { wait(delay(30.0)); - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { if (BM_DEBUG) { printf("Range mover stopping\n"); } @@ -4456,8 +4462,7 @@ ACTOR Future purgeRange(Reference self, KeyRangeRef range wait(checkManagerLock(&tr, self)); // FIXME: need to handle this better if range is unaligned. Need to not truncate existing granules, and // instead cover whole of intersecting granules at begin/end - wait(krmSetRangeCoalescing( - &tr, blobGranuleForcePurgedKeys.begin, range, normalKeys, LiteralStringRef("1"))); + wait(krmSetRangeCoalescing(&tr, blobGranuleForcePurgedKeys.begin, range, normalKeys, "1"_sr)); wait(tr.commit()); break; } catch (Error& e) { @@ -5042,6 +5047,28 @@ ACTOR Future bgccCheckGranule(Reference bmData, KeyRan return bytesRead; } +// Check if there is any pending split. It's a precheck for manifest backup +ACTOR Future hasPendingSplit(Reference self) { + state Transaction tr(self->db); + loop { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + RangeResult result = wait(tr.getRange(blobGranuleSplitKeys, GetRangeLimits::BYTE_LIMIT_UNLIMITED)); + for (auto& row : result) { + std::pair gss = decodeBlobGranuleSplitValue(row.value); + if (gss.first != BlobGranuleSplitState::Done) { + return true; + } + } + return false; + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + // 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 bgConsistencyCheck(Reference bmData) { @@ -5053,15 +5080,25 @@ ACTOR Future bgConsistencyCheck(Reference bmData) { if (BM_DEBUG) { fmt::print("BGCC starting\n"); } + if (isFullRestoreMode()) + wait(printRestoreSummary(bmData->db, bmData->bstore)); loop { - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { if (BM_DEBUG) { printf("BGCC stopping\n"); } 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; @@ -5240,10 +5277,10 @@ TEST_CASE("/blobmanager/updateranges") { RangeResult dbDataEmpty; std::vector> kbrRanges; - StringRef keyA = StringRef(ar, LiteralStringRef("A")); - StringRef keyB = StringRef(ar, LiteralStringRef("B")); - StringRef keyC = StringRef(ar, LiteralStringRef("C")); - StringRef keyD = StringRef(ar, LiteralStringRef("D")); + StringRef keyA = StringRef(ar, "A"_sr); + StringRef keyB = StringRef(ar, "B"_sr); + StringRef keyC = StringRef(ar, "C"_sr); + StringRef keyD = StringRef(ar, "D"_sr); // db data setup RangeResult dbDataAB; diff --git a/fdbserver/BlobManifest.actor.cpp b/fdbserver/BlobManifest.actor.cpp new file mode 100644 index 0000000000..5083f69b8f --- /dev/null +++ b/fdbserver/BlobManifest.actor.cpp @@ -0,0 +1,374 @@ +/* + * BlobManifest.actor.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fdbclient/BackupContainer.h" +#include "fdbserver/Knobs.h" +#include "flow/FastRef.h" +#include "flow/flow.h" +#include "fdbclient/NativeAPI.actor.h" +#include "fdbclient/BlobConnectionProvider.h" +#include "fdbclient/FDBTypes.h" +#include "fdbclient/KeyRangeMap.h" +#include "fdbclient/SystemData.h" +#include "fdbclient/BackupContainerFileSystem.h" +#include "fdbclient/BlobGranuleReader.actor.h" +#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 + +#define ENABLE_DEBUG_PRINT true +template +inline void dprint(fmt::format_string fmt, T&&... args) { + if (ENABLE_DEBUG_PRINT) + fmt::print(fmt, std::forward(args)...); +} + +// This class dumps blob manifest to external blob storage. +class BlobManifestDumper : public ReferenceCounted { +public: + BlobManifestDumper(Database& db, Reference blobConn) : db_(db), blobConn_(blobConn) {} + virtual ~BlobManifestDumper() {} + + // Execute the dumper + ACTOR static Future execute(Reference self) { + try { + state Standalone manifest; + Standalone> rows = wait(getSystemKeys(self)); + manifest.rows = rows; + Value data = encode(manifest); + wait(writeToFile(self, data)); + } catch (Error& e) { + dprint("WARNING: unexpected blob manifest dumper error {}\n", e.what()); // skip error handling for now + } + return Void(); + } + +private: + // Return system keys that to be backed up + ACTOR static Future>> getSystemKeys(Reference self) { + state Standalone> rows; + state Transaction tr(self->db_); + loop { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + state std::vector ranges = { + blobGranuleMappingKeys, // Map granule to workers. Track the active granules + blobGranuleFileKeys, // Map a granule version to granule files. Track files for a granule + blobGranuleHistoryKeys, // Map granule to its parents and parent bundaries. for time-travel read + blobRangeKeys // Key ranges managed by blob + }; + for (auto range : ranges) { + // todo use getRangeStream for better performance + RangeResult result = wait(tr.getRange(range, GetRangeLimits::BYTE_LIMIT_UNLIMITED)); + for (auto& row : result) { + rows.push_back_deep(rows.arena(), KeyValueRef(row.key, row.value)); + } + } + return rows; + } catch (Error& e) { + wait(tr.onError(e)); + } + } + } + + // Write data to blob manifest file + ACTOR static Future writeToFile(Reference self, Value data) { + state Reference writer; + state std::string fileName; + + std::tie(writer, fileName) = self->blobConn_->createForWrite(MANIFEST_FILENAME); + state Reference 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()); + return Void(); + } + + // Encode manifest as binary data + static Value encode(BlobManifest& manifest) { + BinaryWriter wr(IncludeVersion(ProtocolVersion::withBlobGranuleFile())); + wr << manifest; + return wr.toValue(); + } + + Database db_; + Reference blobConn_; +}; + +// Defines granule info that interests full restore +struct BlobGranuleVersion { + // Two constructors required by VectorRef + BlobGranuleVersion() {} + BlobGranuleVersion(Arena& a, const BlobGranuleVersion& copyFrom) + : granuleID(copyFrom.granuleID), keyRange(a, copyFrom.keyRange), version(copyFrom.version), + sizeInBytes(copyFrom.sizeInBytes) {} + + UID granuleID; + KeyRangeRef keyRange; + Version version; + int64_t sizeInBytes; +}; + +// Defines a vector for BlobGranuleVersion +typedef Standalone> BlobGranuleVersionVector; + +// Defines filename, version, size for each granule file that interests full restore +struct GranuleFileVersion { + Version version; + uint8_t fileType; + std::string filename; + int64_t sizeInBytes; +}; + +// This class is to load blob manifest into system key space, which is part of for bare metal restore +class BlobManifestLoader : public ReferenceCounted { +public: + BlobManifestLoader(Database& db, Reference blobConn) : db_(db), blobConn_(blobConn) {} + virtual ~BlobManifestLoader() {} + + // Execute the loader + ACTOR static Future execute(Reference self) { + try { + Value data = wait(readFromFile(self)); + Standalone manifest = decode(data); + wait(writeSystemKeys(self, manifest.rows)); + BlobGranuleVersionVector _ = wait(listGranules(self)); + } catch (Error& e) { + dprint("WARNING: unexpected manifest loader error {}\n", e.what()); // skip error handling so far + } + return Void(); + } + + // Print out a summary for blob granules + ACTOR static Future print(Reference self) { + state BlobGranuleVersionVector granules = wait(listGranules(self)); + for (auto granule : granules) { + wait(checkGranuleFiles(self, granule)); + } + return Void(); + } + +private: + // Read data from a manifest file + ACTOR static Future readFromFile(Reference self) { + state Reference readBstore = self->blobConn_->getForRead(MANIFEST_FILENAME); + state Reference reader = wait(readBstore->readFile(MANIFEST_FILENAME)); + state int64_t fileSize = wait(reader->size()); + state Arena arena; + state uint8_t* data = new (arena) uint8_t[fileSize]; + int readSize = wait(reader->read(data, fileSize, 0)); + dprint("Blob manifest restoring {} bytes\n", readSize); + StringRef ref = StringRef(data, readSize); + return Value(ref, arena); + } + + // Decode blob manifest from binary data + static Standalone decode(Value data) { + Standalone manifest; + BinaryReader binaryReader(data, IncludeVersion()); + binaryReader >> manifest; + return manifest; + } + + // Write system keys to database + ACTOR static Future writeSystemKeys(Reference self, VectorRef rows) { + state Transaction tr(self->db_); + loop { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + for (auto& row : rows) { + tr.set(row.key, row.value); + } + wait(tr.commit()); + dprint("Blob manifest loaded {} rows\n", rows.size()); + return Void(); + } catch (Error& e) { + wait(tr.onError(e)); + } + } + } + + // Iterate active granules and return their version/sizes + ACTOR static Future listGranules(Reference self) { + state Transaction tr(self->db_); + loop { + state BlobGranuleVersionVector results; + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + + try { + std::vector granules; + state int i = 0; + auto limit = GetRangeLimits::BYTE_LIMIT_UNLIMITED; + state RangeResult blobRanges = wait(tr.getRange(blobGranuleMappingKeys, limit)); + for (i = 0; i < blobRanges.size() - 1; i++) { + Key startKey = blobRanges[i].key.removePrefix(blobGranuleMappingKeys.begin); + Key endKey = blobRanges[i + 1].key.removePrefix(blobGranuleMappingKeys.begin); + state KeyRange granuleRange = KeyRangeRef(startKey, endKey); + try { + Standalone granule = wait(getGranule(&tr, granuleRange)); + results.push_back_deep(results.arena(), granule); + } catch (Error& e) { + dprint("missing data for key range {} \n", granuleRange.toString()); + } + } + return results; + } catch (Error& e) { + wait(tr.onError(e)); + } + } + } + + // Find the newest granule for a key range. The newest granule has the max version and relevant files + ACTOR static Future> getGranule(Transaction* tr, KeyRangeRef range) { + state Standalone granuleVersion; + KeyRange historyKeyRange = blobGranuleHistoryKeyRangeFor(range); + // reverse lookup so that the first row is the newest version + state RangeResult results = + wait(tr->getRange(historyKeyRange, GetRangeLimits::BYTE_LIMIT_UNLIMITED, Snapshot::False, Reverse::True)); + + for (KeyValueRef row : results) { + state KeyRange keyRange; + state Version version; + std::tie(keyRange, version) = decodeBlobGranuleHistoryKey(row.key); + Standalone historyValue = decodeBlobGranuleHistoryValue(row.value); + state UID granuleID = historyValue.granuleID; + + std::vector files = wait(listGranuleFiles(tr, granuleID)); + if (files.empty()) { + dprint("Granule {} doesn't have files for version {}\n", granuleID.toString(), version); + continue; // check previous version + } + + granuleVersion.keyRange = KeyRangeRef(granuleVersion.arena(), keyRange); + granuleVersion.granuleID = granuleID; + granuleVersion.version = files.back().version; + granuleVersion.sizeInBytes = granuleSizeInBytes(files); + + dprint("Granule {}: \n", granuleVersion.granuleID.toString()); + dprint(" {} {} {}\n", keyRange.toString(), granuleVersion.version, granuleVersion.sizeInBytes); + for (auto& file : files) { + dprint(" File {}: {} bytes\n", file.filename, file.sizeInBytes); + } + return granuleVersion; + } + throw restore_missing_data(); // todo a better error code + } + + // Return sum of last snapshot file size and delta files afterwards + static int64_t granuleSizeInBytes(std::vector files) { + int64_t totalSize = 0; + for (auto it = files.rbegin(); it < files.rend(); ++it) { + totalSize += it->sizeInBytes; + if (it->fileType == BG_FILE_TYPE_SNAPSHOT) + break; + } + return totalSize; + } + + // List all files for given granule + ACTOR static Future> listGranuleFiles(Transaction* tr, UID granuleID) { + state KeyRange fileKeyRange = blobGranuleFileKeyRangeFor(granuleID); + RangeResult results = wait(tr->getRange(fileKeyRange, GetRangeLimits::BYTE_LIMIT_UNLIMITED)); + + std::vector files; + for (auto& row : results) { + UID gid; + Version version; + uint8_t fileType; + Standalone filename; + int64_t offset; + int64_t length; + int64_t fullFileLength; + Optional cipherKeysMeta; + + std::tie(gid, version, fileType) = decodeBlobGranuleFileKey(row.key); + std::tie(filename, offset, length, fullFileLength, cipherKeysMeta) = decodeBlobGranuleFileValue(row.value); + GranuleFileVersion vs = { version, fileType, filename.toString(), length }; + files.push_back(vs); + } + return files; + } + + // Read data from granules and print out summary + ACTOR static Future checkGranuleFiles(Reference self, BlobGranuleVersion granule) { + state KeyRangeRef range = granule.keyRange; + state Version readVersion = granule.version; + state Transaction tr(self->db_); + loop { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + state Standalone> chunks = + wait(tr.readBlobGranules(range, 0, readVersion)); + state int count = 0; + for (const BlobGranuleChunkRef& chunk : chunks) { + RangeResult rows = wait(readBlobGranule(chunk, range, 0, readVersion, self->blobConn_)); + count += rows.size(); + } + + dprint("Restorable blob granule {} @ {}\n", granule.granuleID.toString(), readVersion); + dprint(" Range: {}\n", range.toString()); + dprint(" Keys : {}\n", count); + dprint(" Size : {} bytes\n", granule.sizeInBytes); + return Void(); + } catch (Error& e) { + wait(tr.onError(e)); + } + } + } + + Database db_; + Reference blobConn_; +}; + +// API to dump a manifest copy to external storage +ACTOR Future dumpManifest(Database db, Reference blobConn) { + Reference dumper = makeReference(db, blobConn); + wait(BlobManifestDumper::execute(dumper)); + return Void(); +} + +// API to load manifest from external blob storage +ACTOR Future loadManifest(Database db, Reference blobConn) { + Reference loader = makeReference(db, blobConn); + wait(BlobManifestLoader::execute(loader)); + return Void(); +} + +// API to print summary for restorable granules +ACTOR Future printRestoreSummary(Database db, Reference blobConn) { + Reference loader = makeReference(db, blobConn); + wait(BlobManifestLoader::print(loader)); + return Void(); +} diff --git a/fdbserver/BlobWorker.actor.cpp b/fdbserver/BlobWorker.actor.cpp index 9bb22f9e6a..454d26f1d4 100644 --- a/fdbserver/BlobWorker.actor.cpp +++ b/fdbserver/BlobWorker.actor.cpp @@ -19,6 +19,7 @@ */ #include "fdbclient/ClientBooleanParams.h" +#include "fdbclient/BlobCipher.h" #include "fdbclient/BlobGranuleFiles.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/KeyRangeMap.h" @@ -44,7 +45,6 @@ #include "fdbserver/WaitFailure.h" #include "flow/Arena.h" -#include "flow/BlobCipher.h" #include "flow/CompressionUtils.h" #include "flow/EncryptUtils.h" #include "flow/Error.h" @@ -253,7 +253,7 @@ struct BlobWorkerData : NonCopyable, ReferenceCounted { return false; } if (g_network->isSimulated()) { - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { return false; } return buggifyFull; @@ -357,16 +357,17 @@ ACTOR Future getLatestGranuleCipherKeys(Reference domains; - domains.emplace(tenantData->entry.id, StringRef(*arena, tenantData->name)); + std::unordered_map domains; + domains.emplace(tenantData->entry.id, tenantData->name); std::unordered_map> domainKeyMap = - wait(getLatestEncryptCipherKeys(bwData->dbInfo, domains)); + wait(getLatestEncryptCipherKeys(bwData->dbInfo, domains, BlobCipherMetrics::BLOB_GRANULE)); auto domainKeyItr = domainKeyMap.find(tenantData->entry.id); ASSERT(domainKeyItr != domainKeyMap.end()); cipherKeysCtx.textCipherKey = BlobGranuleCipherKey::fromBlobCipherKey(domainKeyItr->second, *arena); - TextAndHeaderCipherKeys systemCipherKeys = wait(getLatestSystemEncryptCipherKeys(bwData->dbInfo)); + TextAndHeaderCipherKeys systemCipherKeys = + wait(getLatestSystemEncryptCipherKeys(bwData->dbInfo, BlobCipherMetrics::BLOB_GRANULE)); cipherKeysCtx.headerCipherKey = BlobGranuleCipherKey::fromBlobCipherKey(systemCipherKeys.cipherHeaderKey, *arena); cipherKeysCtx.ivRef = makeString(AES_256_IV_LENGTH, *arena); @@ -392,7 +393,7 @@ ACTOR Future lookupCipherKey(Reference bwD std::unordered_set cipherDetailsSet; cipherDetailsSet.emplace(cipherDetails); state std::unordered_map> cipherKeyMap = - wait(getEncryptCipherKeys(bwData->dbInfo, cipherDetailsSet)); + wait(getEncryptCipherKeys(bwData->dbInfo, cipherDetailsSet, BlobCipherMetrics::BLOB_GRANULE)); ASSERT(cipherKeyMap.size() == 1); @@ -1972,6 +1973,10 @@ ACTOR Future blobGranuleUpdateFiles(Reference bwData, metadata->historyVersion = startState.history.present() ? startState.history.get().version : startVersion; } + // No need to start Change Feed in full restore mode + if (isFullRestoreMode()) + return Void(); + checkMergeCandidate = granuleCheckMergeCandidate(bwData, metadata, startState.granuleID, @@ -3396,10 +3401,12 @@ ACTOR Future doBlobGranuleFileRequest(Reference bwData, Bl } state Reference metadata = m; state Version granuleBeginVersion = req.beginVersion; - - choose { - when(wait(metadata->readable.getFuture())) {} - when(wait(metadata->cancelled.getFuture())) { throw wrong_shard_server(); } + // skip waiting for CF ready for recovery mode + if (!isFullRestoreMode()) { + choose { + when(wait(metadata->readable.getFuture())) {} + when(wait(metadata->cancelled.getFuture())) { throw wrong_shard_server(); } + } } // in case both readable and cancelled are ready, check cancelled @@ -3452,6 +3459,10 @@ ACTOR Future doBlobGranuleFileRequest(Reference bwData, Bl CODE_PROBE(true, "Granule Active Read"); // this is an active granule query loop { + // skip check since CF doesn't start for bare metal recovery mode + if (isFullRestoreMode()) { + break; + } if (!metadata->activeCFData.get().isValid() || !metadata->cancelled.canBeSet()) { throw wrong_shard_server(); } @@ -3492,12 +3503,14 @@ ACTOR Future doBlobGranuleFileRequest(Reference bwData, Bl // if feed was popped by another worker and BW only got empty versions, it wouldn't itself see that it // got popped, but we can still reject the in theory this should never happen with other protections but // it's a useful and inexpensive sanity check - Version emptyVersion = metadata->activeCFData.get()->popVersion - 1; - if (req.readVersion > metadata->durableDeltaVersion.get() && - emptyVersion > metadata->bufferedDeltaVersion) { - CODE_PROBE(true, "feed popped for read but granule updater didn't notice yet"); - // FIXME: could try to cancel the actor here somehow, but it should find out eventually - throw wrong_shard_server(); + if (!isFullRestoreMode()) { + Version emptyVersion = metadata->activeCFData.get()->popVersion - 1; + if (req.readVersion > metadata->durableDeltaVersion.get() && + emptyVersion > metadata->bufferedDeltaVersion) { + CODE_PROBE(true, "feed popped for read but granule updater didn't notice yet"); + // FIXME: could try to cancel the actor here somehow, but it should find out eventually + throw wrong_shard_server(); + } } rangeGranulePair.push_back(std::pair(metadata->keyRange, metadata->files)); } @@ -3794,7 +3807,6 @@ ACTOR Future openGranule(Reference bwData, As std::tuple prevOwner = decodeBlobGranuleLockValue(prevLockValue.get()); info.granuleID = std::get<2>(prevOwner); - state bool doLockCheck = true; // if it's the first snapshot of a new granule, history won't be present if (info.history.present()) { @@ -3858,9 +3870,28 @@ ACTOR Future openGranule(Reference bwData, As // if this granule is not derived from a split or merge, use new granule id info.granuleID = newGranuleID; } - createChangeFeed = true; - info.doSnapshot = true; - info.previousDurableVersion = invalidVersion; + + // for recovery mode - don't create change feed, don't create snapshot + if (isFullRestoreMode()) { + createChangeFeed = false; + info.doSnapshot = false; + GranuleFiles granuleFiles = wait(loadPreviousFiles(&tr, info.granuleID)); + info.existingFiles = granuleFiles; + + if (info.existingFiles.get().snapshotFiles.empty()) { + ASSERT(info.existingFiles.get().deltaFiles.empty()); + info.previousDurableVersion = invalidVersion; + } else if (info.existingFiles.get().deltaFiles.empty()) { + info.previousDurableVersion = info.existingFiles.get().snapshotFiles.back().version; + } else { + info.previousDurableVersion = info.existingFiles.get().deltaFiles.back().version; + } + info.changeFeedStartVersion = info.previousDurableVersion; + } else { + createChangeFeed = true; + info.doSnapshot = true; + info.previousDurableVersion = invalidVersion; + } } if (createChangeFeed) { @@ -3875,7 +3906,7 @@ ACTOR Future openGranule(Reference bwData, As // If anything in previousGranules, need to do the handoff logic and set // ret.previousChangeFeedId, and the previous durable version will come from the previous // granules - if (info.history.present() && info.history.get().value.parentVersions.size() > 0) { + if (info.history.present() && info.history.get().value.parentVersions.size() > 0 && !isFullRestoreMode()) { CODE_PROBE(true, "Granule open found parent"); if (info.history.get().value.parentVersions.size() == 1) { // split state KeyRangeRef parentRange(info.history.get().value.parentBoundaries[0], @@ -4705,7 +4736,7 @@ ACTOR Future simForceFileWriteContention(Reference bwData) } // check for speed up sim when(wait(delay(5.0))) { - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { if (BW_DEBUG) { fmt::print("BW {0} releasing {1} file writes b/c speed up simulation\n", bwData->id.toString().substr(0, 5), @@ -4722,7 +4753,7 @@ ACTOR Future simForceFullMemory(Reference bwData) { // instead of randomly rejecting each request or not, simulate periods in which BW is full loop { wait(delayJittered(deterministicRandom()->randomInt(5, 20))); - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { bwData->buggifyFull = false; if (BW_DEBUG) { fmt::print("BW {0}: ForceFullMemory exiting\n", bwData->id.toString().substr(0, 6)); diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 60f45d0d5c..f30319b042 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -26,6 +26,7 @@ #include #include "fdbclient/SystemData.h" +#include "fdbclient/DatabaseContext.h" #include "fdbrpc/FailureMonitor.h" #include "fdbclient/EncryptKeyProxyInterface.h" #include "fdbserver/Knobs.h" @@ -43,7 +44,6 @@ #include "fdbserver/ClusterRecovery.actor.h" #include "fdbserver/DataDistributorInterface.h" #include "fdbserver/DBCoreState.h" -#include "fdbserver/ConfigBroadcaster.h" #include "fdbserver/MoveKeys.actor.h" #include "fdbserver/LeaderElection.h" #include "fdbserver/LogSystem.h" @@ -139,6 +139,31 @@ struct DataDistributorSingleton : Singleton { } }; +struct ConsistencyScanSingleton : Singleton { + + ConsistencyScanSingleton(const Optional& interface) : Singleton(interface) {} + + Role getRole() const { return Role::CONSISTENCYSCAN; } + ProcessClass::ClusterRole getClusterRole() const { return ProcessClass::ConsistencyScan; } + + void setInterfaceToDbInfo(ClusterControllerData* cc) const { + if (interface.present()) { + TraceEvent("CCCK_SetInf", cc->id).detail("Id", interface.get().id()); + cc->db.setConsistencyScan(interface.get()); + } + } + void halt(ClusterControllerData* cc, Optional> pid) const { + if (interface.present()) { + cc->id_worker[pid].haltConsistencyScan = + brokenPromiseToNever(interface.get().haltConsistencyScan.getReply(HaltConsistencyScanRequest(cc->id))); + } + } + void recruit(ClusterControllerData* cc) const { + cc->lastRecruitTime = now(); + cc->recruitConsistencyScan.set(true); + } +}; + struct BlobManagerSingleton : Singleton { BlobManagerSingleton(const Optional& interface) : Singleton(interface) {} @@ -196,6 +221,21 @@ struct EncryptKeyProxySingleton : Singleton { } }; +ACTOR Future> getPreviousCoordinators(ClusterControllerData* self) { + state ReadYourWritesTransaction tr(self->db.db); + loop { + try { + tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + Optional previousCoordinators = wait(tr.get(previousCoordinatorsKey)); + return previousCoordinators; + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + ACTOR Future clusterWatchDatabase(ClusterControllerData* cluster, ClusterControllerData::DBInfo* db, ServerCoordinators coordinators, @@ -234,6 +274,7 @@ ACTOR Future clusterWatchDatabase(ClusterControllerData* cluster, dbInfo.ratekeeper = db->serverInfo->get().ratekeeper; dbInfo.blobManager = db->serverInfo->get().blobManager; dbInfo.encryptKeyProxy = db->serverInfo->get().encryptKeyProxy; + dbInfo.consistencyScan = db->serverInfo->get().consistencyScan; dbInfo.latencyBandConfig = db->serverInfo->get().latencyBandConfig; dbInfo.myLocality = db->serverInfo->get().myLocality; dbInfo.client = ClientDBInfo(); @@ -262,7 +303,7 @@ ACTOR Future clusterWatchDatabase(ClusterControllerData* cluster, db->serverInfo->get().masterLifetime, coordinators, db->serverInfo->get().clusterInterface, - LiteralStringRef(""), + ""_sr, addActor, db->forceRecovery); @@ -608,6 +649,7 @@ void checkBetterSingletons(ClusterControllerData* self) { // Try to find a new process for each singleton. WorkerDetails newRKWorker = findNewProcessForSingleton(self, ProcessClass::Ratekeeper, id_used); WorkerDetails newDDWorker = findNewProcessForSingleton(self, ProcessClass::DataDistributor, id_used); + WorkerDetails newCSWorker = findNewProcessForSingleton(self, ProcessClass::ConsistencyScan, id_used); WorkerDetails newBMWorker; if (self->db.blobGranulesEnabled.get()) { @@ -622,6 +664,7 @@ void checkBetterSingletons(ClusterControllerData* self) { // Find best possible fitnesses for each singleton. auto bestFitnessForRK = findBestFitnessForSingleton(self, newRKWorker, ProcessClass::Ratekeeper); auto bestFitnessForDD = findBestFitnessForSingleton(self, newDDWorker, ProcessClass::DataDistributor); + auto bestFitnessForCS = findBestFitnessForSingleton(self, newCSWorker, ProcessClass::ConsistencyScan); ProcessClass::Fitness bestFitnessForBM; if (self->db.blobGranulesEnabled.get()) { @@ -636,6 +679,7 @@ void checkBetterSingletons(ClusterControllerData* self) { auto& db = self->db.serverInfo->get(); auto rkSingleton = RatekeeperSingleton(db.ratekeeper); auto ddSingleton = DataDistributorSingleton(db.distributor); + ConsistencyScanSingleton csSingleton(db.consistencyScan); BlobManagerSingleton bmSingleton(db.blobManager); EncryptKeyProxySingleton ekpSingleton(db.encryptKeyProxy); @@ -647,6 +691,9 @@ void checkBetterSingletons(ClusterControllerData* self) { bool ddHealthy = isHealthySingleton( self, newDDWorker, ddSingleton, bestFitnessForDD, self->recruitingDistributorID); + bool csHealthy = isHealthySingleton( + self, newCSWorker, csSingleton, bestFitnessForCS, self->recruitingConsistencyScanID); + bool bmHealthy = true; if (self->db.blobGranulesEnabled.get()) { bmHealthy = isHealthySingleton( @@ -660,7 +707,7 @@ void checkBetterSingletons(ClusterControllerData* self) { } // if any of the singletons are unhealthy (rerecruited or not stable), then do not // consider any further re-recruitments - if (!(rkHealthy && ddHealthy && bmHealthy && ekpHealthy)) { + if (!(rkHealthy && ddHealthy && bmHealthy && ekpHealthy && csHealthy)) { return; } @@ -668,8 +715,10 @@ void checkBetterSingletons(ClusterControllerData* self) { // check if we can colocate the singletons in a more optimal way Optional> currRKProcessId = rkSingleton.interface.get().locality.processId(); Optional> currDDProcessId = ddSingleton.interface.get().locality.processId(); + Optional> currCSProcessId = csSingleton.interface.get().locality.processId(); Optional> newRKProcessId = newRKWorker.interf.locality.processId(); Optional> newDDProcessId = newDDWorker.interf.locality.processId(); + Optional> newCSProcessId = newCSWorker.interf.locality.processId(); Optional> currBMProcessId, newBMProcessId; if (self->db.blobGranulesEnabled.get()) { @@ -683,8 +732,8 @@ void checkBetterSingletons(ClusterControllerData* self) { newEKPProcessId = newEKPWorker.interf.locality.processId(); } - std::vector>> currPids = { currRKProcessId, currDDProcessId }; - std::vector>> newPids = { newRKProcessId, newDDProcessId }; + std::vector>> currPids = { currRKProcessId, currDDProcessId, currCSProcessId }; + std::vector>> newPids = { newRKProcessId, newDDProcessId, newCSProcessId }; if (self->db.blobGranulesEnabled.get()) { currPids.emplace_back(currBMProcessId); newPids.emplace_back(newBMProcessId); @@ -714,7 +763,8 @@ void checkBetterSingletons(ClusterControllerData* self) { if (newColocMap[newRKProcessId] <= currColocMap[currRKProcessId] && newColocMap[newDDProcessId] <= currColocMap[currDDProcessId] && newColocMap[newBMProcessId] <= currColocMap[currBMProcessId] && - newColocMap[newEKPProcessId] <= currColocMap[currEKPProcessId]) { + newColocMap[newEKPProcessId] <= currColocMap[currEKPProcessId] && + newColocMap[newCSProcessId] <= currColocMap[currCSProcessId]) { // rerecruit the singleton for which we have found a better process, if any if (newColocMap[newRKProcessId] < currColocMap[currRKProcessId]) { rkSingleton.recruit(self); @@ -724,6 +774,8 @@ void checkBetterSingletons(ClusterControllerData* self) { bmSingleton.recruit(self); } else if (SERVER_KNOBS->ENABLE_ENCRYPTION && newColocMap[newEKPProcessId] < currColocMap[currEKPProcessId]) { ekpSingleton.recruit(self); + } else if (newColocMap[newCSProcessId] < currColocMap[currCSProcessId]) { + csSingleton.recruit(self); } } } @@ -1209,13 +1261,12 @@ ACTOR Future registerWorker(RegisterWorkerRequest req, w.locality.processId() == self->db.serverInfo->get().master.locality.processId()) { self->masterProcessId = w.locality.processId(); } - if (configBroadcaster != nullptr && isCoordinator) { - self->addActor.send(configBroadcaster->registerNode( - w, - req.lastSeenKnobVersion, - req.knobConfigClassSet, - self->id_worker[w.locality.processId()].watcher, - self->id_worker[w.locality.processId()].details.interf.configBroadcastInterface)); + if (configBroadcaster != nullptr && req.lastSeenKnobVersion.present() && req.knobConfigClassSet.present()) { + self->addActor.send(configBroadcaster->registerNode(req.configBroadcastInterface, + req.lastSeenKnobVersion.get(), + req.knobConfigClassSet.get(), + self->id_worker[w.locality.processId()].watcher, + isCoordinator)); } self->updateDBInfoEndpoints.insert(w.updateServerDBInfo.getEndpoint()); self->updateDBInfo.trigger(); @@ -1246,12 +1297,12 @@ ACTOR Future registerWorker(RegisterWorkerRequest req, self->updateDBInfoEndpoints.insert(w.updateServerDBInfo.getEndpoint()); self->updateDBInfo.trigger(); } - if (configBroadcaster != nullptr && isCoordinator) { - self->addActor.send(configBroadcaster->registerNode(w, - req.lastSeenKnobVersion, - req.knobConfigClassSet, + if (configBroadcaster != nullptr && req.lastSeenKnobVersion.present() && req.knobConfigClassSet.present()) { + self->addActor.send(configBroadcaster->registerNode(req.configBroadcastInterface, + req.lastSeenKnobVersion.get(), + req.knobConfigClassSet.get(), info->second.watcher, - info->second.details.interf.configBroadcastInterface)); + isCoordinator)); } checkOutstandingRequests(self); } else { @@ -1289,6 +1340,13 @@ ACTOR Future registerWorker(RegisterWorkerRequest req, self, w, currSingleton, registeringSingleton, self->recruitingEncryptKeyProxyID); } + if (req.consistencyScanInterf.present()) { + auto currSingleton = ConsistencyScanSingleton(self->db.serverInfo->get().consistencyScan); + auto registeringSingleton = ConsistencyScanSingleton(req.consistencyScanInterf); + haltRegisteringOrCurrentSingleton( + self, w, currSingleton, registeringSingleton, self->recruitingConsistencyScanID); + } + // Notify the worker to register again with new process class/exclusive property if (!req.reply.isSet() && newPriorityInfo != req.priorityInfo) { req.reply.send(RegisterWorkerReply(newProcessClass, newPriorityInfo)); @@ -1297,7 +1355,7 @@ ACTOR Future registerWorker(RegisterWorkerRequest req, return Void(); } -#define TIME_KEEPER_VERSION LiteralStringRef("1") +#define TIME_KEEPER_VERSION "1"_sr ACTOR Future timeKeeperSetVersion(ClusterControllerData* self) { state Reference tr = makeReference(self->cx); @@ -2150,6 +2208,101 @@ ACTOR Future monitorRatekeeper(ClusterControllerData* self) { } } +ACTOR Future startConsistencyScan(ClusterControllerData* self) { + wait(delay(0.0)); // If master fails at the same time, give it a chance to clear master PID. + TraceEvent("CCStartConsistencyScan", self->id).log(); + loop { + try { + state bool no_consistencyScan = !self->db.serverInfo->get().consistencyScan.present(); + while (!self->masterProcessId.present() || + self->masterProcessId != self->db.serverInfo->get().master.locality.processId() || + self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + wait(self->db.serverInfo->onChange() || delay(SERVER_KNOBS->WAIT_FOR_GOOD_RECRUITMENT_DELAY)); + } + if (no_consistencyScan && self->db.serverInfo->get().consistencyScan.present()) { + // Existing consistencyScan registers while waiting, so skip. + return Void(); + } + + std::map>, int> id_used = self->getUsedIds(); + WorkerFitnessInfo csWorker = self->getWorkerForRoleInDatacenter(self->clusterControllerDcId, + ProcessClass::ConsistencyScan, + ProcessClass::NeverAssign, + self->db.config, + id_used); + + InitializeConsistencyScanRequest req(deterministicRandom()->randomUniqueID()); + state WorkerDetails worker = csWorker.worker; + if (self->onMasterIsBetter(worker, ProcessClass::ConsistencyScan)) { + worker = self->id_worker[self->masterProcessId.get()].details; + } + + self->recruitingConsistencyScanID = req.reqId; + TraceEvent("CCRecruitConsistencyScan", self->id) + .detail("Addr", worker.interf.address()) + .detail("CSID", req.reqId); + + ErrorOr interf = wait(worker.interf.consistencyScan.getReplyUnlessFailedFor( + req, SERVER_KNOBS->WAIT_FOR_CONSISTENCYSCAN_JOIN_DELAY, 0)); + if (interf.present()) { + self->recruitConsistencyScan.set(false); + self->recruitingConsistencyScanID = interf.get().id(); + const auto& consistencyScan = self->db.serverInfo->get().consistencyScan; + TraceEvent("CCConsistencyScanRecruited", self->id) + .detail("Addr", worker.interf.address()) + .detail("CKID", interf.get().id()); + if (consistencyScan.present() && consistencyScan.get().id() != interf.get().id() && + self->id_worker.count(consistencyScan.get().locality.processId())) { + TraceEvent("CCHaltConsistencyScanAfterRecruit", self->id) + .detail("CKID", consistencyScan.get().id()) + .detail("DcID", printable(self->clusterControllerDcId)); + ConsistencyScanSingleton(consistencyScan).halt(self, consistencyScan.get().locality.processId()); + } + if (!consistencyScan.present() || consistencyScan.get().id() != interf.get().id()) { + self->db.setConsistencyScan(interf.get()); + } + checkOutstandingRequests(self); + return Void(); + } else { + TraceEvent("CCConsistencyScanRecruitEmpty", self->id).log(); + } + } catch (Error& e) { + TraceEvent("CCConsistencyScanRecruitError", self->id).error(e); + if (e.code() != error_code_no_more_servers) { + throw; + } + } + wait(lowPriorityDelay(SERVER_KNOBS->ATTEMPT_RECRUITMENT_DELAY)); + } +} + +ACTOR Future monitorConsistencyScan(ClusterControllerData* self) { + while (self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + TraceEvent("CCMonitorConsistencyScanWaitingForRecovery", self->id).log(); + wait(self->db.serverInfo->onChange()); + } + + TraceEvent("CCMonitorConsistencyScan", self->id).log(); + loop { + if (self->db.serverInfo->get().consistencyScan.present() && !self->recruitConsistencyScan.get()) { + state Future wfClient = + waitFailureClient(self->db.serverInfo->get().consistencyScan.get().waitFailure, + SERVER_KNOBS->CONSISTENCYSCAN_FAILURE_TIME); + choose { + when(wait(wfClient)) { + TraceEvent("CCMonitorConsistencyScanDied", self->id) + .detail("CKID", self->db.serverInfo->get().consistencyScan.get().id()); + self->db.clearInterf(ProcessClass::ConsistencyScanClass); + } + when(wait(self->recruitConsistencyScan.onChange())) {} + } + } else { + TraceEvent("CCMonitorConsistencyScanStarting", self->id).log(); + wait(startConsistencyScan(self)); + } + } +} + ACTOR Future startEncryptKeyProxy(ClusterControllerData* self, double waitTime) { // If master fails at the same time, give it a chance to clear master PID. // Also wait to avoid too many consecutive recruits in a small time window. @@ -2355,7 +2508,7 @@ ACTOR Future watchBlobGranulesConfigKey(ClusterControllerData* self) { Optional blobConfig = wait(tr->get(blobGranuleConfigKey)); if (blobConfig.present()) { - self->db.blobGranulesEnabled.set(blobConfig.get() == LiteralStringRef("1")); + self->db.blobGranulesEnabled.set(blobConfig.get() == "1"_sr); } state Future watch = tr->watch(blobGranuleConfigKey); @@ -2536,10 +2689,13 @@ ACTOR Future clusterControllerCore(ClusterControllerFullInterface interf, ConfigDBType configDBType, Future recoveredDiskFiles) { state ClusterControllerData self(interf, locality, coordinators); - state ConfigBroadcaster configBroadcaster(coordinators, configDBType); state Future coordinationPingDelay = delay(SERVER_KNOBS->WORKER_COORDINATION_PING_DELAY); state uint64_t step = 0; state Future> error = errorOr(actorCollection(self.addActor.getFuture())); + state ConfigBroadcaster configBroadcaster; + if (configDBType != ConfigDBType::DISABLED) { + configBroadcaster = ConfigBroadcaster(coordinators, configDBType, getPreviousCoordinators(&self)); + } // EncryptKeyProxy is necessary for TLog recovery, recruit it as the first process if (SERVER_KNOBS->ENABLE_ENCRYPTION) { @@ -2564,6 +2720,7 @@ ACTOR Future clusterControllerCore(ClusterControllerFullInterface interf, self.addActor.send(monitorRatekeeper(&self)); self.addActor.send(monitorBlobManager(&self)); self.addActor.send(watchBlobGranulesConfigKey(&self)); + self.addActor.send(monitorConsistencyScan(&self)); self.addActor.send(dbInfoUpdater(&self)); self.addActor.send(traceCounters("ClusterControllerMetrics", self.id, @@ -2752,7 +2909,7 @@ ACTOR Future clusterController(Reference connRec state bool hasConnected = false; loop { try { - ServerCoordinators coordinators(connRecord); + ServerCoordinators coordinators(connRecord, configDBType); wait(clusterController( coordinators, currentCC, hasConnected, asyncPriorityInfo, locality, configDBType, recoveredDiskFiles)); hasConnected = true; diff --git a/fdbserver/ClusterRecovery.actor.cpp b/fdbserver/ClusterRecovery.actor.cpp index a7668bba46..d211f16060 100644 --- a/fdbserver/ClusterRecovery.actor.cpp +++ b/fdbserver/ClusterRecovery.actor.cpp @@ -1301,7 +1301,7 @@ void updateConfigForForcedRecovery(Reference self, Standalone regionCommit; regionCommit.mutations.push_back_deep( regionCommit.arena(), - MutationRef(MutationRef::SetValue, configKeysPrefix.toString() + "usable_regions", LiteralStringRef("1"))); + MutationRef(MutationRef::SetValue, configKeysPrefix.toString() + "usable_regions", "1"_sr)); self->configuration.applyMutation(regionCommit.mutations.back()); if (regionsChanged) { std::sort( @@ -1481,8 +1481,8 @@ ACTOR Future clusterRecoveryCore(Reference self) { (self->cstate.myDBState.oldTLogData.size() - CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION))); } if (g_network->isSimulated() && self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->MAX_GENERATIONS_SIM) { - g_simulator.connectionFailuresDisableDuration = 1e6; - g_simulator.speedUpSimulation = true; + g_simulator->connectionFailuresDisableDuration = 1e6; + g_simulator->speedUpSimulation = true; TraceEvent(SevWarnAlways, "DisableConnectionFailures_TooManyGenerations").log(); } } @@ -1637,6 +1637,11 @@ ACTOR Future clusterRecoveryCore(Reference self) { tr.set( recoveryCommitRequest.arena, primaryLocalityKey, BinaryWriter::toValue(self->primaryLocality, Unversioned())); tr.set(recoveryCommitRequest.arena, backupVersionKey, backupVersionValue); + Optional txnStateStoreCoords = self->txnStateStore->readValue(coordinatorsKey).get(); + if (txnStateStoreCoords.present() && + txnStateStoreCoords.get() != self->coordinators.ccr->getConnectionString().toString()) { + tr.set(recoveryCommitRequest.arena, previousCoordinatorsKey, txnStateStoreCoords.get()); + } tr.set(recoveryCommitRequest.arena, coordinatorsKey, self->coordinators.ccr->getConnectionString().toString()); tr.set(recoveryCommitRequest.arena, logsKey, self->logSystem->getLogsValue()); tr.set(recoveryCommitRequest.arena, diff --git a/fdbserver/CommitProxyServer.actor.cpp b/fdbserver/CommitProxyServer.actor.cpp index b22c5120e8..7919ec2b0f 100644 --- a/fdbserver/CommitProxyServer.actor.cpp +++ b/fdbserver/CommitProxyServer.actor.cpp @@ -22,6 +22,7 @@ #include #include "fdbclient/Atomic.h" +#include "fdbclient/BlobCipher.h" #include "fdbclient/CommitTransaction.h" #include "fdbclient/DatabaseContext.h" #include "fdbclient/FDBTypes.h" @@ -29,7 +30,9 @@ #include "fdbclient/CommitProxyInterface.h" #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/SystemData.h" +#include "fdbclient/Tenant.h" #include "fdbclient/TransactionLineage.h" +#include "fdbrpc/TenantInfo.h" #include "fdbrpc/sim_validation.h" #include "fdbserver/ApplyMetadataMutation.h" #include "fdbserver/ConflictSet.h" @@ -50,7 +53,7 @@ #include "fdbserver/WaitFailure.h" #include "fdbserver/WorkerInterface.actor.h" #include "flow/ActorCollection.h" -#include "flow/BlobCipher.h" +#include "flow/EncryptUtils.h" #include "flow/Error.h" #include "flow/IRandom.h" #include "flow/Knobs.h" @@ -442,7 +445,7 @@ void createWhitelistBinPathVec(const std::string& binPath, std::vector oldCoordinators; + Optional previousCoordinators; StoreCommit_t storeCommits; @@ -765,8 +768,8 @@ bool canReject(const std::vector& trs) { for (const auto& tr : trs) { if (tr.transaction.mutations.empty()) continue; - if (!tr.tenantInfo.name.present() && (tr.transaction.mutations[0].param1.startsWith(LiteralStringRef("\xff")) || - tr.transaction.read_conflict_ranges.empty())) { + if (!tr.tenantInfo.name.present() && + (tr.transaction.mutations[0].param1.startsWith("\xff"_sr) || tr.transaction.read_conflict_ranges.empty())) { return false; } } @@ -874,6 +877,81 @@ ACTOR Future preresolutionProcessing(CommitBatchContext* self) { return Void(); } +namespace { +// Routine allows caller to find TenantName for a given TenantId. It returns empty optional when either TenantId is +// invalid or tenant is unknown +Optional getTenantName(ProxyCommitData* commitData, int64_t tenantId) { + if (tenantId != TenantInfo::INVALID_TENANT) { + auto itr = commitData->tenantIdIndex.find(tenantId); + if (itr != commitData->tenantIdIndex.end()) { + return Optional(itr->second); + } + } + + return Optional(); +} + +std::pair getEncryptDetailsFromMutationRef(ProxyCommitData* commitData, + MutationRef m) { + std::pair details(EncryptCipherDomainName(), + INVALID_ENCRYPT_DOMAIN_ID); + + // Possible scenarios: + // 1. Encryption domain (Tenant details) weren't explicitly provided, extract Tenant details using + // TenantPrefix (first 8 bytes of FDBKey) + // 2. Encryption domain isn't available, leverage 'default encryption domain' + + if (isSystemKey(m.param1)) { + // Encryption domain == FDB SystemKeyspace encryption domain + details.first = EncryptCipherDomainName(FDB_SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_NAME); + details.second = SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID; + } else if (commitData->tenantMap.empty()) { + // Cluster serves no-tenants; use 'default encryption domain' + } else if (isSingleKeyMutation((MutationRef::Type)m.type)) { + ASSERT_NE((MutationRef::Type)m.type, MutationRef::Type::ClearRange); + + if (m.param1.size() >= TENANT_PREFIX_SIZE) { + // Parse mutation key to determine mutation encryption domain + StringRef prefix = m.param1.substr(0, TENANT_PREFIX_SIZE); + int64_t tenantId = TenantMapEntry::prefixToId(prefix, EnforceValidTenantId::False); + if (tenantId != TenantInfo::INVALID_TENANT) { + Optional tenantName = getTenantName(commitData, tenantId); + if (tenantName.present()) { + details.first = tenantName.get(); + details.second = tenantId; + } + } else { + // Leverage 'default encryption domain' + } + } + } else { + // ClearRange is the 'only' MultiKey transaction allowed + ASSERT_EQ((MutationRef::Type)m.type, MutationRef::Type::ClearRange); + + // FIXME: Handle Clear-range transaction, actions needed: + // 1. Transaction range can spawn multiple encryption domains (tenants) + // 2. Transaction can be a multi-key transaction spawning multiple tenants + // For now fallback to 'default encryption domain' + + CODE_PROBE(true, "ClearRange mutation encryption"); + } + + // Unknown tenant, fallback to fdb default encryption domain + if (details.second == INVALID_ENCRYPT_DOMAIN_ID) { + ASSERT_EQ(details.first.size(), 0); + details.first = EncryptCipherDomainName(FDB_DEFAULT_ENCRYPT_DOMAIN_NAME); + details.second = FDB_DEFAULT_ENCRYPT_DOMAIN_ID; + + CODE_PROBE(true, "Default domain mutation encryption"); + } + + ASSERT_GT(details.first.size(), 0); + + return details; +} + +} // namespace + ACTOR Future getResolution(CommitBatchContext* self) { state double resolutionStart = now(); // Sending these requests is the fuzzy border between phase 1 and phase 2; it could conceivably overlap with @@ -917,22 +995,28 @@ ACTOR Future getResolution(CommitBatchContext* self) { // Fetch cipher keys if needed. state Future>> getCipherKeys; if (pProxyCommitData->isEncryptionEnabled) { - static std::unordered_map defaultDomains = { - { SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME }, - { ENCRYPT_HEADER_DOMAIN_ID, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME } + static std::unordered_map defaultDomains = { + { SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, FDB_SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_NAME }, + { ENCRYPT_HEADER_DOMAIN_ID, FDB_ENCRYPT_HEADER_DOMAIN_NAME }, + { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME } }; - std::unordered_map encryptDomains = defaultDomains; + std::unordered_map encryptDomains = defaultDomains; for (int t = 0; t < trs.size(); t++) { TenantInfo const& tenantInfo = trs[t].tenantInfo; int64_t tenantId = tenantInfo.tenantId; Optional const& tenantName = tenantInfo.name; - // TODO(yiwu): In raw access mode, use tenant prefix to figure out tenant id for user data if (tenantId != TenantInfo::INVALID_TENANT) { ASSERT(tenantName.present()); - encryptDomains[tenantId] = tenantName.get(); + encryptDomains[tenantId] = Standalone(tenantName.get(), tenantInfo.arena); + } else { + for (auto m : trs[t].transaction.mutations) { + std::pair details = + getEncryptDetailsFromMutationRef(pProxyCommitData, m); + encryptDomains[details.second] = details.first; + } } } - getCipherKeys = getLatestEncryptCipherKeys(pProxyCommitData->db, encryptDomains); + getCipherKeys = getLatestEncryptCipherKeys(pProxyCommitData->db, encryptDomains, BlobCipherMetrics::TLOG); } self->releaseFuture = releaseResolvingAfter(pProxyCommitData, self->releaseDelay, self->localBatchNumber); @@ -1146,7 +1230,7 @@ ACTOR Future applyMetadataToCommittedTransactions(CommitBatchContext* self ASSERT(self->commitVersion); if (!self->isMyFirstBatch && - pProxyCommitData->txnStateStore->readValue(coordinatorsKey).get().get() != self->oldCoordinators.get()) { + pProxyCommitData->txnStateStore->readValue(coordinatorsKey).get().get() != self->previousCoordinators.get()) { wait(brokenPromiseToNever(pProxyCommitData->db->get().clusterInterface.changeCoordinators.getReply( ChangeCoordinatorsRequest(pProxyCommitData->txnStateStore->readValue(coordinatorsKey).get().get(), self->pProxyCommitData->master.id())))); @@ -1157,17 +1241,24 @@ ACTOR Future applyMetadataToCommittedTransactions(CommitBatchContext* self } void writeMutation(CommitBatchContext* self, int64_t tenantId, const MutationRef& mutation) { - static_assert(TenantInfo::INVALID_TENANT == ENCRYPT_INVALID_DOMAIN_ID); - if (!self->pProxyCommitData->isEncryptionEnabled || tenantId == TenantInfo::INVALID_TENANT) { - // TODO(yiwu): In raw access mode, use tenant prefix to figure out tenant id for user data - bool isRawAccess = tenantId == TenantInfo::INVALID_TENANT && !isSystemKey(mutation.param1) && - !(mutation.type == MutationRef::ClearRange && isSystemKey(mutation.param2)) && - self->pProxyCommitData->db->get().client.tenantMode == TenantMode::REQUIRED; - CODE_PROBE(isRawAccess, "Raw access to tenant key space"); - self->toCommit.writeTypedMessage(mutation); - } else { + static_assert(TenantInfo::INVALID_TENANT == INVALID_ENCRYPT_DOMAIN_ID); + + if (self->pProxyCommitData->isEncryptionEnabled) { + EncryptCipherDomainId domainId = tenantId; + if (domainId == INVALID_ENCRYPT_DOMAIN_ID) { + std::pair p = + getEncryptDetailsFromMutationRef(self->pProxyCommitData, mutation); + domainId = p.second; + + CODE_PROBE(true, "Raw access mutation encryption"); + } + + ASSERT_NE(domainId, INVALID_ENCRYPT_DOMAIN_ID); + Arena arena; - self->toCommit.writeTypedMessage(mutation.encrypt(self->cipherKeys, tenantId /*domainId*/, arena)); + self->toCommit.writeTypedMessage(mutation.encrypt(self->cipherKeys, domainId, arena, BlobCipherMetrics::TLOG)); + } else { + self->toCommit.writeTypedMessage(mutation); } } @@ -1373,7 +1464,7 @@ ACTOR Future postResolution(CommitBatchContext* self) { } self->isMyFirstBatch = !pProxyCommitData->version.get(); - self->oldCoordinators = pProxyCommitData->txnStateStore->readValue(coordinatorsKey).get(); + self->previousCoordinators = pProxyCommitData->txnStateStore->readValue(coordinatorsKey).get(); assertResolutionStateMutationsSizeConsistent(self->resolution); @@ -2138,9 +2229,7 @@ ACTOR Future proxySnapCreate(ProxySnapRequest snapReq, ProxyCommitData* co throw snap_not_fully_recovered_unsupported(); } - auto result = - commitData->txnStateStore->readValue(LiteralStringRef("log_anti_quorum").withPrefix(configKeysPrefix)) - .get(); + auto result = commitData->txnStateStore->readValue("log_anti_quorum"_sr.withPrefix(configKeysPrefix)).get(); int logAntiQuorum = 0; if (result.present()) { logAntiQuorum = atoi(result.get().toString().c_str()); @@ -2481,10 +2570,8 @@ ACTOR Future commitProxyServerCore(CommitProxyInterface proxy, commitData.localTLogCount = commitData.db->get().logSystemConfig.numLogs(); ASSERT(commitData.resolvers.size() != 0); for (int i = 0; i < commitData.resolvers.size(); ++i) { - commitData.stats.resolverDist.push_back( - Histogram::getHistogram(LiteralStringRef("CommitProxy"), - "ToResolver_" + commitData.resolvers[i].id().toString(), - Histogram::Unit::microseconds)); + commitData.stats.resolverDist.push_back(Histogram::getHistogram( + "CommitProxy"_sr, "ToResolver_" + commitData.resolvers[i].id().toString(), Histogram::Unit::microseconds)); } // Initialize keyResolvers map diff --git a/fdbserver/ConfigBroadcaster.actor.cpp b/fdbserver/ConfigBroadcaster.actor.cpp index c402ab7b27..eafce9c9cb 100644 --- a/fdbserver/ConfigBroadcaster.actor.cpp +++ b/fdbserver/ConfigBroadcaster.actor.cpp @@ -20,6 +20,7 @@ #include +#include "fdbclient/ClusterConnectionMemoryRecord.h" #include "fdbserver/ConfigBroadcaster.h" #include "fdbserver/Knobs.h" #include "fdbserver/IConfigConsumer.h" @@ -77,13 +78,25 @@ class ConfigBroadcasterImpl { std::deque mutationHistory; std::deque annotationHistory; Version lastCompactedVersion; + Version largestLiveVersion; Version mostRecentVersion; + CoordinatorsHash coordinatorsHash; std::unique_ptr consumer; Future consumerFuture; ActorCollection actors{ false }; + std::unordered_map> registrationActors; std::map clients; std::map> clientFailures; + // State related to changing coordinators + + // Used to read a snapshot from the previous coordinators after a change + // coordinators command. + Version maxLastSeenVersion = ::invalidVersion; + Future> previousCoordinatorsFuture; + std::unique_ptr previousCoordinatorsConsumer; + Future previousCoordinatorsSnapshotFuture; + UID id; CounterCollection cc; Counter compactRequest; @@ -95,6 +108,7 @@ class ConfigBroadcasterImpl { int coordinators = 0; std::unordered_set activeConfigNodes; std::unordered_set registrationResponses; + std::unordered_set registrationResponsesUnregistered; bool disallowUnregistered = false; Promise newConfigNodesAllowed; @@ -111,6 +125,11 @@ class ConfigBroadcasterImpl { } } request.version = snapshotVersion; + // TODO: Don't need a delay if there are no atomic changes. + // Delay restarting the cluster controller to allow messages to be sent to workers. + request.restartDelay = client.broadcastInterface.address() == g_network->getLocalAddress() + ? SERVER_KNOBS->BROADCASTER_SELF_UPDATE_DELAY + : 0.0; TraceEvent(SevDebug, "ConfigBroadcasterSnapshotRequest", id) .detail("Size", request.snapshot.size()) .detail("Version", request.version); @@ -150,13 +169,18 @@ class ConfigBroadcasterImpl { client.lastSeenVersion = mostRecentVersion; req.mostRecentVersion = mostRecentVersion; + // TODO: Don't need a delay if there are no atomic changes. + // Delay restarting the cluster controller to allow messages to be sent to workers. + req.restartDelay = client.broadcastInterface.address() == g_network->getLocalAddress() + ? SERVER_KNOBS->BROADCASTER_SELF_UPDATE_DELAY + : 0.0; ++successfulChangeRequest; return success(client.broadcastInterface.changes.getReply(req)); } ConfigBroadcasterImpl() - : lastCompactedVersion(0), mostRecentVersion(0), id(deterministicRandom()->randomUniqueID()), - cc("ConfigBroadcaster"), compactRequest("CompactRequest", cc), + : lastCompactedVersion(0), largestLiveVersion(0), mostRecentVersion(0), + id(deterministicRandom()->randomUniqueID()), cc("ConfigBroadcaster"), compactRequest("CompactRequest", cc), successfulChangeRequest("SuccessfulChangeRequest", cc), failedChangeRequest("FailedChangeRequest", cc), snapshotRequest("SnapshotRequest", cc) { logger = traceCounters( @@ -183,45 +207,44 @@ class ConfigBroadcasterImpl { } } - template - Future setSnapshot(Snapshot&& snapshot, Version snapshotVersion) { - this->snapshot = std::forward(snapshot); - this->lastCompactedVersion = snapshotVersion; + ACTOR static Future pushSnapshotAndChanges(ConfigBroadcasterImpl* self, Version snapshotVersion) { std::vector> futures; - for (const auto& [id, client] : clients) { - futures.push_back(brokenPromiseToNever(pushSnapshot(snapshotVersion, client))); + for (const auto& [id, client] : self->clients) { + futures.push_back(brokenPromiseToNever(self->pushSnapshot(snapshotVersion, client))); } - return waitForAll(futures); - } - - ACTOR template - static Future pushSnapshotAndChanges(ConfigBroadcasterImpl* self, - Snapshot snapshot, - Version snapshotVersion, - Standalone> changes, - Version changesVersion, - Standalone> annotations) { - // Make sure all snapshot messages were received before sending changes. - wait(self->setSnapshot(snapshot, snapshotVersion)); - self->addChanges(changes, changesVersion, annotations); + wait(waitForAll(futures)); return Void(); } ACTOR static Future waitForFailure(ConfigBroadcasterImpl* self, Future watcher, UID clientUID, - NetworkAddress clientAddress) { + NetworkAddress clientAddress, + bool isCoordinator) { wait(watcher); TraceEvent(SevDebug, "ConfigBroadcastClientDied", self->id) .detail("ClientID", clientUID) - .detail("Address", clientAddress); + .detail("Address", clientAddress) + .detail("IsUnregistered", + self->registrationResponsesUnregistered.find(clientAddress) != + self->registrationResponsesUnregistered.end()) + .detail("IsActive", self->activeConfigNodes.find(clientAddress) != self->activeConfigNodes.end()); self->clients.erase(clientUID); self->clientFailures.erase(clientUID); - self->activeConfigNodes.erase(clientAddress); - self->registrationResponses.erase(clientAddress); - // See comment where this promise is reset below. - if (self->newConfigNodesAllowed.isSet()) { - self->newConfigNodesAllowed.reset(); + if (isCoordinator) { + self->registrationResponses.erase(clientAddress); + if (self->activeConfigNodes.find(clientAddress) != self->activeConfigNodes.end()) { + self->activeConfigNodes.erase(clientAddress); + if (self->registrationResponsesUnregistered.find(clientAddress) != + self->registrationResponsesUnregistered.end()) { + self->registrationResponsesUnregistered.erase(clientAddress); + self->disallowUnregistered = false; + // See comment where this promise is reset below. + if (self->newConfigNodesAllowed.isSet()) { + self->newConfigNodesAllowed.reset(); + } + } + } } return Void(); } @@ -231,84 +254,167 @@ class ConfigBroadcasterImpl { // ensure strict serializability, some nodes may be temporarily restricted // from participation until the other nodes in the system are brought up to // date. - ACTOR static Future registerNodeInternal(ConfigBroadcasterImpl* self, - WorkerInterface w, - Version lastSeenVersion) { + ACTOR static Future registerNodeInternal(ConfigBroadcaster* broadcaster, + ConfigBroadcasterImpl* self, + ConfigBroadcastInterface configBroadcastInterface) { if (self->configDBType == ConfigDBType::SIMPLE) { - wait(success(retryBrokenPromise(w.configBroadcastInterface.ready, ConfigBroadcastReadyRequest{}))); + self->consumerFuture = self->consumer->consume(*broadcaster); + wait(success(brokenPromiseToNever( + configBroadcastInterface.ready.getReply(ConfigBroadcastReadyRequest{ 0, {}, -1, -1 })))); return Void(); } - state NetworkAddress address = w.address(); - + state NetworkAddress address = configBroadcastInterface.address(); // Ask the registering ConfigNode whether it has registered in the past. - ConfigBroadcastRegisteredReply reply = - wait(w.configBroadcastInterface.registered.getReply(ConfigBroadcastRegisteredRequest{})); + state ConfigBroadcastRegisteredReply reply = wait( + brokenPromiseToNever(configBroadcastInterface.registered.getReply(ConfigBroadcastRegisteredRequest{}))); + self->maxLastSeenVersion = std::max(self->maxLastSeenVersion, reply.lastSeenVersion); state bool registered = reply.registered; + TraceEvent("ConfigBroadcasterRegisterNodeReceivedRegistrationReply", self->id) + .detail("Address", address) + .detail("Registered", registered) + .detail("DisallowUnregistered", self->disallowUnregistered) + .detail("LastSeenVersion", reply.lastSeenVersion); if (self->activeConfigNodes.find(address) != self->activeConfigNodes.end()) { self->activeConfigNodes.erase(address); - // Since a node can die and re-register before the broadcaster - // receives notice that the node has died, we need to check for - // re-registration of a node here. There are two places that can - // reset the promise to allow new nodes, make sure the promise is - // actually set before resetting it. This prevents a node from - // dying, registering, waiting on the promise, then the broadcaster - // receives the notification the node has died and resets the - // promise again. - if (self->newConfigNodesAllowed.isSet()) { - self->newConfigNodesAllowed.reset(); + if (self->registrationResponsesUnregistered.find(address) != + self->registrationResponsesUnregistered.end()) { + self->registrationResponsesUnregistered.erase(address); + // If an unregistered node died which was active, reset the + // disallow unregistered flag so if it re-registers it can be + // set as active again. + self->disallowUnregistered = false; + // Since a node can die and re-register before the broadcaster + // receives notice that the node has died, we need to check for + // re-registration of a node here. There are two places that can + // reset the promise to allow new nodes, so make sure the promise + // is actually set before resetting it. This prevents a node from + // dying, registering, waiting on the promise, then the broadcaster + // receives the notification the node has died and resets the + // promise again. + if (self->newConfigNodesAllowed.isSet()) { + self->newConfigNodesAllowed.reset(); + } } } - self->registrationResponses.insert(address); + int responsesRemaining = self->coordinators - (int)self->registrationResponses.size(); + int nodesTillQuorum = self->coordinators / 2 + 1 - (int)self->activeConfigNodes.size(); if (registered) { - if (!self->disallowUnregistered) { - self->activeConfigNodes.clear(); - } self->activeConfigNodes.insert(address); self->disallowUnregistered = true; } else if ((self->activeConfigNodes.size() < self->coordinators / 2 + 1 && !self->disallowUnregistered) || - self->coordinators - self->registrationResponses.size() <= - self->coordinators / 2 + 1 - self->activeConfigNodes.size()) { + (self->registrationResponsesUnregistered.size() < self->coordinators / 2 && + responsesRemaining <= nodesTillQuorum)) { // Received a registration request from an unregistered node. There // are two cases where we want to allow unregistered nodes to // register: // * the cluster is just starting and no nodes are registered - // * a minority of nodes are registered and a majority are - // unregistered. This situation should only occur in rare - // circumstances where the cluster controller dies with only a - // minority of config nodes having received a - // ConfigBroadcastReadyRequest + // * there are registered and unregistered nodes, but the + // registered nodes may not represent a majority due to previous + // data loss. In this case, unregistered nodes must be allowed + // to register so they can be rolled forward and form a quorum. + // But only a minority of unregistered nodes should be allowed + // to register so they cannot override the registered nodes as + // a source of truth self->activeConfigNodes.insert(address); - if (self->activeConfigNodes.size() >= self->coordinators / 2 + 1 && + self->registrationResponsesUnregistered.insert(address); + if ((self->activeConfigNodes.size() >= self->coordinators / 2 + 1 || + self->registrationResponsesUnregistered.size() >= self->coordinators / 2 + 1) && self->newConfigNodesAllowed.canBeSet()) { self->newConfigNodesAllowed.send(Void()); } } else { self->disallowUnregistered = true; } + self->registrationResponses.insert(address); - if (!registered) { + // Read previous coordinators and fetch snapshot from them if they + // exist. This path should only be hit once after the coordinators are + // changed. + wait(yield()); + Optional previousCoordinators = wait(self->previousCoordinatorsFuture); + TraceEvent("ConfigBroadcasterRegisterNodeReadPreviousCoordinators", self->id) + .detail("PreviousCoordinators", previousCoordinators) + .detail("HasStartedConsumer", self->previousCoordinatorsSnapshotFuture.isValid()); + + if (previousCoordinators.present()) { + if (!self->previousCoordinatorsSnapshotFuture.isValid()) { + // Create a consumer to read a snapshot from the previous + // coordinators. The snapshot will be forwarded to the new + // coordinators to bring them up to date. + size_t previousCoordinatorsHash = std::hash()(previousCoordinators.get().toString()); + if (previousCoordinatorsHash != self->coordinatorsHash) { + ServerCoordinators previousCoordinatorsData(Reference( + new ClusterConnectionMemoryRecord(previousCoordinators.get().toString()))); + TraceEvent("ConfigBroadcasterRegisterNodeStartingConsumer", self->id).log(); + self->previousCoordinatorsConsumer = IConfigConsumer::createPaxos( + previousCoordinatorsData, 0.5, SERVER_KNOBS->COMPACTION_INTERVAL, true); + self->previousCoordinatorsSnapshotFuture = + self->previousCoordinatorsConsumer->readSnapshot(*broadcaster); + } else { + // If the cluster controller restarts without a coordinator + // change having taken place, there is no need to read a + // previous snapshot. + self->previousCoordinatorsSnapshotFuture = Void(); + } + } + wait(self->previousCoordinatorsSnapshotFuture); + } + + state bool sendSnapshot = + self->previousCoordinatorsConsumer && reply.lastSeenVersion <= self->mostRecentVersion; + // Unregistered nodes need to wait for either: + // 1. A quorum of registered nodes to register and send their + // snapshots, so the unregistered nodes can be rolled forward, or + // 2. A quorum of unregistered nodes to contact the broadcaster (this + // means there is no previous data in the configuration database) + // The above conditions do not apply when changing coordinators, as a + // snapshot of the current state of the configuration database needs to + // be sent to all new coordinators. + TraceEvent("ConfigBroadcasterRegisterNodeDetermineEligibility", self->id) + .detail("Registered", registered) + .detail("SendSnapshot", sendSnapshot); + if (!registered && !sendSnapshot) { wait(self->newConfigNodesAllowed.getFuture()); } - wait(success(w.configBroadcastInterface.ready.getReply(ConfigBroadcastReadyRequest{}))); + sendSnapshot = sendSnapshot || (!registered && self->snapshot.size() > 0); + TraceEvent("ConfigBroadcasterRegisterNodeSendingReadyRequest", self->id) + .detail("ConfigNodeAddress", address) + .detail("SendSnapshot", sendSnapshot) + .detail("SnapshotVersion", self->mostRecentVersion) + .detail("SnapshotSize", self->snapshot.size()) + .detail("LargestLiveVersion", self->largestLiveVersion); + if (sendSnapshot) { + Version liveVersion = std::max(self->largestLiveVersion, self->mostRecentVersion); + wait(success(brokenPromiseToNever(configBroadcastInterface.ready.getReply(ConfigBroadcastReadyRequest{ + self->coordinatorsHash, self->snapshot, self->mostRecentVersion, liveVersion })))); + } else { + wait(success(brokenPromiseToNever(configBroadcastInterface.ready.getReply( + ConfigBroadcastReadyRequest{ self->coordinatorsHash, {}, -1, -1 })))); + } + + // Start the consumer last, so at least some nodes will be registered. + if (!self->consumerFuture.isValid()) { + if (sendSnapshot) { + self->consumer->allowSpecialCaseRollforward(); + } + self->consumerFuture = self->consumer->consume(*broadcaster); + } return Void(); } ACTOR static Future registerNode(ConfigBroadcaster* self, ConfigBroadcasterImpl* impl, - WorkerInterface w, + ConfigBroadcastInterface broadcastInterface, Version lastSeenVersion, ConfigClassSet configClassSet, Future watcher, - ConfigBroadcastInterface broadcastInterface) { + bool isCoordinator) { state BroadcastClientDetails client( watcher, std::move(configClassSet), lastSeenVersion, std::move(broadcastInterface)); - if (!impl->consumerFuture.isValid()) { - impl->consumerFuture = impl->consumer->consume(*self); - } if (impl->clients.count(broadcastInterface.id())) { // Client already registered @@ -317,26 +423,32 @@ class ConfigBroadcasterImpl { TraceEvent(SevDebug, "ConfigBroadcasterRegisteringWorker", impl->id) .detail("ClientID", broadcastInterface.id()) - .detail("MostRecentVersion", impl->mostRecentVersion); + .detail("MostRecentVersion", impl->mostRecentVersion) + .detail("IsCoordinator", isCoordinator); - impl->actors.add(registerNodeInternal(impl, w, lastSeenVersion)); + if (isCoordinator) { + // A client re-registering will cause the old actor to be + // cancelled. + impl->registrationActors[broadcastInterface.address()] = + registerNodeInternal(self, impl, broadcastInterface); + } // Push full snapshot to worker if it isn't up to date. wait(impl->pushSnapshot(impl->mostRecentVersion, client)); impl->clients[broadcastInterface.id()] = client; impl->clientFailures[broadcastInterface.id()] = - waitForFailure(impl, watcher, broadcastInterface.id(), w.address()); + waitForFailure(impl, watcher, broadcastInterface.id(), broadcastInterface.address(), isCoordinator); return Void(); } public: Future registerNode(ConfigBroadcaster& self, - WorkerInterface const& w, + ConfigBroadcastInterface const& broadcastInterface, Version lastSeenVersion, ConfigClassSet configClassSet, Future watcher, - ConfigBroadcastInterface const& broadcastInterface) { - return registerNode(&self, this, w, lastSeenVersion, configClassSet, watcher, broadcastInterface); + bool isCoordinator) { + return registerNode(&self, this, broadcastInterface, lastSeenVersion, configClassSet, watcher, isCoordinator); } // Updates the broadcasters knowledge of which replicas are fully up to @@ -360,8 +472,9 @@ public: Standalone> const& annotations, std::vector const& readReplicas) { if (mostRecentVersion >= 0) { - TraceEvent(SevDebug, "ConfigBroadcasterApplyingChanges", id) + TraceEvent(SevInfo, "ConfigBroadcasterApplyingChanges", id) .detail("ChangesSize", changes.size()) + .detail("AnnotationsSize", annotations.size()) .detail("CurrentMostRecentVersion", this->mostRecentVersion) .detail("NewMostRecentVersion", mostRecentVersion) .detail("ActiveReplicas", readReplicas.size()); @@ -377,17 +490,37 @@ public: Standalone> const& changes, Version changesVersion, Standalone> const& annotations, - std::vector const& readReplicas) { - TraceEvent(SevDebug, "ConfigBroadcasterApplyingSnapshotAndChanges", id) + std::vector const& readReplicas, + Version largestLiveVersion, + bool fromPreviousCoordinators) { + TraceEvent(SevInfo, "ConfigBroadcasterApplyingSnapshotAndChanges", id) .detail("CurrentMostRecentVersion", this->mostRecentVersion) .detail("SnapshotSize", snapshot.size()) .detail("SnapshotVersion", snapshotVersion) .detail("ChangesSize", changes.size()) .detail("ChangesVersion", changesVersion) - .detail("ActiveReplicas", readReplicas.size()); - actors.add(pushSnapshotAndChanges(this, snapshot, snapshotVersion, changes, changesVersion, annotations)); + .detail("AnnotationsSize", annotations.size()) + .detail("ActiveReplicas", readReplicas.size()) + .detail("LargestLiveVersion", largestLiveVersion) + .detail("FromPreviousCoordinators", fromPreviousCoordinators); + // Avoid updating state if the snapshot contains no mutations, or if it + // contains old mutations. This can happen when the set of coordinators + // is changed, and a new coordinator comes online that has not yet had + // the current configuration database pushed to it, or when a new + // coordinator contains state from an old configuration database + // generation. + if ((snapshot.size() != 0 || changes.size() != 0) && + (snapshotVersion > this->mostRecentVersion || changesVersion > this->mostRecentVersion)) { + this->snapshot = std::forward(snapshot); + this->lastCompactedVersion = snapshotVersion; + this->largestLiveVersion = std::max(this->largestLiveVersion, largestLiveVersion); + addChanges(changes, changesVersion, annotations); + actors.add(pushSnapshotAndChanges(this, snapshotVersion)); + } - updateKnownReplicas(readReplicas); + if (!fromPreviousCoordinators) { + updateKnownReplicas(readReplicas); + } } ConfigBroadcasterImpl(ConfigFollowerInterface const& cfi) : ConfigBroadcasterImpl() { @@ -397,18 +530,28 @@ public: TraceEvent(SevDebug, "ConfigBroadcasterStartingConsumer", id).detail("Consumer", consumer->getID()); } - ConfigBroadcasterImpl(ServerCoordinators const& coordinators, ConfigDBType configDBType) : ConfigBroadcasterImpl() { + ConfigBroadcasterImpl(ServerCoordinators const& coordinators, + ConfigDBType configDBType, + Future> previousCoordinatorsFuture) + : ConfigBroadcasterImpl() { this->configDBType = configDBType; this->coordinators = coordinators.configServers.size(); if (configDBType != ConfigDBType::DISABLED) { if (configDBType == ConfigDBType::SIMPLE) { consumer = IConfigConsumer::createSimple(coordinators, 0.5, SERVER_KNOBS->COMPACTION_INTERVAL); } else { + this->previousCoordinatorsFuture = previousCoordinatorsFuture; consumer = IConfigConsumer::createPaxos(coordinators, 0.5, SERVER_KNOBS->COMPACTION_INTERVAL); } - TraceEvent(SevDebug, "ConfigBroadcasterStartingConsumer", id) + + coordinatorsHash = std::hash()(coordinators.ccr->getConnectionString().toString()); + + TraceEvent(SevInfo, "ConfigBroadcasterStartingConsumer", id) .detail("Consumer", consumer->getID()) - .detail("UsingSimpleConsumer", configDBType == ConfigDBType::SIMPLE); + .detail("UsingSimpleConsumer", configDBType == ConfigDBType::SIMPLE) + .detail("CoordinatorsCount", this->coordinators) + .detail("CoordinatorsHash", coordinatorsHash) + .detail("CompactionInterval", SERVER_KNOBS->COMPACTION_INTERVAL); } } @@ -419,9 +562,12 @@ public: JsonBuilderObject mutationObject; mutationObject["version"] = versionedMutation.version; const auto& mutation = versionedMutation.mutation; + mutationObject["type"] = mutation.isSet() ? "set" : "clear"; mutationObject["config_class"] = mutation.getConfigClass().orDefault(""_sr); mutationObject["knob_name"] = mutation.getKnobName(); - mutationObject["knob_value"] = mutation.getValue().toString(); + if (mutation.isSet()) { + mutationObject["knob_value"] = mutation.getValue().toString(); + } mutationsArray.push_back(std::move(mutationObject)); } result["mutations"] = std::move(mutationsArray); @@ -477,11 +623,15 @@ public: static void runPendingRequestStoreTest(bool includeGlobalMutation, int expectedMatches); }; +ConfigBroadcaster::ConfigBroadcaster() {} + ConfigBroadcaster::ConfigBroadcaster(ConfigFollowerInterface const& cfi) : impl(PImpl::create(cfi)) {} -ConfigBroadcaster::ConfigBroadcaster(ServerCoordinators const& coordinators, ConfigDBType configDBType) - : impl(PImpl::create(coordinators, configDBType)) {} +ConfigBroadcaster::ConfigBroadcaster(ServerCoordinators const& coordinators, + ConfigDBType configDBType, + Future> previousCoordinatorsFuture) + : impl(PImpl::create(coordinators, configDBType, previousCoordinatorsFuture)) {} ConfigBroadcaster::ConfigBroadcaster(ConfigBroadcaster&&) = default; @@ -489,12 +639,12 @@ ConfigBroadcaster& ConfigBroadcaster::operator=(ConfigBroadcaster&&) = default; ConfigBroadcaster::~ConfigBroadcaster() = default; -Future ConfigBroadcaster::registerNode(WorkerInterface const& w, +Future ConfigBroadcaster::registerNode(ConfigBroadcastInterface const& broadcastInterface, Version lastSeenVersion, ConfigClassSet const& configClassSet, Future watcher, - ConfigBroadcastInterface const& broadcastInterface) { - return impl->registerNode(*this, w, lastSeenVersion, configClassSet, watcher, broadcastInterface); + bool isCoordinator) { + return impl->registerNode(*this, broadcastInterface, lastSeenVersion, configClassSet, watcher, isCoordinator); } void ConfigBroadcaster::applyChanges(Standalone> const& changes, @@ -510,8 +660,17 @@ void ConfigBroadcaster::applySnapshotAndChanges( Standalone> const& changes, Version changesVersion, Standalone> const& annotations, - std::vector const& readReplicas) { - impl->applySnapshotAndChanges(snapshot, snapshotVersion, changes, changesVersion, annotations, readReplicas); + std::vector const& readReplicas, + Version largestLiveVersion, + bool fromPreviousCoordinators) { + impl->applySnapshotAndChanges(snapshot, + snapshotVersion, + changes, + changesVersion, + annotations, + readReplicas, + largestLiveVersion, + fromPreviousCoordinators); } void ConfigBroadcaster::applySnapshotAndChanges( @@ -520,9 +679,17 @@ void ConfigBroadcaster::applySnapshotAndChanges( Standalone> const& changes, Version changesVersion, Standalone> const& annotations, - std::vector const& readReplicas) { - impl->applySnapshotAndChanges( - std::move(snapshot), snapshotVersion, changes, changesVersion, annotations, readReplicas); + std::vector const& readReplicas, + Version largestLiveVersion, + bool fromPreviousCoordinators) { + impl->applySnapshotAndChanges(std::move(snapshot), + snapshotVersion, + changes, + changesVersion, + annotations, + readReplicas, + largestLiveVersion, + fromPreviousCoordinators); } Future ConfigBroadcaster::getError() const { @@ -544,3 +711,31 @@ JsonBuilderObject ConfigBroadcaster::getStatus() const { void ConfigBroadcaster::compact(Version compactionVersion) { impl->compact(compactionVersion); } + +ACTOR static Future lockConfigNodesImpl(ServerCoordinators coordinators) { + if (coordinators.configServers.empty()) { + return Void(); + } + + CoordinatorsHash coordinatorsHash = std::hash()(coordinators.ccr->getConnectionString().toString()); + + std::vector> lockRequests; + lockRequests.reserve(coordinators.configServers.size()); + for (int i = 0; i < coordinators.configServers.size(); i++) { + if (coordinators.configServers[i].hostname.present()) { + lockRequests.push_back(retryGetReplyFromHostname(ConfigFollowerLockRequest{ coordinatorsHash }, + coordinators.configServers[i].hostname.get(), + WLTOKEN_CONFIGFOLLOWER_LOCK)); + } else { + lockRequests.push_back( + retryBrokenPromise(coordinators.configServers[i].lock, ConfigFollowerLockRequest{ coordinatorsHash })); + } + } + int quorum_size = lockRequests.size() / 2 + 1; + wait(quorum(lockRequests, quorum_size)); + return Void(); +} + +Future ConfigBroadcaster::lockConfigNodes(ServerCoordinators coordinators) { + return lockConfigNodesImpl(coordinators); +} diff --git a/fdbserver/ConfigDatabaseUnitTests.actor.cpp b/fdbserver/ConfigDatabaseUnitTests.actor.cpp index 40ff575b61..3472fe28af 100644 --- a/fdbserver/ConfigDatabaseUnitTests.actor.cpp +++ b/fdbserver/ConfigDatabaseUnitTests.actor.cpp @@ -114,7 +114,7 @@ public: Standalone> mutations, Standalone> annotations) { return cfi.rollforward.getReply( - ConfigFollowerRollforwardRequest{ rollback, lastKnownCommitted, target, mutations, annotations }); + ConfigFollowerRollforwardRequest{ rollback, lastKnownCommitted, target, mutations, annotations, false }); } void restartNode() { @@ -232,7 +232,7 @@ class LocalConfigEnvironment { Future addMutation(Optional configClass, KeyRef knobName, Optional value) { Standalone> versionedMutations; appendVersionedMutation(versionedMutations, ++lastWrittenVersion, configClass, knobName, value); - return readFrom.getMutableLocalConfiguration().addChanges(versionedMutations, lastWrittenVersion); + return readFrom.getMutableLocalConfiguration().addChanges(versionedMutations, lastWrittenVersion, 0); } public: @@ -269,8 +269,8 @@ class BroadcasterToLocalConfigEnvironment { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); - self->broadcastServer = self->broadcaster.registerNode( - WorkerInterface(), 0, configClassSet, self->workerFailure.getFuture(), self->cbi->get()); + self->broadcastServer = + self->broadcaster.registerNode(self->cbi->get(), 0, configClassSet, self->workerFailure.getFuture(), true); return Void(); } @@ -305,11 +305,8 @@ public: broadcastServer.cancel(); cbi->set(ConfigBroadcastInterface{}); readFrom.connectToBroadcaster(cbi); - broadcastServer = broadcaster.registerNode(WorkerInterface(), - readFrom.lastSeenVersion(), - readFrom.configClassSet(), - workerFailure.getFuture(), - cbi->get()); + broadcastServer = broadcaster.registerNode( + cbi->get(), readFrom.lastSeenVersion(), readFrom.configClassSet(), workerFailure.getFuture(), true); } Future restartLocalConfig(std::string const& newConfigPath) { @@ -441,8 +438,8 @@ class TransactionToLocalConfigEnvironment { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); - self->broadcastServer = self->broadcaster.registerNode( - WorkerInterface(), 0, configClassSet, self->workerFailure.getFuture(), self->cbi->get()); + self->broadcastServer = + self->broadcaster.registerNode(self->cbi->get(), 0, configClassSet, self->workerFailure.getFuture(), true); return Void(); } @@ -461,11 +458,8 @@ public: broadcastServer.cancel(); cbi->set(ConfigBroadcastInterface{}); readFrom.connectToBroadcaster(cbi); - broadcastServer = broadcaster.registerNode(WorkerInterface(), - readFrom.lastSeenVersion(), - readFrom.configClassSet(), - workerFailure.getFuture(), - cbi->get()); + broadcastServer = broadcaster.registerNode( + cbi->get(), readFrom.lastSeenVersion(), readFrom.configClassSet(), workerFailure.getFuture(), true); } Future restartLocalConfig(std::string const& newConfigPath) { diff --git a/fdbserver/ConfigFollowerInterface.cpp b/fdbserver/ConfigFollowerInterface.cpp index d95f772d85..54b964d2b4 100644 --- a/fdbserver/ConfigFollowerInterface.cpp +++ b/fdbserver/ConfigFollowerInterface.cpp @@ -29,6 +29,7 @@ void ConfigFollowerInterface::setupWellKnownEndpoints() { compact.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_COMPACT, TaskPriority::Coordination); rollforward.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_ROLLFORWARD, TaskPriority::Coordination); getCommittedVersion.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_GETCOMMITTEDVERSION, TaskPriority::Coordination); + lock.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_LOCK, TaskPriority::Coordination); } ConfigFollowerInterface::ConfigFollowerInterface() : _id(deterministicRandom()->randomUniqueID()) {} @@ -39,7 +40,8 @@ ConfigFollowerInterface::ConfigFollowerInterface(NetworkAddress const& remote) getChanges(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_GETCHANGES)), compact(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_COMPACT)), rollforward(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_ROLLFORWARD)), - getCommittedVersion(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_GETCOMMITTEDVERSION)) {} + getCommittedVersion(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_GETCOMMITTEDVERSION)), + lock(Endpoint::wellKnown({ remote }, WLTOKEN_CONFIGFOLLOWER_LOCK)) {} ConfigFollowerInterface::ConfigFollowerInterface(Hostname const& remote) : _id(deterministicRandom()->randomUniqueID()), hostname(remote) {} diff --git a/fdbserver/ConfigNode.actor.cpp b/fdbserver/ConfigNode.actor.cpp index 25c4cbc625..fce3ee5010 100644 --- a/fdbserver/ConfigNode.actor.cpp +++ b/fdbserver/ConfigNode.actor.cpp @@ -32,9 +32,11 @@ namespace { +const KeyRef coordinatorsHashKey = "id"_sr; const KeyRef lastCompactedVersionKey = "lastCompactedVersion"_sr; const KeyRef currentGenerationKey = "currentGeneration"_sr; const KeyRef registeredKey = "registered"_sr; +const KeyRef lockedKey = "locked"_sr; const KeyRangeRef kvKeys = KeyRangeRef("kv/"_sr, "kv0"_sr); const KeyRangeRef mutationKeys = KeyRangeRef("mutation/"_sr, "mutation0"_sr); const KeyRangeRef annotationKeys = KeyRangeRef("annotation/"_sr, "annotation0"_sr); @@ -122,6 +124,7 @@ class ConfigNodeImpl { Counter failedChangeRequests; Counter snapshotRequests; Counter getCommittedVersionRequests; + Counter lockRequests; // Transaction counters Counter successfulCommits; @@ -132,6 +135,27 @@ class ConfigNodeImpl { Counter getGenerationRequests; Future logger; + ACTOR static Future getCoordinatorsHash(ConfigNodeImpl* self) { + state CoordinatorsHash coordinatorsHash = 0; + Optional value = wait(self->kvStore->readValue(coordinatorsHashKey)); + if (value.present()) { + coordinatorsHash = BinaryReader::fromStringRef(value.get(), IncludeVersion()); + } else { + self->kvStore->set( + KeyValueRef(coordinatorsHashKey, BinaryWriter::toValue(coordinatorsHash, IncludeVersion()))); + wait(self->kvStore->commit()); + } + return coordinatorsHash; + } + + ACTOR static Future> getLocked(ConfigNodeImpl* self) { + Optional value = wait(self->kvStore->readValue(lockedKey)); + if (!value.present()) { + return Optional(); + } + return BinaryReader::fromStringRef>(value.get(), IncludeVersion()); + } + ACTOR static Future getGeneration(ConfigNodeImpl* self) { state ConfigGeneration generation; Optional value = wait(self->kvStore->readValue(currentGenerationKey)); @@ -214,8 +238,9 @@ class ConfigNodeImpl { wait(getMutations(self, req.lastSeenVersion + 1, committedVersion)); state Standalone> versionedAnnotations = wait(getAnnotations(self, req.lastSeenVersion + 1, committedVersion)); - TraceEvent(SevDebug, "ConfigNodeSendingChanges", self->id) + TraceEvent(SevInfo, "ConfigNodeSendingChanges", self->id) .detail("ReqLastSeenVersion", req.lastSeenVersion) + .detail("ReqMostRecentVersion", req.mostRecentVersion) .detail("CommittedVersion", committedVersion) .detail("NumMutations", versionedMutations.size()) .detail("NumCommits", versionedAnnotations.size()); @@ -227,6 +252,11 @@ class ConfigNodeImpl { // New transactions increment the database's current live version. This effectively serves as a lock, providing // serializability ACTOR static Future getNewGeneration(ConfigNodeImpl* self, ConfigTransactionGetGenerationRequest req) { + state CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (req.coordinatorsHash != coordinatorsHash) { + req.reply.sendError(coordinators_changed()); + return Void(); + } state ConfigGeneration generation = wait(getGeneration(self)); ++generation.liveVersion; if (req.lastSeenLiveVersion.present()) { @@ -241,6 +271,17 @@ class ConfigNodeImpl { } ACTOR static Future get(ConfigNodeImpl* self, ConfigTransactionGetRequest req) { + state Optional locked = wait(getLocked(self)); + if (locked.present()) { + CODE_PROBE(true, "attempting to read from a locked ConfigNode"); + req.reply.sendError(coordinators_changed()); + return Void(); + } + state CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (req.coordinatorsHash != coordinatorsHash) { + req.reply.sendError(coordinators_changed()); + return Void(); + } ConfigGeneration currentGeneration = wait(getGeneration(self)); if (req.generation != currentGeneration) { // TODO: Also send information about highest seen version @@ -273,6 +314,17 @@ class ConfigNodeImpl { // TODO: Currently it is possible that extra configuration classes may be returned, we // may want to fix this to clean up the contract ACTOR static Future getConfigClasses(ConfigNodeImpl* self, ConfigTransactionGetConfigClassesRequest req) { + state Optional locked = wait(getLocked(self)); + if (locked.present()) { + CODE_PROBE(true, "attempting to read config classes from locked ConfigNode"); + req.reply.sendError(coordinators_changed()); + return Void(); + } + state CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (req.coordinatorsHash != coordinatorsHash) { + req.reply.sendError(coordinators_changed()); + return Void(); + } ConfigGeneration currentGeneration = wait(getGeneration(self)); if (req.generation != currentGeneration) { req.reply.sendError(transaction_too_old()); @@ -306,6 +358,18 @@ class ConfigNodeImpl { // Retrieve all knobs explicitly defined for the specified configuration class ACTOR static Future getKnobs(ConfigNodeImpl* self, ConfigTransactionGetKnobsRequest req) { + state Optional locked = wait(getLocked(self)); + if (locked.present()) { + CODE_PROBE(true, "attempting to read knobs from locked ConfigNode"); + req.reply.sendError(coordinators_changed()); + return Void(); + } + state CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (req.coordinatorsHash != coordinatorsHash) { + req.reply.sendError(coordinators_changed()); + return Void(); + } + ConfigGeneration currentGeneration = wait(getGeneration(self)); if (req.generation != currentGeneration) { req.reply.sendError(transaction_too_old()); @@ -344,7 +408,8 @@ class ConfigNodeImpl { ACTOR static Future commitMutations(ConfigNodeImpl* self, Standalone> mutations, Standalone> annotations, - Version commitVersion) { + Version commitVersion, + Version liveVersion = ::invalidVersion) { Version latestVersion = 0; int index = 0; for (const auto& mutation : mutations) { @@ -376,6 +441,9 @@ class ConfigNodeImpl { BinaryWriter::toValue(annotation.annotation, IncludeVersion()))); } ConfigGeneration newGeneration = { commitVersion, commitVersion }; + if (liveVersion != ::invalidVersion) { + newGeneration.liveVersion = liveVersion; + } self->kvStore->set(KeyValueRef(currentGenerationKey, BinaryWriter::toValue(newGeneration, IncludeVersion()))); wait(self->kvStore->commit()); ++self->successfulCommits; @@ -383,6 +451,18 @@ class ConfigNodeImpl { } ACTOR static Future commit(ConfigNodeImpl* self, ConfigTransactionCommitRequest req) { + state Optional locked = wait(getLocked(self)); + if (locked.present()) { + CODE_PROBE(true, "attempting to write to locked ConfigNode"); + req.reply.sendError(coordinators_changed()); + return Void(); + } + state CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (req.coordinatorsHash != coordinatorsHash) { + req.reply.sendError(coordinators_changed()); + return Void(); + } + ConfigGeneration currentGeneration = wait(getGeneration(self)); if (req.generation.committedVersion != currentGeneration.committedVersion) { ++self->failedCommits; @@ -399,6 +479,7 @@ class ConfigNodeImpl { } Standalone> annotations; annotations.emplace_back_deep(annotations.arena(), req.generation.liveVersion, req.annotation); + wait(commitMutations(self, mutations, annotations, req.generation.liveVersion)); req.reply.send(Void()); return Void(); @@ -441,10 +522,11 @@ class ConfigNodeImpl { wait(store(reply.snapshotVersion, getLastCompactedVersion(self))); wait(store(reply.changes, getMutations(self, reply.snapshotVersion + 1, req.mostRecentVersion))); wait(store(reply.annotations, getAnnotations(self, reply.snapshotVersion + 1, req.mostRecentVersion))); - TraceEvent(SevDebug, "ConfigNodeGettingSnapshot", self->id) + TraceEvent(SevInfo, "ConfigNodeGettingSnapshot", self->id) .detail("SnapshotVersion", reply.snapshotVersion) .detail("SnapshotSize", reply.snapshot.size()) - .detail("ChangesSize", reply.changes.size()); + .detail("ChangesSize", reply.changes.size()) + .detail("AnnotationsSize", reply.annotations.size()); req.reply.send(reply); return Void(); } @@ -454,7 +536,7 @@ class ConfigNodeImpl { // However, commit annotations for compacted mutations are lost ACTOR static Future compact(ConfigNodeImpl* self, ConfigFollowerCompactRequest req) { state Version lastCompactedVersion = wait(getLastCompactedVersion(self)); - TraceEvent(SevDebug, "ConfigNodeCompacting", self->id) + TraceEvent(SevInfo, "ConfigNodeCompacting", self->id) .detail("Version", req.version) .detail("LastCompacted", lastCompactedVersion); if (req.version <= lastCompactedVersion) { @@ -506,11 +588,14 @@ class ConfigNodeImpl { req.reply.sendError(transaction_too_old()); return Void(); } - TraceEvent("ConfigNodeRollforward") + TraceEvent("ConfigNodeRollforward", self->id) .detail("RollbackTo", req.rollback) .detail("Target", req.target) .detail("LastKnownCommitted", req.lastKnownCommitted) - .detail("Committed", currentGeneration.committedVersion); + .detail("Committed", currentGeneration.committedVersion) + .detail("CurrentGeneration", currentGeneration.toString()) + .detail("LastCompactedVersion", lastCompactedVersion) + .detail("SpecialZeroQuorum", req.specialZeroQuorum); // Rollback to prior known committed version to erase any commits not // made on a quorum. if (req.rollback.present() && req.rollback.get() < currentGeneration.committedVersion) { @@ -539,8 +624,15 @@ class ConfigNodeImpl { } // Now rollforward by applying all mutations between last known // committed version and rollforward version. - ASSERT_GT(req.mutations[0].version, currentGeneration.committedVersion); - wait(commitMutations(self, req.mutations, req.annotations, req.target)); + if (req.mutations.size() > 0) { + ASSERT_GT(req.mutations.size(), 0); + ASSERT_GT(req.mutations[0].version, currentGeneration.committedVersion); + wait(commitMutations(self, + req.mutations, + req.annotations, + req.target, + req.specialZeroQuorum ? currentGeneration.liveVersion : ::invalidVersion)); + } req.reply.send(Void()); return Void(); @@ -548,39 +640,20 @@ class ConfigNodeImpl { ACTOR static Future getCommittedVersion(ConfigNodeImpl* self, ConfigFollowerGetCommittedVersionRequest req) { state Version lastCompacted = wait(getLastCompactedVersion(self)); - ConfigGeneration generation = wait(getGeneration(self)); - req.reply.send(ConfigFollowerGetCommittedVersionReply{ lastCompacted, generation.committedVersion }); + state ConfigGeneration generation = wait(getGeneration(self)); + bool isRegistered = wait(registered(self)); + req.reply.send(ConfigFollowerGetCommittedVersionReply{ + isRegistered, lastCompacted, generation.liveVersion, generation.committedVersion }); return Void(); } - ACTOR static Future serve(ConfigNodeImpl* self, ConfigFollowerInterface const* cfi) { - loop { - choose { - when(ConfigFollowerGetSnapshotAndChangesRequest req = - waitNext(cfi->getSnapshotAndChanges.getFuture())) { - ++self->snapshotRequests; - wait(getSnapshotAndChanges(self, req)); - } - when(ConfigFollowerGetChangesRequest req = waitNext(cfi->getChanges.getFuture())) { - wait(getChanges(self, req)); - } - when(ConfigFollowerCompactRequest req = waitNext(cfi->compact.getFuture())) { - ++self->compactRequests; - wait(compact(self, req)); - } - when(ConfigFollowerRollforwardRequest req = waitNext(cfi->rollforward.getFuture())) { - ++self->rollforwardRequests; - wait(rollforward(self, req)); - } - when(ConfigFollowerGetCommittedVersionRequest req = waitNext(cfi->getCommittedVersion.getFuture())) { - ++self->getCommittedVersionRequests; - wait(getCommittedVersion(self, req)); - } - when(wait(self->kvStore->getError())) { ASSERT(false); } - } - } - } - + // Requires ConfigNodes to register with the ConfigBroadcaster before being + // allowed to respond to most requests. The ConfigBroadcaster will first + // ask the ConfigNode whether it is registered (kickstarted by the worker + // registering with the cluster controller). Then, the ConfigBroadcaster + // will send the ConfigNode a ready message, containing a snapshot if the + // ConfigNode is a new coordinator and needs updated state, or empty + // otherwise. ACTOR static Future serve(ConfigNodeImpl* self, ConfigBroadcastInterface const* cbi, bool infinite) { loop { // Normally, the ConfigBroadcaster will first send a @@ -593,10 +666,61 @@ class ConfigNodeImpl { // ConfigNode. choose { when(state ConfigBroadcastRegisteredRequest req = waitNext(cbi->registered.getFuture())) { - bool isRegistered = wait(registered(self)); - req.reply.send(ConfigBroadcastRegisteredReply{ isRegistered }); + state bool isRegistered = wait(registered(self)); + ConfigGeneration generation = wait(getGeneration(self)); + TraceEvent("ConfigNodeSendingRegisteredReply", self->id) + .detail("Generation", generation.toString()); + req.reply.send(ConfigBroadcastRegisteredReply{ isRegistered, generation.committedVersion }); } - when(ConfigBroadcastReadyRequest readyReq = waitNext(cbi->ready.getFuture())) { + when(state ConfigBroadcastReadyRequest readyReq = waitNext(cbi->ready.getFuture())) { + state Optional locked = wait(getLocked(self)); + + // New ConfigNodes with no previous state should always + // apply snapshots from the ConfigBroadcaster. Otherwise, + // the ConfigNode must be part of a new generation to + // accept a snapshot. An existing ConfigNode that restarts + // shouldn't apply a snapshot and overwrite its state if + // the set of coordinators hasn't changed. + if ((!infinite && !locked.present()) || + (locked.present() && locked.get() != readyReq.coordinatorsHash)) { + // Apply snapshot if necessary. + if (readyReq.snapshot.size() > 0) { + for (const auto& [configKey, knobValue] : readyReq.snapshot) { + TraceEvent("ConfigNodeSettingFromSnapshot", self->id) + .detail("ConfigClass", configKey.configClass) + .detail("KnobName", configKey.knobName) + .detail("Value", knobValue.toString()) + .detail("Version", readyReq.snapshotVersion); + self->kvStore->set(KeyValueRef( + BinaryWriter::toValue(configKey, IncludeVersion()).withPrefix(kvKeys.begin), + ObjectWriter::toValue(knobValue, IncludeVersion()))); + } + ConfigGeneration newGeneration = { readyReq.snapshotVersion, readyReq.liveVersion }; + self->kvStore->set(KeyValueRef(currentGenerationKey, + BinaryWriter::toValue(newGeneration, IncludeVersion()))); + // Clear out any mutations to the keys. If these + // aren't cleared, they will overwrite the + // snapshotted values when the knobs are read. + self->kvStore->clear(KeyRangeRef(versionedMutationKey(0, 0), + versionedMutationKey(readyReq.snapshotVersion + 1, 0))); + self->kvStore->clear(KeyRangeRef(versionedAnnotationKey(0), + versionedAnnotationKey(readyReq.snapshotVersion + 1))); + + self->kvStore->set( + KeyValueRef(lastCompactedVersionKey, + BinaryWriter::toValue(readyReq.snapshotVersion, IncludeVersion()))); + } + // Make sure freshly up to date ConfigNode isn't + // locked! This is possible if it was a coordinator in + // a previous generation. + self->kvStore->set(KeyValueRef( + lockedKey, BinaryWriter::toValue(Optional(), IncludeVersion()))); + } + self->kvStore->set(KeyValueRef(coordinatorsHashKey, + BinaryWriter::toValue(readyReq.coordinatorsHash, IncludeVersion()))); + wait(self->kvStore->commit()); + + TraceEvent("ConfigNodeReady", self->id).detail("WasLocked", locked.present()); readyReq.reply.send(ConfigBroadcastReadyReply{}); if (!infinite) { return Void(); @@ -606,17 +730,70 @@ class ConfigNodeImpl { } } + ACTOR static Future serveRegistered(ConfigNodeImpl* self, ConfigFollowerInterface const* cfi) { + loop { + ConfigFollowerCompactRequest req = waitNext(cfi->compact.getFuture()); + ++self->compactRequests; + wait(compact(self, req)); + } + } + + // Many of the ConfigNode interfaces need to be served before the + // ConfigNode is officially registered with the ConfigBroadcaster. This is + // necessary due to edge cases around coordinator changes. For example, a + // ConfigNode that loses its coordinator status but then restarts before + // serving its snapshot to the new coordinators needs to be able to + // continue serving its snapshot interface when it restarts, even though it + // is no longer a coordinator. + ACTOR static Future serveUnregistered(ConfigNodeImpl* self, ConfigFollowerInterface const* cfi) { + loop { + choose { + when(ConfigFollowerGetSnapshotAndChangesRequest req = + waitNext(cfi->getSnapshotAndChanges.getFuture())) { + ++self->snapshotRequests; + wait(getSnapshotAndChanges(self, req)); + } + when(ConfigFollowerGetChangesRequest req = waitNext(cfi->getChanges.getFuture())) { + wait(getChanges(self, req)); + } + when(ConfigFollowerRollforwardRequest req = waitNext(cfi->rollforward.getFuture())) { + ++self->rollforwardRequests; + wait(rollforward(self, req)); + } + when(ConfigFollowerGetCommittedVersionRequest req = waitNext(cfi->getCommittedVersion.getFuture())) { + ++self->getCommittedVersionRequests; + wait(getCommittedVersion(self, req)); + } + when(state ConfigFollowerLockRequest req = waitNext(cfi->lock.getFuture())) { + ++self->lockRequests; + CoordinatorsHash coordinatorsHash = wait(getCoordinatorsHash(self)); + if (coordinatorsHash == 0 || coordinatorsHash == req.coordinatorsHash) { + TraceEvent("ConfigNodeLocking", self->id).log(); + self->kvStore->set(KeyValueRef(registeredKey, BinaryWriter::toValue(false, IncludeVersion()))); + self->kvStore->set(KeyValueRef( + lockedKey, + BinaryWriter::toValue(Optional(req.coordinatorsHash), IncludeVersion()))); + wait(self->kvStore->commit()); + } + req.reply.send(Void()); + } + when(wait(self->kvStore->getError())) { ASSERT(false); } + } + } + } + ACTOR static Future serve(ConfigNodeImpl* self, ConfigBroadcastInterface const* cbi, ConfigTransactionInterface const* cti, ConfigFollowerInterface const* cfi) { + state Future serveUnregisteredFuture = serveUnregistered(self, cfi); wait(serve(self, cbi, false)); self->kvStore->set(KeyValueRef(registeredKey, BinaryWriter::toValue(true, IncludeVersion()))); wait(self->kvStore->commit()); // Shouldn't return (coordinationServer will throw an error if it does). - wait(serve(self, cbi, true) || serve(self, cti) || serve(self, cfi)); + wait(serve(self, cbi, true) || serve(self, cti) || serveRegistered(self, cfi) || serveUnregisteredFuture); return Void(); } @@ -631,11 +808,12 @@ public: compactRequests("CompactRequests", cc), rollbackRequests("RollbackRequests", cc), rollforwardRequests("RollforwardRequests", cc), successfulChangeRequests("SuccessfulChangeRequests", cc), failedChangeRequests("FailedChangeRequests", cc), snapshotRequests("SnapshotRequests", cc), - getCommittedVersionRequests("GetCommittedVersionRequests", cc), successfulCommits("SuccessfulCommits", cc), - failedCommits("FailedCommits", cc), setMutations("SetMutations", cc), clearMutations("ClearMutations", cc), + getCommittedVersionRequests("GetCommittedVersionRequests", cc), lockRequests("LockRequests", cc), + successfulCommits("SuccessfulCommits", cc), failedCommits("FailedCommits", cc), + setMutations("SetMutations", cc), clearMutations("ClearMutations", cc), getValueRequests("GetValueRequests", cc), getGenerationRequests("GetGenerationRequests", cc) { logger = traceCounters("ConfigNodeMetrics", id, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "ConfigNode"); - TraceEvent(SevDebug, "StartingConfigNode", id).detail("KVStoreAlreadyExists", kvStore.exists()); + TraceEvent(SevInfo, "StartingConfigNode", id).detail("KVStoreAlreadyExists", kvStore.exists()); } Future serve(ConfigBroadcastInterface const& cbi, @@ -644,9 +822,13 @@ public: return serve(this, &cbi, &cti, &cfi); } + Future serve(ConfigBroadcastInterface const& cbi) { return serve(this, &cbi, true); } + Future serve(ConfigTransactionInterface const& cti) { return serve(this, &cti); } - Future serve(ConfigFollowerInterface const& cfi) { return serve(this, &cfi); } + Future serve(ConfigFollowerInterface const& cfi) { + return serveUnregistered(this, &cfi) && serveRegistered(this, &cfi); + } void close() { kvStore.close(); } @@ -663,6 +845,10 @@ Future ConfigNode::serve(ConfigBroadcastInterface const& cbi, return impl->serve(cbi, cti, cfi); } +Future ConfigNode::serve(ConfigBroadcastInterface const& cbi) { + return impl->serve(cbi); +} + Future ConfigNode::serve(ConfigTransactionInterface const& cti) { return impl->serve(cti); } diff --git a/fdbserver/ConsistencyScan.actor.cpp b/fdbserver/ConsistencyScan.actor.cpp new file mode 100644 index 0000000000..1a6046c659 --- /dev/null +++ b/fdbserver/ConsistencyScan.actor.cpp @@ -0,0 +1,1132 @@ +/* + * ConsistencyScan.actor.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2019 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fdbrpc/TenantInfo.h" +#include "fdbserver/WorkerInterface.actor.h" +#include "flow/IRandom.h" +#include "flow/IndexedSet.h" +#include "fdbrpc/FailureMonitor.h" +#include "fdbrpc/Smoother.h" +#include "fdbrpc/simulator.h" +#include "fdbclient/DatabaseContext.h" +#include "fdbclient/ReadYourWrites.h" +#include "fdbclient/TagThrottle.actor.h" +#include "fdbserver/Knobs.h" +#include "fdbserver/StorageMetrics.h" +#include "fdbserver/DataDistribution.actor.h" +#include "fdbserver/RatekeeperInterface.h" +#include "fdbserver/ServerDBInfo.h" +#include "fdbserver/WaitFailure.h" +#include "fdbserver/TesterInterface.actor.h" +#include "flow/DeterministicRandom.h" +#include "flow/Trace.h" +#include "flow/actorcompiler.h" // This must be the last #include. + +// Core of the data consistency checking (checkDataConsistency) and many of the supporting functions are shared between +// the ConsistencyScan role and the ConsistencyCheck workload. They are currently part of this file. ConsistencyScan +// role's main goal is to simply validate data across all shards, while ConsistencyCheck workload does more than that. +// Potentially a re-factor candidate! + +struct ConsistencyScanData { + UID id; + Database db; + + DatabaseConfiguration configuration; + PromiseStream> addActor; + + // TODO: Consider holding a ConsistencyScanInfo object to use as its state, as many of the members are the same. + int64_t restart = 1; + int64_t maxRate = 0; + int64_t targetInterval = 0; + int64_t bytesReadInPrevRound = 0; + int finishedRounds = 0; + KeyRef progressKey; + AsyncVar consistencyScanEnabled = false; + + ConsistencyScanData(UID id, Database db) : id(id), db(db) {} +}; + +// Gets a version at which to read from the storage servers +ACTOR Future getVersion(Database cx) { + loop { + state Transaction tr(cx); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + Version version = wait(tr.getReadVersion()); + return version; + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + +void testFailure(std::string message, bool performQuiescentChecks, bool isError) { + TraceEvent failEvent(isError ? SevError : SevWarn, "TestFailure"); + if (performQuiescentChecks) + failEvent.detail("Workload", "QuiescentCheck"); + else + failEvent.detail("Workload", "ConsistencyCheck"); + + failEvent.detail("Reason", "Consistency check: " + message); +} + +// Get a list of storage servers(persisting keys within range "kr") from the master and compares them with the +// TLogs. If this is a quiescent check, then each commit proxy needs to respond, otherwise only one needs to +// respond. Returns false if there is a failure (in this case, keyServersPromise will never be set) +ACTOR Future getKeyServers( + Database cx, + Promise>>> keyServersPromise, + KeyRangeRef kr, + bool performQuiescentChecks) { + state std::vector>> keyServers; + + // Try getting key server locations from the master proxies + state std::vector>> keyServerLocationFutures; + state Key begin = kr.begin; + state Key end = kr.end; + state int limitKeyServers = BUGGIFY ? 1 : 100; + state Span span(SpanContext(deterministicRandom()->randomUniqueID(), deterministicRandom()->randomUInt64()), + "WL:ConsistencyCheck"_loc); + + while (begin < end) { + state Reference commitProxyInfo = + wait(cx->getCommitProxiesFuture(UseProvisionalProxies::False)); + keyServerLocationFutures.clear(); + for (int i = 0; i < commitProxyInfo->size(); i++) + keyServerLocationFutures.push_back( + commitProxyInfo->get(i, &CommitProxyInterface::getKeyServersLocations) + .getReplyUnlessFailedFor( + GetKeyServerLocationsRequest( + span.context, TenantInfo(), begin, end, limitKeyServers, false, latestVersion, Arena()), + 2, + 0)); + + state bool keyServersInsertedForThisIteration = false; + choose { + when(wait(waitForAll(keyServerLocationFutures))) { + // Read the key server location results + for (int i = 0; i < keyServerLocationFutures.size(); i++) { + ErrorOr shards = keyServerLocationFutures[i].get(); + + // If performing quiescent check, then all master proxies should be reachable. Otherwise, only + // one needs to be reachable + if (performQuiescentChecks && !shards.present()) { + TraceEvent("ConsistencyCheck_CommitProxyUnavailable") + .detail("CommitProxyID", commitProxyInfo->getId(i)); + testFailure("Commit proxy unavailable", performQuiescentChecks, true); + return false; + } + + // Get the list of shards if one was returned. If not doing a quiescent check, we can break if + // it is. If we are doing a quiescent check, then we only need to do this for the first shard. + if (shards.present() && !keyServersInsertedForThisIteration) { + keyServers.insert(keyServers.end(), shards.get().results.begin(), shards.get().results.end()); + keyServersInsertedForThisIteration = true; + begin = shards.get().results.back().first.end; + + if (!performQuiescentChecks) + break; + } + } // End of For + } + when(wait(cx->onProxiesChanged())) {} + } // End of choose + + if (!keyServersInsertedForThisIteration) // Retry the entire workflow + wait(delay(1.0)); + + } // End of while + + keyServersPromise.send(keyServers); + return true; +} + +// Retrieves the locations of all shards in the database +// Returns false if there is a failure (in this case, keyLocationPromise will never be set) +ACTOR Future getKeyLocations(Database cx, + std::vector>> shards, + Promise>> keyLocationPromise, + bool performQuiescentChecks) { + state Standalone> keyLocations; + state Key beginKey = allKeys.begin.withPrefix(keyServersPrefix); + state Key endKey = allKeys.end.withPrefix(keyServersPrefix); + state int i = 0; + state Transaction onErrorTr(cx); // This transaction exists only to access onError and its backoff behavior + + // If the responses are too big, we may use multiple requests to get the key locations. Each request begins + // where the last left off + for (; i < shards.size(); i++) { + while (beginKey < std::min(shards[i].first.end, endKey)) { + try { + Version version = wait(getVersion(cx)); + + GetKeyValuesRequest req; + req.begin = firstGreaterOrEqual(beginKey); + req.end = firstGreaterOrEqual(std::min(shards[i].first.end, endKey)); + req.limit = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT; + req.limitBytes = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES; + req.version = version; + req.tags = TagSet(); + + // Try getting the shard locations from the key servers + state std::vector>> keyValueFutures; + for (const auto& kv : shards[i].second) { + resetReply(req); + keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); + } + + wait(waitForAll(keyValueFutures)); + + int firstValidStorageServer = -1; + + // Read the shard location results + for (int j = 0; j < keyValueFutures.size(); j++) { + ErrorOr reply = keyValueFutures[j].get(); + + if (!reply.present() || reply.get().error.present()) { + // If no storage servers replied, then throw all_alternatives_failed to force a retry + if (firstValidStorageServer < 0 && j == keyValueFutures.size() - 1) + throw all_alternatives_failed(); + } + + // If this is the first storage server, store the locations to send back to the caller + else if (firstValidStorageServer < 0) { + firstValidStorageServer = j; + + // Otherwise, compare the data to the results from the first storage server. If they are + // different, then the check fails + } else if (reply.get().data != keyValueFutures[firstValidStorageServer].get().get().data || + reply.get().more != keyValueFutures[firstValidStorageServer].get().get().more) { + TraceEvent("ConsistencyCheck_InconsistentKeyServers") + .detail("StorageServer1", shards[i].second[firstValidStorageServer].id()) + .detail("StorageServer2", shards[i].second[j].id()); + testFailure("Key servers inconsistent", performQuiescentChecks, true); + return false; + } + } + + auto keyValueResponse = keyValueFutures[firstValidStorageServer].get().get(); + RangeResult currentLocations = krmDecodeRanges( + keyServersPrefix, + KeyRangeRef(beginKey.removePrefix(keyServersPrefix), + std::min(shards[i].first.end, endKey).removePrefix(keyServersPrefix)), + RangeResultRef(keyValueResponse.data, keyValueResponse.more)); + + if (keyValueResponse.data.size() && beginKey == keyValueResponse.data[0].key) { + keyLocations.push_back_deep(keyLocations.arena(), currentLocations[0]); + } + + if (currentLocations.size() > 2) { + keyLocations.append_deep(keyLocations.arena(), ¤tLocations[1], currentLocations.size() - 2); + } + + // Next iteration should pick up where we left off + ASSERT(currentLocations.size() > 1); + if (!keyValueResponse.more) { + beginKey = shards[i].first.end; + } else { + beginKey = keyValueResponse.data.end()[-1].key; + } + + // If this is the last iteration, then push the allKeys.end KV pair + if (beginKey >= endKey) + keyLocations.push_back_deep(keyLocations.arena(), currentLocations.end()[-1]); + } catch (Error& e) { + state Error err = e; + wait(onErrorTr.onError(err)); + TraceEvent("ConsistencyCheck_RetryGetKeyLocations").error(err); + } + } + } + + keyLocationPromise.send(keyLocations); + return true; +} + +// Retrieves a vector of the storage servers' estimates for the size of a particular shard +// If a storage server can't be reached, its estimate will be -1 +// If there is an error, then the returned vector will have 0 size +ACTOR Future> getStorageSizeEstimate(std::vector storageServers, + KeyRangeRef shard) { + state std::vector estimatedBytes; + + state WaitMetricsRequest req; + req.keys = shard; + req.max.bytes = -1; + req.min.bytes = 0; + + state std::vector>> metricFutures; + + try { + // Check the size of the shard on each storage server + for (int i = 0; i < storageServers.size(); i++) { + resetReply(req); + metricFutures.push_back(storageServers[i].waitMetrics.getReplyUnlessFailedFor(req, 2, 0)); + } + + // Wait for the storage servers to respond + wait(waitForAll(metricFutures)); + + int firstValidStorageServer = -1; + + // Retrieve the size from the storage server responses + for (int i = 0; i < storageServers.size(); i++) { + ErrorOr reply = metricFutures[i].get(); + + // If the storage server doesn't reply, then return -1 + if (!reply.present()) { + TraceEvent("ConsistencyCheck_FailedToFetchMetrics") + .error(reply.getError()) + .detail("Begin", printable(shard.begin)) + .detail("End", printable(shard.end)) + .detail("StorageServer", storageServers[i].id()) + .detail("IsTSS", storageServers[i].isTss() ? "True" : "False"); + estimatedBytes.push_back(-1); + } + + // Add the result to the list of estimates + else if (reply.present()) { + int64_t numBytes = reply.get().bytes; + estimatedBytes.push_back(numBytes); + if (firstValidStorageServer < 0) + firstValidStorageServer = i; + else if (estimatedBytes[firstValidStorageServer] != numBytes) { + TraceEvent("ConsistencyCheck_InconsistentStorageMetrics") + .detail("ByteEstimate1", estimatedBytes[firstValidStorageServer]) + .detail("ByteEstimate2", numBytes) + .detail("Begin", shard.begin) + .detail("End", shard.end) + .detail("StorageServer1", storageServers[firstValidStorageServer].id()) + .detail("StorageServer2", storageServers[i].id()) + .detail("IsTSS", + storageServers[i].isTss() || storageServers[firstValidStorageServer].isTss() ? "True" + : "False"); + } + } + } + } catch (Error& e) { + TraceEvent("ConsistencyCheck_ErrorFetchingMetrics") + .error(e) + .detail("Begin", printable(shard.begin)) + .detail("End", printable(shard.end)); + estimatedBytes.clear(); + } + + return estimatedBytes; +} + +ACTOR Future getDatabaseSize(Database cx) { + state Transaction tr(cx); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + loop { + try { + StorageMetrics metrics = + wait(tr.getDatabase()->getStorageMetrics(KeyRangeRef(allKeys.begin, keyServersPrefix), 100000)); + return metrics.bytes; + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + +// Checks that the data in each shard is the same on each storage server that it resides on. Also performs some +// sanity checks on the sizes of shards and storage servers. Returns false if there is a failure +// TODO: Future optimization: Use streaming reads +ACTOR Future checkDataConsistency(Database cx, + VectorRef keyLocations, + DatabaseConfiguration configuration, + std::map 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* bytesReadInPrevRound, + int restart, + int64_t maxRate, + int64_t targetInterval, + KeyRef progressKey) { + // Stores the total number of bytes on each storage server + // In a distributed test, this will be an estimated size + state std::map storageServerSizes; + + // Iterate through each shard, checking its values on all of its storage servers + // If shardSampleFactor > 1, then not all shards are processed + // Also, in a distributed data consistency check, each client processes a subset of the shards + // Note: this may cause some shards to be processed more than once or not at all in a non-quiescent database + state int effectiveClientCount = distributed ? clientCount : 1; + state int i = clientId * (shardSampleFactor + 1); + state int increment = (distributed && !firstClient) ? effectiveClientCount * shardSampleFactor : 1; + state int64_t rateLimitForThisRound = + *bytesReadInPrevRound == 0 + ? maxRate + : std::min(maxRate, static_cast(ceil(*bytesReadInPrevRound / (float)targetInterval))); + ASSERT(rateLimitForThisRound >= 0 && rateLimitForThisRound <= maxRate); + TraceEvent("ConsistencyCheck_RateLimitForThisRound").detail("RateLimit", rateLimitForThisRound); + state Reference rateLimiter = Reference(new SpeedLimit(rateLimitForThisRound, 1)); + state double rateLimiterStartTime = now(); + state int64_t bytesReadInthisRound = 0; + state bool resume = !(restart || shuffleShards); + + state double dbSize = 100e12; + if (g_network->isSimulated()) { + // This call will get all shard ranges in the database, which is too expensive on real clusters. + int64_t _dbSize = wait(getDatabaseSize(cx)); + dbSize = _dbSize; + } + + state std::vector ranges; + + for (int k = 0; k < keyLocations.size() - 1; k++) { + // TODO: check if this is sufficient + if (resume && keyLocations[k].key < progressKey) { + TraceEvent("ConsistencyCheck_SkippingRange") + .detail("KeyBegin", keyLocations[k].key.toString()) + .detail("KeyEnd", keyLocations[k + 1].key.toString()) + .detail("PrevKey", progressKey.toString()); + continue; + } + KeyRangeRef range(keyLocations[k].key, keyLocations[k + 1].key); + ranges.push_back(range); + } + + state std::vector shardOrder; + shardOrder.reserve(ranges.size()); + for (int k = 0; k < ranges.size(); k++) + shardOrder.push_back(k); + if (shuffleShards) { + uint32_t seed = sharedRandomNumber + repetitions; + DeterministicRandom sharedRandom(seed == 0 ? 1 : seed); + sharedRandom.randomShuffle(shardOrder); + } + + for (; i < ranges.size(); i++) { + state int shard = shardOrder[i]; + + state KeyRangeRef range = ranges[shard]; + state std::vector sourceStorageServers; + state std::vector destStorageServers; + state Transaction tr(cx); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + state int bytesReadInRange = 0; + + RangeResult UIDtoTagMap = wait(tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY)); + ASSERT(!UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY); + decodeKeyServersValue(UIDtoTagMap, keyLocations[shard].value, sourceStorageServers, destStorageServers, false); + + // If the destStorageServers is non-empty, then this shard is being relocated + state bool isRelocating = destStorageServers.size() > 0; + + // In a quiescent database, check that the team size is the same as the desired team size + if (firstClient && performQuiescentChecks && + sourceStorageServers.size() != configuration.usableRegions * configuration.storageTeamSize) { + TraceEvent("ConsistencyCheck_InvalidTeamSize") + .detail("ShardBegin", printable(range.begin)) + .detail("ShardEnd", printable(range.end)) + .detail("SourceTeamSize", sourceStorageServers.size()) + .detail("DestServerSize", destStorageServers.size()) + .detail("ConfigStorageTeamSize", configuration.storageTeamSize) + .detail("UsableRegions", configuration.usableRegions); + // Record the server reponsible for the problematic shards + int k = 0; + for (auto& id : sourceStorageServers) { + TraceEvent("IncorrectSizeTeamInfo").detail("ServerUID", id).detail("TeamIndex", k++); + } + testFailure("Invalid team size", performQuiescentChecks, failureIsError); + return false; + } + + state std::vector storageServers = (isRelocating) ? destStorageServers : sourceStorageServers; + state std::vector storageServerInterfaces; + + loop { + try { + std::vector>> serverListEntries; + serverListEntries.reserve(storageServers.size()); + for (int s = 0; s < storageServers.size(); s++) + serverListEntries.push_back(tr.get(serverListKeyFor(storageServers[s]))); + state std::vector> serverListValues = wait(getAll(serverListEntries)); + for (int s = 0; s < serverListValues.size(); s++) { + if (serverListValues[s].present()) + storageServerInterfaces.push_back(decodeServerListValue(serverListValues[s].get())); + else if (performQuiescentChecks) + testFailure( + "/FF/serverList changing in a quiescent database", performQuiescentChecks, failureIsError); + } + + break; + } catch (Error& e) { + wait(tr.onError(e)); + } + } + + // add TSS to end of list, if configured and if not relocating + if (!isRelocating && performTSSCheck) { + int initialSize = storageServers.size(); + for (int i = 0; i < initialSize; i++) { + auto tssPair = tssMapping.find(storageServers[i]); + if (tssPair != tssMapping.end()) { + CODE_PROBE(true, "TSS checked in consistency check"); + storageServers.push_back(tssPair->second.id()); + storageServerInterfaces.push_back(tssPair->second); + } + } + } + + state std::vector estimatedBytes = wait(getStorageSizeEstimate(storageServerInterfaces, range)); + + // Gets permitted size range of shard + int64_t maxShardSize = getMaxShardSize(dbSize); + state ShardSizeBounds shardBounds = getShardSizeBounds(range, maxShardSize); + + if (firstClient) { + // If there was an error retrieving shard estimated size + if (performQuiescentChecks && estimatedBytes.size() == 0) + testFailure("Error fetching storage metrics", performQuiescentChecks, failureIsError); + + // If running a distributed test, storage server size is an accumulation of shard estimates + else if (distributed && firstClient) + for (int j = 0; j < storageServers.size(); j++) + storageServerSizes[storageServers[j]] += std::max(estimatedBytes[j], (int64_t)0); + } + + // The first client may need to skip the rest of the loop contents if it is just processing this shard to + // get a size estimate + if (!firstClient || shard % (effectiveClientCount * shardSampleFactor) == 0) { + state int shardKeys = 0; + state int shardBytes = 0; + state int sampledBytes = 0; + state int splitBytes = 0; + state int firstKeySampledBytes = 0; + state int sampledKeys = 0; + state int sampledKeysWithProb = 0; + state double shardVariance = 0; + state bool canSplit = false; + state Key lastSampleKey; + state Key lastStartSampleKey; + state int64_t totalReadAmount = 0; + + state KeySelector begin = firstGreaterOrEqual(range.begin); + state Transaction onErrorTr(cx); // This transaction exists only to access onError and its backoff behavior + + // Read a limited number of entries at a time, repeating until all keys in the shard have been read + loop { + try { + lastSampleKey = lastStartSampleKey; + + // Get the min version of the storage servers + Version version = wait(getVersion(cx)); + + state GetKeyValuesRequest req; + req.begin = begin; + req.end = firstGreaterOrEqual(range.end); + req.limit = 1e4; + req.limitBytes = CLIENT_KNOBS->REPLY_BYTE_LIMIT; + req.version = version; + req.tags = TagSet(); + + // Try getting the entries in the specified range + state std::vector>> keyValueFutures; + state int j = 0; + TraceEvent("ConsistencyCheck_StoringGetFutures").detail("SSISize", storageServerInterfaces.size()); + for (j = 0; j < storageServerInterfaces.size(); j++) { + resetReply(req); + keyValueFutures.push_back( + storageServerInterfaces[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); + } + + wait(waitForAll(keyValueFutures)); + + // Read the resulting entries + state int firstValidServer = -1; + totalReadAmount = 0; + for (j = 0; j < storageServerInterfaces.size(); j++) { + ErrorOr rangeResult = keyValueFutures[j].get(); + + // Compare the results with other storage servers + if (rangeResult.present() && !rangeResult.get().error.present()) { + state GetKeyValuesReply current = rangeResult.get(); + TraceEvent("ConsistencyCheck_GetKeyValuesStream") + .detail("DataSize", current.data.size()) + .detail(format("StorageServer%d", j).c_str(), storageServers[j].toString()); + totalReadAmount += current.data.expectedSize(); + // If we haven't encountered a valid storage server yet, then mark this as the baseline + // to compare against + if (firstValidServer == -1) { + TraceEvent("ConsistencyCheck_FirstValidServer").detail("Iter", j); + firstValidServer = j; + // Compare this shard against the first + } else { + GetKeyValuesReply reference = keyValueFutures[firstValidServer].get().get(); + + if (current.data != reference.data || current.more != reference.more) { + // Be especially verbose if in simulation + if (g_network->isSimulated()) { + int invalidIndex = -1; + printf("\n%sSERVER %d (%s); shard = %s - %s:\n", + "", + j, + storageServerInterfaces[j].address().toString().c_str(), + printable(req.begin.getKey()).c_str(), + printable(req.end.getKey()).c_str()); + for (int k = 0; k < current.data.size(); k++) { + printf("%d. %s => %s\n", + k, + printable(current.data[k].key).c_str(), + printable(current.data[k].value).c_str()); + if (invalidIndex < 0 && (k >= reference.data.size() || + current.data[k].key != reference.data[k].key || + current.data[k].value != reference.data[k].value)) + invalidIndex = k; + } + + printf("\n%sSERVER %d (%s); shard = %s - %s:\n", + "", + firstValidServer, + storageServerInterfaces[firstValidServer].address().toString().c_str(), + printable(req.begin.getKey()).c_str(), + printable(req.end.getKey()).c_str()); + for (int k = 0; k < reference.data.size(); k++) { + printf("%d. %s => %s\n", + k, + printable(reference.data[k].key).c_str(), + printable(reference.data[k].value).c_str()); + if (invalidIndex < 0 && (k >= current.data.size() || + reference.data[k].key != current.data[k].key || + reference.data[k].value != current.data[k].value)) + invalidIndex = k; + } + + printf("\nMISMATCH AT %d\n\n", invalidIndex); + } + + // Data for trace event + // The number of keys unique to the current shard + int currentUniques = 0; + // The number of keys unique to the reference shard + int referenceUniques = 0; + // The number of keys in both shards with conflicting values + int valueMismatches = 0; + // The number of keys in both shards with matching values + int matchingKVPairs = 0; + // Last unique key on the current shard + KeyRef currentUniqueKey; + // Last unique key on the reference shard + KeyRef referenceUniqueKey; + // Last value mismatch + KeyRef valueMismatchKey; + + // Loop indeces + int currentI = 0; + int referenceI = 0; + while (currentI < current.data.size() || referenceI < reference.data.size()) { + if (currentI >= current.data.size()) { + referenceUniqueKey = reference.data[referenceI].key; + referenceUniques++; + referenceI++; + } else if (referenceI >= reference.data.size()) { + currentUniqueKey = current.data[currentI].key; + currentUniques++; + currentI++; + } else { + KeyValueRef currentKV = current.data[currentI]; + KeyValueRef referenceKV = reference.data[referenceI]; + + if (currentKV.key == referenceKV.key) { + if (currentKV.value == referenceKV.value) + matchingKVPairs++; + else { + valueMismatchKey = currentKV.key; + valueMismatches++; + } + + currentI++; + referenceI++; + } else if (currentKV.key < referenceKV.key) { + currentUniqueKey = currentKV.key; + currentUniques++; + currentI++; + } else { + referenceUniqueKey = referenceKV.key; + referenceUniques++; + referenceI++; + } + } + } + + TraceEvent("ConsistencyCheck_DataInconsistent") + .detail(format("StorageServer%d", j).c_str(), storageServers[j].toString()) + .detail(format("StorageServer%d", firstValidServer).c_str(), + storageServers[firstValidServer].toString()) + .detail("ShardBegin", req.begin.getKey()) + .detail("ShardEnd", req.end.getKey()) + .detail("VersionNumber", req.version) + .detail(format("Server%dUniques", j).c_str(), currentUniques) + .detail(format("Server%dUniqueKey", j).c_str(), currentUniqueKey) + .detail(format("Server%dUniques", firstValidServer).c_str(), referenceUniques) + .detail(format("Server%dUniqueKey", firstValidServer).c_str(), + referenceUniqueKey) + .detail("ValueMismatches", valueMismatches) + .detail("ValueMismatchKey", valueMismatchKey) + .detail("MatchingKVPairs", matchingKVPairs) + .detail("IsTSS", + storageServerInterfaces[j].isTss() || + storageServerInterfaces[firstValidServer].isTss() + ? "True" + : "False"); + + if ((g_network->isSimulated() && + g_simulator->tssMode != ISimulator::TSSMode::EnabledDropMutations) || + (!storageServerInterfaces[j].isTss() && + !storageServerInterfaces[firstValidServer].isTss())) { + testFailure("Data inconsistent", performQuiescentChecks, true); + return false; + } + } + } + } + + // If the data is not available and we aren't relocating this shard + else if (!isRelocating) { + Error e = rangeResult.isError() ? rangeResult.getError() : rangeResult.get().error.get(); + + TraceEvent("ConsistencyCheck_StorageServerUnavailable") + .errorUnsuppressed(e) + .suppressFor(1.0) + .detail("StorageServer", storageServers[j]) + .detail("ShardBegin", printable(range.begin)) + .detail("ShardEnd", printable(range.end)) + .detail("Address", storageServerInterfaces[j].address()) + .detail("UID", storageServerInterfaces[j].id()) + .detail("GetKeyValuesToken", + storageServerInterfaces[j].getKeyValues.getEndpoint().token) + .detail("IsTSS", storageServerInterfaces[j].isTss() ? "True" : "False"); + + // All shards should be available in quiscence + if (performQuiescentChecks && !storageServerInterfaces[j].isTss()) { + testFailure("Storage server unavailable", performQuiescentChecks, failureIsError); + return false; + } + } + } + + if (firstValidServer >= 0) { + state VectorRef data = keyValueFutures[firstValidServer].get().get().data; + + // Persist the last key of the range we just verified as the progressKey + if (data.size()) { + state Reference csInfoTr = + makeReference(cx); + progressKey = data[data.size() - 1].key; + loop { + try { + csInfoTr->reset(); + csInfoTr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + + state Optional val = wait(ConsistencyScanInfo::getInfo(csInfoTr)); + wait(csInfoTr->commit()); + if (val.present()) { + ConsistencyScanInfo consistencyScanInfo = + ObjectReader::fromStringRef(val.get(), + IncludeVersion()); + consistencyScanInfo.progress_key = progressKey; + csInfoTr->reset(); + csInfoTr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + wait(ConsistencyScanInfo::setInfo(csInfoTr, consistencyScanInfo)); + wait(csInfoTr->commit()); + } + break; + } catch (Error& e) { + wait(csInfoTr->onError(e)); + } + } + } + + // Calculate the size of the shard, the variance of the shard size estimate, and the correct + // shard size estimate + for (int k = 0; k < data.size(); k++) { + ByteSampleInfo sampleInfo = isKeyValueInSample(data[k]); + shardBytes += sampleInfo.size; + double itemProbability = ((double)sampleInfo.size) / sampleInfo.sampledSize; + if (itemProbability < 1) + shardVariance += + itemProbability * (1 - itemProbability) * pow((double)sampleInfo.sampledSize, 2); + + if (sampleInfo.inSample) { + sampledBytes += sampleInfo.sampledSize; + if (!canSplit && sampledBytes >= shardBounds.min.bytes && + data[k].key.size() <= CLIENT_KNOBS->SPLIT_KEY_SIZE_LIMIT && + sampledBytes <= + shardBounds.max.bytes * CLIENT_KNOBS->STORAGE_METRICS_UNFAIR_SPLIT_LIMIT / 2) { + canSplit = true; + splitBytes = sampledBytes; + } + + /*TraceEvent("ConsistencyCheck_ByteSample").detail("ShardBegin", printable(range.begin)).detail("ShardEnd", printable(range.end)) + .detail("SampledBytes", sampleInfo.sampledSize).detail("Key", + printable(data[k].key)).detail("KeySize", data[k].key.size()).detail("ValueSize", + data[k].value.size());*/ + + // In data distribution, the splitting process ignores the first key in a shard. + // Thus, we shouldn't consider it when validating the upper bound of estimated shard + // sizes + if (k == 0) + firstKeySampledBytes += sampleInfo.sampledSize; + + sampledKeys++; + if (itemProbability < 1) { + sampledKeysWithProb++; + } + } + } + + // Accumulate number of keys in this shard + shardKeys += data.size(); + } + // after requesting each shard, enforce rate limit based on how much data will likely be read + if (rateLimitForThisRound > 0) { + TraceEvent("ConsistencyCheck_RateLimit") + .detail("RateLimitForThisRound", rateLimitForThisRound) + .detail("TotalAmountRead", totalReadAmount); + wait(rateLimiter->getAllowance(totalReadAmount)); + TraceEvent("ConsistencyCheck_AmountRead1").detail("TotalAmountRead", totalReadAmount); + // Set ratelimit to max allowed if current round has been going on for a while + if (now() - rateLimiterStartTime > 1.1 * targetInterval && rateLimitForThisRound != maxRate) { + rateLimitForThisRound = maxRate; + rateLimiter = Reference(new SpeedLimit(rateLimitForThisRound, 1)); + rateLimiterStartTime = now(); + TraceEvent(SevInfo, "ConsistencyCheck_RateLimitSetMaxForThisRound") + .detail("RateLimit", rateLimitForThisRound); + } + } + bytesReadInRange += totalReadAmount; + bytesReadInthisRound += totalReadAmount; + TraceEvent("ConsistencyCheck_BytesRead") + .detail("BytesReadInRange", bytesReadInRange) + .detail("BytesReadInthisRound", bytesReadInthisRound); + + // Advance to the next set of entries + if (firstValidServer >= 0 && keyValueFutures[firstValidServer].get().get().more) { + VectorRef result = keyValueFutures[firstValidServer].get().get().data; + ASSERT(result.size() > 0); + begin = firstGreaterThan(result[result.size() - 1].key); + ASSERT(begin.getKey() != allKeys.end); + lastStartSampleKey = lastSampleKey; + } else + break; + } catch (Error& e) { + state Error err = e; + wait(onErrorTr.onError(err)); + TraceEvent("ConsistencyCheck_RetryDataConsistency").error(err); + } + } + + canSplit = canSplit && sampledBytes - splitBytes >= shardBounds.min.bytes && sampledBytes > splitBytes; + + // Update the size of all storage servers containing this shard + // This is only done in a non-distributed consistency check; the distributed check uses shard size + // estimates + if (!distributed) + for (int j = 0; j < storageServers.size(); j++) + storageServerSizes[storageServers[j]] += shardBytes; + + // If the storage servers' sampled estimate of shard size is different from ours + if (performQuiescentChecks) { + for (int j = 0; j < estimatedBytes.size(); j++) { + if (estimatedBytes[j] >= 0 && estimatedBytes[j] != sampledBytes) { + TraceEvent("ConsistencyCheck_IncorrectEstimate") + .detail("EstimatedBytes", estimatedBytes[j]) + .detail("CorrectSampledBytes", sampledBytes) + .detail("StorageServer", storageServers[j]) + .detail("IsTSS", storageServerInterfaces[j].isTss() ? "True" : "False"); + + if (!storageServerInterfaces[j].isTss()) { + testFailure("Storage servers had incorrect sampled estimate", + performQuiescentChecks, + failureIsError); + } + + break; + } else if (estimatedBytes[j] < 0 && ((g_network->isSimulated() && + g_simulator->tssMode <= ISimulator::TSSMode::EnabledNormal) || + !storageServerInterfaces[j].isTss())) { + // Ignore a non-responding TSS outside of simulation, or if tss fault injection is enabled + break; + } + } + } + + // Compute the difference between the shard size estimate and its actual size. If it is sufficiently + // large, then fail + double stdDev = sqrt(shardVariance); + + double failErrorNumStdDev = 7; + int estimateError = abs(shardBytes - sampledBytes); + + // Only perform the check if there are sufficient keys to get a distribution that should resemble a + // normal distribution + if (sampledKeysWithProb > 30 && estimateError > failErrorNumStdDev * stdDev) { + double numStdDev = estimateError / sqrt(shardVariance); + TraceEvent("ConsistencyCheck_InaccurateShardEstimate") + .detail("Min", shardBounds.min.bytes) + .detail("Max", shardBounds.max.bytes) + .detail("Estimate", sampledBytes) + .detail("Actual", shardBytes) + .detail("NumStdDev", numStdDev) + .detail("Variance", shardVariance) + .detail("StdDev", stdDev) + .detail("ShardBegin", printable(range.begin)) + .detail("ShardEnd", printable(range.end)) + .detail("NumKeys", shardKeys) + .detail("NumSampledKeys", sampledKeys) + .detail("NumSampledKeysWithProb", sampledKeysWithProb); + + testFailure(format("Shard size is more than %f std dev from estimate", failErrorNumStdDev), + performQuiescentChecks, + failureIsError); + } + + // In a quiescent database, check that the (estimated) size of the shard is within permitted bounds + // Min and max shard sizes have a 3 * shardBounds.permittedError.bytes cushion for error since shard + // sizes are not precise Shard splits ignore the first key in a shard, so its size shouldn't be + // considered when checking the upper bound 0xff shards are not checked + if (canSplit && sampledKeys > 5 && performQuiescentChecks && !range.begin.startsWith(keyServersPrefix) && + (sampledBytes < shardBounds.min.bytes - 3 * shardBounds.permittedError.bytes || + sampledBytes - firstKeySampledBytes > shardBounds.max.bytes + 3 * shardBounds.permittedError.bytes)) { + TraceEvent("ConsistencyCheck_InvalidShardSize") + .detail("Min", shardBounds.min.bytes) + .detail("Max", shardBounds.max.bytes) + .detail("Size", shardBytes) + .detail("EstimatedSize", sampledBytes) + .detail("ShardBegin", printable(range.begin)) + .detail("ShardEnd", printable(range.end)) + .detail("ShardCount", ranges.size()) + .detail("SampledKeys", sampledKeys); + testFailure(format("Shard size in quiescent database is too %s", + (sampledBytes < shardBounds.min.bytes) ? "small" : "large"), + performQuiescentChecks, + failureIsError); + return false; + } + } + + if (bytesReadInRange > 0) { + TraceEvent("ConsistencyCheck_ReadRange") + .suppressFor(1.0) + .detail("Range", range) + .detail("BytesRead", bytesReadInRange); + } + } + + *bytesReadInPrevRound = bytesReadInthisRound; + return true; +} + +ACTOR Future runDataValidationCheck(ConsistencyScanData* self) { + state Reference tr = makeReference(self->db); + state ConsistencyScanInfo csInfo = ConsistencyScanInfo(); + csInfo.consistency_scan_enabled = true; + csInfo.restart = self->restart; + csInfo.max_rate = self->maxRate; + csInfo.target_interval = self->targetInterval; + csInfo.last_round_start = StorageMetadataType::currentTime(); + try { + // Get a list of key servers; verify that the TLogs and master all agree about who the key servers are + state Promise>>> keyServerPromise; + state std::map tssMapping; + bool keyServerResult = wait(getKeyServers(self->db, keyServerPromise, keyServersKeys, false)); + if (keyServerResult) { + state std::vector>> keyServers = + keyServerPromise.getFuture().get(); + + // Get the locations of all the shards in the database + state Promise>> keyLocationPromise; + bool keyLocationResult = wait(getKeyLocations(self->db, keyServers, keyLocationPromise, false)); + if (keyLocationResult) { + state Standalone> keyLocations = keyLocationPromise.getFuture().get(); + + // Check that each shard has the same data on all storage servers that it resides on + wait(::success(checkDataConsistency(self->db, + keyLocations, + self->configuration, + tssMapping, + false /* quiescentCheck */, + false /* tssCheck */, + true /* firstClient */, + false /* failureIsError */, + 0 /* clientId */, + 1 /* clientCount */, + false /* distributed */, + false /* shuffleShards */, + 1 /* shardSampleFactor */, + deterministicRandom()->randomInt64(0, 10000000), + self->finishedRounds /* repetitions */, + &(self->bytesReadInPrevRound), + self->restart, + self->maxRate, + self->targetInterval, + self->progressKey))); + } + } + } catch (Error& e) { + if (e.code() == error_code_transaction_too_old || e.code() == error_code_future_version || + e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed || + e.code() == error_code_process_behind || e.code() == error_code_actor_cancelled) + TraceEvent("ConsistencyScan_Retry").error(e); // FIXME: consistency check does not retry in this case + else + throw; + } + + TraceEvent("ConsistencyScan_FinishedCheck"); + + // Update the ConsistencyScanInfo object and persist to the database + csInfo.last_round_finish = StorageMetadataType::currentTime(); + csInfo.finished_rounds = self->finishedRounds + 1; + auto duration = csInfo.last_round_finish - csInfo.last_round_start; + csInfo.smoothed_round_duration.setTotal((double)duration); + csInfo.progress_key = self->progressKey; + csInfo.bytes_read_prev_round = self->bytesReadInPrevRound; + loop { + try { + tr->reset(); + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + wait(ConsistencyScanInfo::setInfo(tr, csInfo)); + wait(tr->commit()); + break; + } catch (Error& e) { + wait(tr->onError(e)); + } + } + + return Void(); +} + +ACTOR Future watchConsistencyScanInfoKey(ConsistencyScanData* self) { + state Reference tr = makeReference(self->db); + + loop { + try { + tr->reset(); + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + + state Optional val = wait(ConsistencyScanInfo::getInfo(tr)); + if (val.present()) { + ConsistencyScanInfo consistencyScanInfo = + ObjectReader::fromStringRef(val.get(), IncludeVersion()); + self->restart = consistencyScanInfo.restart; + self->maxRate = consistencyScanInfo.max_rate; + self->targetInterval = consistencyScanInfo.target_interval; + self->progressKey = consistencyScanInfo.progress_key; + self->bytesReadInPrevRound = consistencyScanInfo.bytes_read_prev_round; + self->finishedRounds = consistencyScanInfo.finished_rounds; + self->consistencyScanEnabled.set(consistencyScanInfo.consistency_scan_enabled); + //TraceEvent("ConsistencyScan_WatchGotVal", self->id) + // .detail("Enabled", consistencyScanInfo.consistency_scan_enabled) + // .detail("MaxRateRead", consistencyScanInfo.max_rate) + // .detail("MaxRateSelf", self->maxRate); + } + state Future watch = tr->watch(consistencyScanInfoKey); + wait(tr->commit()); + wait(watch); + } catch (Error& e) { + wait(tr->onError(e)); + } + } +} + +ACTOR Future consistencyScan(ConsistencyScanInterface csInterf, Reference const> dbInfo) { + state ConsistencyScanData self(csInterf.id(), + openDBOnServer(dbInfo, TaskPriority::DefaultEndpoint, LockAware::True)); + state Promise err; + state Future collection = actorCollection(self.addActor.getFuture()); + state ConsistencyScanInfo csInfo = ConsistencyScanInfo(); + + TraceEvent("ConsistencyScan_Starting", csInterf.id()).log(); + + // Randomly enable consistencyScan in simulation + if (g_network->isSimulated()) { + if (deterministicRandom()->random01() < 0.5) { + csInfo.consistency_scan_enabled = false; + } else { + csInfo.consistency_scan_enabled = true; + csInfo.restart = false; + csInfo.max_rate = 50e6; + csInfo.target_interval = 24 * 7 * 60 * 60; + } + TraceEvent("SimulatedConsistencyScanConfigRandom") + .detail("ConsistencyScanEnabled", csInfo.consistency_scan_enabled) + .detail("MaxRate", csInfo.max_rate) + .detail("Interval", csInfo.target_interval); + state Reference tr = makeReference(self.db); + loop { + try { + tr->reset(); + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + wait(ConsistencyScanInfo::setInfo(tr, csInfo)); + wait(tr->commit()); + break; + } catch (Error& e) { + wait(tr->onError(e)); + } + } + } + + self.addActor.send(waitFailureServer(csInterf.waitFailure.getFuture())); + self.addActor.send(traceRole(Role::CONSISTENCYSCAN, csInterf.id())); + self.addActor.send(watchConsistencyScanInfoKey(&self)); + + loop { + if (self.consistencyScanEnabled.get()) { + try { + loop choose { + when(wait(runDataValidationCheck(&self))) { + TraceEvent("ConsistencyScan_Done", csInterf.id()).log(); + return Void(); + } + when(HaltConsistencyScanRequest req = waitNext(csInterf.haltConsistencyScan.getFuture())) { + req.reply.send(Void()); + TraceEvent("ConsistencyScan_Halted", csInterf.id()).detail("ReqID", req.requesterID); + break; + } + when(wait(err.getFuture())) {} + when(wait(collection)) { + ASSERT(false); + throw internal_error(); + } + } + } catch (Error& err) { + if (err.code() == error_code_actor_cancelled) { + TraceEvent("ConsistencyScan_ActorCanceled", csInterf.id()).errorUnsuppressed(err); + return Void(); + } + TraceEvent("ConsistencyScan_Died", csInterf.id()).errorUnsuppressed(err); + } + } else { + TraceEvent("ConsistencyScan_WaitingForConfigChange", self.id).log(); + wait(self.consistencyScanEnabled.onChange()); + } + } +} \ No newline at end of file diff --git a/fdbserver/CoordinatedState.actor.cpp b/fdbserver/CoordinatedState.actor.cpp index e822e8a74c..b8852fe80a 100644 --- a/fdbserver/CoordinatedState.actor.cpp +++ b/fdbserver/CoordinatedState.actor.cpp @@ -19,6 +19,7 @@ */ #include "fdbclient/ClusterConnectionMemoryRecord.h" +#include "fdbserver/ConfigBroadcaster.h" #include "fdbserver/CoordinatedState.h" #include "fdbserver/CoordinationInterface.h" #include "fdbserver/Knobs.h" @@ -346,7 +347,8 @@ struct MovableCoordinatedStateImpl { // SOMEDAY: If we are worried about someone magically getting the new cluster ID and interfering, do a second // cs.setExclusive( encode( ReallyTo, ... ) ) TraceEvent("ChangingQuorum").detail("ConnectionString", nc.toString()); - wait(changeLeaderCoordinators(self->coordinators, StringRef(nc.toString()))); + wait(ConfigBroadcaster::lockConfigNodes(self->coordinators) && + changeLeaderCoordinators(self->coordinators, StringRef(nc.toString()))); TraceEvent("ChangedQuorum").detail("ConnectionString", nc.toString()); throw coordinators_changed(); } diff --git a/fdbserver/Coordination.actor.cpp b/fdbserver/Coordination.actor.cpp index 3f44412799..e2263890ac 100644 --- a/fdbserver/Coordination.actor.cpp +++ b/fdbserver/Coordination.actor.cpp @@ -97,17 +97,22 @@ LeaderElectionRegInterface::LeaderElectionRegInterface(INetwork* local) : Client forward.makeWellKnownEndpoint(WLTOKEN_LEADERELECTIONREG_FORWARD, TaskPriority::Coordination); } -ServerCoordinators::ServerCoordinators(Reference ccr) : ClientCoordinators(ccr) { +ServerCoordinators::ServerCoordinators(Reference ccr, ConfigDBType configDBType) + : ClientCoordinators(ccr) { ClusterConnectionString cs = ccr->getConnectionString(); for (auto h : cs.hostnames) { leaderElectionServers.emplace_back(h); stateServers.emplace_back(h); - configServers.emplace_back(h); + if (configDBType != ConfigDBType::DISABLED) { + configServers.emplace_back(h); + } } for (auto s : cs.coords) { leaderElectionServers.emplace_back(s); stateServers.emplace_back(s); - configServers.emplace_back(s); + if (configDBType != ConfigDBType::DISABLED) { + configServers.emplace_back(s); + } } } @@ -183,8 +188,8 @@ TEST_CASE("/fdbserver/Coordination/localGenerationReg/simple") { } { - UniqueGeneration g = wait( - reg.write.getReply(GenerationRegWriteRequest(KeyValueRef(the_key, LiteralStringRef("Value1")), firstGen))); + UniqueGeneration g = + wait(reg.write.getReply(GenerationRegWriteRequest(KeyValueRef(the_key, "Value1"_sr), firstGen))); // (gen1==gen is considered a "successful" write) ASSERT(g == firstGen); } @@ -193,7 +198,7 @@ TEST_CASE("/fdbserver/Coordination/localGenerationReg/simple") { GenerationRegReadReply r = wait(reg.read.getReply(GenerationRegReadRequest(the_key, UniqueGeneration()))); // read(key,gen2) returns (value,gen,rgen). // There was some earlier or concurrent write(key,value,gen). - ASSERT(r.value == LiteralStringRef("Value1")); + ASSERT(r.value == "Value1"_sr); ASSERT(r.gen == firstGen); // There was some earlier or concurrent read(key,rgen). ASSERT(r.rgen == firstGen); @@ -725,9 +730,9 @@ ACTOR Future leaderServer(LeaderElectionRegInterface interf, } when(ForwardRequest req = waitNext(interf.forward.getFuture())) { Optional forward = regs.getForward(req.key); - if (forward.present()) + if (forward.present()) { req.reply.send(Void()); - else { + } else { StringRef clusterName = ccr->getConnectionString().clusterKeyName(); if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) { TraceEvent(SevWarn, "CCRMismatch") @@ -761,12 +766,14 @@ ACTOR Future coordinationServer(std::string dataFolder, state Future configDatabaseServer = Never(); TraceEvent("CoordinationServer", myID) .detail("MyInterfaceAddr", myInterface.read.getEndpoint().getPrimaryAddress()) - .detail("Folder", dataFolder); + .detail("Folder", dataFolder) + .detail("ConfigNodeValid", configNode.isValid()); if (configNode.isValid()) { configTransactionInterface.setupWellKnownEndpoints(); configFollowerInterface.setupWellKnownEndpoints(); - configDatabaseServer = configNode->serve(cbi, configTransactionInterface, configFollowerInterface); + configDatabaseServer = + brokenPromiseToNever(configNode->serve(cbi, configTransactionInterface, configFollowerInterface)); } try { diff --git a/fdbserver/DDRelocationQueue.actor.cpp b/fdbserver/DDRelocationQueue.actor.cpp index ba0d8fbd2d..c5243af77c 100644 --- a/fdbserver/DDRelocationQueue.actor.cpp +++ b/fdbserver/DDRelocationQueue.actor.cpp @@ -1770,19 +1770,19 @@ ACTOR Future dataDistributionRelocator(DDQueue* self, state Error error = success(); state Promise dataMovementComplete; // Move keys from source to destination by changing the serverKeyList and keyServerList system keys - state Future doMoveKeys = moveKeys(self->cx, - rd.dataMoveId, - rd.keys, - destIds, - healthyIds, - self->lock, - dataMovementComplete, - &self->startMoveKeysParallelismLock, - &self->finishMoveKeysParallelismLock, - self->teamCollections.size() > 1, - relocateShardInterval.pairID, - ddEnabledState, - CancelConflictingDataMoves::False); + state Future doMoveKeys = + self->txnProcessor->moveKeys(MoveKeysParams{ rd.dataMoveId, + rd.keys, + destIds, + healthyIds, + self->lock, + dataMovementComplete, + &self->startMoveKeysParallelismLock, + &self->finishMoveKeysParallelismLock, + self->teamCollections.size() > 1, + relocateShardInterval.pairID, + ddEnabledState, + CancelConflictingDataMoves::False }); state Future pollHealth = signalledTransferComplete ? Never() : delay(SERVER_KNOBS->HEALTH_POLL_TIME, TaskPriority::DataDistributionLaunch); @@ -1795,19 +1795,19 @@ ACTOR Future dataDistributionRelocator(DDQueue* self, healthyIds.insert(healthyIds.end(), extraIds.begin(), extraIds.end()); extraIds.clear(); ASSERT(totalIds == destIds.size()); // Sanity check the destIDs before we move keys - doMoveKeys = moveKeys(self->cx, - rd.dataMoveId, - rd.keys, - destIds, - healthyIds, - self->lock, - Promise(), - &self->startMoveKeysParallelismLock, - &self->finishMoveKeysParallelismLock, - self->teamCollections.size() > 1, - relocateShardInterval.pairID, - ddEnabledState, - CancelConflictingDataMoves::False); + doMoveKeys = + self->txnProcessor->moveKeys(MoveKeysParams{ rd.dataMoveId, + rd.keys, + destIds, + healthyIds, + self->lock, + Promise(), + &self->startMoveKeysParallelismLock, + &self->finishMoveKeysParallelismLock, + self->teamCollections.size() > 1, + relocateShardInterval.pairID, + ddEnabledState, + CancelConflictingDataMoves::False }); } else { self->fetchKeysComplete.insert(rd); if (SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA) { @@ -1981,7 +1981,7 @@ ACTOR Future rebalanceReadLoad(DDQueue* self, Reference destTeam, bool primary, TraceEvent* traceEvent) { - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { traceEvent->detail("CancelingDueToSimulationSpeedup", true); return false; } @@ -2067,7 +2067,7 @@ ACTOR static Future rebalanceTeams(DDQueue* self, Reference destTeam, bool primary, TraceEvent* traceEvent) { - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { traceEvent->detail("CancelingDueToSimulationSpeedup", true); return false; } @@ -2668,4 +2668,4 @@ TEST_CASE("/DataDistribution/DDQueue/ServerCounterTrace") { } std::cout << "Finished."; return Void(); -} \ No newline at end of file +} diff --git a/fdbserver/DDShardTracker.actor.cpp b/fdbserver/DDShardTracker.actor.cpp index a25c6b1e1c..4c1eb7c4f8 100644 --- a/fdbserver/DDShardTracker.actor.cpp +++ b/fdbserver/DDShardTracker.actor.cpp @@ -29,6 +29,7 @@ #include "fdbclient/DatabaseContext.h" #include "flow/ActorCollection.h" #include "flow/Arena.h" +#include "flow/CodeProbe.h" #include "flow/FastRef.h" #include "flow/Trace.h" #include "flow/actorcompiler.h" // This must be the last #include. @@ -605,6 +606,8 @@ std::vector findTenantShardBoundaries(KeyRangeMapvalue().stats; if (startShardSize->get().present()) { @@ -613,12 +616,17 @@ std::vector findTenantShardBoundaries(KeyRangeMapvalue().stats; auto endShardSize = shardContainingTenantEnd->value().stats; + CODE_PROBE(true, "Splitting multiple shards that a tenant key range straddles"); + if (startShardSize->get().present() && endShardSize->get().present()) { if (shardContainingTenantStart->begin() != tenantKeys.begin) { auto faultLines = findShardFaultLines(shardContainingTenantStart->begin(), @@ -635,6 +643,8 @@ std::vector findTenantShardBoundaries(KeyRangeMap(std::to_string(id))); - interface.locality.set(LiteralStringRef("zoneid"), Standalone(std::to_string(id % 5))); - interface.locality.set(LiteralStringRef("data_hall"), Standalone(std::to_string(id % 3))); + interface.locality.set("machineid"_sr, Standalone(std::to_string(id))); + interface.locality.set("zoneid"_sr, Standalone(std::to_string(id % 5))); + interface.locality.set("data_hall"_sr, Standalone(std::to_string(id % 3))); collection->server_info[uid] = makeReference( interface, collection.get(), ProcessClass(), true, collection->storageServerSet); collection->server_status.set(uid, ServerStatus(false, false, false, interface.locality)); @@ -5229,11 +5229,11 @@ public: zone_id, machine_id, interface.address().toString().c_str()); - interface.locality.set(LiteralStringRef("processid"), Standalone(std::to_string(process_id))); - interface.locality.set(LiteralStringRef("machineid"), Standalone(std::to_string(machine_id))); - interface.locality.set(LiteralStringRef("zoneid"), Standalone(std::to_string(zone_id))); - interface.locality.set(LiteralStringRef("data_hall"), Standalone(std::to_string(data_hall_id))); - interface.locality.set(LiteralStringRef("dcid"), Standalone(std::to_string(dc_id))); + interface.locality.set("processid"_sr, Standalone(std::to_string(process_id))); + interface.locality.set("machineid"_sr, Standalone(std::to_string(machine_id))); + interface.locality.set("zoneid"_sr, Standalone(std::to_string(zone_id))); + interface.locality.set("data_hall"_sr, Standalone(std::to_string(data_hall_id))); + interface.locality.set("dcid"_sr, Standalone(std::to_string(dc_id))); collection->server_info[uid] = makeReference( interface, collection.get(), ProcessClass(), true, collection->storageServerSet); diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 3b8691d248..4301584306 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -882,8 +882,7 @@ ACTOR Future>> workersMap[worker.interf.address()] = worker.interf; } - Optional regionsValue = - wait(tr.get(LiteralStringRef("usable_regions").withPrefix(configKeysPrefix))); + Optional regionsValue = wait(tr.get("usable_regions"_sr.withPrefix(configKeysPrefix))); int usableRegions = 1; if (regionsValue.present()) { usableRegions = atoi(regionsValue.get().toString().c_str()); diff --git a/fdbserver/DiskQueue.actor.cpp b/fdbserver/DiskQueue.actor.cpp index 4ca2f6ca0e..40342945b6 100644 --- a/fdbserver/DiskQueue.actor.cpp +++ b/fdbserver/DiskQueue.actor.cpp @@ -193,7 +193,7 @@ public: memset(firstPages[0], 0xFF, sizeof(Page)); firstPages[1] = (Page*)((uintptr_t)firstPages[0] + 4096); memset(firstPages[1], 0xFF, sizeof(Page)); - stallCount.init(LiteralStringRef("RawDiskQueue.StallCount")); + stallCount.init("RawDiskQueue.StallCount"_sr); } Future pushAndCommit(StringRef pageData, StringBuffer* pageMem, uint64_t poppedPages) { diff --git a/fdbserver/EncryptKeyProxy.actor.cpp b/fdbserver/EncryptKeyProxy.actor.cpp index 2ebf98ae95..55f3d83d4e 100644 --- a/fdbserver/EncryptKeyProxy.actor.cpp +++ b/fdbserver/EncryptKeyProxy.actor.cpp @@ -21,7 +21,6 @@ #include "fdbclient/EncryptKeyProxyInterface.h" #include "fdbrpc/Locality.h" -#include "fdbrpc/Stats.h" #include "fdbserver/KmsConnector.h" #include "fdbserver/KmsConnectorInterface.h" #include "fdbserver/Knobs.h" @@ -223,6 +222,10 @@ public: Counter blobMetadataRefreshed; Counter numBlobMetadataRefreshErrors; + LatencySample kmsLookupByIdsReqLatency; + LatencySample kmsLookupByDomainIdsReqLatency; + LatencySample kmsBlobMetadataReqLatency; + explicit EncryptKeyProxyData(UID id) : myId(id), ekpCacheMetrics("EKPMetrics", myId.toString()), baseCipherKeyIdCacheMisses("EKPCipherIdCacheMisses", ekpCacheMetrics), @@ -235,7 +238,19 @@ public: blobMetadataCacheHits("EKPBlobMetadataCacheHits", ekpCacheMetrics), blobMetadataCacheMisses("EKPBlobMetadataCacheMisses", ekpCacheMetrics), blobMetadataRefreshed("EKPBlobMetadataRefreshed", ekpCacheMetrics), - numBlobMetadataRefreshErrors("EKPBlobMetadataRefreshErrors", ekpCacheMetrics) {} + numBlobMetadataRefreshErrors("EKPBlobMetadataRefreshErrors", ekpCacheMetrics), + kmsLookupByIdsReqLatency("EKPKmsLookupByIdsReqLatency", + id, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + kmsLookupByDomainIdsReqLatency("EKPKmsLookupByDomainIdsReqLatency", + id, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + kmsBlobMetadataReqLatency("EKPKmsBlobMetadataReqLatency", + id, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE) {} EncryptBaseCipherDomainIdKeyIdCacheKey getBaseCipherDomainIdKeyIdCacheKey( const EncryptCipherDomainId domainId, @@ -375,7 +390,9 @@ ACTOR Future getCipherKeysByBaseCipherKeyIds(ReferencekmsLookupByIdsReqLatency.addMeasurement(now() - startTime); for (const auto& item : keysByIdsRep.cipherKeyDetails) { keyIdsReply.baseCipherDetails.emplace_back( @@ -511,8 +528,10 @@ ACTOR Future getLatestCipherKeys(Reference ekpProxyDa } keysByDomainIdReq.debugId = latestKeysReq.debugId; + state double startTime = now(); KmsConnLookupEKsByDomainIdsRep keysByDomainIdRep = wait(kmsConnectorInf.ekLookupByDomainIds.getReply(keysByDomainIdReq)); + ekpProxyData->kmsLookupByDomainIdsReqLatency.addMeasurement(now() - startTime); for (auto& item : keysByDomainIdRep.cipherKeyDetails) { CipherKeyValidityTS validityTS = getCipherKeyValidityTS(item.refreshAfterSec, item.expireAfterSec); @@ -609,7 +628,9 @@ ACTOR Future refreshEncryptionKeysCore(Reference ekpP } } + state double startTime = now(); KmsConnLookupEKsByDomainIdsRep rep = wait(kmsConnectorInf.ekLookupByDomainIds.getReply(req)); + ekpProxyData->kmsLookupByDomainIdsReqLatency.addMeasurement(now() - startTime); for (const auto& item : rep.cipherKeyDetails) { const auto itr = ekpProxyData->baseCipherDomainIdCache.find(item.encryptDomainId); if (itr == ekpProxyData->baseCipherDomainIdCache.end()) { @@ -707,7 +728,9 @@ ACTOR Future getLatestBlobMetadata(Reference ekpProxy if (!lookupDomains.empty()) { try { KmsConnBlobMetadataReq kmsReq(lookupDomains, req.debugId); + state double startTime = now(); KmsConnBlobMetadataRep kmsRep = wait(kmsConnectorInf.blobMetadataReq.getReply(kmsReq)); + ekpProxyData->kmsBlobMetadataReqLatency.addMeasurement(now() - startTime); metadataDetails.arena().dependsOn(kmsRep.metadataDetails.arena()); for (auto& item : kmsRep.metadataDetails) { @@ -753,7 +776,9 @@ ACTOR Future refreshBlobMetadataCore(Reference ekpPro for (auto& item : ekpProxyData->blobMetadataDomainIdCache) { req.domainIds.emplace_back(item.first); } + state double startTime = now(); KmsConnBlobMetadataRep rep = wait(kmsConnectorInf.blobMetadataReq.getReply(req)); + ekpProxyData->kmsBlobMetadataReqLatency.addMeasurement(now() - startTime); for (auto& item : rep.metadataDetails) { ekpProxyData->insertIntoBlobMetadataCache(item.domainId, item); t.detail("BM" + std::to_string(item.domainId), ""); diff --git a/fdbserver/FDBExecHelper.actor.cpp b/fdbserver/FDBExecHelper.actor.cpp index a8110f438e..982ebfadef 100644 --- a/fdbserver/FDBExecHelper.actor.cpp +++ b/fdbserver/FDBExecHelper.actor.cpp @@ -75,7 +75,7 @@ VectorRef ExecCmdValueString::getBinaryArgs() const { void ExecCmdValueString::parseCmdValue() { StringRef param = this->cmdValueString; // get the binary path - this->binaryPath = param.eat(LiteralStringRef(" ")); + this->binaryPath = param.eat(" "_sr); // no arguments provided if (param == StringRef()) { @@ -84,7 +84,7 @@ void ExecCmdValueString::parseCmdValue() { // extract the arguments while (param != StringRef()) { - StringRef token = param.eat(LiteralStringRef(" ")); + StringRef token = param.eat(" "_sr); this->binaryArgs.push_back(this->binaryArgs.arena(), token); } return; @@ -108,7 +108,7 @@ ACTOR void destoryChildProcess(Future parentSSClosed, ISimulator::ProcessI wait(parentSSClosed); TraceEvent(SevDebug, message.c_str()).log(); // This one is root cause for most failures, make sure it's okay to destory - g_pSimulator->destroyProcess(childInfo); + g_simulator->destroyProcess(childInfo); // Explicitly reset the connection with the child process in case re-spawn very quickly FlowTransport::transport().resetConnection(childInfo->address); } @@ -118,7 +118,7 @@ ACTOR Future spawnSimulated(std::vector paramList, bool isSync, double maxSimDelayTime, IClosable* parent) { - state ISimulator::ProcessInfo* self = g_pSimulator->getCurrentProcess(); + state ISimulator::ProcessInfo* self = g_simulator->getCurrentProcess(); state ISimulator::ProcessInfo* child; state std::string role; @@ -160,7 +160,7 @@ ACTOR Future spawnSimulated(std::vector paramList, } } state int result = 0; - child = g_pSimulator->newProcess( + child = g_simulator->newProcess( "remote flow process", self->address.ip, 0, @@ -171,7 +171,7 @@ ACTOR Future spawnSimulated(std::vector paramList, self->dataFolder.c_str(), self->coordinationFolder.c_str(), // do we need to customize this coordination folder path? self->protocolVersion); - wait(g_pSimulator->onProcess(child)); + wait(g_simulator->onProcess(child)); state Future onShutdown = child->onShutdown(); state Future parentShutdown = self->onShutdown(); state Future flowProcessF; @@ -199,7 +199,7 @@ ACTOR Future spawnSimulated(std::vector paramList, choose { when(wait(flowProcessF)) { TraceEvent(SevDebug, "ChildProcessKilled").log(); - wait(g_pSimulator->onProcess(self)); + wait(g_simulator->onProcess(self)); TraceEvent(SevDebug, "BackOnParentProcess").detail("Result", std::to_string(result)); destoryChildProcess(parentSSClosed, child, "StorageServerReceivedClosedMessage"); } diff --git a/fdbserver/GlobalTagThrottler.actor.cpp b/fdbserver/GlobalTagThrottler.actor.cpp index 237d99ddfc..fa98256b86 100644 --- a/fdbserver/GlobalTagThrottler.actor.cpp +++ b/fdbserver/GlobalTagThrottler.actor.cpp @@ -701,7 +701,7 @@ Future monitor(GlobalTagThrottler* globalTagThrottler, Check check) { } bool isNear(double a, double b) { - return abs(a - b) < 1.0; + return abs(a - b) < 3.0; } bool isNear(Optional a, Optional b) { diff --git a/fdbserver/GrvProxyServer.actor.cpp b/fdbserver/GrvProxyServer.actor.cpp index a3b4aab647..789b2973b1 100644 --- a/fdbserver/GrvProxyServer.actor.cpp +++ b/fdbserver/GrvProxyServer.actor.cpp @@ -130,12 +130,10 @@ struct GrvProxyStats { SERVER_KNOBS->LATENCY_SAMPLE_SIZE), recentRequests(0), lastBucketBegin(now()), bucketInterval(FLOW_KNOBS->BASIC_LOAD_BALANCE_UPDATE_RATE / FLOW_KNOBS->BASIC_LOAD_BALANCE_BUCKETS), - grvConfirmEpochLiveDist(Histogram::getHistogram(LiteralStringRef("GrvProxy"), - LiteralStringRef("GrvConfirmEpochLive"), - Histogram::Unit::microseconds)), - grvGetCommittedVersionRpcDist(Histogram::getHistogram(LiteralStringRef("GrvProxy"), - LiteralStringRef("GrvGetCommittedVersionRpc"), - Histogram::Unit::microseconds)) { + grvConfirmEpochLiveDist( + Histogram::getHistogram("GrvProxy"_sr, "GrvConfirmEpochLive"_sr, Histogram::Unit::microseconds)), + grvGetCommittedVersionRpcDist( + Histogram::getHistogram("GrvProxy"_sr, "GrvGetCommittedVersionRpc"_sr, Histogram::Unit::microseconds)) { // The rate at which the limit(budget) is allowed to grow. specialCounter(cc, "SystemGRVQueueSize", [this]() { return this->systemGRVQueueSize; }); specialCounter(cc, "DefaultGRVQueueSize", [this]() { return this->defaultGRVQueueSize; }); @@ -563,7 +561,7 @@ ACTOR Future queueGetReadVersionRequests( bool canBeQueued = true; if (stats->txnRequestIn.getValue() - stats->txnRequestOut.getValue() > SERVER_KNOBS->START_TRANSACTION_MAX_QUEUE_SIZE || - (g_network->isSimulated() && !g_simulator.speedUpSimulation && + (g_network->isSimulated() && !g_simulator->speedUpSimulation && deterministicRandom()->random01() < 0.01)) { // When the limit is hit, try to drop requests from the lower priority queues. if (req.priority == TransactionPriority::BATCH) { diff --git a/fdbserver/IConfigConsumer.cpp b/fdbserver/IConfigConsumer.cpp index 9d4c7d7e70..59e3500297 100644 --- a/fdbserver/IConfigConsumer.cpp +++ b/fdbserver/IConfigConsumer.cpp @@ -36,6 +36,8 @@ std::unique_ptr IConfigConsumer::createSimple(ServerCoordinator std::unique_ptr IConfigConsumer::createPaxos(ServerCoordinators const& coordinators, double pollingInterval, - Optional compactionInterval) { - return std::make_unique(coordinators, pollingInterval, compactionInterval); + Optional compactionInterval, + bool readPreviousCoordinators) { + return std::make_unique( + coordinators, pollingInterval, compactionInterval, readPreviousCoordinators); } diff --git a/fdbserver/KeyValueStoreCompressTestData.actor.cpp b/fdbserver/KeyValueStoreCompressTestData.actor.cpp index a05f297fd5..a5098baf4e 100644 --- a/fdbserver/KeyValueStoreCompressTestData.actor.cpp +++ b/fdbserver/KeyValueStoreCompressTestData.actor.cpp @@ -119,12 +119,12 @@ private: // If the value starts with a 0-byte, then we don't compress it if (c == 0) - return val.withPrefix(LiteralStringRef("\x00")); + return val.withPrefix("\x00"_sr); for (int i = 1; i < val.size(); i++) { if (val[i] != c) { // The value is something other than a single repeated character, so not compressible :-) - return val.withPrefix(LiteralStringRef("\x00")); + return val.withPrefix("\x00"_sr); } } diff --git a/fdbserver/KeyValueStoreMemory.actor.cpp b/fdbserver/KeyValueStoreMemory.actor.cpp index a632c6c717..980c3df122 100644 --- a/fdbserver/KeyValueStoreMemory.actor.cpp +++ b/fdbserver/KeyValueStoreMemory.actor.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "fdbclient/BlobCipher.h" #include "fdbclient/Knobs.h" #include "fdbclient/Notified.h" #include "fdbclient/SystemData.h" @@ -488,8 +489,10 @@ private: ASSERT(cipherKeys.cipherTextKey.isValid()); ASSERT(cipherKeys.cipherHeaderKey.isValid()); - EncryptBlobCipherAes265Ctr cipher( - cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + EncryptBlobCipherAes265Ctr cipher(cipherKeys.cipherTextKey, + cipherKeys.cipherHeaderKey, + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + BlobCipherMetrics::KV_MEMORY); BlobCipherEncryptHeader cipherHeader; Arena arena; StringRef ciphertext = @@ -497,7 +500,7 @@ private: log->push(StringRef((const uint8_t*)&cipherHeader, BlobCipherEncryptHeader::headerSize)); log->push(ciphertext); } - return log->push(LiteralStringRef("\x01")); // Changes here should be reflected in OP_DISK_OVERHEAD + return log->push("\x01"_sr); // Changes here should be reflected in OP_DISK_OVERHEAD } // In case the op data is not encrypted, simply read the operands and the zero fill flag. @@ -527,8 +530,10 @@ private: return data; } state BlobCipherEncryptHeader cipherHeader = *(BlobCipherEncryptHeader*)data.begin(); - TextAndHeaderCipherKeys cipherKeys = wait(getEncryptCipherKeys(self->db, cipherHeader)); - DecryptBlobCipherAes256Ctr cipher(cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, cipherHeader.iv); + TextAndHeaderCipherKeys cipherKeys = + wait(getEncryptCipherKeys(self->db, cipherHeader, BlobCipherMetrics::KV_MEMORY)); + DecryptBlobCipherAes256Ctr cipher( + cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, cipherHeader.iv, BlobCipherMetrics::KV_MEMORY); Arena arena; StringRef plaintext = cipher .decrypt(data.begin() + BlobCipherEncryptHeader::headerSize, @@ -825,7 +830,7 @@ private: auto thisSnapshotEnd = self->log_op(OpSnapshotEnd, StringRef(), StringRef()); //TraceEvent("SnapshotEnd", self->id) - // .detail("LastKey", lastKey.present() ? lastKey.get() : LiteralStringRef("")) + // .detail("LastKey", lastKey.present() ? lastKey.get() : ""_sr) // .detail("CurrentSnapshotEndLoc", self->currentSnapshotEnd) // .detail("PreviousSnapshotEndLoc", self->previousSnapshotEnd) // .detail("ThisSnapshotEnd", thisSnapshotEnd) @@ -961,7 +966,8 @@ private: } ACTOR static Future updateCipherKeys(KeyValueStoreMemory* self) { - TextAndHeaderCipherKeys cipherKeys = wait(getLatestSystemEncryptCipherKeys(self->db)); + TextAndHeaderCipherKeys cipherKeys = + wait(getLatestSystemEncryptCipherKeys(self->db, BlobCipherMetrics::KV_MEMORY)); self->cipherKeys = cipherKeys; return Void(); } diff --git a/fdbserver/KeyValueStoreRocksDB.actor.cpp b/fdbserver/KeyValueStoreRocksDB.actor.cpp index 005e13f7b1..a4ee485fb8 100644 --- a/fdbserver/KeyValueStoreRocksDB.actor.cpp +++ b/fdbserver/KeyValueStoreRocksDB.actor.cpp @@ -259,24 +259,24 @@ using CF = rocksdb::ColumnFamilyHandle*; #define PERSIST_PREFIX "\xff\xff" const KeyRef persistVersion = LiteralStringRef(PERSIST_PREFIX "Version"); -const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = LiteralStringRef("RocksDBStorage"); -const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBCommitLatency"); -const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = LiteralStringRef("RocksDBCommitAction"); -const StringRef ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBCommitQueueWait"); -const StringRef ROCKSDB_WRITE_HISTOGRAM = LiteralStringRef("RocksDBWrite"); -const StringRef ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM = LiteralStringRef("RocksDBDeleteCompactRange"); -const StringRef ROCKSDB_READRANGE_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadRangeLatency"); -const StringRef ROCKSDB_READVALUE_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadValueLatency"); -const StringRef ROCKSDB_READPREFIX_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixLatency"); -const StringRef ROCKSDB_READRANGE_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadRangeAction"); -const StringRef ROCKSDB_READVALUE_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadValueAction"); -const StringRef ROCKSDB_READPREFIX_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixAction"); -const StringRef ROCKSDB_READRANGE_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadRangeQueueWait"); -const StringRef ROCKSDB_READVALUE_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadValueQueueWait"); -const StringRef ROCKSDB_READPREFIX_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixQueueWait"); -const StringRef ROCKSDB_READRANGE_NEWITERATOR_HISTOGRAM = LiteralStringRef("RocksDBReadRangeNewIterator"); -const StringRef ROCKSDB_READVALUE_GET_HISTOGRAM = LiteralStringRef("RocksDBReadValueGet"); -const StringRef ROCKSDB_READPREFIX_GET_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixGet"); +const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = "RocksDBStorage"_sr; +const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = "RocksDBCommitLatency"_sr; +const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = "RocksDBCommitAction"_sr; +const StringRef ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM = "RocksDBCommitQueueWait"_sr; +const StringRef ROCKSDB_WRITE_HISTOGRAM = "RocksDBWrite"_sr; +const StringRef ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM = "RocksDBDeleteCompactRange"_sr; +const StringRef ROCKSDB_READRANGE_LATENCY_HISTOGRAM = "RocksDBReadRangeLatency"_sr; +const StringRef ROCKSDB_READVALUE_LATENCY_HISTOGRAM = "RocksDBReadValueLatency"_sr; +const StringRef ROCKSDB_READPREFIX_LATENCY_HISTOGRAM = "RocksDBReadPrefixLatency"_sr; +const StringRef ROCKSDB_READRANGE_ACTION_HISTOGRAM = "RocksDBReadRangeAction"_sr; +const StringRef ROCKSDB_READVALUE_ACTION_HISTOGRAM = "RocksDBReadValueAction"_sr; +const StringRef ROCKSDB_READPREFIX_ACTION_HISTOGRAM = "RocksDBReadPrefixAction"_sr; +const StringRef ROCKSDB_READRANGE_QUEUEWAIT_HISTOGRAM = "RocksDBReadRangeQueueWait"_sr; +const StringRef ROCKSDB_READVALUE_QUEUEWAIT_HISTOGRAM = "RocksDBReadValueQueueWait"_sr; +const StringRef ROCKSDB_READPREFIX_QUEUEWAIT_HISTOGRAM = "RocksDBReadPrefixQueueWait"_sr; +const StringRef ROCKSDB_READRANGE_NEWITERATOR_HISTOGRAM = "RocksDBReadRangeNewIterator"_sr; +const StringRef ROCKSDB_READVALUE_GET_HISTOGRAM = "RocksDBReadValueGet"_sr; +const StringRef ROCKSDB_READPREFIX_GET_HISTOGRAM = "RocksDBReadPrefixGet"_sr; rocksdb::ExportImportFilesMetaData getMetaData(const CheckpointMetaData& checkpoint) { rocksdb::ExportImportFilesMetaData metaData; @@ -2314,11 +2314,11 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/RocksDBReopen") { state IKeyValueStore* kvStore = new RocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); wait(kvStore->init()); - kvStore->set({ LiteralStringRef("foo"), LiteralStringRef("bar") }); + kvStore->set({ "foo"_sr, "bar"_sr }); wait(kvStore->commit(false)); - Optional val = wait(kvStore->readValue(LiteralStringRef("foo"))); - ASSERT(Optional(LiteralStringRef("bar")) == val); + Optional val = wait(kvStore->readValue("foo"_sr)); + ASSERT(Optional("bar"_sr) == val); Future closed = kvStore->onClosed(); kvStore->close(); @@ -2329,8 +2329,8 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/RocksDBReopen") { // Confirm that `init()` is idempotent. wait(kvStore->init()); - Optional val = wait(kvStore->readValue(LiteralStringRef("foo"))); - ASSERT(Optional(LiteralStringRef("bar")) == val); + Optional val = wait(kvStore->readValue("foo"_sr)); + ASSERT(Optional("bar"_sr) == val); Future closed = kvStore->onClosed(); kvStore->dispose(); @@ -2348,11 +2348,11 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/CheckpointRestoreColumnFamily") state IKeyValueStore* kvStore = new RocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); wait(kvStore->init()); - kvStore->set({ LiteralStringRef("foo"), LiteralStringRef("bar") }); + kvStore->set({ "foo"_sr, "bar"_sr }); wait(kvStore->commit(false)); - Optional val = wait(kvStore->readValue(LiteralStringRef("foo"))); - ASSERT(Optional(LiteralStringRef("bar")) == val); + Optional val = wait(kvStore->readValue("foo"_sr)); + ASSERT(Optional("bar"_sr) == val); state std::string rocksDBRestoreDir = "rocksdb-kvstore-br-restore-db"; platform::eraseDirectoryRecursive(rocksDBRestoreDir); @@ -2372,8 +2372,8 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/CheckpointRestoreColumnFamily") checkpoints.push_back(metaData); wait(kvStoreCopy->restore(checkpoints)); - Optional val = wait(kvStoreCopy->readValue(LiteralStringRef("foo"))); - ASSERT(Optional(LiteralStringRef("bar")) == val); + Optional val = wait(kvStoreCopy->readValue("foo"_sr)); + ASSERT(Optional("bar"_sr) == val); std::vector> closes; closes.push_back(kvStore->onClosed()); @@ -2395,10 +2395,10 @@ TEST_CASE("noSim/fdbserver/KeyValueStoreRocksDB/CheckpointRestoreKeyValues") { state IKeyValueStore* kvStore = new RocksDBKeyValueStore(rocksDBTestDir, deterministicRandom()->randomUniqueID()); wait(kvStore->init()); - kvStore->set({ LiteralStringRef("foo"), LiteralStringRef("bar") }); + kvStore->set({ "foo"_sr, "bar"_sr }); wait(kvStore->commit(false)); - Optional val = wait(kvStore->readValue(LiteralStringRef("foo"))); - ASSERT(Optional(LiteralStringRef("bar")) == val); + Optional val = wait(kvStore->readValue("foo"_sr)); + ASSERT(Optional("bar"_sr) == val); platform::eraseDirectoryRecursive("checkpoint"); std::string checkpointDir = cwd + "checkpoint"; diff --git a/fdbserver/KeyValueStoreSQLite.actor.cpp b/fdbserver/KeyValueStoreSQLite.actor.cpp index 123bd04a52..07d9e06171 100644 --- a/fdbserver/KeyValueStoreSQLite.actor.cpp +++ b/fdbserver/KeyValueStoreSQLite.actor.cpp @@ -1333,7 +1333,7 @@ int SQLiteDB::checkAllPageChecksums() { // then we could instead open a read cursor for the same effect, as currently tryReadEveryDbPage() requires it. Statement* jm = new Statement(*this, "PRAGMA journal_mode"); ASSERT(jm->nextRow()); - if (jm->column(0) != LiteralStringRef("wal")) { + if (jm->column(0) != "wal"_sr) { TraceEvent(SevError, "JournalModeError").detail("Filename", filename).detail("Mode", jm->column(0)); ASSERT(false); } @@ -1502,7 +1502,7 @@ void SQLiteDB::open(bool writable) { Statement jm(*this, "PRAGMA journal_mode"); ASSERT(jm.nextRow()); - if (jm.column(0) != LiteralStringRef("wal")) { + if (jm.column(0) != "wal"_sr) { TraceEvent(SevError, "JournalModeError").detail("Filename", filename).detail("Mode", jm.column(0)); ASSERT(false); } @@ -1810,7 +1810,7 @@ private: cursor->set(a.kv); ++setsThisCommit; ++writesComplete; - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("SetActionFinished", dbgid).detail("Elapsed", now() - s); } @@ -1824,7 +1824,7 @@ private: cursor->fastClear(a.range, freeTableEmpty); cursor->clear(a.range); // TODO: at most one ++writesComplete; - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("ClearActionFinished", dbgid).detail("Elapsed", now() - s); } @@ -1864,7 +1864,7 @@ private: diskBytesUsed = waitForAndGet(conn.dbFile->size()) + waitForAndGet(conn.walFile->size()); - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("CommitActionFinished", dbgid).detail("Elapsed", now() - t1); } @@ -1987,7 +1987,7 @@ private: a.result.send(workPerformed); ++writesComplete; - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("SpringCleaningActionFinished", dbgid).detail("Elapsed", now() - s); } }; @@ -1996,7 +1996,7 @@ private: state int64_t lastReadsComplete = 0; state int64_t lastWritesComplete = 0; loop { - wait(delay(FLOW_KNOBS->DISK_METRIC_LOGGING_INTERVAL)); + wait(delay(FLOW_KNOBS->SQLITE_DISK_METRIC_LOGGING_INTERVAL)); int64_t rc = self->readsComplete, wc = self->writesComplete; TraceEvent("DiskMetrics", self->logID) @@ -2287,9 +2287,9 @@ ACTOR Future KVFileCheck(std::string filename, bool integrity) { StringRef kvFile(filename); KeyValueStoreType type = KeyValueStoreType::END; - if (kvFile.endsWith(LiteralStringRef(".fdb"))) + if (kvFile.endsWith(".fdb"_sr)) type = KeyValueStoreType::SSD_BTREE_V1; - else if (kvFile.endsWith(LiteralStringRef(".sqlite"))) + else if (kvFile.endsWith(".sqlite"_sr)) type = KeyValueStoreType::SSD_BTREE_V2; ASSERT(type != KeyValueStoreType::END); diff --git a/fdbserver/KeyValueStoreShardedRocksDB.actor.cpp b/fdbserver/KeyValueStoreShardedRocksDB.actor.cpp index 8c8d87da61..d2d2736fac 100644 --- a/fdbserver/KeyValueStoreShardedRocksDB.actor.cpp +++ b/fdbserver/KeyValueStoreShardedRocksDB.actor.cpp @@ -48,26 +48,26 @@ static_assert((ROCKSDB_MAJOR == 6 && ROCKSDB_MINOR == 27) ? ROCKSDB_PATCH >= 3 : "Unsupported rocksdb version. Update the rocksdb to 6.27.3 version"); const std::string rocksDataFolderSuffix = "-data"; -const KeyRef shardMappingPrefix(LiteralStringRef("\xff\xff/ShardMapping/")); +const KeyRef shardMappingPrefix("\xff\xff/ShardMapping/"_sr); // TODO: move constants to a header file. -const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = LiteralStringRef("RocksDBStorage"); -const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBCommitLatency"); -const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = LiteralStringRef("RocksDBCommitAction"); -const StringRef ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBCommitQueueWait"); -const StringRef ROCKSDB_WRITE_HISTOGRAM = LiteralStringRef("RocksDBWrite"); -const StringRef ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM = LiteralStringRef("RocksDBDeleteCompactRange"); -const StringRef ROCKSDB_READRANGE_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadRangeLatency"); -const StringRef ROCKSDB_READVALUE_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadValueLatency"); -const StringRef ROCKSDB_READPREFIX_LATENCY_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixLatency"); -const StringRef ROCKSDB_READRANGE_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadRangeAction"); -const StringRef ROCKSDB_READVALUE_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadValueAction"); -const StringRef ROCKSDB_READPREFIX_ACTION_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixAction"); -const StringRef ROCKSDB_READRANGE_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadRangeQueueWait"); -const StringRef ROCKSDB_READVALUE_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadValueQueueWait"); -const StringRef ROCKSDB_READPREFIX_QUEUEWAIT_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixQueueWait"); -const StringRef ROCKSDB_READRANGE_NEWITERATOR_HISTOGRAM = LiteralStringRef("RocksDBReadRangeNewIterator"); -const StringRef ROCKSDB_READVALUE_GET_HISTOGRAM = LiteralStringRef("RocksDBReadValueGet"); -const StringRef ROCKSDB_READPREFIX_GET_HISTOGRAM = LiteralStringRef("RocksDBReadPrefixGet"); +const StringRef ROCKSDBSTORAGE_HISTOGRAM_GROUP = "RocksDBStorage"_sr; +const StringRef ROCKSDB_COMMIT_LATENCY_HISTOGRAM = "RocksDBCommitLatency"_sr; +const StringRef ROCKSDB_COMMIT_ACTION_HISTOGRAM = "RocksDBCommitAction"_sr; +const StringRef ROCKSDB_COMMIT_QUEUEWAIT_HISTOGRAM = "RocksDBCommitQueueWait"_sr; +const StringRef ROCKSDB_WRITE_HISTOGRAM = "RocksDBWrite"_sr; +const StringRef ROCKSDB_DELETE_COMPACTRANGE_HISTOGRAM = "RocksDBDeleteCompactRange"_sr; +const StringRef ROCKSDB_READRANGE_LATENCY_HISTOGRAM = "RocksDBReadRangeLatency"_sr; +const StringRef ROCKSDB_READVALUE_LATENCY_HISTOGRAM = "RocksDBReadValueLatency"_sr; +const StringRef ROCKSDB_READPREFIX_LATENCY_HISTOGRAM = "RocksDBReadPrefixLatency"_sr; +const StringRef ROCKSDB_READRANGE_ACTION_HISTOGRAM = "RocksDBReadRangeAction"_sr; +const StringRef ROCKSDB_READVALUE_ACTION_HISTOGRAM = "RocksDBReadValueAction"_sr; +const StringRef ROCKSDB_READPREFIX_ACTION_HISTOGRAM = "RocksDBReadPrefixAction"_sr; +const StringRef ROCKSDB_READRANGE_QUEUEWAIT_HISTOGRAM = "RocksDBReadRangeQueueWait"_sr; +const StringRef ROCKSDB_READVALUE_QUEUEWAIT_HISTOGRAM = "RocksDBReadValueQueueWait"_sr; +const StringRef ROCKSDB_READPREFIX_QUEUEWAIT_HISTOGRAM = "RocksDBReadPrefixQueueWait"_sr; +const StringRef ROCKSDB_READRANGE_NEWITERATOR_HISTOGRAM = "RocksDBReadRangeNewIterator"_sr; +const StringRef ROCKSDB_READVALUE_GET_HISTOGRAM = "RocksDBReadValueGet"_sr; +const StringRef ROCKSDB_READPREFIX_GET_HISTOGRAM = "RocksDBReadPrefixGet"_sr; namespace { struct PhysicalShard; diff --git a/fdbserver/LocalConfiguration.actor.cpp b/fdbserver/LocalConfiguration.actor.cpp index b918fb2235..7a8e04e76b 100644 --- a/fdbserver/LocalConfiguration.actor.cpp +++ b/fdbserver/LocalConfiguration.actor.cpp @@ -228,7 +228,8 @@ class LocalConfigurationImpl { ACTOR static Future setSnapshot(LocalConfigurationImpl* self, std::map snapshot, - Version snapshotVersion) { + Version snapshotVersion, + double restartDelay) { if (snapshotVersion <= self->lastSeenVersion) { TraceEvent(SevWarnAlways, "LocalConfigGotOldSnapshot", self->id) .detail("NewSnapshotVersion", snapshotVersion) @@ -249,6 +250,9 @@ class LocalConfigurationImpl { self->kvStore->set(KeyValueRef(lastSeenVersionKey, BinaryWriter::toValue(snapshotVersion, IncludeVersion()))); wait(self->kvStore->commit()); if (restartRequired) { + if (restartDelay > 0) { + wait(delay(restartDelay)); + } throw local_config_changed(); } self->updateInMemoryState(snapshotVersion); @@ -257,7 +261,8 @@ class LocalConfigurationImpl { ACTOR static Future addChanges(LocalConfigurationImpl* self, Standalone> changes, - Version mostRecentVersion) { + Version mostRecentVersion, + double restartDelay) { // TODO: Concurrency control? ++self->changeRequestsFetched; state bool restartRequired = false; @@ -271,7 +276,7 @@ class LocalConfigurationImpl { ++self->mutations; const auto& mutation = versionedMutation.mutation; { - TraceEvent te(SevDebug, "LocalConfigAddingChange", self->id); + TraceEvent te(SevInfo, "LocalConfigAddingChange", self->id); te.detail("ConfigClass", mutation.getConfigClass()) .detail("Version", versionedMutation.version) .detail("KnobName", mutation.getKnobName()); @@ -293,6 +298,9 @@ class LocalConfigurationImpl { self->kvStore->set(KeyValueRef(lastSeenVersionKey, BinaryWriter::toValue(mostRecentVersion, IncludeVersion()))); wait(self->kvStore->commit()); if (restartRequired) { + if (restartDelay > 0) { + wait(delay(restartDelay)); + } throw local_config_changed(); } self->updateInMemoryState(mostRecentVersion); @@ -304,11 +312,12 @@ class LocalConfigurationImpl { loop { choose { when(state ConfigBroadcastSnapshotRequest snapshotReq = waitNext(broadcaster.snapshot.getFuture())) { - wait(setSnapshot(self, std::move(snapshotReq.snapshot), snapshotReq.version)); + wait(setSnapshot( + self, std::move(snapshotReq.snapshot), snapshotReq.version, snapshotReq.restartDelay)); snapshotReq.reply.send(ConfigBroadcastSnapshotReply{}); } when(state ConfigBroadcastChangesRequest req = waitNext(broadcaster.changes.getFuture())) { - wait(self->addChanges(req.changes, req.mostRecentVersion)); + wait(self->addChanges(req.changes, req.mostRecentVersion, req.restartDelay)); req.reply.send(ConfigBroadcastChangesReply{}); } } @@ -342,8 +351,10 @@ public: "LocalConfigurationMetrics", id, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "LocalConfigurationMetrics"); } - Future addChanges(Standalone> changes, Version mostRecentVersion) { - return addChanges(this, changes, mostRecentVersion); + Future addChanges(Standalone> changes, + Version mostRecentVersion, + double restartDelay) { + return addChanges(this, changes, mostRecentVersion, restartDelay); } FlowKnobs const& getFlowKnobs() const { return getKnobs().getFlowKnobs(); } @@ -447,8 +458,9 @@ Future LocalConfiguration::consume(ConfigBroadcastInterface const& broadca } Future LocalConfiguration::addChanges(Standalone> changes, - Version mostRecentVersion) { - return impl->addChanges(changes, mostRecentVersion); + Version mostRecentVersion, + double restartDelay) { + return impl->addChanges(changes, mostRecentVersion, restartDelay); } void LocalConfiguration::close() { diff --git a/fdbserver/LogRouter.actor.cpp b/fdbserver/LogRouter.actor.cpp index 649fc429c9..08485b4722 100644 --- a/fdbserver/LogRouter.actor.cpp +++ b/fdbserver/LogRouter.actor.cpp @@ -137,9 +137,7 @@ struct LogRouterData { : dbgid(dbgid), logSystem(new AsyncVar>()), version(req.startVersion - 1), minPopped(0), startVersion(req.startVersion), minKnownCommittedVersion(0), poppedVersion(0), routerTag(req.routerTag), allowPops(false), foundEpochEnd(false), generation(req.recoveryCount), - peekLatencyDist(Histogram::getHistogram(LiteralStringRef("LogRouter"), - LiteralStringRef("PeekTLogLatency"), - Histogram::Unit::microseconds)), + peekLatencyDist(Histogram::getHistogram("LogRouter"_sr, "PeekTLogLatency"_sr, Histogram::Unit::microseconds)), cc("LogRouter", dbgid.toString()), getMoreCount("GetMoreCount", cc), getMoreBlockedCount("GetMoreBlockedCount", cc) { // setup just enough of a logSet to be able to call getPushLocations diff --git a/fdbserver/MetricLogger.actor.cpp b/fdbserver/MetricLogger.actor.cpp index 94eb7d91df..ef6a2601b1 100644 --- a/fdbserver/MetricLogger.actor.cpp +++ b/fdbserver/MetricLogger.actor.cpp @@ -90,12 +90,10 @@ struct MetricsRule { struct MetricsConfig { MetricsConfig(Key prefix = KeyRef()) - : space(prefix), ruleMap(space.get(LiteralStringRef("Rules")).key()), - addressMap(space.get(LiteralStringRef("Enum")).get(LiteralStringRef("Address")).key()), - nameAndTypeMap(space.get(LiteralStringRef("Enum")).get(LiteralStringRef("NameType")).key()), - ruleChangeKey(space.get(LiteralStringRef("RulesChanged")).key()), - enumsChangeKey(space.get(LiteralStringRef("EnumsChanged")).key()), - fieldChangeKey(space.get(LiteralStringRef("FieldsChanged")).key()) {} + : space(prefix), ruleMap(space.get("Rules"_sr).key()), addressMap(space.get("Enum"_sr).get("Address"_sr).key()), + nameAndTypeMap(space.get("Enum"_sr).get("NameType"_sr).key()), + ruleChangeKey(space.get("RulesChanged"_sr).key()), enumsChangeKey(space.get("EnumsChanged"_sr).key()), + fieldChangeKey(space.get("FieldsChanged"_sr).key()) {} Subspace space; @@ -419,7 +417,7 @@ TEST_CASE("/fdbserver/metrics/TraceEvents") { fprintf(stdout, "Using environment variables METRICS_CONNFILE and METRICS_PREFIX.\n"); state Database metricsDb = Database::createDatabase(metricsConnFile, ApiVersion::LATEST_VERSION); - TDMetricCollection::getTDMetrics()->address = LiteralStringRef("0.0.0.0:0"); + TDMetricCollection::getTDMetrics()->address = "0.0.0.0:0"_sr; state Future metrics = runMetrics(metricsDb, KeyRef(metricsPrefix)); state int64_t x = 0; @@ -438,9 +436,9 @@ TEST_CASE("/fdbserver/metrics/TraceEvents") { fprintf(stdout, " d is always present, is a string, and rotates through the values 'one', 'two', and ''.\n"); fprintf(stdout, " Plotting j on the x axis and k on the y axis should look like x=sin(2t), y=sin(3t)\n"); - state Int64MetricHandle intMetric = Int64MetricHandle(LiteralStringRef("DummyInt")); - state BoolMetricHandle boolMetric = BoolMetricHandle(LiteralStringRef("DummyBool")); - state StringMetricHandle stringMetric = StringMetricHandle(LiteralStringRef("DummyString")); + state Int64MetricHandle intMetric = Int64MetricHandle("DummyInt"_sr); + state BoolMetricHandle boolMetric = BoolMetricHandle("DummyBool"_sr); + state StringMetricHandle stringMetric = StringMetricHandle("DummyString"_sr); static const char* dStrings[] = { "one", "two", "" }; state const char** d = dStrings; diff --git a/fdbserver/MoveKeys.actor.cpp b/fdbserver/MoveKeys.actor.cpp index 9e7f9e30c4..7cd38d9281 100644 --- a/fdbserver/MoveKeys.actor.cpp +++ b/fdbserver/MoveKeys.actor.cpp @@ -282,6 +282,7 @@ Future checkMoveKeysLockReadOnly(Transaction* tr, MoveKeysLock lock, const return checkMoveKeysLock(tr, lock, ddEnabledState, false); } +namespace { ACTOR Future> checkReadWrite(Future> fReply, UID uid, Version version) { ErrorOr reply = wait(fReply); if (!reply.present() || reply.get().first < version) @@ -1795,6 +1796,8 @@ ACTOR static Future finishMoveShards(Database occ, return Void(); } +}; // anonymous namespace + ACTOR Future> addStorageServer(Database cx, StorageServerInterface server) { state Reference tr = makeReference(cx); state KeyBackedMap tssMapDB = KeyBackedMap(tssMappingKeys.begin); @@ -2444,76 +2447,75 @@ ACTOR Future cleanUpDataMove(Database occ, return Void(); } -ACTOR Future moveKeys(Database cx, - UID dataMoveId, - KeyRange keys, - std::vector destinationTeam, - std::vector healthyDestinations, - MoveKeysLock lock, - Promise dataMovementComplete, - FlowLock* startMoveKeysParallelismLock, - FlowLock* finishMoveKeysParallelismLock, - bool hasRemote, - UID relocationIntervalId, - const DDEnabledState* ddEnabledState, - CancelConflictingDataMoves cancelConflictingDataMoves) { - ASSERT(destinationTeam.size()); - std::sort(destinationTeam.begin(), destinationTeam.end()); +Future startMovement(Database occ, MoveKeysParams& params, std::map& tssMapping) { + if (SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA) { + return startMoveShards(std::move(occ), + params.dataMoveId, + params.keys, + params.destinationTeam, + params.lock, + params.startMoveKeysParallelismLock, + params.relocationIntervalId, + params.ddEnabledState, + params.cancelConflictingDataMoves); + } + return startMoveKeys(std::move(occ), + params.keys, + params.destinationTeam, + params.lock, + params.startMoveKeysParallelismLock, + params.relocationIntervalId, + &tssMapping, + params.ddEnabledState); +} + +Future finishMovement(Database occ, + MoveKeysParams& params, + const std::map& tssMapping) { + if (SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA) { + return finishMoveShards(std::move(occ), + params.dataMoveId, + params.keys, + params.destinationTeam, + params.lock, + params.finishMoveKeysParallelismLock, + params.hasRemote, + params.relocationIntervalId, + tssMapping, + params.ddEnabledState); + } + return finishMoveKeys(std::move(occ), + params.keys, + params.destinationTeam, + params.lock, + params.finishMoveKeysParallelismLock, + params.hasRemote, + params.relocationIntervalId, + tssMapping, + params.ddEnabledState); +} + +ACTOR Future moveKeys(Database occ, MoveKeysParams params) { + ASSERT(params.destinationTeam.size()); + std::sort(params.destinationTeam.begin(), params.destinationTeam.end()); state std::map tssMapping; - if (SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA) { - wait(startMoveShards(cx, - dataMoveId, - keys, - destinationTeam, - lock, - startMoveKeysParallelismLock, - relocationIntervalId, - ddEnabledState, - cancelConflictingDataMoves)); + wait(startMovement(occ, params, tssMapping)); - } else { - wait(startMoveKeys(cx, - keys, - destinationTeam, - lock, - startMoveKeysParallelismLock, - relocationIntervalId, - &tssMapping, - ddEnabledState)); - } + state Future completionSignaller = checkFetchingState(occ, + params.healthyDestinations, + params.keys, + params.dataMovementComplete, + params.relocationIntervalId, + tssMapping); - state Future completionSignaller = - checkFetchingState(cx, healthyDestinations, keys, dataMovementComplete, relocationIntervalId, tssMapping); - - if (SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA) { - wait(finishMoveShards(cx, - dataMoveId, - keys, - destinationTeam, - lock, - finishMoveKeysParallelismLock, - hasRemote, - relocationIntervalId, - tssMapping, - ddEnabledState)); - } else { - wait(finishMoveKeys(cx, - keys, - destinationTeam, - lock, - finishMoveKeysParallelismLock, - hasRemote, - relocationIntervalId, - tssMapping, - ddEnabledState)); - } + wait(finishMovement(occ, params, tssMapping)); // This is defensive, but make sure that we always say that the movement is complete before moveKeys completes completionSignaller.cancel(); - if (!dataMovementComplete.isSet()) - dataMovementComplete.send(Void()); + if (!params.dataMovementComplete.isSet()) + params.dataMovementComplete.send(Void()); return Void(); } diff --git a/fdbserver/OldTLogServer_4_6.actor.cpp b/fdbserver/OldTLogServer_4_6.actor.cpp index 3a8fd868c8..0ecc22032a 100644 --- a/fdbserver/OldTLogServer_4_6.actor.cpp +++ b/fdbserver/OldTLogServer_4_6.actor.cpp @@ -221,17 +221,14 @@ private: ////// Persistence format (for self->persistentData) // Immutable keys -static const KeyValueRef persistFormat(LiteralStringRef("Format"), LiteralStringRef("FoundationDB/LogServer/2/3")); -static const KeyRangeRef persistFormatReadableRange(LiteralStringRef("FoundationDB/LogServer/2/3"), - LiteralStringRef("FoundationDB/LogServer/2/4")); -static const KeyRangeRef persistRecoveryCountKeys = - KeyRangeRef(LiteralStringRef("DbRecoveryCount/"), LiteralStringRef("DbRecoveryCount0")); +static const KeyValueRef persistFormat("Format"_sr, "FoundationDB/LogServer/2/3"_sr); +static const KeyRangeRef persistFormatReadableRange("FoundationDB/LogServer/2/3"_sr, "FoundationDB/LogServer/2/4"_sr); +static const KeyRangeRef persistRecoveryCountKeys = KeyRangeRef("DbRecoveryCount/"_sr, "DbRecoveryCount0"_sr); // Updated on updatePersistentData() -static const KeyRangeRef persistCurrentVersionKeys = - KeyRangeRef(LiteralStringRef("version/"), LiteralStringRef("version0")); -static const KeyRange persistTagMessagesKeys = prefixRange(LiteralStringRef("TagMsg/")); -static const KeyRange persistTagPoppedKeys = prefixRange(LiteralStringRef("TagPop/")); +static const KeyRangeRef persistCurrentVersionKeys = KeyRangeRef("version/"_sr, "version0"_sr); +static const KeyRange persistTagMessagesKeys = prefixRange("TagMsg/"_sr); +static const KeyRange persistTagPoppedKeys = prefixRange("TagPop/"_sr); static Key persistTagMessagesKey(UID id, OldTag tag, Version version) { BinaryWriter wr(Unversioned()); @@ -450,10 +447,10 @@ struct LogData : NonCopyable, public ReferenceCounted { "Restored"); addActor.send(traceRole(Role::TRANSACTION_LOG, interf.id())); - persistentDataVersion.init(LiteralStringRef("TLog.PersistentDataVersion"), cc.id); - persistentDataDurableVersion.init(LiteralStringRef("TLog.PersistentDataDurableVersion"), cc.id); - version.initMetric(LiteralStringRef("TLog.Version"), cc.id); - queueCommittedVersion.initMetric(LiteralStringRef("TLog.QueueCommittedVersion"), cc.id); + persistentDataVersion.init("TLog.PersistentDataVersion"_sr, cc.id); + persistentDataDurableVersion.init("TLog.PersistentDataDurableVersion"_sr, cc.id); + version.initMetric("TLog.Version"_sr, cc.id); + queueCommittedVersion.initMetric("TLog.QueueCommittedVersion"_sr, cc.id); specialCounter(cc, "Version", [this]() { return this->version.get(); }); specialCounter(cc, "SharedBytesInput", [tLogData]() { return tLogData->bytesInput; }); @@ -1463,7 +1460,7 @@ ACTOR Future restorePersistentState(TLogData* self, LocalityData locality) } if (!fFormat.get().present()) { - RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), LiteralStringRef("\xff")), 1)); + RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), "\xff"_sr), 1)); if (!v.size()) { CODE_PROBE(true, "The DB is completely empty, so it was never initialized. Delete it."); throw worker_removed(); diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 9f7fae452e..b2a31e09ff 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -187,24 +187,18 @@ private: ////// Persistence format (for self->persistentData) // Immutable keys -static const KeyValueRef persistFormat(LiteralStringRef("Format"), LiteralStringRef("FoundationDB/LogServer/2/4")); -static const KeyRangeRef persistFormatReadableRange(LiteralStringRef("FoundationDB/LogServer/2/3"), - LiteralStringRef("FoundationDB/LogServer/2/5")); -static const KeyRangeRef persistRecoveryCountKeys = - KeyRangeRef(LiteralStringRef("DbRecoveryCount/"), LiteralStringRef("DbRecoveryCount0")); +static const KeyValueRef persistFormat("Format"_sr, "FoundationDB/LogServer/2/4"_sr); +static const KeyRangeRef persistFormatReadableRange("FoundationDB/LogServer/2/3"_sr, "FoundationDB/LogServer/2/5"_sr); +static const KeyRangeRef persistRecoveryCountKeys = KeyRangeRef("DbRecoveryCount/"_sr, "DbRecoveryCount0"_sr); // Updated on updatePersistentData() -static const KeyRangeRef persistCurrentVersionKeys = - KeyRangeRef(LiteralStringRef("version/"), LiteralStringRef("version0")); -static const KeyRangeRef persistKnownCommittedVersionKeys = - KeyRangeRef(LiteralStringRef("knownCommitted/"), LiteralStringRef("knownCommitted0")); -static const KeyRangeRef persistLocalityKeys = - KeyRangeRef(LiteralStringRef("Locality/"), LiteralStringRef("Locality0")); -static const KeyRangeRef persistLogRouterTagsKeys = - KeyRangeRef(LiteralStringRef("LogRouterTags/"), LiteralStringRef("LogRouterTags0")); -static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef(LiteralStringRef("TxsTags/"), LiteralStringRef("TxsTags0")); -static const KeyRange persistTagMessagesKeys = prefixRange(LiteralStringRef("TagMsg/")); -static const KeyRange persistTagPoppedKeys = prefixRange(LiteralStringRef("TagPop/")); +static const KeyRangeRef persistCurrentVersionKeys = KeyRangeRef("version/"_sr, "version0"_sr); +static const KeyRangeRef persistKnownCommittedVersionKeys = KeyRangeRef("knownCommitted/"_sr, "knownCommitted0"_sr); +static const KeyRangeRef persistLocalityKeys = KeyRangeRef("Locality/"_sr, "Locality0"_sr); +static const KeyRangeRef persistLogRouterTagsKeys = KeyRangeRef("LogRouterTags/"_sr, "LogRouterTags0"_sr); +static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef("TxsTags/"_sr, "TxsTags0"_sr); +static const KeyRange persistTagMessagesKeys = prefixRange("TagMsg/"_sr); +static const KeyRange persistTagPoppedKeys = prefixRange("TagPop/"_sr); static Key persistTagMessagesKey(UID id, Tag tag, Version version) { BinaryWriter wr(Unversioned()); @@ -539,10 +533,10 @@ struct LogData : NonCopyable, public ReferenceCounted { context); addActor.send(traceRole(Role::TRANSACTION_LOG, interf.id())); - persistentDataVersion.init(LiteralStringRef("TLog.PersistentDataVersion"), cc.id); - persistentDataDurableVersion.init(LiteralStringRef("TLog.PersistentDataDurableVersion"), cc.id); - version.initMetric(LiteralStringRef("TLog.Version"), cc.id); - queueCommittedVersion.initMetric(LiteralStringRef("TLog.QueueCommittedVersion"), cc.id); + persistentDataVersion.init("TLog.PersistentDataVersion"_sr, cc.id); + persistentDataDurableVersion.init("TLog.PersistentDataDurableVersion"_sr, cc.id); + version.initMetric("TLog.Version"_sr, cc.id); + queueCommittedVersion.initMetric("TLog.QueueCommittedVersion"_sr, cc.id); specialCounter(cc, "Version", [this]() { return this->version.get(); }); specialCounter(cc, "QueueCommittedVersion", [this]() { return this->queueCommittedVersion.get(); }); @@ -1491,7 +1485,7 @@ ACTOR Future doQueueCommit(TLogData* self, wait(ioDegradedOrTimeoutError( c, SERVER_KNOBS->MAX_STORAGE_COMMIT_TIME, self->degraded, SERVER_KNOBS->TLOG_DEGRADED_DURATION)); - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { wait(delay(6.0)); } wait(self->queueCommitEnd.whenAtLeast(commitNumber - 1)); @@ -2329,7 +2323,7 @@ ACTOR Future restorePersistentState(TLogData* self, } if (!fFormat.get().present()) { - RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), LiteralStringRef("\xff")), 1)); + RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), "\xff"_sr), 1)); if (!v.size()) { CODE_PROBE(true, "The DB is completely empty, so it was never initialized. Delete it."); throw worker_removed(); @@ -2343,7 +2337,7 @@ ACTOR Future restorePersistentState(TLogData* self, state std::vector>> removed; - if (fFormat.get().get() == LiteralStringRef("FoundationDB/LogServer/2/3")) { + if (fFormat.get().get() == "FoundationDB/LogServer/2/3"_sr) { // FIXME: need for upgrades from 5.X to 6.0, remove once this upgrade path is no longer needed if (recovered.canBeSet()) recovered.send(Void()); diff --git a/fdbserver/OldTLogServer_6_2.actor.cpp b/fdbserver/OldTLogServer_6_2.actor.cpp index 093341f9d5..7caeea9aba 100644 --- a/fdbserver/OldTLogServer_6_2.actor.cpp +++ b/fdbserver/OldTLogServer_6_2.actor.cpp @@ -199,28 +199,21 @@ private: // Immutable keys // persistFormat has been mostly invalidated by TLogVersion, and can probably be removed when // 4.6's TLog code is removed. -static const KeyValueRef persistFormat(LiteralStringRef("Format"), LiteralStringRef("FoundationDB/LogServer/3/0")); -static const KeyRangeRef persistFormatReadableRange(LiteralStringRef("FoundationDB/LogServer/3/0"), - LiteralStringRef("FoundationDB/LogServer/4/0")); -static const KeyRangeRef persistProtocolVersionKeys(LiteralStringRef("ProtocolVersion/"), - LiteralStringRef("ProtocolVersion0")); -static const KeyRangeRef persistRecoveryCountKeys = - KeyRangeRef(LiteralStringRef("DbRecoveryCount/"), LiteralStringRef("DbRecoveryCount0")); +static const KeyValueRef persistFormat("Format"_sr, "FoundationDB/LogServer/3/0"_sr); +static const KeyRangeRef persistFormatReadableRange("FoundationDB/LogServer/3/0"_sr, "FoundationDB/LogServer/4/0"_sr); +static const KeyRangeRef persistProtocolVersionKeys("ProtocolVersion/"_sr, "ProtocolVersion0"_sr); +static const KeyRangeRef persistRecoveryCountKeys = KeyRangeRef("DbRecoveryCount/"_sr, "DbRecoveryCount0"_sr); // Updated on updatePersistentData() -static const KeyRangeRef persistCurrentVersionKeys = - KeyRangeRef(LiteralStringRef("version/"), LiteralStringRef("version0")); -static const KeyRangeRef persistKnownCommittedVersionKeys = - KeyRangeRef(LiteralStringRef("knownCommitted/"), LiteralStringRef("knownCommitted0")); -static const KeyRef persistRecoveryLocationKey = KeyRef(LiteralStringRef("recoveryLocation")); -static const KeyRangeRef persistLocalityKeys = - KeyRangeRef(LiteralStringRef("Locality/"), LiteralStringRef("Locality0")); -static const KeyRangeRef persistLogRouterTagsKeys = - KeyRangeRef(LiteralStringRef("LogRouterTags/"), LiteralStringRef("LogRouterTags0")); -static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef(LiteralStringRef("TxsTags/"), LiteralStringRef("TxsTags0")); -static const KeyRange persistTagMessagesKeys = prefixRange(LiteralStringRef("TagMsg/")); -static const KeyRange persistTagMessageRefsKeys = prefixRange(LiteralStringRef("TagMsgRef/")); -static const KeyRange persistTagPoppedKeys = prefixRange(LiteralStringRef("TagPop/")); +static const KeyRangeRef persistCurrentVersionKeys = KeyRangeRef("version/"_sr, "version0"_sr); +static const KeyRangeRef persistKnownCommittedVersionKeys = KeyRangeRef("knownCommitted/"_sr, "knownCommitted0"_sr); +static const KeyRef persistRecoveryLocationKey = KeyRef("recoveryLocation"_sr); +static const KeyRangeRef persistLocalityKeys = KeyRangeRef("Locality/"_sr, "Locality0"_sr); +static const KeyRangeRef persistLogRouterTagsKeys = KeyRangeRef("LogRouterTags/"_sr, "LogRouterTags0"_sr); +static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef("TxsTags/"_sr, "TxsTags0"_sr); +static const KeyRange persistTagMessagesKeys = prefixRange("TagMsg/"_sr); +static const KeyRange persistTagMessageRefsKeys = prefixRange("TagMsgRef/"_sr); +static const KeyRange persistTagPoppedKeys = prefixRange("TagPop/"_sr); static Key persistTagMessagesKey(UID id, Tag tag, Version version) { BinaryWriter wr(Unversioned()); @@ -623,10 +616,10 @@ struct LogData : NonCopyable, public ReferenceCounted { context); addActor.send(traceRole(Role::TRANSACTION_LOG, interf.id())); - persistentDataVersion.init(LiteralStringRef("TLog.PersistentDataVersion"), cc.id); - persistentDataDurableVersion.init(LiteralStringRef("TLog.PersistentDataDurableVersion"), cc.id); - version.initMetric(LiteralStringRef("TLog.Version"), cc.id); - queueCommittedVersion.initMetric(LiteralStringRef("TLog.QueueCommittedVersion"), cc.id); + persistentDataVersion.init("TLog.PersistentDataVersion"_sr, cc.id); + persistentDataDurableVersion.init("TLog.PersistentDataDurableVersion"_sr, cc.id); + version.initMetric("TLog.Version"_sr, cc.id); + queueCommittedVersion.initMetric("TLog.QueueCommittedVersion"_sr, cc.id); specialCounter(cc, "Version", [this]() { return this->version.get(); }); specialCounter(cc, "QueueCommittedVersion", [this]() { return this->queueCommittedVersion.get(); }); @@ -1905,7 +1898,7 @@ ACTOR Future tLogPeekStream(TLogData* self, TLogPeekStreamRequest req, Ref } ACTOR Future watchDegraded(TLogData* self) { - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { return Void(); } @@ -1932,7 +1925,7 @@ ACTOR Future doQueueCommit(TLogData* self, state Future degraded = watchDegraded(self); wait(c); - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { wait(delay(6.0)); } degraded.cancel(); @@ -2797,7 +2790,7 @@ ACTOR Future restorePersistentState(TLogData* self, } if (!fFormat.get().present()) { - RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), LiteralStringRef("\xff")), 1)); + RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), "\xff"_sr), 1)); if (!v.size()) { CODE_PROBE(true, "The DB is completely empty, so it was never initialized. Delete it."); throw worker_removed(); @@ -2811,7 +2804,7 @@ ACTOR Future restorePersistentState(TLogData* self, state std::vector>> removed; - ASSERT(fFormat.get().get() == LiteralStringRef("FoundationDB/LogServer/3/0")); + ASSERT(fFormat.get().get() == "FoundationDB/LogServer/3/0"_sr); ASSERT(fVers.get().size() == fRecoverCounts.get().size()); diff --git a/fdbserver/PaxosConfigConsumer.actor.cpp b/fdbserver/PaxosConfigConsumer.actor.cpp index 1c1d0d075e..3515c1db5e 100644 --- a/fdbserver/PaxosConfigConsumer.actor.cpp +++ b/fdbserver/PaxosConfigConsumer.actor.cpp @@ -53,10 +53,18 @@ class GetCommittedVersionQuorum { Version largestCompactedResponse{ 0 }; // Last durably committed version. Version lastSeenVersion; + // Largest compacted version on the existing ConfigNodes. + Version largestCompacted; size_t totalRepliesReceived{ 0 }; size_t maxAgreement{ 0 }; + // Stores the largest live version out of all the responses. + Version largestLive{ 0 }; // Stores the largest committed version out of all responses. Version largestCommitted{ 0 }; + bool allowSpecialCaseRollforward_{ false }; + // True if a quorum has zero as their committed version. See explanation + // comment below. + bool specialZeroQuorum{ false }; // Sends rollback/rollforward messages to any nodes that are not up to date // with the latest committed version as determined by the quorum. Should @@ -67,9 +75,18 @@ class GetCommittedVersionQuorum { Version lastCompacted, ConfigFollowerInterface cfi) { state Version target = quorumVersion.lastCommitted; + // TraceEvent("ConsumerUpdateNodeStart") + // .detail("NodeAddress", cfi.address()) + // .detail("Target", target) + // .detail("NodeVersionLastCommitted", nodeVersion.lastCommitted) + // .detail("NodeVersionSecondToLastCommitted", nodeVersion.secondToLastCommitted) + // .detail("QuorumVersionLastCommitted", quorumVersion.lastCommitted) + // .detail("QuorumVersionSecondToLastCommitted", quorumVersion.secondToLastCommitted) + // .detail("LargestCompacted", self->largestCompacted); if (nodeVersion.lastCommitted == target) { return Void(); } + if (nodeVersion.lastCommitted < target) { state Optional rollback; if (nodeVersion.lastCommitted > quorumVersion.secondToLastCommitted) { @@ -83,7 +100,7 @@ class GetCommittedVersionQuorum { // On the other hand, if the node is on an older committed // version, it's possible the version it is on was never made // durable. To be safe, roll it back by one version. - rollback = std::max(nodeVersion.lastCommitted - 1, Version{ 0 }); + rollback = std::max(nodeVersion.lastCommitted - 1, self->largestCompacted); } if (rollback.present()) { @@ -118,18 +135,34 @@ class GetCommittedVersionQuorum { ConfigFollowerGetChangesRequest{ lastSeenVersion, target }), SERVER_KNOBS->GET_COMMITTED_VERSION_TIMEOUT)); + // TraceEvent("ConsumerUpdateNodeSendingRollforward") + // .detail("NodeAddress", cfi.address()) + // .detail("RollbackTo", rollback) + // .detail("LastKnownCommitted", nodeVersion.lastCommitted) + // .detail("Target", target) + // .detail("ChangesSize", reply.changes.size()) + // .detail("AnnotationsSize", reply.annotations.size()) + // .detail("LargestCompacted", self->largestCompactedResponse) + // .detail("SpecialZeroQuorum", self->specialZeroQuorum); if (cfi.hostname.present()) { wait(timeoutError( - retryGetReplyFromHostname( - ConfigFollowerRollforwardRequest{ - rollback, nodeVersion.lastCommitted, target, reply.changes, reply.annotations }, - cfi.hostname.get(), - WLTOKEN_CONFIGFOLLOWER_ROLLFORWARD), + retryGetReplyFromHostname(ConfigFollowerRollforwardRequest{ rollback, + nodeVersion.lastCommitted, + target, + reply.changes, + reply.annotations, + self->specialZeroQuorum }, + cfi.hostname.get(), + WLTOKEN_CONFIGFOLLOWER_ROLLFORWARD), SERVER_KNOBS->GET_COMMITTED_VERSION_TIMEOUT)); } else { wait(timeoutError( - cfi.rollforward.getReply(ConfigFollowerRollforwardRequest{ - rollback, nodeVersion.lastCommitted, target, reply.changes, reply.annotations }), + cfi.rollforward.getReply(ConfigFollowerRollforwardRequest{ rollback, + nodeVersion.lastCommitted, + target, + reply.changes, + reply.annotations, + self->specialZeroQuorum }), SERVER_KNOBS->GET_COMMITTED_VERSION_TIMEOUT)); } } catch (Error& e) { @@ -139,7 +172,7 @@ class GetCommittedVersionQuorum { // one of these errors in response to a get changes or // rollforward request. The retry loop should handle this // case. - TraceEvent(SevInfo, "ConfigNodeRollforwardError").error(e); + TraceEvent(SevInfo, "ConsumerConfigNodeRollforwardError").error(e); } else { throw; } @@ -163,9 +196,19 @@ class GetCommittedVersionQuorum { SERVER_KNOBS->GET_COMMITTED_VERSION_TIMEOUT)); } + if (!reply.registered) { + // ConfigNodes serve their GetCommittedVersion interface before + // being registered to allow them to be rolled forward. + // However, their responses should not count towards the + // quorum. + throw future_version(); + } + ++self->totalRepliesReceived; self->largestCompactedResponse = std::max(self->largestCompactedResponse, reply.lastCompacted); state Version lastCompacted = reply.lastCompacted; + self->committed[cfi.address()] = reply.lastCommitted; + self->largestLive = std::max(self->largestLive, reply.lastLive); self->largestCommitted = std::max(self->largestCommitted, reply.lastCommitted); state CommittedVersions committedVersions = CommittedVersions{ self->lastSeenVersion, reply.lastCommitted }; if (self->priorVersions.find(committedVersions.lastCommitted) == self->priorVersions.end()) { @@ -174,7 +217,59 @@ class GetCommittedVersionQuorum { auto& nodes = self->replies[committedVersions.lastCommitted]; nodes.push_back(cfi); self->maxAgreement = std::max(nodes.size(), self->maxAgreement); + // TraceEvent("ConsumerGetCommittedVersionReply") + // .detail("From", cfi.address()) + // .detail("LastCompactedVersion", lastCompacted) + // .detail("LastCommittedVersion", reply.lastCommitted) + // .detail("LastSeenVersion", self->lastSeenVersion) + // .detail("Replies", self->totalRepliesReceived) + // .detail("RepliesMatchingVersion", nodes.size()) + // .detail("Coordinators", self->cfis.size()) + // .detail("AllowSpecialCaseRollforward", self->allowSpecialCaseRollforward_); if (nodes.size() >= self->cfis.size() / 2 + 1) { + // A quorum at version 0 should use any higher committed + // version seen instead of 0. Imagine the following scenario + // with three coordinators: + // + // t0 t1 t2 t3 + // A 1 1 | 1 + // B 1 dies | 0 + // C 0 0 | 0 + // + // At t0, a value at version 1 is committed to A and B. At t1, + // B dies, and now the value only exists on A. At t2, a change + // coordinators command is executed by a client, causing a + // recovery. When the ConfigBroadcaster comes online and + // attempts to read the state of the previous coordinators (at + // time t3) so it can transfer it to the new coordinators, 2/3 + // ConfigNodes are unregistered and only know about version 0. + // Quorum logic dictates the committed version is, thus, + // version 0. But we know a majority committed version 1. This + // is a special case error where a ConfigNode losing data is + // immediately followed by a coordinator change and recovery, + // and 0 is a special case. Imagine the following if C instead + // has had some values committed: + // + // t0 t1 t2 t3 t4 + // A 1 2 2 | 2 + // B 1 2 dies | 0 + // C 1 1 1 | 1 + // + // In this case, there is no quorum, and so all nodes would + // (correctly) be rolled forward to version 2. Since a node + // losing data is equivalent to saying it has a committed + // version of 0, we must treat a quorum of nodes at version 0 + // as a special case, and instead use the largest committed + // version we've seen as the quorum version. This does not + // affect correctness because version 0 means nothing was + // committed, so there shouldn't be an issue rolling those + // nodes forward. + if (self->allowSpecialCaseRollforward_ && committedVersions.lastCommitted == 0 && + self->largestCommitted > 0) { + self->specialZeroQuorum = true; + committedVersions = CommittedVersions{ 0, self->largestCommitted }; + } + // A quorum of ConfigNodes agree on the latest committed version. if (self->quorumVersion.canBeSet()) { self->quorumVersion.send(QuorumVersion{ committedVersions, true }); @@ -186,7 +281,8 @@ class GetCommittedVersionQuorum { // but the node we just got a reply from is not one of them. We may // need to roll it forward or back. QuorumVersion quorumVersion = wait(self->quorumVersion.getFuture()); - ASSERT(committedVersions.lastCommitted != quorumVersion.versions.lastCommitted); + ASSERT(committedVersions.lastCommitted != quorumVersion.versions.lastCommitted || + self->specialZeroQuorum); wait(self->updateNode(self, committedVersions, quorumVersion.versions, lastCompacted, cfi)); } else if (self->maxAgreement + (self->cfis.size() - self->totalRepliesReceived) < (self->cfis.size() / 2 + 1)) { @@ -213,13 +309,18 @@ class GetCommittedVersionQuorum { } catch (Error& e) { // Count a timeout as a reply. ++self->totalRepliesReceived; + // TraceEvent("ConsumerGetCommittedVersionError").error(e) + // .detail("From", cfi.address()) + // .detail("Replies", self->totalRepliesReceived) + // .detail("Coordinators", self->cfis.size()); if (e.code() == error_code_version_already_compacted) { if (self->quorumVersion.canBeSet()) { // Calling sendError could delete self auto local = self->quorumVersion; local.sendError(e); } - } else if (e.code() != error_code_timed_out && e.code() != error_code_broken_promise) { + } else if (e.code() != error_code_timed_out && e.code() != error_code_future_version && + e.code() != error_code_broken_promise) { if (self->quorumVersion.canBeSet()) { // Calling sendError could delete self auto local = self->quorumVersion; @@ -231,6 +332,7 @@ class GetCommittedVersionQuorum { std::accumulate(self->replies.begin(), self->replies.end(), 0, [](int value, auto const& p) { return value + p.second.size(); }); + if (nonTimeoutReplies >= self->cfis.size() / 2 + 1) { // Make sure to trigger the quorumVersion if a timeout // occurred, a quorum disagree on the committed version, @@ -239,6 +341,14 @@ class GetCommittedVersionQuorum { // back the largest committed version seen. self->quorumVersion.send( QuorumVersion{ CommittedVersions{ self->lastSeenVersion, self->largestCommitted }, false }); + + if (e.code() == error_code_future_version) { + wait(self->updateNode(self, + CommittedVersions{ self->lastSeenVersion, self->largestCommitted }, + self->quorumVersion.getFuture().get().versions, + self->largestCompactedResponse, + cfi)); + } } else if (!self->quorumVersion.isSet()) { // Otherwise, if a quorum agree on the committed version, // some other occurred. Notify the caller of it. @@ -253,8 +363,10 @@ class GetCommittedVersionQuorum { } public: - explicit GetCommittedVersionQuorum(std::vector const& cfis, Version lastSeenVersion) - : cfis(cfis), lastSeenVersion(lastSeenVersion) {} + explicit GetCommittedVersionQuorum(std::vector const& cfis, + Version lastSeenVersion, + Version largestCompacted) + : cfis(cfis), lastSeenVersion(lastSeenVersion), largestCompacted(largestCompacted) {} Future getCommittedVersion() { ASSERT(!isReady()); // ensures this function is not accidentally called before resetting state for (const auto& cfi : cfis) { @@ -273,6 +385,7 @@ public: ASSERT(isReady()); return replies.at(quorumVersion.getFuture().get().versions.lastCommitted); } + Version getLargestLive() const { return largestLive; } Version getSmallestCommitted() const { if (committed.size() == cfis.size()) { Version smallest = MAX_VERSION; @@ -283,6 +396,8 @@ public: } return ::invalidVersion; } + void allowSpecialCaseRollforward() { allowSpecialCaseRollforward_ = true; } + bool isSpecialZeroQuorum() const { return specialZeroQuorum; } Future complete() const { return waitForAll(actors); } }; @@ -293,9 +408,14 @@ class PaxosConfigConsumerImpl { Version compactionVersion{ 0 }; double pollingInterval; Optional compactionInterval; + bool allowSpecialCaseRollforward_{ false }; + bool readPreviousCoordinators{ false }; UID id; ACTOR static Future getCommittedVersion(PaxosConfigConsumerImpl* self) { + if (self->allowSpecialCaseRollforward_) { + self->getCommittedVersionQuorum.allowSpecialCaseRollforward(); + } QuorumVersion quorumVersion = wait(self->getCommittedVersionQuorum.getCommittedVersion()); if (!quorumVersion.isQuorum) { throw failed_to_reach_quorum(); @@ -357,29 +477,37 @@ class PaxosConfigConsumerImpl { &ConfigFollowerInterface::getSnapshotAndChanges, ConfigFollowerGetSnapshotAndChangesRequest{ committedVersion }), SERVER_KNOBS->GET_SNAPSHOT_AND_CHANGES_TIMEOUT)); + Version smallestCommitted = self->getCommittedVersionQuorum.getSmallestCommitted(); TraceEvent(SevDebug, "ConfigConsumerGotSnapshotAndChanges", self->id) .detail("SnapshotVersion", reply.snapshotVersion) .detail("SnapshotSize", reply.snapshot.size()) .detail("ChangesVersion", committedVersion) .detail("ChangesSize", reply.changes.size()) - .detail("AnnotationsSize", reply.annotations.size()); + .detail("AnnotationsSize", reply.annotations.size()) + .detail("LargestLiveVersion", self->getCommittedVersionQuorum.getLargestLive()) + .detail("SmallestCommitted", smallestCommitted); ASSERT_GE(committedVersion, self->lastSeenVersion); self->lastSeenVersion = committedVersion; - Version smallestCommitted = self->getCommittedVersionQuorum.getSmallestCommitted(); self->compactionVersion = std::max(self->compactionVersion, smallestCommitted); broadcaster->applySnapshotAndChanges(std::move(reply.snapshot), reply.snapshotVersion, reply.changes, committedVersion, reply.annotations, - self->getCommittedVersionQuorum.getReadReplicas()); + self->getCommittedVersionQuorum.getReadReplicas(), + self->getCommittedVersionQuorum.getLargestLive(), + self->readPreviousCoordinators); wait(self->getCommittedVersionQuorum.complete()); + if (self->allowSpecialCaseRollforward_) { + self->allowSpecialCaseRollforward_ = false; + } break; } catch (Error& e) { if (e.code() == error_code_failed_to_reach_quorum) { wait(self->getCommittedVersionQuorum.complete()); } else if (e.code() != error_code_timed_out && e.code() != error_code_broken_promise && - e.code() != error_code_version_already_compacted && e.code() != error_code_process_behind) { + e.code() != error_code_version_already_compacted && e.code() != error_code_process_behind && + e.code() != error_code_future_version) { throw; } wait(delayJittered(0.1)); @@ -404,7 +532,8 @@ class PaxosConfigConsumerImpl { // ConfigNodes changes to 1, 1, 2, the committed version // returned would be 1. if (committedVersion > self->lastSeenVersion) { - ASSERT(self->getCommittedVersionQuorum.getReadReplicas().size() >= self->cfis.size() / 2 + 1); + ASSERT(self->getCommittedVersionQuorum.getReadReplicas().size() >= self->cfis.size() / 2 + 1 || + self->getCommittedVersionQuorum.isSpecialZeroQuorum()); state std::vector readReplicas = self->getCommittedVersionQuorum.getReadReplicas(); std::vector> fs; @@ -448,8 +577,8 @@ class PaxosConfigConsumerImpl { } catch (Error& e) { if (e.code() == error_code_version_already_compacted || e.code() == error_code_timed_out || e.code() == error_code_failed_to_reach_quorum || e.code() == error_code_version_already_compacted || - e.code() == error_code_process_behind) { - CODE_PROBE(true, "PaxosConfigConsumer get version_already_compacted error"); + e.code() == error_code_process_behind || e.code() == error_code_future_version) { + CODE_PROBE(true, "PaxosConfigConsumer fetch error"); if (e.code() == error_code_failed_to_reach_quorum) { try { wait(self->getCommittedVersionQuorum.complete()); @@ -483,39 +612,58 @@ class PaxosConfigConsumerImpl { } void resetCommittedVersionQuorum() { - getCommittedVersionQuorum = GetCommittedVersionQuorum{ cfis, lastSeenVersion }; + getCommittedVersionQuorum = GetCommittedVersionQuorum{ cfis, lastSeenVersion, compactionVersion }; } public: + Future readSnapshot(ConfigBroadcaster& broadcaster) { return getSnapshotAndChanges(this, &broadcaster); } + Future consume(ConfigBroadcaster& broadcaster) { return fetchChanges(this, &broadcaster) || compactor(this, &broadcaster); } + void allowSpecialCaseRollforward() { this->allowSpecialCaseRollforward_ = true; } + UID getID() const { return id; } PaxosConfigConsumerImpl(std::vector const& cfis, double pollingInterval, - Optional compactionInterval) - : cfis(cfis), getCommittedVersionQuorum(cfis, 0), pollingInterval(pollingInterval), - compactionInterval(compactionInterval), id(deterministicRandom()->randomUniqueID()) {} + Optional compactionInterval, + bool readPreviousCoordinators) + : cfis(cfis), getCommittedVersionQuorum(cfis, 0, 0), pollingInterval(pollingInterval), + compactionInterval(compactionInterval), readPreviousCoordinators(readPreviousCoordinators), + id(deterministicRandom()->randomUniqueID()) {} }; PaxosConfigConsumer::PaxosConfigConsumer(std::vector const& cfis, double pollingInterval, - Optional compactionInterval) - : impl(PImpl::create(cfis, pollingInterval, compactionInterval)) {} + Optional compactionInterval, + bool readPreviousCoordinators) + : impl(PImpl::create(cfis, pollingInterval, compactionInterval, readPreviousCoordinators)) {} PaxosConfigConsumer::PaxosConfigConsumer(ServerCoordinators const& coordinators, double pollingInterval, - Optional compactionInterval) - : impl(PImpl::create(coordinators.configServers, pollingInterval, compactionInterval)) {} + Optional compactionInterval, + bool readPreviousCoordinators) + : impl(PImpl::create(coordinators.configServers, + pollingInterval, + compactionInterval, + readPreviousCoordinators)) {} PaxosConfigConsumer::~PaxosConfigConsumer() = default; +Future PaxosConfigConsumer::readSnapshot(ConfigBroadcaster& broadcaster) { + return impl->readSnapshot(broadcaster); +} + Future PaxosConfigConsumer::consume(ConfigBroadcaster& broadcaster) { return impl->consume(broadcaster); } +void PaxosConfigConsumer::allowSpecialCaseRollforward() { + impl->allowSpecialCaseRollforward(); +} + UID PaxosConfigConsumer::getID() const { return impl->getID(); } diff --git a/fdbserver/QuietDatabase.actor.cpp b/fdbserver/QuietDatabase.actor.cpp index 62558fc52e..72bf6d7e93 100644 --- a/fdbserver/QuietDatabase.actor.cpp +++ b/fdbserver/QuietDatabase.actor.cpp @@ -111,8 +111,8 @@ ACTOR Future getDataDistributorWorker(Database cx, Reference getDataInFlight(Database cx, WorkerInterface distributorWorker) { try { TraceEvent("DataInFlight").detail("Stage", "ContactingDataDistributor"); - TraceEventFields md = wait(timeoutError( - distributorWorker.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("TotalDataInFlight"))), 1.0)); + TraceEventFields md = wait( + timeoutError(distributorWorker.eventLogRequest.getReply(EventLogRequest("TotalDataInFlight"_sr)), 1.0)); int64_t dataInFlight = boost::lexical_cast(md.getValue("TotalBytes")); return dataInFlight; } catch (Error& e) { @@ -300,7 +300,7 @@ getStorageWorkers(Database cx, Reference const> dbInfo, b wait(runRYWTransaction(cx, [=](Reference tr) -> Future> { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); - return tr->get(LiteralStringRef("usable_regions").withPrefix(configKeysPrefix)); + return tr->get("usable_regions"_sr.withPrefix(configKeysPrefix)); })); int usableRegions = 1; if (regionsValue.present()) { @@ -440,8 +440,8 @@ ACTOR Future getDataDistributionQueueSize(Database cx, try { TraceEvent("DataDistributionQueueSize").detail("Stage", "ContactingDataDistributor"); - TraceEventFields movingDataMessage = wait(timeoutError( - distributorWorker.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("MovingData"))), 1.0)); + TraceEventFields movingDataMessage = + wait(timeoutError(distributorWorker.eventLogRequest.getReply(EventLogRequest("MovingData"_sr)), 1.0)); TraceEvent("DataDistributionQueueSize").detail("Stage", "GotString"); @@ -483,8 +483,7 @@ ACTOR Future getTeamCollectionValid(Database cx, WorkerInterface dataDistr TraceEvent("GetTeamCollectionValid").detail("Stage", "ContactingMaster"); TraceEventFields teamCollectionInfoMessage = wait(timeoutError( - dataDistributorWorker.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("TeamCollectionInfo"))), - 1.0)); + dataDistributorWorker.eventLogRequest.getReply(EventLogRequest("TeamCollectionInfo"_sr)), 1.0)); TraceEvent("GetTeamCollectionValid").detail("Stage", "GotString"); @@ -591,8 +590,8 @@ ACTOR Future getDataDistributionActive(Database cx, WorkerInterface distri try { TraceEvent("DataDistributionActive").detail("Stage", "ContactingDataDistributor"); - TraceEventFields activeMessage = wait(timeoutError( - distributorWorker.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("DDTrackerStarting"))), 1.0)); + TraceEventFields activeMessage = wait( + timeoutError(distributorWorker.eventLogRequest.getReply(EventLogRequest("DDTrackerStarting"_sr)), 1.0)); return activeMessage.getValue("State") == "Active"; } catch (Error& e) { @@ -657,9 +656,9 @@ ACTOR Future getVersionOffset(Database cx, ACTOR Future repairDeadDatacenter(Database cx, Reference const> dbInfo, std::string context) { - if (g_network->isSimulated() && g_simulator.usableRegions > 1) { - bool primaryDead = g_simulator.datacenterDead(g_simulator.primaryDcId); - bool remoteDead = g_simulator.datacenterDead(g_simulator.remoteDcId); + if (g_network->isSimulated() && g_simulator->usableRegions > 1) { + bool primaryDead = g_simulator->datacenterDead(g_simulator->primaryDcId); + bool remoteDead = g_simulator->datacenterDead(g_simulator->remoteDcId); // FIXME: the primary and remote can both be considered dead because excludes are not handled properly by the // datacenterDead function @@ -673,10 +672,10 @@ ACTOR Future repairDeadDatacenter(Database cx, .detail("Stage", "Repopulate") .detail("RemoteDead", remoteDead) .detail("PrimaryDead", primaryDead); - g_simulator.usableRegions = 1; + g_simulator->usableRegions = 1; wait(success(ManagementAPI::changeConfig( cx.getReference(), - (primaryDead ? g_simulator.disablePrimary : g_simulator.disableRemote) + " repopulate_anti_quorum=1", + (primaryDead ? g_simulator->disablePrimary : g_simulator->disableRemote) + " repopulate_anti_quorum=1", true))); while (dbInfo->get().recoveryState < RecoveryState::STORAGE_RECOVERED) { wait(dbInfo->onChange()); diff --git a/fdbserver/RESTKmsConnector.actor.cpp b/fdbserver/RESTKmsConnector.actor.cpp index 2aef038144..c1f0fbbc23 100644 --- a/fdbserver/RESTKmsConnector.actor.cpp +++ b/fdbserver/RESTKmsConnector.actor.cpp @@ -1102,9 +1102,8 @@ void testGetEncryptKeysByKeyIdsRequestBody(Reference ctx, A const int nKeys = deterministicRandom()->randomInt(7, 8); for (int i = 1; i < nKeys; i++) { EncryptCipherDomainId domainId = getRandomDomainId(); - EncryptCipherDomainNameRef domainName = domainId < 0 - ? StringRef(arena, std::string(FDB_DEFAULT_ENCRYPT_DOMAIN_NAME)) - : StringRef(arena, std::to_string(domainId)); + EncryptCipherDomainNameRef domainName = domainId < 0 ? StringRef(arena, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME) + : StringRef(arena, std::to_string(domainId)); req.encryptKeyInfos.emplace_back_deep(req.arena, domainId, i, domainName); keyMap[i] = domainId; } @@ -1141,9 +1140,8 @@ void testGetEncryptKeysByDomainIdsRequestBody(Reference ctx const int nKeys = deterministicRandom()->randomInt(7, 25); for (int i = 1; i < nKeys; i++) { EncryptCipherDomainId domainId = getRandomDomainId(); - EncryptCipherDomainNameRef domainName = domainId < 0 - ? StringRef(arena, std::string(FDB_DEFAULT_ENCRYPT_DOMAIN_NAME)) - : StringRef(arena, std::to_string(domainId)); + EncryptCipherDomainNameRef domainName = domainId < 0 ? StringRef(arena, FDB_DEFAULT_ENCRYPT_DOMAIN_NAME) + : StringRef(arena, std::to_string(domainId)); KmsConnLookupDomainIdsReqInfoRef reqInfo(req.arena, domainId, domainName); if (domainInfoMap.insert({ domainId, reqInfo }).second) { req.encryptDomainInfos.push_back(req.arena, reqInfo); diff --git a/fdbserver/Ratekeeper.actor.cpp b/fdbserver/Ratekeeper.actor.cpp index e5f71fbad2..a7d06dc533 100644 --- a/fdbserver/Ratekeeper.actor.cpp +++ b/fdbserver/Ratekeeper.actor.cpp @@ -616,17 +616,16 @@ Future Ratekeeper::run(RatekeeperInterface rkInterf, ReferenceSMOOTHING_AMOUNT), smoothBatchReleasedTransactions(SERVER_KNOBS->SMOOTHING_AMOUNT), - smoothTotalDurableBytes(SERVER_KNOBS->SLOW_SMOOTHING_AMOUNT), - actualTpsMetric(LiteralStringRef("Ratekeeper.ActualTPS")), lastWarning(0), lastSSListFetchedTimestamp(now()), - normalLimits(TransactionPriority::DEFAULT, - "", - SERVER_KNOBS->TARGET_BYTES_PER_STORAGE_SERVER, - SERVER_KNOBS->SPRING_BYTES_STORAGE_SERVER, - SERVER_KNOBS->TARGET_BYTES_PER_TLOG, - SERVER_KNOBS->SPRING_BYTES_TLOG, - SERVER_KNOBS->MAX_TL_SS_VERSION_DIFFERENCE, - SERVER_KNOBS->TARGET_DURABILITY_LAG_VERSIONS, - SERVER_KNOBS->TARGET_BW_LAG), + smoothTotalDurableBytes(SERVER_KNOBS->SLOW_SMOOTHING_AMOUNT), actualTpsMetric("Ratekeeper.ActualTPS"_sr), + lastWarning(0), lastSSListFetchedTimestamp(now()), normalLimits(TransactionPriority::DEFAULT, + "", + SERVER_KNOBS->TARGET_BYTES_PER_STORAGE_SERVER, + SERVER_KNOBS->SPRING_BYTES_STORAGE_SERVER, + SERVER_KNOBS->TARGET_BYTES_PER_TLOG, + SERVER_KNOBS->SPRING_BYTES_TLOG, + SERVER_KNOBS->MAX_TL_SS_VERSION_DIFFERENCE, + SERVER_KNOBS->TARGET_DURABILITY_LAG_VERSIONS, + SERVER_KNOBS->TARGET_BW_LAG), batchLimits(TransactionPriority::BATCH, "Batch", SERVER_KNOBS->TARGET_BYTES_PER_STORAGE_SERVER_BATCH, @@ -1208,7 +1207,7 @@ void Ratekeeper::updateRate(RatekeeperLimits* limits) { limits->tpsLimit = std::max(limits->tpsLimit, 0.0); - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { limits->tpsLimit = std::max(limits->tpsLimit, 100.0); } diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 7b361af4a7..afa1487cd1 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -286,7 +286,7 @@ ACTOR static Future getAndComputeStagingKeys( ASSERT(!g_network->isSimulated()); int i = 0; for (auto& key : incompleteStagingKeys) { - MutationRef m(MutationRef::SetValue, key.first, LiteralStringRef("0")); + MutationRef m(MutationRef::SetValue, key.first, "0"_sr); key.second->second.add(m, LogMessageVersion(1)); key.second->second.precomputeResult("GetAndComputeStagingKeys", applierID, batchIndex); i++; diff --git a/fdbserver/RestoreCommon.actor.cpp b/fdbserver/RestoreCommon.actor.cpp index fbaaaf8ef5..d87e416f70 100644 --- a/fdbserver/RestoreCommon.actor.cpp +++ b/fdbserver/RestoreCommon.actor.cpp @@ -68,7 +68,7 @@ KeyBackedProperty> RestoreConfigFR::sourceContainer( } // Get the source container as a bare URL, without creating a container instance KeyBackedProperty RestoreConfigFR::sourceContainerURL() { - return configSpace.pack(LiteralStringRef("sourceContainer")); + return configSpace.pack("sourceContainer"_sr); } // Total bytes written by all log and range restore tasks. diff --git a/fdbserver/RestoreUtil.actor.cpp b/fdbserver/RestoreUtil.actor.cpp index e25b665fd2..622fe98b19 100644 --- a/fdbserver/RestoreUtil.actor.cpp +++ b/fdbserver/RestoreUtil.actor.cpp @@ -29,7 +29,7 @@ int numRoles = RestoreRoleStr.size(); // Similar to debugMutation(), we use debugFRMutation to track mutations for fast restore systems only. #if CENABLED(0, NOT_IN_CLEAN) -StringRef debugFRKey = LiteralStringRef("\xff\xff\xff\xff"); +StringRef debugFRKey = "\xff\xff\xff\xff"_sr; // Track any mutation in fast restore that has overlap with debugFRKey bool debugFRMutation(const char* context, Version version, MutationRef const& mutation) { diff --git a/fdbserver/RestoreWorker.actor.cpp b/fdbserver/RestoreWorker.actor.cpp index 0dd684a0f5..db724ee2b9 100644 --- a/fdbserver/RestoreWorker.actor.cpp +++ b/fdbserver/RestoreWorker.actor.cpp @@ -366,13 +366,13 @@ ACTOR Future _restoreWorker(Database cx, LocalityData locality) { // Protect restore worker from being killed in simulation; // Future: Remove the protection once restore can tolerate failure if (g_network->isSimulated()) { - auto addresses = g_simulator.getProcessByAddress(myWorkerInterf.address())->addresses; + auto addresses = g_simulator->getProcessByAddress(myWorkerInterf.address())->addresses; - g_simulator.protectedAddresses.insert(addresses.address); + g_simulator->protectedAddresses.insert(addresses.address); if (addresses.secondaryAddress.present()) { - g_simulator.protectedAddresses.insert(addresses.secondaryAddress.get()); + g_simulator->protectedAddresses.insert(addresses.secondaryAddress.get()); } - ISimulator::ProcessInfo* p = g_simulator.getProcessByAddress(myWorkerInterf.address()); + ISimulator::ProcessInfo* p = g_simulator->getProcessByAddress(myWorkerInterf.address()); TraceEvent("ProtectRestoreWorker") .detail("Address", addresses.toString()) .detail("IsReliable", p->isReliable()) diff --git a/fdbserver/RocksDBCheckpointUtils.actor.cpp b/fdbserver/RocksDBCheckpointUtils.actor.cpp index 88b4795171..1facf2631b 100644 --- a/fdbserver/RocksDBCheckpointUtils.actor.cpp +++ b/fdbserver/RocksDBCheckpointUtils.actor.cpp @@ -109,9 +109,9 @@ public: Future nextKeyValues(const int rowLimit, const int byteLimit) override; - Future> nextChunk(const int byteLimit) { throw not_implemented(); } + Future> nextChunk(const int byteLimit) override { throw not_implemented(); } - Future close() { return doClose(this); } + Future close() override { return doClose(this); } private: struct Reader : IThreadPoolReceiver { diff --git a/fdbserver/SimKmsConnector.actor.cpp b/fdbserver/SimKmsConnector.actor.cpp index 6b72a2849f..72c2f81010 100644 --- a/fdbserver/SimKmsConnector.actor.cpp +++ b/fdbserver/SimKmsConnector.actor.cpp @@ -22,11 +22,11 @@ #include "fmt/format.h" #include "fdbrpc/sim_validation.h" +#include "fdbclient/BlobCipher.h" #include "fdbserver/KmsConnectorInterface.h" #include "fdbserver/Knobs.h" #include "flow/ActorCollection.h" #include "flow/Arena.h" -#include "flow/BlobCipher.h" #include "flow/EncryptUtils.h" #include "flow/Error.h" #include "flow/FastRef.h" diff --git a/fdbserver/SimpleConfigConsumer.actor.cpp b/fdbserver/SimpleConfigConsumer.actor.cpp index 619f211c11..7241ffe48d 100644 --- a/fdbserver/SimpleConfigConsumer.actor.cpp +++ b/fdbserver/SimpleConfigConsumer.actor.cpp @@ -145,7 +145,8 @@ class SimpleConfigConsumerImpl { reply.changes, committedVersion, reply.annotations, - { self->cfi }); + { self->cfi }, + committedVersion); return Void(); } @@ -186,6 +187,11 @@ SimpleConfigConsumer::SimpleConfigConsumer(ServerCoordinators const& coordinator Optional compactionInterval) : impl(PImpl::create(coordinators, pollingInterval, compactionInterval)) {} +Future SimpleConfigConsumer::readSnapshot(ConfigBroadcaster& broadcaster) { + ASSERT(false); + return Void(); +} + Future SimpleConfigConsumer::consume(ConfigBroadcaster& broadcaster) { return impl->consume(broadcaster); } diff --git a/fdbserver/SimulatedCluster.actor.cpp b/fdbserver/SimulatedCluster.actor.cpp index 76d80c153f..6a719bbf69 100644 --- a/fdbserver/SimulatedCluster.actor.cpp +++ b/fdbserver/SimulatedCluster.actor.cpp @@ -282,7 +282,7 @@ class TestConfig { } if (attrib == "configDBType") { if (value == "random") { - configDBType = deterministicRandom()->coinflip() ? ConfigDBType::SIMPLE : ConfigDBType::PAXOS; + configDBType = deterministicRandom()->random01() < 0.1 ? ConfigDBType::SIMPLE : ConfigDBType::PAXOS; } else { configDBType = configDBTypeFromString(value); } @@ -420,7 +420,6 @@ public: .add("allowDisablingTenants", &allowDisablingTenants) .add("allowCreatingTenants", &allowCreatingTenants) .add("randomlyRenameZoneId", &randomlyRenameZoneId) - .add("randomlyRenameZoneId", &randomlyRenameZoneId) .add("injectTargetedSSRestart", &injectTargetedSSRestart) .add("injectSSDelay", &injectSSDelay); try { @@ -475,18 +474,18 @@ T simulate(const T& in) { ACTOR Future runBackup(Reference connRecord) { state std::vector> agentFutures; - while (g_simulator.backupAgents == ISimulator::BackupAgentType::WaitForType) { + while (g_simulator->backupAgents == ISimulator::BackupAgentType::WaitForType) { wait(delay(1.0)); } - if (g_simulator.backupAgents == ISimulator::BackupAgentType::BackupToFile) { + if (g_simulator->backupAgents == ISimulator::BackupAgentType::BackupToFile) { Database cx = Database::createDatabase(connRecord, ApiVersion::LATEST_VERSION); state FileBackupAgent fileAgent; agentFutures.push_back(fileAgent.run( cx, 1.0 / CLIENT_KNOBS->BACKUP_AGGREGATE_POLL_RATE, CLIENT_KNOBS->SIM_BACKUP_TASKS_PER_AGENT)); - while (g_simulator.backupAgents == ISimulator::BackupAgentType::BackupToFile) { + while (g_simulator->backupAgents == ISimulator::BackupAgentType::BackupToFile) { wait(delay(1.0)); } @@ -502,16 +501,16 @@ ACTOR Future runBackup(Reference connRecord) { ACTOR Future runDr(Reference connRecord) { state std::vector> agentFutures; - while (g_simulator.drAgents == ISimulator::BackupAgentType::WaitForType) { + while (g_simulator->drAgents == ISimulator::BackupAgentType::WaitForType) { wait(delay(1.0)); } - if (g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) { - ASSERT(g_simulator.extraDatabases.size() == 1); + if (g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) { + ASSERT(g_simulator->extraDatabases.size() == 1); Database cx = Database::createDatabase(connRecord, ApiVersion::LATEST_VERSION); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); state Database drDatabase = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); TraceEvent("StartingDrAgents") @@ -526,7 +525,7 @@ ACTOR Future runDr(Reference connRecord) { agentFutures.push_back(extraAgent.run(cx, drPollDelay, CLIENT_KNOBS->SIM_BACKUP_TASKS_PER_AGENT)); agentFutures.push_back(dbAgent.run(drDatabase, drPollDelay, CLIENT_KNOBS->SIM_BACKUP_TASKS_PER_AGENT)); - while (g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) { + while (g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) { wait(delay(1.0)); } @@ -562,7 +561,7 @@ ACTOR Future simulatedFDBDRebooter(ReferencegetCurrentProcess(); state UID randomId = nondeterministicRandom()->randomUniqueID(); state int cycles = 0; state IPAllowList allowList; @@ -585,17 +584,17 @@ ACTOR Future simulatedFDBDRebooter(Referencec_str(), - coordFolder->c_str(), - protocolVersion); - wait(g_simulator.onProcess( + state ISimulator::ProcessInfo* process = g_simulator->newProcess("Server", + ip, + port, + sslEnabled, + listenPerProcess, + localities, + processClass, + dataFolder->c_str(), + coordFolder->c_str(), + protocolVersion); + wait(g_simulator->onProcess( process, TaskPriority::DefaultYield)); // Now switch execution to the process on which we will run state Future onShutdown = process->onShutdown(); @@ -630,7 +629,7 @@ ACTOR Future simulatedFDBDRebooter(ReferenceauthKeys) { FlowTransport::transport().addPublicKey(p.first, p.second.toPublic()); } Sim2FileSystem::newFileSystem(); @@ -681,7 +680,7 @@ ACTOR Future simulatedFDBDRebooter(ReferencegetCurrentProcess() == process); // simulatedFDBD catch called on different process TraceEvent(e.code() == error_code_actor_cancelled || e.code() == error_code_file_not_found || e.code() == error_code_incompatible_software_version || destructed ? SevInfo @@ -709,7 +708,7 @@ ACTOR Future simulatedFDBDRebooter(ReferencegetCurrentProcess() == process); if (!process->shutdownSignal.isSet() && !destructed) { process->rebooting = true; @@ -722,11 +721,11 @@ ACTOR Future simulatedFDBDRebooter(Referenceexcluded) .detail("Rebooting", process->rebooting) .detail("ZoneId", localities.zoneId()); - wait(g_simulator.onProcess(simProcess)); + wait(g_simulator->onProcess(simProcess)); wait(delay(0.00001 + FLOW_KNOBS->MAX_BUGGIFIED_DELAY)); // One last chance for the process to clean up? - g_simulator.destroyProcess( + g_simulator->destroyProcess( process); // Leak memory here; the process may be used in other parts of the simulation auto shutdownResult = onShutdown.get(); @@ -849,7 +848,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, const int listenPort = i * listenPerProcess + 1; AgentMode agentMode = runBackupAgents == AgentOnly ? (i == ips.size() - 1 ? AgentOnly : AgentNone) : runBackupAgents; - if (g_simulator.hasDiffProtocolProcess && !g_simulator.setDiffProtocol && agentMode == AgentNone) { + if (g_simulator->hasDiffProtocolProcess && !g_simulator->setDiffProtocol && agentMode == AgentNone) { processes.push_back(simulatedFDBDRebooter(clusterFile, ips[i], sslEnabled, @@ -866,7 +865,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, whitelistBinPaths, protocolVersion, configDBType)); - g_simulator.setDiffProtocol = true; + g_simulator->setDiffProtocol = true; } else { processes.push_back(simulatedFDBDRebooter(clusterFile, ips[i], @@ -922,7 +921,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, { // Kill all open files, which may cause them to write invalid data. - auto& machineCache = g_simulator.getMachineById(localities.machineId())->openFiles; + auto& machineCache = g_simulator->getMachineById(localities.machineId())->openFiles; // Copy the file pointers to a vector because the map may be modified while we are killing files std::vector files; @@ -940,14 +939,14 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, state std::set filenames; state std::string closingStr; - auto& machineCache = g_simulator.getMachineById(localities.machineId())->openFiles; + auto& machineCache = g_simulator->getMachineById(localities.machineId())->openFiles; for (auto it : machineCache) { filenames.insert(it.first); closingStr += it.first + ", "; ASSERT(it.second.get().canGet()); } - for (auto it : g_simulator.getMachineById(localities.machineId())->deletingOrClosingFiles) { + for (auto it : g_simulator->getMachineById(localities.machineId())->deletingOrClosingFiles) { filenames.insert(it); closingStr += it + ", "; } @@ -960,9 +959,9 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, .detail("ZoneId", localities.zoneId()) .detail("DataHall", localities.dataHallId()); - ISimulator::MachineInfo* machine = g_simulator.getMachineById(localities.machineId()); + ISimulator::MachineInfo* machine = g_simulator->getMachineById(localities.machineId()); machine->closingFiles = filenames; - g_simulator.getMachineById(localities.machineId())->openFiles.clear(); + g_simulator->getMachineById(localities.machineId())->openFiles.clear(); // During a reboot: // The process is expected to close all files and be inactive in zero time, but not necessarily @@ -972,7 +971,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, state int shutdownDelayCount = 0; state double backoff = 0; loop { - auto& machineCache = g_simulator.getMachineById(localities.machineId())->closingFiles; + auto& machineCache = g_simulator->getMachineById(localities.machineId())->closingFiles; if (!machineCache.empty()) { std::string openFiles; @@ -1004,7 +1003,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, .detail("ZoneId", localities.zoneId()) .detail("DataHall", localities.dataHallId()); - g_simulator.destroyMachine(localities.machineId()); + g_simulator->destroyMachine(localities.machineId()); // SOMEDAY: when processes can be rebooted, this check will be needed // ASSERT( this machine is rebooting ); @@ -1017,7 +1016,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, CODE_PROBE(true, "Simulated machine has been rebooted"); state bool swap = killType == ISimulator::Reboot && BUGGIFY_WITH_PROB(0.75) && - g_simulator.canSwapToMachine(localities.zoneId()); + g_simulator->canSwapToMachine(localities.zoneId()); if (swap) availableFolders[localities.dcId()].push_back(myFolders); @@ -1073,7 +1072,7 @@ ACTOR Future simulatedMachine(ClusterConnectionString connStr, // this machine is rebooting = false; } } catch (Error& e) { - g_simulator.getMachineById(localities.machineId())->openFiles.clear(); + g_simulator->getMachineById(localities.machineId())->openFiles.clear(); throw; } } @@ -1131,12 +1130,12 @@ ACTOR Future restartSimulatedSystem(std::vector>* systemActor int testerCount = atoi(ini.GetValue("META", "testerCount")); auto tssModeStr = ini.GetValue("META", "tssMode"); if (tssModeStr != nullptr) { - g_simulator.tssMode = (ISimulator::TSSMode)atoi(tssModeStr); + g_simulator->tssMode = (ISimulator::TSSMode)atoi(tssModeStr); } ClusterConnectionString conn(ini.GetValue("META", "connectionString")); if (testConfig.extraDatabaseMode == ISimulator::ExtraDatabaseMode::Local) { - g_simulator.extraDatabases.clear(); - g_simulator.extraDatabases.push_back(conn.toString()); + g_simulator->extraDatabases.clear(); + g_simulator->extraDatabases.push_back(conn.toString()); } if (!testConfig.disableHostname) { auto mockDNSStr = ini.GetValue("META", "mockDNS"); @@ -1259,8 +1258,8 @@ ACTOR Future restartSimulatedSystem(std::vector>* systemActor processClass == ProcessClass::TesterClass ? "SimulatedTesterMachine" : "SimulatedMachine")); } - g_simulator.desiredCoordinators = desiredCoordinators; - g_simulator.processesPerMachine = processesPerMachine; + g_simulator->desiredCoordinators = desiredCoordinators; + g_simulator->processesPerMachine = processesPerMachine; uniquify(dcIds); if (!BUGGIFY && dcIds.size() == 2 && dcIds[0] != "" && dcIds[1] != "") { @@ -1290,11 +1289,11 @@ ACTOR Future restartSimulatedSystem(std::vector>* systemActor json_spirit::write_string(json_spirit::mValue(regionArr), json_spirit::Output_options::none); } - g_simulator.restarted = true; + g_simulator->restarted = true; TraceEvent("RestartSimulatorSettings") - .detail("DesiredCoordinators", g_simulator.desiredCoordinators) - .detail("ProcessesPerMachine", g_simulator.processesPerMachine) + .detail("DesiredCoordinators", g_simulator->desiredCoordinators) + .detail("ProcessesPerMachine", g_simulator->processesPerMachine) .detail("ListenersPerProcess", listenersPerProcess); } catch (Error& e) { TraceEvent(SevError, "RestartSimulationError").error(e); @@ -1747,18 +1746,18 @@ void SimulationConfig::setRegions(const TestConfig& testConfig) { } if (needsRemote) { - g_simulator.originalRegions = + g_simulator->originalRegions = "regions=" + json_spirit::write_string(json_spirit::mValue(regionArr), json_spirit::Output_options::none); StatusArray disablePrimary = regionArr; disablePrimary[0].get_obj()["datacenters"].get_array()[0].get_obj()["priority"] = -1; - g_simulator.disablePrimary = "regions=" + json_spirit::write_string(json_spirit::mValue(disablePrimary), - json_spirit::Output_options::none); + g_simulator->disablePrimary = "regions=" + json_spirit::write_string(json_spirit::mValue(disablePrimary), + json_spirit::Output_options::none); StatusArray disableRemote = regionArr; disableRemote[1].get_obj()["datacenters"].get_array()[0].get_obj()["priority"] = -1; - g_simulator.disableRemote = "regions=" + json_spirit::write_string(json_spirit::mValue(disableRemote), - json_spirit::Output_options::none); + g_simulator->disableRemote = "regions=" + json_spirit::write_string(json_spirit::mValue(disableRemote), + json_spirit::Output_options::none); } else { // In order to generate a starting configuration with the remote disabled, do not apply the region // configuration to the DatabaseConfiguration until after creating the starting conf string. @@ -1852,21 +1851,21 @@ void SimulationConfig::setTss(const TestConfig& testConfig) { double tssRandom = deterministicRandom()->random01(); if (tssRandom > 0.5 || !faultInjectionActivated) { // normal tss mode - g_simulator.tssMode = ISimulator::TSSMode::EnabledNormal; + g_simulator->tssMode = ISimulator::TSSMode::EnabledNormal; } else if (tssRandom < 0.25 && !testConfig.isFirstTestInRestart) { // fault injection - don't enable in first test in restart because second test won't know it intentionally // lost data - g_simulator.tssMode = ISimulator::TSSMode::EnabledDropMutations; + g_simulator->tssMode = ISimulator::TSSMode::EnabledDropMutations; } else { // delay injection - g_simulator.tssMode = ISimulator::TSSMode::EnabledAddDelay; + g_simulator->tssMode = ISimulator::TSSMode::EnabledAddDelay; } - printf("enabling tss for simulation in mode %d: %s\n", g_simulator.tssMode, confStr.c_str()); + printf("enabling tss for simulation in mode %d: %s\n", g_simulator->tssMode, confStr.c_str()); } } void setConfigDB(TestConfig const& testConfig) { - g_simulator.configDBType = testConfig.getConfigDBType(); + g_simulator->configDBType = testConfig.getConfigDBType(); } // Generates and sets an appropriate configuration for the database according to @@ -1985,57 +1984,57 @@ void setupSimulatedSystem(std::vector>* systemActors, startingConfigString += format(" tss_storage_engine:=%d", simconfig.db.testingStorageServerStoreType); } - if (g_simulator.originalRegions != "") { - simconfig.set_config(g_simulator.originalRegions); - g_simulator.startingDisabledConfiguration = startingConfigString + " " + g_simulator.disableRemote; - startingConfigString += " " + g_simulator.originalRegions; + if (g_simulator->originalRegions != "") { + simconfig.set_config(g_simulator->originalRegions); + g_simulator->startingDisabledConfiguration = startingConfigString + " " + g_simulator->disableRemote; + startingConfigString += " " + g_simulator->originalRegions; } - g_simulator.storagePolicy = simconfig.db.storagePolicy; - g_simulator.tLogPolicy = simconfig.db.tLogPolicy; - g_simulator.tLogWriteAntiQuorum = simconfig.db.tLogWriteAntiQuorum; - g_simulator.remoteTLogPolicy = simconfig.db.getRemoteTLogPolicy(); - g_simulator.usableRegions = simconfig.db.usableRegions; + g_simulator->storagePolicy = simconfig.db.storagePolicy; + g_simulator->tLogPolicy = simconfig.db.tLogPolicy; + g_simulator->tLogWriteAntiQuorum = simconfig.db.tLogWriteAntiQuorum; + g_simulator->remoteTLogPolicy = simconfig.db.getRemoteTLogPolicy(); + g_simulator->usableRegions = simconfig.db.usableRegions; if (simconfig.db.regions.size() > 0) { - g_simulator.primaryDcId = simconfig.db.regions[0].dcId; - g_simulator.hasSatelliteReplication = simconfig.db.regions[0].satelliteTLogReplicationFactor > 0; + g_simulator->primaryDcId = simconfig.db.regions[0].dcId; + g_simulator->hasSatelliteReplication = simconfig.db.regions[0].satelliteTLogReplicationFactor > 0; if (simconfig.db.regions[0].satelliteTLogUsableDcsFallback > 0) { - g_simulator.satelliteTLogPolicyFallback = simconfig.db.regions[0].satelliteTLogPolicyFallback; - g_simulator.satelliteTLogWriteAntiQuorumFallback = + g_simulator->satelliteTLogPolicyFallback = simconfig.db.regions[0].satelliteTLogPolicyFallback; + g_simulator->satelliteTLogWriteAntiQuorumFallback = simconfig.db.regions[0].satelliteTLogWriteAntiQuorumFallback; } else { - g_simulator.satelliteTLogPolicyFallback = simconfig.db.regions[0].satelliteTLogPolicy; - g_simulator.satelliteTLogWriteAntiQuorumFallback = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum; + g_simulator->satelliteTLogPolicyFallback = simconfig.db.regions[0].satelliteTLogPolicy; + g_simulator->satelliteTLogWriteAntiQuorumFallback = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum; } - g_simulator.satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicy; - g_simulator.satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum; + g_simulator->satelliteTLogPolicy = simconfig.db.regions[0].satelliteTLogPolicy; + g_simulator->satelliteTLogWriteAntiQuorum = simconfig.db.regions[0].satelliteTLogWriteAntiQuorum; for (auto s : simconfig.db.regions[0].satellites) { - g_simulator.primarySatelliteDcIds.push_back(s.dcId); + g_simulator->primarySatelliteDcIds.push_back(s.dcId); } } else { - g_simulator.hasSatelliteReplication = false; - g_simulator.satelliteTLogWriteAntiQuorum = 0; + g_simulator->hasSatelliteReplication = false; + g_simulator->satelliteTLogWriteAntiQuorum = 0; } if (simconfig.db.regions.size() == 2) { - g_simulator.remoteDcId = simconfig.db.regions[1].dcId; + g_simulator->remoteDcId = simconfig.db.regions[1].dcId; ASSERT((!simconfig.db.regions[0].satelliteTLogPolicy && !simconfig.db.regions[1].satelliteTLogPolicy) || simconfig.db.regions[0].satelliteTLogPolicy->info() == simconfig.db.regions[1].satelliteTLogPolicy->info()); for (auto s : simconfig.db.regions[1].satellites) { - g_simulator.remoteSatelliteDcIds.push_back(s.dcId); + g_simulator->remoteSatelliteDcIds.push_back(s.dcId); } } - if (g_simulator.usableRegions < 2 || !g_simulator.hasSatelliteReplication) { - g_simulator.allowLogSetKills = false; + if (g_simulator->usableRegions < 2 || !g_simulator->hasSatelliteReplication) { + g_simulator->allowLogSetKills = false; } - ASSERT(g_simulator.storagePolicy && g_simulator.tLogPolicy); - ASSERT(!g_simulator.hasSatelliteReplication || g_simulator.satelliteTLogPolicy); + ASSERT(g_simulator->storagePolicy && g_simulator->tLogPolicy); + ASSERT(!g_simulator->hasSatelliteReplication || g_simulator->satelliteTLogPolicy); TraceEvent("SimulatorConfig").setMaxFieldLength(10000).detail("ConfigString", StringRef(startingConfigString)); const int dataCenters = simconfig.datacenters; @@ -2050,7 +2049,7 @@ void setupSimulatedSystem(std::vector>* systemActors, bool sslEnabled = deterministicRandom()->random01() < 0.10; bool sslOnly = sslEnabled && deterministicRandom()->coinflip(); bool isTLS = sslEnabled && sslOnly; - g_simulator.listenersPerProcess = sslEnabled && !sslOnly ? 2 : 1; + g_simulator->listenersPerProcess = sslEnabled && !sslOnly ? 2 : 1; CODE_PROBE(sslEnabled, "SSL enabled"); CODE_PROBE(!sslEnabled, "SSL disabled"); @@ -2192,10 +2191,10 @@ void setupSimulatedSystem(std::vector>* systemActors, TraceEvent("ProtectCoordinator") .detail("Address", coordinatorAddresses[i]) .detail("Coordinators", describe(coordinatorAddresses)); - g_simulator.protectedAddresses.insert(NetworkAddress( + g_simulator->protectedAddresses.insert(NetworkAddress( coordinatorAddresses[i].ip, coordinatorAddresses[i].port, true, coordinatorAddresses[i].isTLS())); if (coordinatorAddresses[i].port == 2) { - g_simulator.protectedAddresses.insert(NetworkAddress(coordinatorAddresses[i].ip, 1, true, true)); + g_simulator->protectedAddresses.insert(NetworkAddress(coordinatorAddresses[i].ip, 1, true, true)); } } deterministicRandom()->randomShuffle(coordinatorAddresses); @@ -2207,12 +2206,12 @@ void setupSimulatedSystem(std::vector>* systemActors, } if (useLocalDatabase) { - g_simulator.extraDatabases.push_back( + g_simulator->extraDatabases.push_back( useHostname ? ClusterConnectionString(coordinatorHostnames, "TestCluster:0"_sr).toString() : ClusterConnectionString(coordinatorAddresses, "TestCluster:0"_sr).toString()); } else if (testConfig.extraDatabaseMode != ISimulator::ExtraDatabaseMode::Disabled) { for (int i = 0; i < extraDatabaseCount; ++i) { - g_simulator.extraDatabases.push_back( + g_simulator->extraDatabases.push_back( useHostname ? ClusterConnectionString(extraCoordinatorHostnames[i], StringRef(format("ExtraCluster%04d:0", i))) .toString() @@ -2227,7 +2226,7 @@ void setupSimulatedSystem(std::vector>* systemActors, .detail("String", conn.toString()) .detail("ConfigString", startingConfigString); - bool requiresExtraDBMachines = !g_simulator.extraDatabases.empty() && !useLocalDatabase; + bool requiresExtraDBMachines = !g_simulator->extraDatabases.empty() && !useLocalDatabase; int assignedMachines = 0, nonVersatileMachines = 0; bool gradualMigrationPossible = true; std::vector processClassesSubSet = { ProcessClass::UnsetClass, @@ -2336,7 +2335,7 @@ void setupSimulatedSystem(std::vector>* systemActors, if (requiresExtraDBMachines) { int cluster = 4; - for (auto extraDatabase : g_simulator.extraDatabases) { + for (auto extraDatabase : g_simulator->extraDatabases) { std::vector extraIps; extraIps.reserve(processesPerMachine); for (int i = 0; i < processesPerMachine; i++) { @@ -2374,14 +2373,14 @@ void setupSimulatedSystem(std::vector>* systemActors, } } - g_simulator.desiredCoordinators = coordinatorCount; - g_simulator.physicalDatacenters = dataCenters; - g_simulator.processesPerMachine = processesPerMachine; + g_simulator->desiredCoordinators = coordinatorCount; + g_simulator->physicalDatacenters = dataCenters; + g_simulator->processesPerMachine = processesPerMachine; TraceEvent("SetupSimulatorSettings") - .detail("DesiredCoordinators", g_simulator.desiredCoordinators) - .detail("PhysicalDatacenters", g_simulator.physicalDatacenters) - .detail("ProcessesPerMachine", g_simulator.processesPerMachine); + .detail("DesiredCoordinators", g_simulator->desiredCoordinators) + .detail("PhysicalDatacenters", g_simulator->physicalDatacenters) + .detail("ProcessesPerMachine", g_simulator->processesPerMachine); // SOMEDAY: add locality for testers to simulate network topology // FIXME: Start workers with tester class instead, at least sometimes run tests with the testers-only flag @@ -2410,16 +2409,16 @@ void setupSimulatedSystem(std::vector>* systemActors, "SimulatedTesterMachine")); } - if (g_simulator.setDiffProtocol) { + if (g_simulator->setDiffProtocol) { --(*pTesterCount); } *pStartingConfiguration = startingConfigString; // save some state that we only need when restarting the simulator. - g_simulator.connectionString = conn.toString(); - g_simulator.testerCount = testerCount; - g_simulator.allowStorageMigrationTypeChange = gradualMigrationPossible; + g_simulator->connectionString = conn.toString(); + g_simulator->testerCount = testerCount; + g_simulator->allowStorageMigrationTypeChange = gradualMigrationPossible; TraceEvent("SimulatedClusterStarted") .detail("DataCenters", dataCenters) @@ -2457,14 +2456,14 @@ ACTOR void setupAndRun(std::string dataFolder, state TestConfig testConfig; state IPAllowList allowList; testConfig.readFromConfig(testFile); - g_simulator.hasDiffProtocolProcess = testConfig.startIncompatibleProcess; - g_simulator.setDiffProtocol = false; + g_simulator->hasDiffProtocolProcess = testConfig.startIncompatibleProcess; + g_simulator->setDiffProtocol = false; if (testConfig.injectTargetedSSRestart && deterministicRandom()->random01() < 0.25) { - g_simulator.injectTargetedSSRestartTime = 60.0 + 340.0 * deterministicRandom()->random01(); + g_simulator->injectTargetedSSRestartTime = 60.0 + 340.0 * deterministicRandom()->random01(); } if (testConfig.injectSSDelay && deterministicRandom()->random01() < 0.25) { - g_simulator.injectSSDelayTime = 60.0 + 240.0 * deterministicRandom()->random01(); + g_simulator->injectSSDelayTime = 60.0 + 240.0 * deterministicRandom()->random01(); } // Build simulator allow list @@ -2524,21 +2523,21 @@ ACTOR void setupAndRun(std::string dataFolder, // TODO (IPv6) Use IPv6? auto testSystem = - g_simulator.newProcess("TestSystem", - IPAddress(0x01010101), - 1, - false, - 1, - LocalityData(Optional>(), - Standalone(deterministicRandom()->randomUniqueID().toString()), - Standalone(deterministicRandom()->randomUniqueID().toString()), - Optional>()), - ProcessClass(ProcessClass::TesterClass, ProcessClass::CommandLineSource), - "", - "", - currentProtocolVersion()); + g_simulator->newProcess("TestSystem", + IPAddress(0x01010101), + 1, + false, + 1, + LocalityData(Optional>(), + Standalone(deterministicRandom()->randomUniqueID().toString()), + Standalone(deterministicRandom()->randomUniqueID().toString()), + Optional>()), + ProcessClass(ProcessClass::TesterClass, ProcessClass::CommandLineSource), + "", + "", + currentProtocolVersion()); testSystem->excludeFromRestarts = true; - wait(g_simulator.onProcess(testSystem, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(testSystem, TaskPriority::DefaultYield)); Sim2FileSystem::newFileSystem(); FlowTransport::createInstance(true, 1, WLTOKEN_RESERVED_COUNT, &allowList); CODE_PROBE(true, "Simulation start"); @@ -2621,7 +2620,7 @@ ACTOR void setupAndRun(std::string dataFolder, TraceEvent("TracingMissingCodeProbes").log(); probe::traceMissedProbes(probe::ExecutionContext::Simulation); TraceEvent("SimulatedSystemDestruct").log(); - g_simulator.stop(); + g_simulator->stop(); destructed = true; wait(Never()); ASSERT(false); diff --git a/fdbserver/SkipList.cpp b/fdbserver/SkipList.cpp index 180dc13dfa..3e4e31eeb5 100644 --- a/fdbserver/SkipList.cpp +++ b/fdbserver/SkipList.cpp @@ -1046,32 +1046,32 @@ void miniConflictSetTest() { void operatorLessThanTest() { { // Longer strings before shorter strings. - KeyInfo a(LiteralStringRef("hello"), /*begin=*/false, /*write=*/true, 0, nullptr); - KeyInfo b(LiteralStringRef("hello\0"), /*begin=*/false, /*write=*/false, 0, nullptr); + KeyInfo a("hello"_sr, /*begin=*/false, /*write=*/true, 0, nullptr); + KeyInfo b("hello\0"_sr, /*begin=*/false, /*write=*/false, 0, nullptr); ASSERT(a < b); ASSERT(!(b < a)); ASSERT(!(a == b)); } { // Reads before writes. - KeyInfo a(LiteralStringRef("hello"), /*begin=*/false, /*write=*/false, 0, nullptr); - KeyInfo b(LiteralStringRef("hello"), /*begin=*/false, /*write=*/true, 0, nullptr); + KeyInfo a("hello"_sr, /*begin=*/false, /*write=*/false, 0, nullptr); + KeyInfo b("hello"_sr, /*begin=*/false, /*write=*/true, 0, nullptr); ASSERT(a < b); ASSERT(!(b < a)); ASSERT(!(a == b)); } { // Begin reads after writes. - KeyInfo a(LiteralStringRef("hello"), /*begin=*/false, /*write=*/true, 0, nullptr); - KeyInfo b(LiteralStringRef("hello"), /*begin=*/true, /*write=*/false, 0, nullptr); + KeyInfo a("hello"_sr, /*begin=*/false, /*write=*/true, 0, nullptr); + KeyInfo b("hello"_sr, /*begin=*/true, /*write=*/false, 0, nullptr); ASSERT(a < b); ASSERT(!(b < a)); ASSERT(!(a == b)); } { // Begin writes after writes. - KeyInfo a(LiteralStringRef("hello"), /*begin=*/false, /*write=*/true, 0, nullptr); - KeyInfo b(LiteralStringRef("hello"), /*begin=*/true, /*write=*/true, 0, nullptr); + KeyInfo a("hello"_sr, /*begin=*/false, /*write=*/true, 0, nullptr); + KeyInfo b("hello"_sr, /*begin=*/true, /*write=*/true, 0, nullptr); ASSERT(a < b); ASSERT(!(b < a)); ASSERT(!(a == b)); diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index deb0aec396..0959d2315f 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -34,6 +34,7 @@ #include "fdbserver/ClusterRecovery.actor.h" #include "fdbserver/CoordinationInterface.h" #include "fdbserver/DataDistribution.actor.h" +#include "fdbclient/ConsistencyScanInterface.h" #include "flow/UnitTest.h" #include "fdbserver/QuietDatabase.h" #include "fdbserver/RecoveryState.h" @@ -809,6 +810,10 @@ ACTOR static Future processStatusFetcher( roles.addRole("blob_manager", db->get().blobManager.get()); } + if (db->get().consistencyScan.present()) { + roles.addRole("consistency_scan", db->get().consistencyScan.get()); + } + if (SERVER_KNOBS->ENABLE_ENCRYPTION && db->get().encryptKeyProxy.present()) { roles.addRole("encrypt_key_proxy", db->get().encryptKeyProxy.get()); } @@ -1264,7 +1269,7 @@ ACTOR static Future doReadProbe(Future grvProbe, Transaction* tr loop { tr->setOption(FDBTransactionOptions::LOCK_AWARE); try { - Optional> _ = wait(tr->get(LiteralStringRef("\xff/StatusJsonTestKey62793"))); + Optional> _ = wait(tr->get("\xff/StatusJsonTestKey62793"_sr)); return g_network->timer_monotonic() - start; } catch (Error& e) { wait(tr->onError(e)); @@ -1697,17 +1702,16 @@ ACTOR static Future dataStatusFetcher(WorkerDetails ddWorker, std::vector> futures; // TODO: Should this be serial? - futures.push_back(timeoutError( - ddWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("DDTrackerStarting"))), 1.0)); - futures.push_back(timeoutError( - ddWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("DDTrackerStats"))), 1.0)); - futures.push_back(timeoutError( - ddWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("MovingData"))), 1.0)); - futures.push_back(timeoutError( - ddWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("TotalDataInFlight"))), 1.0)); - futures.push_back(timeoutError( - ddWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("TotalDataInFlightRemote"))), - 1.0)); + futures.push_back( + timeoutError(ddWorker.interf.eventLogRequest.getReply(EventLogRequest("DDTrackerStarting"_sr)), 1.0)); + futures.push_back( + timeoutError(ddWorker.interf.eventLogRequest.getReply(EventLogRequest("DDTrackerStats"_sr)), 1.0)); + futures.push_back( + timeoutError(ddWorker.interf.eventLogRequest.getReply(EventLogRequest("MovingData"_sr)), 1.0)); + futures.push_back( + timeoutError(ddWorker.interf.eventLogRequest.getReply(EventLogRequest("TotalDataInFlight"_sr)), 1.0)); + futures.push_back( + timeoutError(ddWorker.interf.eventLogRequest.getReply(EventLogRequest("TotalDataInFlightRemote"_sr)), 1.0)); std::vector dataInfo = wait(getAll(futures)); @@ -2079,8 +2083,7 @@ ACTOR static Future workloadStatusFetcher( auto worker = getWorker(workersMap, p.address()); if (worker.present()) commitProxyStatFutures.push_back(timeoutError( - worker.get().interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("ProxyMetrics"))), - 1.0)); + worker.get().interf.eventLogRequest.getReply(EventLogRequest("ProxyMetrics"_sr)), 1.0)); else throw all_alternatives_failed(); // We need data from all proxies for this result to be trustworthy } @@ -2088,8 +2091,7 @@ ACTOR static Future workloadStatusFetcher( auto worker = getWorker(workersMap, p.address()); if (worker.present()) grvProxyStatFutures.push_back(timeoutError( - worker.get().interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("GrvProxyMetrics"))), - 1.0)); + worker.get().interf.eventLogRequest.getReply(EventLogRequest("GrvProxyMetrics"_sr)), 1.0)); else throw all_alternatives_failed(); // We need data from all proxies for this result to be trustworthy } @@ -2156,9 +2158,9 @@ ACTOR static Future workloadStatusFetcher( // Transactions try { state Future f1 = - timeoutError(rkWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("RkUpdate"))), 1.0); - state Future f2 = timeoutError( - rkWorker.interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("RkUpdateBatch"))), 1.0); + timeoutError(rkWorker.interf.eventLogRequest.getReply(EventLogRequest("RkUpdate"_sr)), 1.0); + state Future f2 = + timeoutError(rkWorker.interf.eventLogRequest.getReply(EventLogRequest("RkUpdateBatch"_sr)), 1.0); wait(success(f1) && success(f2)); TraceEventFields ratekeeper = f1.get(); TraceEventFields batchRatekeeper = f2.get(); @@ -2539,7 +2541,7 @@ static JsonBuilderObject faultToleranceStatusFetcher(DatabaseConfiguration confi std::map workerZones; for (const auto& worker : workers) { - workerZones[worker.interf.address()] = worker.interf.locality.zoneId().orDefault(LiteralStringRef("")); + workerZones[worker.interf.address()] = worker.interf.locality.zoneId().orDefault(""_sr); } std::map coordinatorZoneCounts; for (const auto& coordinator : coordinatorAddresses) { @@ -2871,6 +2873,26 @@ ACTOR Future storageWigglerStatsFetcher(Optional> consistencyScanInfoFetcher(Database cx) { + state Reference tr(new ReadYourWritesTransaction(cx)); + state Optional val; + loop { + try { + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + wait(store(val, ConsistencyScanInfo::getInfo(tr))); + wait(tr->commit()); + break; + } catch (Error& e) { + wait(tr->onError(e)); + } + } + + TraceEvent("ConsistencyScanInfoFetcher").log(); + return val.get(); +} + // constructs the cluster section of the json status output ACTOR Future clusterGetStatus( Reference> db, @@ -2890,6 +2912,7 @@ ACTOR Future clusterGetStatus( state WorkerDetails ccWorker; // Cluster-Controller worker state WorkerDetails ddWorker; // DataDistributor worker state WorkerDetails rkWorker; // Ratekeeper worker + state WorkerDetails csWorker; // ConsistencyScan worker try { // Get the master Worker interface @@ -2936,6 +2959,19 @@ ACTOR Future clusterGetStatus( rkWorker = _rkWorker.get(); } + // Get the ConsistencyScan worker interface + Optional _csWorker; + if (db->get().consistencyScan.present()) { + _csWorker = getWorker(workers, db->get().consistencyScan.get().address()); + } + + if (!db->get().consistencyScan.present() || !_csWorker.present()) { + messages.push_back(JsonString::makeMessage("unreachable_consistencyScan_worker", + "Unable to locate the consistencyScan worker.")); + } else { + csWorker = _csWorker.get(); + } + // Get latest events for various event types from ALL workers // WorkerEvents is a map of worker's NetworkAddress to its event string // The pair represents worker responses and a set of worker NetworkAddress strings which did not respond. @@ -3350,6 +3386,26 @@ ACTOR Future clusterGetStatus( messages.push_back(clientIssueMessage); } + // Fetch Consistency Scan Information + state Reference tr(new ReadYourWritesTransaction(cx)); + state Optional val; + loop { + try { + tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + wait(store(val, ConsistencyScanInfo::getInfo(tr))); + wait(tr->commit()); + break; + } catch (Error& e) { + wait(tr->onError(e)); + } + } + if (val.present()) { + ConsistencyScanInfo consistencyScanInfo = + ObjectReader::fromStringRef(val.get(), IncludeVersion()); + TraceEvent("StatusConsistencyScanGotVal").log(); + statusObj["consistency_scan_info"] = consistencyScanInfo.toJSON(); + } + // Create the status_incomplete message if there were any reasons that the status is incomplete. if (!status_incomplete_reasons.empty()) { JsonBuilderObject incomplete_message = diff --git a/fdbserver/StorageCache.actor.cpp b/fdbserver/StorageCache.actor.cpp index 7f1e950bc0..0ac15840d2 100644 --- a/fdbserver/StorageCache.actor.cpp +++ b/fdbserver/StorageCache.actor.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "fdbclient/BlobCipher.h" #include "fdbclient/FDBTypes.h" #include "fdbserver/OTELSpanContextMessage.h" #include "flow/Arena.h" @@ -247,9 +248,9 @@ public: lastTLogVersion(0), lastVersionWithData(0), peekVersion(0), compactionInProgress(Void()), fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_BYTES), debug_inApplyUpdate(false), debug_lastValidateTime(0), versionLag(0), behind(false), counters(this) { - version.initMetric(LiteralStringRef("StorageCacheData.Version"), counters.cc.id); - desiredOldestVersion.initMetric(LiteralStringRef("StorageCacheData.DesriedOldestVersion"), counters.cc.id); - oldestVersion.initMetric(LiteralStringRef("StorageCacheData.OldestVersion"), counters.cc.id); + version.initMetric("StorageCacheData.Version"_sr, counters.cc.id); + desiredOldestVersion.initMetric("StorageCacheData.DesriedOldestVersion"_sr, counters.cc.id); + oldestVersion.initMetric("StorageCacheData.OldestVersion"_sr, counters.cc.id); newestAvailableVersion.insert(allKeys, invalidVersion); newestDirtyVersion.insert(allKeys, invalidVersion); @@ -282,7 +283,7 @@ public: void checkChangeCounter(uint64_t oldCacheRangeChangeCounter, KeyRef const& key) { if (oldCacheRangeChangeCounter != cacheRangeChangeCounter && cachedRangeMap[key]->changeCounter > oldCacheRangeChangeCounter) { - CODE_PROBE(true, "CacheRange change during getValueQ"); + // CODE_PROBE(true, "CacheRange change during getValueQ"); // TODO: should we throw the cold_cache_server() error here instead? throw wrong_shard_server(); } @@ -293,8 +294,8 @@ public: auto sh = cachedRangeMap.intersectingRanges(keys); for (auto i = sh.begin(); i != sh.end(); ++i) if (i->value()->changeCounter > oldCacheRangeChangeCounter) { - CODE_PROBE(true, "CacheRange change during range operation"); - // TODO: should we throw the cold_cache_server() error here instead? + // CODE_PROBE(true, "CacheRange change during range operation"); + // TODO: should we throw the cold_cache_server() error here instead? throw wrong_shard_server(); } } @@ -516,9 +517,9 @@ ACTOR Future getValueQ(StorageCacheData* data, GetValueRequest req) { } // DEBUG_MUTATION("CacheGetValue", version, MutationRef(MutationRef::DebugKey, req.key, - // v.present()?v.get():LiteralStringRef(""))); DEBUG_MUTATION("CacheGetPath", version, + // v.present()?v.get():""_sr)); DEBUG_MUTATION("CacheGetPath", version, // MutationRef(MutationRef::DebugKey, req.key, - // path==0?LiteralStringRef("0"):path==1?LiteralStringRef("1"):LiteralStringRef("2"))); + // path==0?"0"_sr:path==1?"1"_sr:"2"_sr)); if (v.present()) { ++data->counters.rowsQueried; @@ -663,7 +664,7 @@ Key findKey(StorageCacheData* data, KeySelectorRef sel, Version version, KeyRang // If we get only one result in the reverse direction as a result of the data being too large, we could get stuck in // a loop if (more && !forward && rep.data.size() == 1) { - CODE_PROBE(true, "Reverse key selector returned only one result in range read"); + // CODE_PROBE(true, "Reverse key selector returned only one result in range read"); maxBytes = std::numeric_limits::max(); GetKeyValuesReply rep2 = readRange(data, version, KeyRangeRef(range.begin, keyAfter(sel.getKey())), -2, &maxBytes); @@ -686,7 +687,7 @@ Key findKey(StorageCacheData* data, KeySelectorRef sel, Version version, KeyRang *pOffset = -*pOffset; if (more) { - CODE_PROBE(true, "Key selector read range had more results"); + // CODE_PROBE(true, "Key selector read range had more results"); ASSERT(rep.data.size()); Key returnKey = forward ? keyAfter(rep.data.back().key) : rep.data.back().key; @@ -780,11 +781,11 @@ ACTOR Future getKeyValues(StorageCacheData* data, GetKeyValuesRequest req) // cachedKeyRange is the end the last actual key returned must be from this cachedKeyRange. A begin offset of 1 // is also OK because then either begin is past end or equal to end (so the result is definitely empty) if ((offset1 && offset1 != 1) || (offset2 && offset2 != 1)) { - CODE_PROBE(true, "wrong_cache_server due to offset"); - // We could detect when offset1 takes us off the beginning of the database or offset2 takes us off the end, - // and return a clipped range rather than an error (since that is what the NativeAPI.getRange will do anyway - // via its "slow path"), but we would have to add some flags to the response to encode whether we went off - // the beginning and the end, since it needs that information. + // CODE_PROBE(true, "wrong_cache_server due to offset"); + // We could detect when offset1 takes us off the beginning of the database or offset2 takes us off the end, + // and return a clipped range rather than an error (since that is what the NativeAPI.getRange will do + // anyway via its "slow path"), but we would have to add some flags to the response to encode whether we + // went off the beginning and the end, since it needs that information. //TraceEvent(SevDebug, "WrongCacheRangeServer2", data->thisServerID).detail("Begin", req.begin.toString()).detail("End", req.end.toString()).detail("Version", version). // detail("CacheRangeBegin", cachedKeyRange.begin).detail("CacheRangeEnd", cachedKeyRange.end).detail("In", // "getKeyValues>checkOffsets"). detail("BeginKey", begin).detail("EndKey", end).detail("BeginOffset", @@ -944,7 +945,7 @@ bool expandMutation(MutationRef& m, StorageCacheData::VersionedData const& data, if (it != data.atLatest().end() && it->isValue() && it.key() == m.param1) oldVal = it->getValue(); else if (it != data.atLatest().end() && it->isClearTo() && it->getEndKey() > m.param1) { - CODE_PROBE(true, "Atomic op right after a clear."); + // CODE_PROBE(true, "Atomic op right after a clear."); } switch (m.type) { @@ -1074,7 +1075,7 @@ void splitMutation(StorageCacheData* data, KeyRangeMap& map, MutationRef cons } void rollback(StorageCacheData* data, Version rollbackVersion, Version nextVersion) { - CODE_PROBE(true, "call to cacheRange rollback"); + // CODE_PROBE(true, "call to cacheRange rollback"); // FIXME: enable when debugKeyRange is active // debugKeyRange("Rollback", rollbackVersion, allKeys); @@ -1282,7 +1283,7 @@ ACTOR Future fetchKeys(StorageCacheData* data, AddingCacheRange* cacheRang lastAvailable = std::max(lastAvailable, r->value()); if (lastAvailable != invalidVersion && lastAvailable >= data->oldestVersion.get()) { - CODE_PROBE(true, "wait for oldest version"); + // CODE_PROBE(true, "wait for oldest version"); wait(data->oldestVersion.whenAtLeast(lastAvailable + 1)); } @@ -1321,7 +1322,7 @@ ACTOR Future fetchKeys(StorageCacheData* data, AddingCacheRange* cacheRang loop { try { - CODE_PROBE(true, "Fetching keys for transferred cacheRange"); + // CODE_PROBE(true, "Fetching keys for transferred cacheRange"); state RangeResult this_block = wait(tryFetchRange(data->cx, @@ -1385,7 +1386,7 @@ ACTOR Future fetchKeys(StorageCacheData* data, AddingCacheRange* cacheRang .suppressFor(1.0) .detail("FKID", interval.pairID); if (e.code() == error_code_transaction_too_old) { - CODE_PROBE(true, "A storage server has forgotten the history data we are fetching"); + // CODE_PROBE(true, "A storage server has forgotten the history data we are fetching"); Version lastFV = fetchVersion; fetchVersion = data->version.get(); isTooOld = false; @@ -1412,9 +1413,9 @@ ACTOR Future fetchKeys(StorageCacheData* data, AddingCacheRange* cacheRang .detail("E", data->version.get()); } } else if (e.code() == error_code_future_version || e.code() == error_code_process_behind) { - CODE_PROBE(true, - "fetchKeys got future_version or process_behind, so there must be a huge storage lag " - "somewhere. Keep trying."); + // CODE_PROBE(true, + // "fetchKeys got future_version or process_behind, so there must be a huge storage lag " + // "somewhere. Keep trying."); } else { throw; } @@ -1474,7 +1475,7 @@ ACTOR Future fetchKeys(StorageCacheData* data, AddingCacheRange* cacheRang } int startSize = batch->changes.size(); - CODE_PROBE(startSize, "Adding fetch data to a batch which already has changes"); + // CODE_PROBE(startSize, "Adding fetch data to a batch which already has changes"); batch->changes.resize(batch->changes.size() + cacheRange->updates.size()); // FIXME: pass the deque back rather than copy the data @@ -1637,7 +1638,7 @@ void cacheWarmup(StorageCacheData* data, const KeyRangeRef& keys, bool nowAssign else { ASSERT(ranges[i].value->adding); data->addCacheRange(CacheRangeInfo::newAdding(data, ranges[i])); - CODE_PROBE(true, "cacheWarmup reFetchKeys"); + // CODE_PROBE(true, "cacheWarmup reFetchKeys"); } } @@ -1776,7 +1777,7 @@ private: br >> rollbackVersion; if (rollbackVersion < fromVersion && rollbackVersion > data->oldestVersion.get()) { - CODE_PROBE(true, "CacheRangeApplyPrivateData cacheRange rollback"); + // CODE_PROBE(true, "CacheRangeApplyPrivateData cacheRange rollback"); TraceEvent(SevWarn, "Rollback", data->thisServerID) .detail("FromVersion", fromVersion) .detail("ToVersion", rollbackVersion) @@ -1801,7 +1802,7 @@ ACTOR Future compactCache(StorageCacheData* data) { loop { // TODO understand this, should we add delay here? // if (g_network->isSimulated()) { - // double endTime = g_simulator.checkDisabled(format("%s/compactCache", + // double endTime = g_simulator->checkDisabled(format("%s/compactCache", // data->thisServerID.toString().c_str())); if(endTime > now()) { wait(delay(endTime - now(), // TaskPriority::CompactCache)); // } @@ -1925,7 +1926,7 @@ ACTOR Future pullAsyncData(StorageCacheData* data) { cipherDetails.insert(header->cipherHeaderDetails); collectingCipherKeys = true; } else { - msg = msg.decrypt(cipherKeys.get(), cloneReader.arena()); + msg = msg.decrypt(cipherKeys.get(), cloneReader.arena(), BlobCipherMetrics::TLOG); } } if (!collectingCipherKeys) { @@ -1946,7 +1947,7 @@ ACTOR Future pullAsyncData(StorageCacheData* data) { if (collectingCipherKeys) { std::unordered_map> result = - wait(getEncryptCipherKeys(data->db, cipherDetails)); + wait(getEncryptCipherKeys(data->db, cipherDetails, BlobCipherMetrics::TLOG)); cipherKeys = result; collectingCipherKeys = false; } else { @@ -2014,14 +2015,14 @@ ACTOR Future pullAsyncData(StorageCacheData* data) { SpanContextMessage scm; reader >> scm; } else if (reader.protocolVersion().hasOTELSpanContext() && OTELSpanContextMessage::isNextIn(reader)) { - CODE_PROBE(true, "StorageCache reading OTELSpanContextMessage"); + // CODE_PROBE(true, "StorageCache reading OTELSpanContextMessage"); OTELSpanContextMessage oscm; reader >> oscm; } else { MutationRef msg; reader >> msg; if (msg.isEncrypted()) { - msg = msg.decrypt(cipherKeys.get(), reader.arena()); + msg = msg.decrypt(cipherKeys.get(), reader.arena(), BlobCipherMetrics::TLOG); } if (ver != invalidVersion) // This change belongs to a version < minVersion diff --git a/fdbserver/StorageMetrics.actor.cpp b/fdbserver/StorageMetrics.actor.cpp index c3afabf924..16efb5411a 100644 --- a/fdbserver/StorageMetrics.actor.cpp +++ b/fdbserver/StorageMetrics.actor.cpp @@ -555,18 +555,18 @@ int64_t TransientStorageMetricSample::add(KeyRef key, int64_t metric) { TEST_CASE("/fdbserver/StorageMetricSample/simple") { StorageMetricSample s(1000); - s.sample.insert(LiteralStringRef("Apple"), 1000); - s.sample.insert(LiteralStringRef("Banana"), 2000); - s.sample.insert(LiteralStringRef("Cat"), 1000); - s.sample.insert(LiteralStringRef("Cathode"), 1000); - s.sample.insert(LiteralStringRef("Dog"), 1000); + s.sample.insert("Apple"_sr, 1000); + s.sample.insert("Banana"_sr, 2000); + s.sample.insert("Cat"_sr, 1000); + s.sample.insert("Cathode"_sr, 1000); + s.sample.insert("Dog"_sr, 1000); - ASSERT(s.getEstimate(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("D"))) == 5000); - ASSERT(s.getEstimate(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("E"))) == 6000); - ASSERT(s.getEstimate(KeyRangeRef(LiteralStringRef("B"), LiteralStringRef("C"))) == 2000); + ASSERT(s.getEstimate(KeyRangeRef("A"_sr, "D"_sr)) == 5000); + ASSERT(s.getEstimate(KeyRangeRef("A"_sr, "E"_sr)) == 6000); + ASSERT(s.getEstimate(KeyRangeRef("B"_sr, "C"_sr)) == 2000); - // ASSERT(s.splitEstimate(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("D")), 3500) == - // LiteralStringRef("Cat")); + // ASSERT(s.splitEstimate(KeyRangeRef("A"_sr, "D"_sr), 3500) == + // "Cat"_sr); return Void(); } @@ -576,19 +576,18 @@ TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/simple") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.byteSample.sample.insert(LiteralStringRef("A"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 800 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 800 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); - std::vector t = ssm.getSplitPoints( - KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("C")), 2000 * sampleUnit, Optional()); + std::vector t = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 2000 * sampleUnit, Optional()); - ASSERT(t.size() == 1 && t[0] == LiteralStringRef("Bah")); + ASSERT(t.size() == 1 && t[0] == "Bah"_sr); return Void(); } @@ -598,20 +597,18 @@ TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/multipleReturnedPoint int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.byteSample.sample.insert(LiteralStringRef("A"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 800 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 800 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); - std::vector t = ssm.getSplitPoints( - KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("C")), 600 * sampleUnit, Optional()); + std::vector t = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 600 * sampleUnit, Optional()); - ASSERT(t.size() == 3 && t[0] == LiteralStringRef("Absolute") && t[1] == LiteralStringRef("Apple") && - t[2] == LiteralStringRef("Bah")); + ASSERT(t.size() == 3 && t[0] == "Absolute"_sr && t[1] == "Apple"_sr && t[2] == "Bah"_sr); return Void(); } @@ -621,17 +618,16 @@ TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/noneSplitable") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.byteSample.sample.insert(LiteralStringRef("A"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 800 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 800 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); - std::vector t = ssm.getSplitPoints( - KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("C")), 10000 * sampleUnit, Optional()); + std::vector t = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 10000 * sampleUnit, Optional()); ASSERT(t.size() == 0); @@ -643,17 +639,16 @@ TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/chunkTooLarge") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.byteSample.sample.insert(LiteralStringRef("A"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 10 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 10 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 30 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 10 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 10 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 30 * sampleUnit); - std::vector t = ssm.getSplitPoints( - KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("C")), 1000 * sampleUnit, Optional()); + std::vector t = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 1000 * sampleUnit, Optional()); ASSERT(t.size() == 0); @@ -665,26 +660,25 @@ TEST_CASE("/fdbserver/StorageMetricSample/readHotDetect/simple") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.bytesReadSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Banana"), 2000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cat"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cathode"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Dog"), 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Banana"_sr, 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cat"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cathode"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Dog"_sr, 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("A"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); std::vector t = - ssm.getReadHotRanges(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("C")), 2.0, 200 * sampleUnit, 0); + ssm.getReadHotRanges(KeyRangeRef("A"_sr, "C"_sr), 2.0, 200 * sampleUnit, 0); - ASSERT(t.size() == 1 && (*t.begin()).keys.begin == LiteralStringRef("Bah") && - (*t.begin()).keys.end == LiteralStringRef("Bob")); + ASSERT(t.size() == 1 && (*t.begin()).keys.begin == "Bah"_sr && (*t.begin()).keys.end == "Bob"_sr); return Void(); } @@ -694,29 +688,28 @@ TEST_CASE("/fdbserver/StorageMetricSample/readHotDetect/moreThanOneRange") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.bytesReadSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Banana"), 2000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cat"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cathode"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Dog"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Final"), 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Banana"_sr, 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cat"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cathode"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Dog"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Final"_sr, 2000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("A"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Dah"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); + ssm.byteSample.sample.insert("Dah"_sr, 300 * sampleUnit); std::vector t = - ssm.getReadHotRanges(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("D")), 2.0, 200 * sampleUnit, 0); + ssm.getReadHotRanges(KeyRangeRef("A"_sr, "D"_sr), 2.0, 200 * sampleUnit, 0); - ASSERT(t.size() == 2 && (*t.begin()).keys.begin == LiteralStringRef("Bah") && - (*t.begin()).keys.end == LiteralStringRef("Bob")); - ASSERT(t.at(1).keys.begin == LiteralStringRef("Cat") && t.at(1).keys.end == LiteralStringRef("Dah")); + ASSERT(t.size() == 2 && (*t.begin()).keys.begin == "Bah"_sr && (*t.begin()).keys.end == "Bob"_sr); + ASSERT(t.at(1).keys.begin == "Cat"_sr && t.at(1).keys.end == "Dah"_sr); return Void(); } @@ -726,30 +719,29 @@ TEST_CASE("/fdbserver/StorageMetricSample/readHotDetect/consecutiveRanges") { int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE; StorageServerMetrics ssm; - ssm.bytesReadSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Banana"), 2000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Bucket"), 2000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cat"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Cathode"), 1000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Dog"), 5000 * sampleUnit); - ssm.bytesReadSample.sample.insert(LiteralStringRef("Final"), 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Banana"_sr, 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Bucket"_sr, 2000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cat"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Cathode"_sr, 1000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Dog"_sr, 5000 * sampleUnit); + ssm.bytesReadSample.sample.insert("Final"_sr, 2000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("A"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Absolute"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Apple"), 1000 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bah"), 20 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Banana"), 80 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Bob"), 200 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("But"), 100 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Cat"), 300 * sampleUnit); - ssm.byteSample.sample.insert(LiteralStringRef("Dah"), 300 * sampleUnit); + ssm.byteSample.sample.insert("A"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Absolute"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit); + ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit); + ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit); + ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit); + ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit); + ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit); + ssm.byteSample.sample.insert("Dah"_sr, 300 * sampleUnit); std::vector t = - ssm.getReadHotRanges(KeyRangeRef(LiteralStringRef("A"), LiteralStringRef("D")), 2.0, 200 * sampleUnit, 0); + ssm.getReadHotRanges(KeyRangeRef("A"_sr, "D"_sr), 2.0, 200 * sampleUnit, 0); - ASSERT(t.size() == 2 && (*t.begin()).keys.begin == LiteralStringRef("Bah") && - (*t.begin()).keys.end == LiteralStringRef("But")); - ASSERT(t.at(1).keys.begin == LiteralStringRef("Cat") && t.at(1).keys.end == LiteralStringRef("Dah")); + ASSERT(t.size() == 2 && (*t.begin()).keys.begin == "Bah"_sr && (*t.begin()).keys.end == "But"_sr); + ASSERT(t.at(1).keys.begin == "Cat"_sr && t.at(1).keys.end == "Dah"_sr); return Void(); } diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index fadb717ff1..c79bc38cbd 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -200,32 +200,24 @@ private: // Immutable keys // persistFormat has been mostly invalidated by TLogVersion, and can probably be removed when // 4.6's TLog code is removed. -static const KeyValueRef persistFormat(LiteralStringRef("Format"), LiteralStringRef("FoundationDB/LogServer/3/0")); -static const KeyRangeRef persistFormatReadableRange(LiteralStringRef("FoundationDB/LogServer/3/0"), - LiteralStringRef("FoundationDB/LogServer/4/0")); -static const KeyRangeRef persistProtocolVersionKeys(LiteralStringRef("ProtocolVersion/"), - LiteralStringRef("ProtocolVersion0")); -static const KeyRangeRef persistTLogSpillTypeKeys(LiteralStringRef("TLogSpillType/"), - LiteralStringRef("TLogSpillType0")); -static const KeyRangeRef persistRecoveryCountKeys = - KeyRangeRef(LiteralStringRef("DbRecoveryCount/"), LiteralStringRef("DbRecoveryCount0")); +static const KeyValueRef persistFormat("Format"_sr, "FoundationDB/LogServer/3/0"_sr); +static const KeyRangeRef persistFormatReadableRange("FoundationDB/LogServer/3/0"_sr, "FoundationDB/LogServer/4/0"_sr); +static const KeyRangeRef persistProtocolVersionKeys("ProtocolVersion/"_sr, "ProtocolVersion0"_sr); +static const KeyRangeRef persistTLogSpillTypeKeys("TLogSpillType/"_sr, "TLogSpillType0"_sr); +static const KeyRangeRef persistRecoveryCountKeys = KeyRangeRef("DbRecoveryCount/"_sr, "DbRecoveryCount0"_sr); // Updated on updatePersistentData() -static const KeyRangeRef persistCurrentVersionKeys = - KeyRangeRef(LiteralStringRef("version/"), LiteralStringRef("version0")); -static const KeyRangeRef persistKnownCommittedVersionKeys = - KeyRangeRef(LiteralStringRef("knownCommitted/"), LiteralStringRef("knownCommitted0")); -static const KeyRef persistRecoveryLocationKey = KeyRef(LiteralStringRef("recoveryLocation")); -static const KeyRangeRef persistLocalityKeys = - KeyRangeRef(LiteralStringRef("Locality/"), LiteralStringRef("Locality0")); -static const KeyRangeRef persistLogRouterTagsKeys = - KeyRangeRef(LiteralStringRef("LogRouterTags/"), LiteralStringRef("LogRouterTags0")); -static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef(LiteralStringRef("TxsTags/"), LiteralStringRef("TxsTags0")); -static const KeyRange persistTagMessagesKeys = prefixRange(LiteralStringRef("TagMsg/")); -static const KeyRange persistTagMessageRefsKeys = prefixRange(LiteralStringRef("TagMsgRef/")); -static const KeyRange persistTagPoppedKeys = prefixRange(LiteralStringRef("TagPop/")); +static const KeyRangeRef persistCurrentVersionKeys = KeyRangeRef("version/"_sr, "version0"_sr); +static const KeyRangeRef persistKnownCommittedVersionKeys = KeyRangeRef("knownCommitted/"_sr, "knownCommitted0"_sr); +static const KeyRef persistRecoveryLocationKey = KeyRef("recoveryLocation"_sr); +static const KeyRangeRef persistLocalityKeys = KeyRangeRef("Locality/"_sr, "Locality0"_sr); +static const KeyRangeRef persistLogRouterTagsKeys = KeyRangeRef("LogRouterTags/"_sr, "LogRouterTags0"_sr); +static const KeyRangeRef persistTxsTagsKeys = KeyRangeRef("TxsTags/"_sr, "TxsTags0"_sr); +static const KeyRange persistTagMessagesKeys = prefixRange("TagMsg/"_sr); +static const KeyRange persistTagMessageRefsKeys = prefixRange("TagMsgRef/"_sr); +static const KeyRange persistTagPoppedKeys = prefixRange("TagPop/"_sr); -static const KeyRef persistClusterIdKey = LiteralStringRef("clusterId"); +static const KeyRef persistClusterIdKey = "clusterId"_sr; static Key persistTagMessagesKey(UID id, Tag tag, Version version) { BinaryWriter wr(Unversioned()); @@ -386,9 +378,8 @@ struct TLogData : NonCopyable { targetVolatileBytes(SERVER_KNOBS->TLOG_SPILL_THRESHOLD), overheadBytesInput(0), overheadBytesDurable(0), peekMemoryLimiter(SERVER_KNOBS->TLOG_SPILL_REFERENCE_MAX_PEEK_MEMORY_BYTES), concurrentLogRouterReads(SERVER_KNOBS->CONCURRENT_LOG_ROUTER_READS), ignorePopDeadline(0), dataFolder(folder), - degraded(degraded), commitLatencyDist(Histogram::getHistogram(LiteralStringRef("tLog"), - LiteralStringRef("commit"), - Histogram::Unit::microseconds)) { + degraded(degraded), + commitLatencyDist(Histogram::getHistogram("tLog"_sr, "commit"_sr, Histogram::Unit::microseconds)) { cx = openDBOnServer(dbInfo, TaskPriority::DefaultEndpoint, LockAware::True); } }; @@ -661,10 +652,10 @@ struct LogData : NonCopyable, public ReferenceCounted { context); addActor.send(traceRole(Role::TRANSACTION_LOG, interf.id())); - persistentDataVersion.init(LiteralStringRef("TLog.PersistentDataVersion"), cc.id); - persistentDataDurableVersion.init(LiteralStringRef("TLog.PersistentDataDurableVersion"), cc.id); - version.initMetric(LiteralStringRef("TLog.Version"), cc.id); - queueCommittedVersion.initMetric(LiteralStringRef("TLog.QueueCommittedVersion"), cc.id); + persistentDataVersion.init("TLog.PersistentDataVersion"_sr, cc.id); + persistentDataDurableVersion.init("TLog.PersistentDataDurableVersion"_sr, cc.id); + version.initMetric("TLog.Version"_sr, cc.id); + queueCommittedVersion.initMetric("TLog.QueueCommittedVersion"_sr, cc.id); specialCounter(cc, "Version", [this]() { return this->version.get(); }); specialCounter(cc, "QueueCommittedVersion", [this]() { return this->queueCommittedVersion.get(); }); @@ -2161,7 +2152,7 @@ ACTOR Future doQueueCommit(TLogData* self, wait(ioDegradedOrTimeoutError( c, SERVER_KNOBS->MAX_STORAGE_COMMIT_TIME, self->degraded, SERVER_KNOBS->TLOG_DEGRADED_DURATION)); - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && BUGGIFY_WITH_PROB(0.0001)) { wait(delay(6.0)); } wait(self->queueCommitEnd.whenAtLeast(commitNumber - 1)); @@ -2624,6 +2615,27 @@ ACTOR Future tLogEnablePopReq(TLogEnablePopRequest enablePopReq, TLogData* return Void(); } +ACTOR Future updateDurableClusterID(TLogData* self) { + loop { + // Persist cluster ID once cluster has recovered. + if (self->dbInfo->get().recoveryState == RecoveryState::FULLY_RECOVERED) { + ASSERT(!self->durableClusterId.isValid()); + state UID ccClusterId = self->dbInfo->get().client.clusterId; + self->durableClusterId = ccClusterId; + ASSERT(ccClusterId.isValid()); + + wait(self->persistentDataCommitLock.take()); + state FlowLock::Releaser commitLockReleaser(self->persistentDataCommitLock); + self->persistentData->set( + KeyValueRef(persistClusterIdKey, BinaryWriter::toValue(ccClusterId, Unversioned()))); + wait(self->persistentData->commit()); + + return Void(); + } + wait(self->dbInfo->onChange()); + } +} + ACTOR Future serveTLogInterface(TLogData* self, TLogInterface tli, Reference logData, @@ -2658,17 +2670,6 @@ ACTOR Future serveTLogInterface(TLogData* self, } else { logData->logSystem->set(Reference()); } - - // Persist cluster ID once cluster has recovered. - auto ccClusterId = self->dbInfo->get().client.clusterId; - if (self->dbInfo->get().recoveryState == RecoveryState::FULLY_RECOVERED && - !self->durableClusterId.isValid()) { - ASSERT(ccClusterId.isValid()); - self->durableClusterId = ccClusterId; - self->persistentData->set( - KeyValueRef(persistClusterIdKey, BinaryWriter::toValue(ccClusterId, Unversioned()))); - wait(self->persistentData->commit()); - } } when(TLogPeekStreamRequest req = waitNext(tli.peekStreamMessages.getFuture())) { TraceEvent(SevDebug, "TLogPeekStream", logData->logId) @@ -3054,7 +3055,7 @@ ACTOR Future restorePersistentState(TLogData* self, } if (!fFormat.get().present()) { - RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), LiteralStringRef("\xff")), 1)); + RangeResult v = wait(self->persistentData->readRange(KeyRangeRef(StringRef(), "\xff"_sr), 1)); if (!v.size()) { CODE_PROBE(true, "The DB is completely empty, so it was never initialized. Delete it."); throw worker_removed(); @@ -3068,7 +3069,7 @@ ACTOR Future restorePersistentState(TLogData* self, state std::vector>> removed; - ASSERT(fFormat.get().get() == LiteralStringRef("FoundationDB/LogServer/3/0")); + ASSERT(fFormat.get().get() == "FoundationDB/LogServer/3/0"_sr); ASSERT(fVers.get().size() == fRecoverCounts.get().size()); @@ -3592,6 +3593,9 @@ ACTOR Future tLog(IKeyValueStore* persistentData, if (recovered.canBeSet()) recovered.send(Void()); + // if (!self.durableClusterId.isValid()) { + // self.sharedActors.send(updateDurableClusterID(&self)); + // } self.sharedActors.send(commitQueue(&self)); self.sharedActors.send(updateStorageLoop(&self)); self.sharedActors.send(traceRole(Role::SHARED_TRANSACTION_LOG, tlogId)); @@ -3600,21 +3604,6 @@ ACTOR Future tLog(IKeyValueStore* persistentData, loop { choose { when(state InitializeTLogRequest req = waitNext(tlogRequests.getFuture())) { - ASSERT(req.clusterId.isValid()); - // Durably persist the cluster ID if it is not already - // durable and the cluster has progressed far enough - // through recovery. To avoid different partitions from - // persisting different cluster IDs, we need to wait - // until a single cluster ID has been persisted in the - // txnStateStore before finally writing it to disk. - auto recoveryState = self.dbInfo->get().recoveryState; - if (!self.durableClusterId.isValid() && recoveryState >= RecoveryState::ACCEPTING_COMMITS) { - self.durableClusterId = req.clusterId; - // Will let commit loop durably write the cluster ID. - self.persistentData->set( - KeyValueRef(persistClusterIdKey, BinaryWriter::toValue(req.clusterId, Unversioned()))); - } - if (!self.tlogCache.exists(req.recruitmentID)) { self.tlogCache.set(req.recruitmentID, req.reply.getFuture()); self.sharedActors.send( diff --git a/fdbserver/TagPartitionedLogSystem.actor.cpp b/fdbserver/TagPartitionedLogSystem.actor.cpp index 27455a99d3..5b34c5b68e 100644 --- a/fdbserver/TagPartitionedLogSystem.actor.cpp +++ b/fdbserver/TagPartitionedLogSystem.actor.cpp @@ -3077,7 +3077,7 @@ ACTOR Future> TagPartitionedLogSystem::newEpoch( // Don't force failure of recovery if it took us a long time to recover. This avoids multiple long running // recoveries causing tests to timeout - if (BUGGIFY && now() - startTime < 300 && g_network->isSimulated() && g_simulator.speedUpSimulation) + if (BUGGIFY && now() - startTime < 300 && g_network->isSimulated() && g_simulator->speedUpSimulation) throw cluster_recovery_failed(); for (int i = 0; i < logSystem->tLogs[0]->logServers.size(); i++) diff --git a/fdbserver/TagThrottler.actor.cpp b/fdbserver/TagThrottler.actor.cpp index 93332bb310..ff22c224a2 100644 --- a/fdbserver/TagThrottler.actor.cpp +++ b/fdbserver/TagThrottler.actor.cpp @@ -52,15 +52,13 @@ class TagThrottlerImpl { wait(success(throttledTagKeys) && success(autoThrottlingEnabled)); - if (autoThrottlingEnabled.get().present() && - autoThrottlingEnabled.get().get() == LiteralStringRef("0")) { + if (autoThrottlingEnabled.get().present() && autoThrottlingEnabled.get().get() == "0"_sr) { CODE_PROBE(true, "Auto-throttling disabled"); if (self->autoThrottlingEnabled) { TraceEvent("AutoTagThrottlingDisabled", self->id).log(); } self->autoThrottlingEnabled = false; - } else if (autoThrottlingEnabled.get().present() && - autoThrottlingEnabled.get().get() == LiteralStringRef("1")) { + } else if (autoThrottlingEnabled.get().present() && autoThrottlingEnabled.get().get() == "1"_sr) { CODE_PROBE(true, "Auto-throttling enabled"); if (!self->autoThrottlingEnabled) { TraceEvent("AutoTagThrottlingEnabled", self->id).log(); diff --git a/fdbserver/VFSAsync.cpp b/fdbserver/VFSAsync.cpp index 9ea9007203..36aa65588e 100644 --- a/fdbserver/VFSAsync.cpp +++ b/fdbserver/VFSAsync.cpp @@ -731,7 +731,7 @@ static int asyncSleep(sqlite3_vfs* pVfs, int microseconds) { try { Future simCancel = Never(); if (g_network->isSimulated()) - simCancel = success(g_simulator.getCurrentProcess()->shutdownSignal.getFuture()); + simCancel = success(g_simulator->getCurrentProcess()->shutdownSignal.getFuture()); if (simCancel.isReady()) { waitFor(delay(FLOW_KNOBS->MAX_BUGGIFIED_DELAY)); return 0; diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 16b836bf40..ccf2329314 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2192,7 +2192,7 @@ public: int64_t remapCleanupWindowBytes, int concurrentExtentReads, bool memoryOnly, - Reference keyProvider, + Reference keyProvider, Promise errorPromise = {}) : keyProvider(keyProvider), ioLock(FLOW_KNOBS->MAX_OUTSTANDING, ioMaxPriority, FLOW_KNOBS->MAX_OUTSTANDING / 2), pageCacheBytes(pageCacheSizeBytes), desiredPageSize(desiredPageSize), desiredExtentSize(desiredExtentSize), @@ -2974,7 +2974,7 @@ public: try { page->postReadHeader(pageID); if (page->isEncrypted()) { - EncryptionKey k = wait(self->keyProvider->getSecrets(page->encryptionKey)); + ArenaPage::EncryptionKey k = wait(self->keyProvider->getEncryptionKey(page->getEncodingHeader())); page->encryptionKey = k; } page->postReadPayload(pageID); @@ -2984,7 +2984,7 @@ public: page->rawData()); } catch (Error& e) { Error err = e; - if (g_network->isSimulated() && g_simulator.checkInjectedCorruption()) { + if (g_network->isSimulated() && g_simulator->checkInjectedCorruption()) { err = err.asInjectedFault(); } @@ -3042,7 +3042,7 @@ public: try { page->postReadHeader(pageIDs.front()); if (page->isEncrypted()) { - EncryptionKey k = wait(self->keyProvider->getSecrets(page->encryptionKey)); + ArenaPage::EncryptionKey k = wait(self->keyProvider->getEncryptionKey(page->getEncodingHeader())); page->encryptionKey = k; } page->postReadPayload(pageIDs.front()); @@ -3955,7 +3955,7 @@ private: int physicalExtentSize; int pagesPerExtent; - Reference keyProvider; + Reference keyProvider; PriorityMultiLock ioLock; @@ -4781,7 +4781,7 @@ struct DecodeBoundaryVerifier { static DecodeBoundaryVerifier* getVerifier(std::string name) { static std::map verifiers; // Only use verifier in a non-restarted simulation so that all page writes are captured - if (g_network->isSimulated() && !g_simulator.restarted) { + if (g_network->isSimulated() && !g_simulator->restarted) { return &verifiers[name]; } return nullptr; @@ -5036,7 +5036,7 @@ public: VersionedBTree(IPager2* pager, std::string name, EncodingType defaultEncodingType, - Reference keyProvider) + Reference keyProvider) : m_pager(pager), m_encodingType(defaultEncodingType), m_enforceEncodingType(false), m_keyProvider(keyProvider), m_pBuffer(nullptr), m_mutationCount(0), m_name(name) { @@ -5064,7 +5064,7 @@ public: state Reference page = self->m_pager->newPageBuffer(); page->init(self->m_encodingType, PageType::BTreeNode, 1); if (page->isEncrypted()) { - EncryptionKey k = wait(self->m_keyProvider->getByRange(dbBegin.key, dbEnd.key)); + ArenaPage::EncryptionKey k = wait(self->m_keyProvider->getLatestDefaultEncryptionKey()); page->encryptionKey = k; } @@ -5543,7 +5543,7 @@ private: IPager2* m_pager; EncodingType m_encodingType; bool m_enforceEncodingType; - Reference m_keyProvider; + Reference m_keyProvider; // Counter to update with DecodeCache memory usage int64_t* m_pDecodeCacheMemory = nullptr; @@ -5843,7 +5843,7 @@ private: (pagesToBuild[pageIndex].blockCount == 1) ? PageType::BTreeNode : PageType::BTreeSuperNode, height); if (page->isEncrypted()) { - EncryptionKey k = wait(self->m_keyProvider->getByRange(pageLowerBound.key, pageUpperBound.key)); + ArenaPage::EncryptionKey k = wait(self->m_keyProvider->getLatestDefaultEncryptionKey()); page->encryptionKey = k; } @@ -7689,12 +7689,12 @@ public: #include "fdbserver/art_impl.h" -RedwoodRecordRef VersionedBTree::dbBegin(LiteralStringRef("")); -RedwoodRecordRef VersionedBTree::dbEnd(LiteralStringRef("\xff\xff\xff\xff\xff")); +RedwoodRecordRef VersionedBTree::dbBegin(""_sr); +RedwoodRecordRef VersionedBTree::dbEnd("\xff\xff\xff\xff\xff"_sr); class KeyValueStoreRedwood : public IKeyValueStore { public: - KeyValueStoreRedwood(std::string filename, UID logID, Reference encryptionKeyProvider) + KeyValueStoreRedwood(std::string filename, UID logID, Reference encryptionKeyProvider) : m_filename(filename), m_concurrentReads(SERVER_KNOBS->REDWOOD_KVSTORE_CONCURRENT_READS, 0), prefetch(SERVER_KNOBS->REDWOOD_KVSTORE_RANGE_PREFETCH) { @@ -7723,7 +7723,7 @@ 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->shouldEnableEncryption()) { + if (encryptionKeyProvider && encryptionKeyProvider->enableEncryption()) { encodingType = EncodingType::AESEncryptionV1; m_keyProvider = encryptionKeyProvider; } @@ -8020,7 +8020,7 @@ private: PriorityMultiLock m_concurrentReads; bool prefetch; Version m_nextCommitVersion; - Reference m_keyProvider; + Reference m_keyProvider; Future m_lastCommit = Void(); template @@ -8031,7 +8031,7 @@ private: IKeyValueStore* keyValueStoreRedwoodV1(std::string const& filename, UID logID, - Reference encryptionKeyProvider) { + Reference encryptionKeyProvider) { return new KeyValueStoreRedwood(filename, logID, encryptionKeyProvider); } @@ -8349,8 +8349,8 @@ ACTOR Future verify(VersionedBTree* btree, wait(btree->initBTreeCursor(&cur, v, PagerEventReasons::RangeRead)); debug_printf("Verifying entire key range at version %" PRId64 "\n", v); - state Future fRangeAll = verifyRangeBTreeCursor( - btree, LiteralStringRef(""), LiteralStringRef("\xff\xff"), v, written, pRecordsRead); + state Future fRangeAll = + verifyRangeBTreeCursor(btree, ""_sr, "\xff\xff"_sr, v, written, pRecordsRead); if (serial) { wait(fRangeAll); } @@ -8675,35 +8675,26 @@ TEST_CASE("/redwood/correctness/unit/RedwoodRecordRef") { ASSERT(r2.getChildPage().begin() != id.begin()); } - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abc"), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef("abc"_sr, ""_sr), RedwoodRecordRef("abc"_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abcd"), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef("abc"_sr, ""_sr), RedwoodRecordRef("abcd"_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef("abcd"), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abc"), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef("abcd"_sr, ""_sr), RedwoodRecordRef("abc"_sr, ""_sr)); deltaTest(RedwoodRecordRef(std::string(300, 'k'), std::string(1e6, 'v')), - RedwoodRecordRef(std::string(300, 'k'), LiteralStringRef(""))); + RedwoodRecordRef(std::string(300, 'k'), ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); - deltaTest(RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), LiteralStringRef(""))); + deltaTest(RedwoodRecordRef(""_sr, ""_sr), RedwoodRecordRef(""_sr, ""_sr)); Arena mem; double start; @@ -8740,8 +8731,8 @@ TEST_CASE("/redwood/correctness/unit/RedwoodRecordRef") { RedwoodRecordRef rec1; RedwoodRecordRef rec2; - rec1.key = LiteralStringRef("alksdfjaklsdfjlkasdjflkasdjfklajsdflk;ajsdflkajdsflkjadsf1"); - rec2.key = LiteralStringRef("alksdfjaklsdfjlkasdjflkasdjfklajsdflk;ajsdflkajdsflkjadsf234"); + rec1.key = "alksdfjaklsdfjlkasdjflkasdjfklajsdflk;ajsdflkajdsflkjadsf1"_sr; + rec2.key = "alksdfjaklsdfjlkasdjflkasdjfklajsdflk;ajsdflkajdsflkjadsf234"_sr; start = timer(); total = 0; @@ -8807,7 +8798,7 @@ TEST_CASE("Lredwood/correctness/unit/deltaTree/RedwoodRecordRef") { const int N = deterministicRandom()->randomInt(200, 1000); RedwoodRecordRef prev; - RedwoodRecordRef next(LiteralStringRef("\xff\xff\xff\xff")); + RedwoodRecordRef next("\xff\xff\xff\xff"_sr); Arena arena; std::set uniqueItems; @@ -8984,7 +8975,7 @@ TEST_CASE("Lredwood/correctness/unit/deltaTree/RedwoodRecordRef2") { const int N = deterministicRandom()->randomInt(200, 1000); RedwoodRecordRef prev; - RedwoodRecordRef next(LiteralStringRef("\xff\xff\xff\xff")); + RedwoodRecordRef next("\xff\xff\xff\xff"_sr); Arena arena; std::set uniqueItems; @@ -9814,7 +9805,7 @@ TEST_CASE("Lredwood/correctness/btree") { state EncodingType encodingType = static_cast(deterministicRandom()->randomInt(0, EncodingType::MAX_ENCODING_TYPE)); - state Reference keyProvider; + state Reference keyProvider; if (encodingType == EncodingType::AESEncryptionV1) { keyProvider = makeReference(); } else if (encodingType == EncodingType::XOREncryption_TestOnly) { @@ -10300,7 +10291,7 @@ TEST_CASE(":/redwood/performance/extentQueue") { remapCleanupWindowBytes, concurrentExtentReads, false, - Reference()); + Reference()); wait(success(pager->init())); @@ -10358,7 +10349,7 @@ TEST_CASE(":/redwood/performance/extentQueue") { remapCleanupWindowBytes, concurrentExtentReads, false, - Reference()); + Reference()); wait(success(pager->init())); printf("Starting ExtentQueue FastPath Recovery from Disk.\n"); @@ -10503,9 +10494,9 @@ TEST_CASE(":/redwood/performance/set") { remapCleanupWindowBytes, concurrentExtentReads, pagerMemoryOnly, - Reference()); + Reference()); state VersionedBTree* btree = - new VersionedBTree(pager, file, EncodingType::XXHash64, Reference()); + new VersionedBTree(pager, file, EncodingType::XXHash64, Reference()); wait(btree->init()); printf("Initialized. StorageBytes=%s\n", btree->getStorageBytes().toString().c_str()); @@ -10883,7 +10874,7 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore* kvs, if (clearAfter) { printf("Clearing all keys\n"); intervalStart = timer(); - kvs->clear(KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff"))); + kvs->clear(KeyRangeRef(""_sr, "\xff"_sr)); state StorageBytes sbClear = wait(getStableStorageBytes(kvs)); printf("Cleared all keys in %.2f seconds, final storageByte: %s\n", timer() - intervalStart, @@ -11117,92 +11108,25 @@ TEST_CASE(":/redwood/performance/randomRangeScans") { return Void(); } -TEST_CASE(":/redwood/performance/histogramThroughput") { - std::default_random_engine generator; - std::uniform_int_distribution distribution(0, UINT32_MAX); - state size_t inputSize = pow(10, 8); - state std::vector uniform; - for (int i = 0; i < inputSize; i++) { - uniform.push_back(distribution(generator)); +TEST_CASE(":/redwood/performance/histograms") { + // Time needed to log 33 histograms. + std::vector> histograms; + for (int i = 0; i < 33; i++) { + std::string levelString = "L" + std::to_string(i); + histograms.push_back(Histogram::getHistogram("histogramTest"_sr, "levelString"_sr, Histogram::Unit::bytes)); } - std::cout << "size of input: " << uniform.size() << std::endl; - { - // Time needed to log 33 histograms. - std::vector> histograms; - for (int i = 0; i < 33; i++) { - std::string levelString = "L" + std::to_string(i); - histograms.push_back(Histogram::getHistogram( - LiteralStringRef("histogramTest"), LiteralStringRef("levelString"), Histogram::Unit::bytes)); + for (int i = 0; i < 33; i++) { + for (int j = 0; j < 32; j++) { + histograms[i]->sample(std::pow(2, j)); } - for (int i = 0; i < 33; i++) { - for (int j = 0; j < 32; j++) { - histograms[i]->sample(std::pow(2, j)); - } - } - auto t_start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < 33; i++) { - histograms[i]->writeToLog(30.0); - } - auto t_end = std::chrono::high_resolution_clock::now(); - double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); - std::cout << "Time needed to log 33 histograms (millisecond): " << elapsed_time_ms << std::endl; } - { - std::cout << "Histogram Unit bytes" << std::endl; - auto t_start = std::chrono::high_resolution_clock::now(); - Reference h = Histogram::getHistogram( - LiteralStringRef("histogramTest"), LiteralStringRef("counts"), Histogram::Unit::bytes); - ASSERT(uniform.size() == inputSize); - for (size_t i = 0; i < uniform.size(); i++) { - h->sample(uniform[i]); - } - auto t_end = std::chrono::high_resolution_clock::now(); - std::cout << h->drawHistogram(); - double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); - std::cout << "Time in millisecond: " << elapsed_time_ms << std::endl; + auto t_start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < 33; i++) { + histograms[i]->writeToLog(30.0); + } + auto t_end = std::chrono::high_resolution_clock::now(); + double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); + std::cout << "Time needed to log 33 histograms (millisecond): " << elapsed_time_ms << std::endl; - Reference hCopy = Histogram::getHistogram( - LiteralStringRef("histogramTest"), LiteralStringRef("counts"), Histogram::Unit::bytes); - std::cout << hCopy->drawHistogram(); - GetHistogramRegistry().logReport(); - } - { - std::cout << "Histogram Unit percentage: " << std::endl; - auto t_start = std::chrono::high_resolution_clock::now(); - Reference h = Histogram::getHistogram( - LiteralStringRef("histogramTest"), LiteralStringRef("counts"), Histogram::Unit::percentageLinear); - ASSERT(uniform.size() == inputSize); - for (size_t i = 0; i < uniform.size(); i++) { - h->samplePercentage((double)uniform[i] / UINT32_MAX); - } - auto t_end = std::chrono::high_resolution_clock::now(); - std::cout << h->drawHistogram(); - GetHistogramRegistry().logReport(); - double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); - std::cout << "Time in millisecond: " << elapsed_time_ms << std::endl; - } - return Void(); -} -TEST_CASE(":/redwood/performance/continuousSmapleThroughput") { - std::default_random_engine generator; - std::uniform_int_distribution distribution(0, UINT32_MAX); - state size_t inputSize = pow(10, 8); - state std::vector uniform; - for (int i = 0; i < inputSize; i++) { - uniform.push_back(distribution(generator)); - } - - { - ContinuousSample s = ContinuousSample(pow(10, 3)); - auto t_start = std::chrono::high_resolution_clock::now(); - ASSERT(uniform.size() == inputSize); - for (size_t i = 0; i < uniform.size(); i++) { - s.addSample(uniform[i]); - } - auto t_end = std::chrono::high_resolution_clock::now(); - double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); - std::cout << "size of input: " << uniform.size() << std::endl; - std::cout << "Time in millisecond: " << elapsed_time_ms << std::endl; - } return Void(); } diff --git a/fdbserver/coroimpl/CoroFlow.actor.cpp b/fdbserver/coroimpl/CoroFlow.actor.cpp index 1af12ffda2..d4b4e954a8 100644 --- a/fdbserver/coroimpl/CoroFlow.actor.cpp +++ b/fdbserver/coroimpl/CoroFlow.actor.cpp @@ -103,7 +103,7 @@ protected: blocked = Promise(); double before = now(); CoroThreadPool::waitFor(blocked.getFuture()); - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("CoroUnblocked").detail("After", now() - before); } diff --git a/fdbserver/coroimpl/CoroFlowCoro.actor.cpp b/fdbserver/coroimpl/CoroFlowCoro.actor.cpp index 93c64b6e3a..5c366099a7 100644 --- a/fdbserver/coroimpl/CoroFlowCoro.actor.cpp +++ b/fdbserver/coroimpl/CoroFlowCoro.actor.cpp @@ -73,7 +73,7 @@ protected: blocked = Promise(); double before = now(); CoroThreadPool::waitFor(blocked.getFuture()); - if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting) + if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting) TraceEvent("CoroUnblocked").detail("After", now() - before); } @@ -265,7 +265,7 @@ ACTOR void coroSwitcher(Future what, TaskPriority taskID, Coro* coro) { try { // state double t = now(); wait(what); - // if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting && now()!=t) + // if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting && now()!=t) // TraceEvent("NonzeroWaitDuringReboot").detail("TaskID", taskID).detail("Elapsed", now()-t).backtrace("Flow"); } catch (Error&) { } @@ -280,7 +280,7 @@ void CoroThreadPool::waitFor(Future what) { // double t = now(); coroSwitcher(what, g_network->getCurrentTask(), current_coro); Coro_switchTo_(swapCoro(main_coro), main_coro); - // if (g_network->isSimulated() && g_simulator.getCurrentProcess()->rebooting && now()!=t) + // if (g_network->isSimulated() && g_simulator->getCurrentProcess()->rebooting && now()!=t) // TraceEvent("NonzeroWaitDuringReboot").detail("TaskID", currentTaskID).detail("Elapsed", // now()-t).backtrace("Coro"); ASSERT(what.isReady()); diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index cbcfd04e45..3d69ab4aa2 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -111,7 +111,7 @@ enum { OPT_DCID, OPT_MACHINE_CLASS, OPT_BUGGIFY, OPT_VERSION, OPT_BUILD_FLAGS, OPT_CRASHONERROR, OPT_HELP, OPT_NETWORKIMPL, OPT_NOBUFSTDOUT, OPT_BUFSTDOUTERR, OPT_TRACECLOCK, OPT_NUMTESTERS, OPT_DEVHELP, OPT_PRINT_CODE_PROBES, OPT_ROLLSIZE, OPT_MAXLOGS, OPT_MAXLOGSSIZE, OPT_KNOB, OPT_UNITTESTPARAM, OPT_TESTSERVERS, OPT_TEST_ON_SERVERS, OPT_METRICSCONNFILE, OPT_METRICSPREFIX, OPT_LOGGROUP, OPT_LOCALITY, OPT_IO_TRUST_SECONDS, OPT_IO_TRUST_WARN_ONLY, OPT_FILESYSTEM, OPT_PROFILER_RSS_SIZE, OPT_KVFILE, - OPT_TRACE_FORMAT, OPT_WHITELIST_BINPATH, OPT_BLOB_CREDENTIAL_FILE, OPT_CONFIG_PATH, OPT_USE_TEST_CONFIG_DB, OPT_FAULT_INJECTION, OPT_PROFILER, OPT_PRINT_SIMTIME, + OPT_TRACE_FORMAT, OPT_WHITELIST_BINPATH, OPT_BLOB_CREDENTIAL_FILE, OPT_CONFIG_PATH, OPT_USE_TEST_CONFIG_DB, OPT_NO_CONFIG_DB, OPT_FAULT_INJECTION, OPT_PROFILER, OPT_PRINT_SIMTIME, OPT_FLOW_PROCESS_NAME, OPT_FLOW_PROCESS_ENDPOINT, OPT_IP_TRUSTED_MASK, OPT_KMS_CONN_DISCOVERY_URL_FILE, OPT_KMS_CONNECTOR_TYPE, OPT_KMS_CONN_VALIDATION_TOKEN_DETAILS, OPT_KMS_CONN_GET_ENCRYPTION_KEYS_ENDPOINT, OPT_NEW_CLUSTER_KEY, OPT_AUTHZ_PUBLIC_KEY_FILE, OPT_USE_FUTURE_PROTOCOL_VERSION }; @@ -200,6 +200,7 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_BLOB_CREDENTIAL_FILE, "--blob-credential-file", SO_REQ_SEP }, { OPT_CONFIG_PATH, "--config-path", SO_REQ_SEP }, { OPT_USE_TEST_CONFIG_DB, "--use-test-config-db", SO_NONE }, + { OPT_NO_CONFIG_DB, "--no-config-db", SO_NONE }, { OPT_FAULT_INJECTION, "-fi", SO_REQ_SEP }, { OPT_FAULT_INJECTION, "--fault-injection", SO_REQ_SEP }, { OPT_PROFILER, "--profiler-", SO_REQ_SEP }, @@ -352,17 +353,17 @@ UID getSharedMemoryMachineId() { #endif } -ACTOR void failAfter(Future trigger, ISimulator::ProcessInfo* m = g_simulator.getCurrentProcess()) { +ACTOR void failAfter(Future trigger, ISimulator::ProcessInfo* m = g_simulator->getCurrentProcess()) { wait(trigger); if (enableFailures) { printf("Killing machine: %s at %f\n", m->address.toString().c_str(), now()); - g_simulator.killProcess(m, ISimulator::KillInstantly); + g_simulator->killProcess(m, ISimulator::KillInstantly); } } void failAfter(Future trigger, Endpoint e) { - if (g_network == &g_simulator) - failAfter(trigger, g_simulator.getProcess(e)); + if (g_network == g_simulator) + failAfter(trigger, g_simulator->getProcess(e)); } ACTOR Future histogramReport() { @@ -389,12 +390,10 @@ void testSerializationSpeed() { CommitTransactionRef& tr = batch[t]; tr.read_snapshot = 0; for (int i = 0; i < 2; i++) - tr.mutations.push_back_deep( - batchArena, - MutationRef(MutationRef::SetValue, LiteralStringRef("KeyABCDE"), LiteralStringRef("SomeValu"))); - tr.mutations.push_back_deep( - batchArena, - MutationRef(MutationRef::ClearRange, LiteralStringRef("BeginKey"), LiteralStringRef("EndKeyAB"))); + tr.mutations.push_back_deep(batchArena, + MutationRef(MutationRef::SetValue, "KeyABCDE"_sr, "SomeValu"_sr)); + tr.mutations.push_back_deep(batchArena, + MutationRef(MutationRef::ClearRange, "BeginKey"_sr, "EndKeyAB"_sr)); } build += timer() - tstart; @@ -884,7 +883,7 @@ std::pair buildNetworkAddresses( for (int ii = 0; ii < publicAddressStrs.size(); ++ii) { const std::string& publicAddressStr = publicAddressStrs[ii]; - bool autoPublicAddress = StringRef(publicAddressStr).startsWith(LiteralStringRef("auto:")); + bool autoPublicAddress = StringRef(publicAddressStr).startsWith("auto:"_sr); NetworkAddress currentPublicAddress; if (autoPublicAddress) { try { @@ -1069,7 +1068,7 @@ struct CLIOptions { const char* blobCredsFromENV = nullptr; std::string configPath; - ConfigDBType configDBType{ ConfigDBType::DISABLED }; + ConfigDBType configDBType{ ConfigDBType::PAXOS }; Reference connectionFile; Standalone machineId; @@ -1627,6 +1626,9 @@ private: case OPT_USE_TEST_CONFIG_DB: configDBType = ConfigDBType::SIMPLE; break; + case OPT_NO_CONFIG_DB: + configDBType = ConfigDBType::DISABLED; + break; case OPT_FLOW_PROCESS_NAME: flowProcessName = args.OptionArg(); std::cout << flowProcessName << std::endl; @@ -1759,7 +1761,7 @@ private: bool autoPublicAddress = std::any_of(publicAddressStrs.begin(), publicAddressStrs.end(), [](const std::string& addr) { - return StringRef(addr).startsWith(LiteralStringRef("auto:")); + return StringRef(addr).startsWith("auto:"_sr); }); if ((role != ServerRole::Simulation && role != ServerRole::CreateTemplateDatabase && role != ServerRole::KVFileIntegrityCheck && role != ServerRole::KVFileGenerateIOLogChecksums && @@ -2261,7 +2263,7 @@ int main(int argc, char* argv[]) { KnobValue::create(ini.GetBoolValue("META", "enableBlobGranuleEncryption", false))); } setupAndRun(dataFolder, opts.testFile, opts.restarting, (isRestoring >= 1), opts.whitelistBinPaths); - g_simulator.run(); + g_simulator->run(); } else if (role == ServerRole::FDBD) { // Update the global blob credential files list so that both fast // restore workers and backup workers can access blob storage. @@ -2471,7 +2473,7 @@ int main(int argc, char* argv[]) { } } - // g_simulator.run(); + // g_simulator->run(); #ifdef ALLOC_INSTRUMENTATION { @@ -2490,19 +2492,19 @@ int main(int argc, char* argv[]) { char* demangled = abi::__cxa_demangle(i->first, nullptr, nullptr, nullptr); if (demangled) { s = demangled; - if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::"))) - s = s.substr(LiteralStringRef("(anonymous namespace)::").size()); + if (StringRef(s).startsWith("(anonymous namespace)::"_sr)) + s = s.substr("(anonymous namespace)::"_sr.size()); free(demangled); } else s = i->first; #else s = i->first; - if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::"))) - s = s.substr(LiteralStringRef("class `anonymous namespace'::").size()); - else if (StringRef(s).startsWith(LiteralStringRef("class "))) - s = s.substr(LiteralStringRef("class ").size()); - else if (StringRef(s).startsWith(LiteralStringRef("struct "))) - s = s.substr(LiteralStringRef("struct ").size()); + if (StringRef(s).startsWith("class `anonymous namespace'::"_sr)) + s = s.substr("class `anonymous namespace'::"_sr.size()); + else if (StringRef(s).startsWith("class "_sr)) + s = s.substr("class "_sr.size()); + else if (StringRef(s).startsWith("struct "_sr)) + s = s.substr("struct "_sr.size()); #endif typeNames.emplace_back(s, i->first); diff --git a/fdbserver/include/fdbserver/ApplyMetadataMutation.h b/fdbserver/include/fdbserver/ApplyMetadataMutation.h index 1550260bc5..8977ea45cc 100644 --- a/fdbserver/include/fdbserver/ApplyMetadataMutation.h +++ b/fdbserver/include/fdbserver/ApplyMetadataMutation.h @@ -25,6 +25,7 @@ #include #include "fdbclient/BackupAgent.actor.h" +#include "fdbclient/BlobCipher.h" #include "fdbclient/MutationList.h" #include "fdbclient/Notified.h" #include "fdbclient/StorageServerInterface.h" @@ -33,7 +34,6 @@ #include "fdbserver/LogProtocolMessage.h" #include "fdbserver/LogSystem.h" #include "fdbserver/ProxyCommitData.actor.h" -#include "flow/BlobCipher.h" #include "flow/FastRef.h" // Resolver's data for applyMetadataMutations() calls. @@ -131,10 +131,10 @@ inline bool containsMetadataMutation(const VectorRef& mutations) { (serverTagKeys.intersects(range)) || (serverTagHistoryKeys.intersects(range)) || (range.intersects(applyMutationsEndRange)) || (range.intersects(applyMutationsKeyVersionMapRange)) || (range.intersects(logRangesRange)) || (tssMappingKeys.intersects(range)) || - (tssQuarantineKeys.intersects(range)) || (range.contains(coordinatorsKey)) || - (range.contains(databaseLockedKey)) || (range.contains(metadataVersionKey)) || - (range.contains(mustContainSystemMutationsKey)) || (range.contains(writeRecoveryKey)) || - (range.intersects(testOnlyTxnStateStorePrefixRange))) { + (tssQuarantineKeys.intersects(range)) || (range.contains(previousCoordinatorsKey)) || + (range.contains(coordinatorsKey)) || (range.contains(databaseLockedKey)) || + (range.contains(metadataVersionKey)) || (range.contains(mustContainSystemMutationsKey)) || + (range.contains(writeRecoveryKey)) || (range.intersects(testOnlyTxnStateStorePrefixRange))) { return true; } } diff --git a/fdbserver/include/fdbserver/BlobGranuleServerCommon.actor.h b/fdbserver/include/fdbserver/BlobGranuleServerCommon.actor.h index 2208c9459a..9b761393b7 100644 --- a/fdbserver/include/fdbserver/BlobGranuleServerCommon.actor.h +++ b/fdbserver/include/fdbserver/BlobGranuleServerCommon.actor.h @@ -33,6 +33,7 @@ #include "fdbclient/Tenant.h" #include "fdbserver/ServerDBInfo.h" +#include "fdbserver/Knobs.h" #include "flow/flow.h" #include "flow/actorcompiler.h" // has to be last include @@ -145,6 +146,13 @@ private: Future collection; }; +ACTOR Future dumpManifest(Database db, Reference blobConn); +ACTOR Future loadManifest(Database db, Reference blobConn); +ACTOR Future printRestoreSummary(Database db, Reference blobConn); +inline bool isFullRestoreMode() { + return SERVER_KNOBS->BLOB_FULL_RESTORE_MODE; +}; + #include "flow/unactorcompiler.h" #endif diff --git a/fdbserver/include/fdbserver/ClusterController.actor.h b/fdbserver/include/fdbserver/ClusterController.actor.h index f90d17f9c8..72f4e92d32 100644 --- a/fdbserver/include/fdbserver/ClusterController.actor.h +++ b/fdbserver/include/fdbserver/ClusterController.actor.h @@ -51,6 +51,7 @@ struct WorkerInfo : NonCopyable { Future haltDistributor; Future haltBlobManager; Future haltEncryptKeyProxy; + Future haltConsistencyScan; Standalone> issues; WorkerInfo() @@ -73,7 +74,7 @@ struct WorkerInfo : NonCopyable { : watcher(std::move(r.watcher)), reply(std::move(r.reply)), gen(r.gen), reboots(r.reboots), initialClass(r.initialClass), priorityInfo(r.priorityInfo), details(std::move(r.details)), haltRatekeeper(r.haltRatekeeper), haltDistributor(r.haltDistributor), haltBlobManager(r.haltBlobManager), - haltEncryptKeyProxy(r.haltEncryptKeyProxy), issues(r.issues) {} + haltEncryptKeyProxy(r.haltEncryptKeyProxy), haltConsistencyScan(r.haltConsistencyScan), issues(r.issues) {} void operator=(WorkerInfo&& r) noexcept { watcher = std::move(r.watcher); reply = std::move(r.reply); @@ -188,6 +189,14 @@ public: serverInfo->set(newInfo); } + void setConsistencyScan(const ConsistencyScanInterface& interf) { + auto newInfo = serverInfo->get(); + newInfo.id = deterministicRandom()->randomUniqueID(); + newInfo.infoGeneration = ++dbInfoCount; + newInfo.consistencyScan = interf; + serverInfo->set(newInfo); + } + void clearInterf(ProcessClass::ClassType t) { auto newInfo = serverInfo->get(); newInfo.id = deterministicRandom()->randomUniqueID(); @@ -200,6 +209,8 @@ public: newInfo.blobManager = Optional(); } else if (t == ProcessClass::EncryptKeyProxyClass) { newInfo.encryptKeyProxy = Optional(); + } else if (t == ProcessClass::ConsistencyScanClass) { + newInfo.consistencyScan = Optional(); } serverInfo->set(newInfo); } @@ -294,7 +305,9 @@ public: (db.serverInfo->get().blobManager.present() && db.serverInfo->get().blobManager.get().locality.processId() == processId) || (db.serverInfo->get().encryptKeyProxy.present() && - db.serverInfo->get().encryptKeyProxy.get().locality.processId() == processId); + db.serverInfo->get().encryptKeyProxy.get().locality.processId() == processId) || + (db.serverInfo->get().consistencyScan.present() && + db.serverInfo->get().consistencyScan.get().locality.processId() == processId); } WorkerDetails getStorageWorker(RecruitStorageRequest const& req) { @@ -2880,7 +2893,8 @@ public: ASSERT(masterProcessId.present()); const auto& pid = worker.interf.locality.processId(); if ((role != ProcessClass::DataDistributor && role != ProcessClass::Ratekeeper && - role != ProcessClass::BlobManager && role != ProcessClass::EncryptKeyProxy) || + role != ProcessClass::BlobManager && role != ProcessClass::EncryptKeyProxy && + role != ProcessClass::ConsistencyScan) || pid == masterProcessId.get()) { return false; } @@ -3263,6 +3277,8 @@ public: Optional recruitingBlobManagerID; AsyncVar recruitEncryptKeyProxy; Optional recruitingEncryptKeyProxyID; + AsyncVar recruitConsistencyScan; + Optional recruitingConsistencyScanID; // Stores the health information from a particular worker's perspective. struct WorkerHealth { @@ -3300,7 +3316,7 @@ public: goodRecruitmentTime(Never()), goodRemoteRecruitmentTime(Never()), datacenterVersionDifference(0), versionDifferenceUpdated(false), remoteDCMonitorStarted(false), remoteTransactionSystemDegraded(false), recruitDistributor(false), recruitRatekeeper(false), recruitBlobManager(false), recruitEncryptKeyProxy(false), - clusterControllerMetrics("ClusterController", id.toString()), + recruitConsistencyScan(false), clusterControllerMetrics("ClusterController", id.toString()), openDatabaseRequests("OpenDatabaseRequests", clusterControllerMetrics), registerWorkerRequests("RegisterWorkerRequests", clusterControllerMetrics), getWorkersRequests("GetWorkersRequests", clusterControllerMetrics), diff --git a/fdbserver/include/fdbserver/ClusterRecovery.actor.h b/fdbserver/include/fdbserver/ClusterRecovery.actor.h index 8a146b001e..a7071a8278 100644 --- a/fdbserver/include/fdbserver/ClusterRecovery.actor.h +++ b/fdbserver/include/fdbserver/ClusterRecovery.actor.h @@ -93,6 +93,8 @@ public: return previousWrite; } + ServerCoordinators getCoordinators() const { return coordinators; } + Future move(ClusterConnectionString const& nc) { return cstate.move(nc); } private: diff --git a/fdbserver/include/fdbserver/ConfigBroadcastInterface.h b/fdbserver/include/fdbserver/ConfigBroadcastInterface.h index cb75e1a251..1c7733680b 100644 --- a/fdbserver/include/fdbserver/ConfigBroadcastInterface.h +++ b/fdbserver/include/fdbserver/ConfigBroadcastInterface.h @@ -72,16 +72,17 @@ struct ConfigBroadcastSnapshotRequest { static constexpr FileIdentifier file_identifier = 10911925; Version version{ 0 }; std::map snapshot; + double restartDelay{ 0.0 }; ReplyPromise reply; ConfigBroadcastSnapshotRequest() = default; template - explicit ConfigBroadcastSnapshotRequest(Version version, Snapshot&& snapshot) - : version(version), snapshot(std::forward(snapshot)) {} + explicit ConfigBroadcastSnapshotRequest(Version version, Snapshot&& snapshot, double restartDelay) + : version(version), snapshot(std::forward(snapshot)), restartDelay(restartDelay) {} template void serialize(Ar& ar) { - serializer(ar, version, snapshot, reply); + serializer(ar, version, snapshot, restartDelay, reply); } }; @@ -98,31 +99,35 @@ struct ConfigBroadcastChangesReply { struct ConfigBroadcastChangesRequest { static constexpr FileIdentifier file_identifier = 601281; - Version mostRecentVersion; + Version mostRecentVersion{ 0 }; Standalone> changes; + double restartDelay{ 0.0 }; ReplyPromise reply; ConfigBroadcastChangesRequest() = default; explicit ConfigBroadcastChangesRequest(Version mostRecentVersion, - Standalone> const& changes) - : mostRecentVersion(mostRecentVersion), changes(changes) {} + Standalone> const& changes, + double restartDelay) + : mostRecentVersion(mostRecentVersion), changes(changes), restartDelay(restartDelay) {} template void serialize(Ar& ar) { - serializer(ar, mostRecentVersion, changes, reply); + serializer(ar, mostRecentVersion, changes, restartDelay, reply); } }; struct ConfigBroadcastRegisteredReply { static constexpr FileIdentifier file_identifier = 12041047; - bool registered; + bool registered{ false }; + Version lastSeenVersion{ 0 }; ConfigBroadcastRegisteredReply() = default; - explicit ConfigBroadcastRegisteredReply(bool registered) : registered(registered) {} + explicit ConfigBroadcastRegisteredReply(bool registered, Version lastSeenVersion) + : registered(registered), lastSeenVersion(lastSeenVersion) {} template void serialize(Ar& ar) { - serializer(ar, registered); + serializer(ar, registered, lastSeenVersion); } }; @@ -151,13 +156,23 @@ struct ConfigBroadcastReadyReply { struct ConfigBroadcastReadyRequest { static constexpr FileIdentifier file_identifier = 7402862; + CoordinatorsHash coordinatorsHash{ 0 }; + std::map snapshot; + Version snapshotVersion{ 0 }; + Version liveVersion{ 0 }; ReplyPromise reply; ConfigBroadcastReadyRequest() = default; + ConfigBroadcastReadyRequest(CoordinatorsHash coordinatorsHash, + std::map const& snapshot, + Version snapshotVersion, + Version liveVersion) + : coordinatorsHash(coordinatorsHash), snapshot(snapshot), snapshotVersion(snapshotVersion), + liveVersion(liveVersion) {} template void serialize(Ar& ar) { - serializer(ar, reply); + serializer(ar, coordinatorsHash, snapshot, snapshotVersion, liveVersion, reply); } }; @@ -180,6 +195,7 @@ public: bool operator==(ConfigBroadcastInterface const& rhs) const { return (_id == rhs._id); } bool operator!=(ConfigBroadcastInterface const& rhs) const { return !(*this == rhs); } UID id() const { return _id; } + NetworkAddress address() const { return snapshot.getEndpoint().getPrimaryAddress(); } template void serialize(Ar& ar) { diff --git a/fdbserver/include/fdbserver/ConfigBroadcaster.h b/fdbserver/include/fdbserver/ConfigBroadcaster.h index bddc1f5209..47808e9861 100644 --- a/fdbserver/include/fdbserver/ConfigBroadcaster.h +++ b/fdbserver/include/fdbserver/ConfigBroadcaster.h @@ -39,15 +39,16 @@ class ConfigBroadcaster { PImpl impl; public: - explicit ConfigBroadcaster(ServerCoordinators const&, ConfigDBType); + ConfigBroadcaster(); + explicit ConfigBroadcaster(ServerCoordinators const&, ConfigDBType, Future>); ConfigBroadcaster(ConfigBroadcaster&&); ConfigBroadcaster& operator=(ConfigBroadcaster&&); ~ConfigBroadcaster(); - Future registerNode(WorkerInterface const& w, + Future registerNode(ConfigBroadcastInterface const& broadcastInterface, Version lastSeenVersion, ConfigClassSet const& configClassSet, Future watcher, - ConfigBroadcastInterface const& worker); + bool isCoordinator); void applyChanges(Standalone> const& changes, Version mostRecentVersion, Standalone> const& annotations, @@ -57,18 +58,26 @@ public: Standalone> const& changes, Version changesVersion, Standalone> const& annotations, - std::vector const& readReplicas); + std::vector const& readReplicas, + Version largestLiveVersion, + bool fromPreviousCoordinators = false); void applySnapshotAndChanges(std::map&& snapshot, Version snapshotVersion, Standalone> const& changes, Version changesVersion, Standalone> const& annotations, - std::vector const& readReplicas); + std::vector const& readReplicas, + Version largestLiveVersion, + bool fromPreviousCoordinators = false); Future getError() const; UID getID() const; JsonBuilderObject getStatus() const; void compact(Version compactionVersion); + // Locks all ConfigNodes running on the given coordinators, returning when + // a quorum have successfully locked. + static Future lockConfigNodes(ServerCoordinators coordinators); + public: // Testing explicit ConfigBroadcaster(ConfigFollowerInterface const&); Future getClientFailure(UID clientUID) const; diff --git a/fdbserver/include/fdbserver/ConfigFollowerInterface.h b/fdbserver/include/fdbserver/ConfigFollowerInterface.h index 67137d86b2..aef438255c 100644 --- a/fdbserver/include/fdbserver/ConfigFollowerInterface.h +++ b/fdbserver/include/fdbserver/ConfigFollowerInterface.h @@ -27,7 +27,7 @@ #include "fdbrpc/fdbrpc.h" struct VersionedConfigMutationRef { - Version version; + Version version{ ::invalidVersion }; ConfigMutationRef mutation; VersionedConfigMutationRef() = default; @@ -46,7 +46,7 @@ struct VersionedConfigMutationRef { using VersionedConfigMutation = Standalone; struct VersionedConfigCommitAnnotationRef { - Version version; + Version version{ ::invalidVersion }; ConfigCommitAnnotationRef annotation; VersionedConfigCommitAnnotationRef() = default; @@ -66,7 +66,7 @@ using VersionedConfigCommitAnnotation = Standalone snapshot; // TODO: Share arena Standalone> changes; @@ -84,14 +84,14 @@ struct ConfigFollowerGetSnapshotAndChangesReply { template void serialize(Ar& ar) { - serializer(ar, snapshotVersion, snapshot, changes); + serializer(ar, snapshotVersion, snapshot, changes, annotations); } }; struct ConfigFollowerGetSnapshotAndChangesRequest { static constexpr FileIdentifier file_identifier = 294811; ReplyPromise reply; - Version mostRecentVersion; + Version mostRecentVersion{ 0 }; ConfigFollowerGetSnapshotAndChangesRequest() = default; explicit ConfigFollowerGetSnapshotAndChangesRequest(Version mostRecentVersion) @@ -158,6 +158,7 @@ struct ConfigFollowerRollforwardRequest { Version target{ 0 }; Standalone> mutations; Standalone> annotations; + bool specialZeroQuorum{ false }; ReplyPromise reply; ConfigFollowerRollforwardRequest() = default; @@ -165,28 +166,34 @@ struct ConfigFollowerRollforwardRequest { Version lastKnownCommitted, Version target, Standalone> mutations, - Standalone> annotations) + Standalone> annotations, + bool specialZeroQuorum) : rollback(rollback), lastKnownCommitted(lastKnownCommitted), target(target), mutations(mutations), - annotations(annotations) {} + annotations(annotations), specialZeroQuorum(specialZeroQuorum) {} template void serialize(Ar& ar) { - serializer(ar, rollback, lastKnownCommitted, target, mutations, annotations, reply); + serializer(ar, rollback, lastKnownCommitted, target, mutations, annotations, specialZeroQuorum, reply); } }; struct ConfigFollowerGetCommittedVersionReply { static constexpr FileIdentifier file_identifier = 9214735; - Version lastCompacted; - Version lastCommitted; + bool registered{ false }; + Version lastCompacted{ 0 }; + Version lastLive{ 0 }; + Version lastCommitted{ 0 }; ConfigFollowerGetCommittedVersionReply() = default; - explicit ConfigFollowerGetCommittedVersionReply(Version lastCompacted, Version lastCommitted) - : lastCompacted(lastCompacted), lastCommitted(lastCommitted) {} + explicit ConfigFollowerGetCommittedVersionReply(bool registered, + Version lastCompacted, + Version lastLive, + Version lastCommitted) + : registered(registered), lastCompacted(lastCompacted), lastLive(lastLive), lastCommitted(lastCommitted) {} template void serialize(Ar& ar) { - serializer(ar, lastCompacted, lastCommitted); + serializer(ar, registered, lastCompacted, lastLive, lastCommitted); } }; @@ -202,9 +209,23 @@ struct ConfigFollowerGetCommittedVersionRequest { } }; +struct ConfigFollowerLockRequest { + static constexpr FileIdentifier file_identifier = 1867800; + CoordinatorsHash coordinatorsHash{ 0 }; + ReplyPromise reply; + + ConfigFollowerLockRequest() = default; + explicit ConfigFollowerLockRequest(CoordinatorsHash coordinatorsHash) : coordinatorsHash(coordinatorsHash) {} + + template + void serialize(Ar& ar) { + serializer(ar, coordinatorsHash, reply); + } +}; + /* * Configuration database nodes serve a ConfigFollowerInterface which contains well known endpoints, - * used by workers to receive configuration database updates + * used by the broadcaster to receive configuration database updates */ class ConfigFollowerInterface { UID _id; @@ -217,6 +238,7 @@ public: RequestStream rollforward; RequestStream getCommittedVersion; Optional hostname; + RequestStream lock; ConfigFollowerInterface(); void setupWellKnownEndpoints(); @@ -229,6 +251,7 @@ public: template void serialize(Ar& ar) { - serializer(ar, _id, getSnapshotAndChanges, getChanges, compact, rollforward, getCommittedVersion, hostname); + serializer( + ar, _id, getSnapshotAndChanges, getChanges, compact, rollforward, getCommittedVersion, hostname, lock); } }; diff --git a/fdbserver/include/fdbserver/ConfigNode.h b/fdbserver/include/fdbserver/ConfigNode.h index 333652893b..5454ac63da 100644 --- a/fdbserver/include/fdbserver/ConfigNode.h +++ b/fdbserver/include/fdbserver/ConfigNode.h @@ -38,6 +38,7 @@ public: ConfigFollowerInterface const&); public: // Testing + Future serve(ConfigBroadcastInterface const&); Future serve(ConfigTransactionInterface const&); Future serve(ConfigFollowerInterface const&); void close(); diff --git a/fdbserver/include/fdbserver/CoordinationInterface.h b/fdbserver/include/fdbserver/CoordinationInterface.h index 3c6c904d4c..43fff9ed65 100644 --- a/fdbserver/include/fdbserver/CoordinationInterface.h +++ b/fdbserver/include/fdbserver/CoordinationInterface.h @@ -224,7 +224,9 @@ class ConfigNode; class ServerCoordinators : public ClientCoordinators { public: - explicit ServerCoordinators(Reference ccr); + ServerCoordinators() {} + explicit ServerCoordinators(Reference ccr, + ConfigDBType configDBType = ConfigDBType::PAXOS); std::vector leaderElectionServers; std::vector stateServers; diff --git a/fdbserver/include/fdbserver/DDRelocationQueue.h b/fdbserver/include/fdbserver/DDRelocationQueue.h index 874e86b83f..e4b4509c90 100644 --- a/fdbserver/include/fdbserver/DDRelocationQueue.h +++ b/fdbserver/include/fdbserver/DDRelocationQueue.h @@ -20,7 +20,7 @@ #ifndef FOUNDATIONDB_DDRELOCATIONQUEUE_H #define FOUNDATIONDB_DDRELOCATIONQUEUE_H -#include "DataDistribution.actor.h" +#include "fdbserver/DataDistribution.actor.h" // send request/signal to DDRelocationQueue through interface // call synchronous method from components outside DDRelocationQueue struct IDDRelocationQueue { diff --git a/fdbserver/include/fdbserver/DDShardTracker.h b/fdbserver/include/fdbserver/DDShardTracker.h index 307a095be6..62a5409509 100644 --- a/fdbserver/include/fdbserver/DDShardTracker.h +++ b/fdbserver/include/fdbserver/DDShardTracker.h @@ -19,7 +19,7 @@ */ #ifndef FOUNDATIONDB_DDSHARDTRACKER_H #define FOUNDATIONDB_DDSHARDTRACKER_H -#include "DataDistribution.actor.h" +#include "fdbserver/DataDistribution.actor.h" // send request/signal to DDTracker through interface // call synchronous method from components outside DDShardTracker diff --git a/fdbserver/include/fdbserver/DDTxnProcessor.h b/fdbserver/include/fdbserver/DDTxnProcessor.h index 451dc84c96..00410a54f8 100644 --- a/fdbserver/include/fdbserver/DDTxnProcessor.h +++ b/fdbserver/include/fdbserver/DDTxnProcessor.h @@ -74,6 +74,8 @@ public: const Optional& tssPairID, const MoveKeysLock& lock, const DDEnabledState* ddEnabledState) const = 0; + + virtual Future moveKeys(const MoveKeysParams& params) const = 0; }; class DDTxnProcessorImpl; @@ -125,6 +127,8 @@ public: const DDEnabledState* ddEnabledState) const override { return ::removeStorageServer(cx, serverID, tssPairID, lock, ddEnabledState); } + + Future moveKeys(const MoveKeysParams& params) const override { return ::moveKeys(cx, params); } }; // A mock transaction implementation for test usage. diff --git a/fdbserver/include/fdbserver/IClosable.h b/fdbserver/include/fdbserver/IClosable.h new file mode 100644 index 0000000000..131dc84df0 --- /dev/null +++ b/fdbserver/include/fdbserver/IClosable.h @@ -0,0 +1,40 @@ +/* + * IClosable.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FDBSERVER_ICLOSABLE_H +#define FDBSERVER_ICLOSABLE_H +#pragma once + +class IClosable { +public: + // IClosable is a base interface for any disk-backed data structure that needs to support asynchronous errors, + // shutdown and deletion + + virtual Future getError() + const = 0; // asynchronously throws an error if there is an internal error. Never set + // inside (on the stack of) a call to another API function on this object. + virtual Future onClosed() + const = 0; // the future is set to Void when this is totally shut down after dispose() or + // close(). But this function cannot be called after dispose or close! + virtual void dispose() = 0; // permanently delete the data AND invalidate this interface + virtual void close() = 0; // invalidate this interface, but do not delete the data. Outstanding operations may or + // may not take effect in the background. +}; + +#endif \ No newline at end of file diff --git a/fdbserver/include/fdbserver/IConfigConsumer.h b/fdbserver/include/fdbserver/IConfigConsumer.h index f485d9f698..83cf0818b4 100644 --- a/fdbserver/include/fdbserver/IConfigConsumer.h +++ b/fdbserver/include/fdbserver/IConfigConsumer.h @@ -35,7 +35,9 @@ class IConfigConsumer { public: virtual ~IConfigConsumer() = default; + virtual Future readSnapshot(ConfigBroadcaster& broadcaster) = 0; virtual Future consume(ConfigBroadcaster& broadcaster) = 0; + virtual void allowSpecialCaseRollforward() = 0; virtual UID getID() const = 0; static std::unique_ptr createTestSimple(ConfigFollowerInterface const& cfi, @@ -46,5 +48,6 @@ public: Optional compactionInterval); static std::unique_ptr createPaxos(ServerCoordinators const& coordinators, double pollingInterval, - Optional compactionInterval); + Optional compactionInterval, + bool readPreviousCoordinators = false); }; diff --git a/fdbserver/include/fdbserver/IEncryptionKeyProvider.actor.h b/fdbserver/include/fdbserver/IEncryptionKeyProvider.actor.h deleted file mode 100644 index 66e2a1ee38..0000000000 --- a/fdbserver/include/fdbserver/IEncryptionKeyProvider.actor.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * IEncryptionKeyProvider.actor.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_IENCRYPTIONKEYPROVIDER_ACTOR_G_H) -#define FDBSERVER_IENCRYPTIONKEYPROVIDER_ACTOR_G_H -#include "fdbserver/IEncryptionKeyProvider.actor.g.h" -#elif !defined(FDBSERVER_IENCRYPTIONKEYPROVIDER_ACTOR_H) -#define FDBSERVER_IENCRYPTIONKEYPROVIDER_ACTOR_H - -#include "fdbclient/GetEncryptCipherKeys.actor.h" -#include "fdbclient/Tenant.h" -#include "fdbserver/EncryptionOpsUtils.h" -#include "fdbserver/ServerDBInfo.h" -#include "flow/Arena.h" - -#define XXH_INLINE_ALL -#include "flow/xxhash.h" - -#include "flow/actorcompiler.h" // This must be the last #include. - -typedef uint64_t XOREncryptionKeyID; - -// EncryptionKeyRef is somewhat multi-variant, it will contain members representing the union -// of all fields relevant to any implemented encryption scheme. They are generally of -// the form -// Page Fields - fields which come from or are stored in the Page -// Secret Fields - fields which are only known by the Key Provider -// but it is up to each encoding and provider which fields are which and which ones are used -// -// TODO(yiwu): Rename and/or refactor this struct. It doesn't sound like an encryption key should -// contain page fields like encryption header. -struct EncryptionKeyRef { - - EncryptionKeyRef(){}; - EncryptionKeyRef(Arena& arena, const EncryptionKeyRef& toCopy) - : cipherKeys(toCopy.cipherKeys), secret(arena, toCopy.secret), id(toCopy.id) {} - int expectedSize() const { return secret.size(); } - - // Fields for AESEncryptionV1 - TextAndHeaderCipherKeys cipherKeys; - Optional cipherHeader; - // Fields for XOREncryption_TestOnly - StringRef secret; - Optional id; -}; -typedef Standalone EncryptionKey; - -// Interface used by pager to get encryption keys reading pages from disk -// and by the BTree to get encryption keys to use for new pages -class IEncryptionKeyProvider : public ReferenceCounted { -public: - virtual ~IEncryptionKeyProvider() {} - - // Get an EncryptionKey with Secret Fields populated based on the given Page Fields. - // It is up to the implementation which fields those are. - // The output Page Fields must match the input Page Fields. - virtual Future getSecrets(const EncryptionKeyRef& key) = 0; - - // Get encryption key that should be used for a given user Key-Value range - virtual Future getByRange(const KeyRef& begin, const KeyRef& end) = 0; - - // Setting tenant prefix to tenant name map. - virtual void setTenantPrefixIndex(Reference tenantPrefixIndex) {} - - virtual bool shouldEnableEncryption() const = 0; -}; - -// The null key provider is useful to simplify page decoding. -// It throws an error for any key info requested. -class NullKeyProvider : public IEncryptionKeyProvider { -public: - virtual ~NullKeyProvider() {} - bool shouldEnableEncryption() const override { return true; } - Future getSecrets(const EncryptionKeyRef& key) override { throw encryption_key_not_found(); } - Future getByRange(const KeyRef& begin, const KeyRef& end) override { - throw encryption_key_not_found(); - } -}; - -// Key provider for dummy XOR encryption scheme -class XOREncryptionKeyProvider_TestOnly : public IEncryptionKeyProvider { -public: - XOREncryptionKeyProvider_TestOnly(std::string filename) { - ASSERT(g_network->isSimulated()); - - // Choose a deterministic random filename (without path) byte for secret generation - // Remove any leading directory names - size_t lastSlash = filename.find_last_of("\\/"); - if (lastSlash != filename.npos) { - filename.erase(0, lastSlash); - } - xorWith = filename.empty() ? 0x5e - : (uint8_t)filename[XXH3_64bits(filename.data(), filename.size()) % filename.size()]; - } - - virtual ~XOREncryptionKeyProvider_TestOnly() {} - - bool shouldEnableEncryption() const override { return true; } - - Future getSecrets(const EncryptionKeyRef& key) override { - if (!key.id.present()) { - throw encryption_key_not_found(); - } - EncryptionKey s = key; - uint8_t secret = ~(uint8_t)key.id.get() ^ xorWith; - s.secret = StringRef(s.arena(), &secret, 1); - return s; - } - - Future getByRange(const KeyRef& begin, const KeyRef& end) override { - EncryptionKeyRef k; - k.id = end.empty() ? 0 : *(end.end() - 1); - return getSecrets(k); - } - - uint8_t xorWith; -}; - -// Key provider to provider cipher keys randomly from a pre-generated pool. Use for testing. -class RandomEncryptionKeyProvider : public IEncryptionKeyProvider { -public: - RandomEncryptionKeyProvider() { - for (unsigned i = 0; i < NUM_CIPHER; i++) { - BlobCipherDetails cipherDetails; - cipherDetails.encryptDomainId = i; - cipherDetails.baseCipherId = deterministicRandom()->randomUInt64(); - cipherDetails.salt = deterministicRandom()->randomUInt64(); - cipherKeys[i] = generateCipherKey(cipherDetails); - } - } - virtual ~RandomEncryptionKeyProvider() = default; - - bool shouldEnableEncryption() const override { return true; } - - Future getSecrets(const EncryptionKeyRef& key) override { - ASSERT(key.cipherHeader.present()); - EncryptionKey s = key; - s.cipherKeys.cipherTextKey = cipherKeys[key.cipherHeader.get().cipherTextDetails.encryptDomainId]; - s.cipherKeys.cipherHeaderKey = cipherKeys[key.cipherHeader.get().cipherHeaderDetails.encryptDomainId]; - return s; - } - - Future getByRange(const KeyRef& /*begin*/, const KeyRef& /*end*/) override { - EncryptionKey s; - s.cipherKeys.cipherTextKey = getRandomCipherKey(); - s.cipherKeys.cipherHeaderKey = getRandomCipherKey(); - return s; - } - -private: - Reference generateCipherKey(const BlobCipherDetails& cipherDetails) { - static unsigned char SHA_KEY[] = "3ab9570b44b8315fdb261da6b1b6c13b"; - Arena arena; - StringRef digest = computeAuthToken(reinterpret_cast(&cipherDetails.baseCipherId), - sizeof(EncryptCipherBaseKeyId), - SHA_KEY, - AES_256_KEY_LENGTH, - arena); - return makeReference(cipherDetails.encryptDomainId, - cipherDetails.baseCipherId, - digest.begin(), - AES_256_KEY_LENGTH, - cipherDetails.salt, - std::numeric_limits::max() /* refreshAt */, - std::numeric_limits::max() /* expireAt */); - } - - Reference getRandomCipherKey() { - return cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)]; - } - - static constexpr int NUM_CIPHER = 1000; - Reference cipherKeys[NUM_CIPHER]; -}; - -// Key provider which extract tenant id from range key prefixes, and fetch tenant specific encryption keys from -// EncryptKeyProxy. -class TenantAwareEncryptionKeyProvider : public IEncryptionKeyProvider { -public: - TenantAwareEncryptionKeyProvider(Reference const> db) : db(db) {} - - virtual ~TenantAwareEncryptionKeyProvider() = default; - - bool shouldEnableEncryption() const override { - return isEncryptionOpSupported(EncryptOperationType::STORAGE_SERVER_ENCRYPTION, db->get().client); - } - - ACTOR static Future getSecrets(TenantAwareEncryptionKeyProvider* self, EncryptionKeyRef key) { - if (!key.cipherHeader.present()) { - TraceEvent("TenantAwareEncryptionKeyProvider_CipherHeaderMissing"); - throw encrypt_ops_error(); - } - TextAndHeaderCipherKeys cipherKeys = wait(getEncryptCipherKeys(self->db, key.cipherHeader.get())); - EncryptionKey s = key; - s.cipherKeys = cipherKeys; - return s; - } - - Future getSecrets(const EncryptionKeyRef& key) override { return getSecrets(this, key); } - - ACTOR static Future getByRange(TenantAwareEncryptionKeyProvider* self, KeyRef begin, KeyRef end) { - EncryptCipherDomainNameRef domainName; - EncryptCipherDomainId domainId = self->getEncryptionDomainId(begin, end, &domainName); - TextAndHeaderCipherKeys cipherKeys = wait(getLatestEncryptCipherKeysForDomain(self->db, domainId, domainName)); - EncryptionKey s; - s.cipherKeys = cipherKeys; - return s; - } - - Future getByRange(const KeyRef& begin, const KeyRef& end) override { - return getByRange(this, begin, end); - } - - void setTenantPrefixIndex(Reference tenantPrefixIndex) override { - ASSERT(tenantPrefixIndex.isValid()); - this->tenantPrefixIndex = tenantPrefixIndex; - } - -private: - EncryptCipherDomainId getEncryptionDomainId(const KeyRef& begin, - const KeyRef& end, - EncryptCipherDomainNameRef* domainName) { - int64_t domainId = SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID; - int64_t beginTenantId = getTenant(begin, true /*inclusive*/); - int64_t endTenantId = getTenant(end, false /*inclusive*/); - if (beginTenantId == endTenantId && beginTenantId != SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID) { - ASSERT(tenantPrefixIndex.isValid()); - Key tenantPrefix = TenantMapEntry::idToPrefix(beginTenantId); - auto view = tenantPrefixIndex->atLatest(); - auto itr = view.find(tenantPrefix); - if (itr != view.end()) { - *domainName = *itr; - domainId = beginTenantId; - } else { - // No tenant with the same tenant id. We could be in optional or disabled tenant mode. - } - } - if (domainId == SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID) { - *domainName = FDB_DEFAULT_ENCRYPT_DOMAIN_NAME; - } - return domainId; - } - - int64_t getTenant(const KeyRef& key, bool inclusive) { - // A valid tenant id is always a valid encrypt domain id. - static_assert(ENCRYPT_INVALID_DOMAIN_ID < 0); - if (key.size() < TENANT_PREFIX_SIZE || key >= systemKeys.begin) { - return SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID; - } - // TODO(yiwu): Use TenantMapEntry::prefixToId() instead. - int64_t tenantId = bigEndian64(*reinterpret_cast(key.begin())); - if (tenantId < 0) { - return SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID; - } - if (!inclusive && key.size() == TENANT_PREFIX_SIZE) { - tenantId = tenantId - 1; - } - ASSERT(tenantId >= 0); - return tenantId; - } - - Reference const> db; - Reference tenantPrefixIndex; -}; - -#include "flow/unactorcompiler.h" -#endif \ No newline at end of file diff --git a/fdbserver/include/fdbserver/IKeyValueStore.h b/fdbserver/include/fdbserver/IKeyValueStore.h index 4df57780f8..b9679d9f92 100644 --- a/fdbserver/include/fdbserver/IKeyValueStore.h +++ b/fdbserver/include/fdbserver/IKeyValueStore.h @@ -26,7 +26,8 @@ #include "fdbclient/StorageCheckpoint.h" #include "fdbclient/Tenant.h" #include "fdbserver/Knobs.h" -#include "fdbserver/IEncryptionKeyProvider.actor.h" +#include "fdbserver/IClosable.h" +#include "fdbserver/IPageEncryptionKeyProvider.actor.h" #include "fdbserver/ServerDBInfo.h" struct CheckpointRequest { @@ -44,22 +45,6 @@ struct CheckpointRequest { : version(version), range(range), format(format), checkpointID(id), checkpointDir(checkpointDir) {} }; -class IClosable { -public: - // IClosable is a base interface for any disk-backed data structure that needs to support asynchronous errors, - // shutdown and deletion - - virtual Future getError() - const = 0; // asynchronously throws an error if there is an internal error. Never set - // inside (on the stack of) a call to another API function on this object. - virtual Future onClosed() - const = 0; // the future is set to Void when this is totally shut down after dispose() or - // close(). But this function cannot be called after dispose or close! - virtual void dispose() = 0; // permanently delete the data AND invalidate this interface - virtual void close() = 0; // invalidate this interface, but do not delete the data. Outstanding operations may or - // may not take effect in the background. -}; - class IKeyValueStore : public IClosable { public: virtual KeyValueStoreType getType() const = 0; @@ -151,7 +136,7 @@ extern IKeyValueStore* keyValueStoreSQLite(std::string const& filename, bool checkIntegrity = false); extern IKeyValueStore* keyValueStoreRedwoodV1(std::string const& filename, UID logID, - Reference encryptionKeyProvider = {}); + Reference encryptionKeyProvider = {}); extern IKeyValueStore* keyValueStoreRocksDB(std::string const& path, UID logID, KeyValueStoreType storeType, @@ -190,7 +175,7 @@ inline IKeyValueStore* openKVStore(KeyValueStoreType storeType, bool checkChecksums = false, bool checkIntegrity = false, bool openRemotely = false, - Reference encryptionKeyProvider = {}) { + Reference encryptionKeyProvider = {}) { if (openRemotely) { return openRemoteKVStore(storeType, filename, logID, memoryLimit, checkChecksums, checkIntegrity); } diff --git a/fdbserver/include/fdbserver/IPageEncryptionKeyProvider.actor.h b/fdbserver/include/fdbserver/IPageEncryptionKeyProvider.actor.h new file mode 100644 index 0000000000..e3eff81e78 --- /dev/null +++ b/fdbserver/include/fdbserver/IPageEncryptionKeyProvider.actor.h @@ -0,0 +1,346 @@ +/* + * IPageEncryptionKeyProvider.actor.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_IPAGEENCRYPTIONKEYPROVIDER_ACTOR_G_H) +#define FDBSERVER_IPAGEENCRYPTIONKEYPROVIDER_ACTOR_G_H +#include "fdbserver/IPageEncryptionKeyProvider.actor.g.h" +#elif !defined(FDBSERVER_IPAGEENCRYPTIONKEYPROVIDER_ACTOR_H) +#define FDBSERVER_IPAGEENCRYPTIONKEYPROVIDER_ACTOR_H + +#include "fdbclient/GetEncryptCipherKeys.actor.h" +#include "fdbclient/Tenant.h" + +#include "fdbserver/EncryptionOpsUtils.h" +#include "fdbserver/IPager.h" +#include "fdbserver/ServerDBInfo.h" + +#include "flow/Arena.h" +#include "flow/EncryptUtils.h" +#define XXH_INLINE_ALL +#include "flow/xxhash.h" + +#include + +#include "flow/actorcompiler.h" // This must be the last #include. + +// Interface used by pager to get encryption keys reading pages from disk +// and by the BTree to get encryption keys to use for new pages. +// +// Cipher key rotation: +// The key provider can rotate encryption keys, potentially per encryption domain (see below). Each of the new pages +// are encrypted using the latest encryption keys. +// +// Encryption domains: +// The key provider can specify how the page split the full key range into encryption domains by key prefixes. +// Encryption domains are expected to have their own set of encryption keys, which is managed by the key provider. +// The pager will ensure that data from different encryption domain won't fall in the same page, to make +// sure it can use one single encryption key to encrypt the whole page. +// The key provider needs to provide a default encryption domain, which is used to encrypt pages contain only +// full or partial encryption domain prefixes. +class IPageEncryptionKeyProvider : public ReferenceCounted { +public: + using EncryptionKey = ArenaPage::EncryptionKey; + + virtual ~IPageEncryptionKeyProvider() = default; + + // Expected encoding type being used with the encryption key provider. + virtual EncodingType expectedEncodingType() const = 0; + + // Checks whether encryption should be enabled. If not, the encryption key provider will not be used by + // the pager, and instead the default non-encrypted encoding type (XXHash64) is used. + virtual bool enableEncryption() const = 0; + + // Whether encryption domain is enabled. + virtual bool enableEncryptionDomain() const { return false; } + + // Get an encryption key from given encoding header. + virtual Future getEncryptionKey(void* encodingHeader) { throw not_implemented(); } + + // Get latest encryption key. If encryption domain is enabled, get encryption key for the default domain. + virtual Future getLatestDefaultEncryptionKey() { throw not_implemented(); } + + // Get latest encryption key for data in given encryption domain. + virtual Future getLatestEncryptionKey(int64_t domainId) { throw not_implemented(); } + + // Return the default encryption domain. + virtual int64_t getDefaultEncryptionDomainId() const { throw not_implemented(); } + + // Get encryption domain from a key. Return the domain id, and the size of the encryption domain prefix. + // It is assumed that all keys with the same encryption domain prefix as the given key falls in the same encryption + // domain. If possibleDomainId is given, it is a valid domain id previously returned by the key provider, + // potentially for a different key. The possibleDomainId parm is used by TenantAwareEncryptionKeyProvider to speed + // up encryption domain lookup. + virtual std::tuple getEncryptionDomain(const KeyRef& key, + Optional possibleDomainId = Optional()) { + throw not_implemented(); + } + + // Get encryption domain of a page given encoding header. + virtual int64_t getEncryptionDomain(void* encodingHeader) { throw not_implemented(); } + + // Setting tenant prefix to tenant name map. Used by TenantAwareEncryptionKeyProvider. + virtual void setTenantPrefixIndex(Reference tenantPrefixIndex) {} +}; + +// The null key provider is useful to simplify page decoding. +// It throws an error for any key info requested. +class NullKeyProvider : public IPageEncryptionKeyProvider { +public: + virtual ~NullKeyProvider() {} + EncodingType expectedEncodingType() const override { return EncodingType::XXHash64; } + bool enableEncryption() const override { return false; } +}; + +// Key provider for dummy XOR encryption scheme +class XOREncryptionKeyProvider_TestOnly : public IPageEncryptionKeyProvider { +public: + using EncodingHeader = ArenaPage::XOREncryptionEncoder::Header; + + XOREncryptionKeyProvider_TestOnly(std::string filename) { + ASSERT(g_network->isSimulated()); + + // Choose a deterministic random filename (without path) byte for secret generation + // Remove any leading directory names + size_t lastSlash = filename.find_last_of("\\/"); + if (lastSlash != filename.npos) { + filename.erase(0, lastSlash); + } + xorWith = filename.empty() ? 0x5e + : (uint8_t)filename[XXH3_64bits(filename.data(), filename.size()) % filename.size()]; + } + + virtual ~XOREncryptionKeyProvider_TestOnly() {} + + EncodingType expectedEncodingType() const override { return EncodingType::XOREncryption_TestOnly; } + + bool enableEncryption() const override { return true; } + + bool enableEncryptionDomain() const override { return true; } + + Future getEncryptionKey(void* encodingHeader) override { + + EncodingHeader* h = reinterpret_cast(encodingHeader); + EncryptionKey s; + s.xorKey = h->xorKey; + return s; + } + + Future getLatestDefaultEncryptionKey() override { return getLatestEncryptionKey(0); } + + Future getLatestEncryptionKey(int64_t domainId) override { + EncryptionKey s; + s.xorKey = ~(uint8_t)domainId ^ xorWith; + return s; + } + + int64_t getDefaultEncryptionDomainId() const override { return 0; } + + std::tuple getEncryptionDomain(const KeyRef& key, + Optional /*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)->xorKey; + return (int64_t)(~xorKey ^ xorWith); + } + + uint8_t xorWith; +}; + +// Key provider to provider cipher keys randomly from a pre-generated pool. It does not maintain encryption domains. +// Use for testing. +class RandomEncryptionKeyProvider : public IPageEncryptionKeyProvider { +public: + RandomEncryptionKeyProvider() { + for (unsigned i = 0; i < NUM_CIPHER; i++) { + BlobCipherDetails cipherDetails; + cipherDetails.encryptDomainId = i; + cipherDetails.baseCipherId = deterministicRandom()->randomUInt64(); + cipherDetails.salt = deterministicRandom()->randomUInt64(); + cipherKeys[i] = generateCipherKey(cipherDetails); + } + } + virtual ~RandomEncryptionKeyProvider() = default; + + EncodingType expectedEncodingType() const override { return EncodingType::AESEncryptionV1; } + + bool enableEncryption() const override { return true; } + + Future getEncryptionKey(void* encodingHeader) override { + using Header = ArenaPage::AESEncryptionV1Encoder::Header; + Header* h = reinterpret_cast(encodingHeader); + EncryptionKey s; + s.aesKey.cipherTextKey = cipherKeys[h->cipherTextDetails.encryptDomainId]; + s.aesKey.cipherHeaderKey = cipherKeys[h->cipherHeaderDetails.encryptDomainId]; + return s; + } + + Future getLatestDefaultEncryptionKey() override { + EncryptionKey s; + s.aesKey.cipherTextKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)]; + s.aesKey.cipherHeaderKey = cipherKeys[deterministicRandom()->randomInt(0, NUM_CIPHER)]; + return s; + } + +private: + Reference generateCipherKey(const BlobCipherDetails& cipherDetails) { + static unsigned char SHA_KEY[] = "3ab9570b44b8315fdb261da6b1b6c13b"; + Arena arena; + StringRef digest = computeAuthToken(reinterpret_cast(&cipherDetails.baseCipherId), + sizeof(EncryptCipherBaseKeyId), + SHA_KEY, + AES_256_KEY_LENGTH, + arena); + return makeReference(cipherDetails.encryptDomainId, + cipherDetails.baseCipherId, + digest.begin(), + AES_256_KEY_LENGTH, + cipherDetails.salt, + std::numeric_limits::max() /* refreshAt */, + std::numeric_limits::max() /* expireAt */); + } + + static constexpr int NUM_CIPHER = 1000; + Reference cipherKeys[NUM_CIPHER]; +}; + +// Key provider which extract tenant id from range key prefixes, and fetch tenant specific encryption keys from +// EncryptKeyProxy. +class TenantAwareEncryptionKeyProvider : public IPageEncryptionKeyProvider { +public: + using EncodingHeader = ArenaPage::AESEncryptionV1Encoder::Header; + + TenantAwareEncryptionKeyProvider(Reference const> db) : db(db) {} + + virtual ~TenantAwareEncryptionKeyProvider() = default; + + EncodingType expectedEncodingType() const override { return EncodingType::AESEncryptionV1; } + + bool enableEncryption() const override { + return isEncryptionOpSupported(EncryptOperationType::STORAGE_SERVER_ENCRYPTION, db->get().client); + } + + bool enableEncryptionDomain() const override { return true; } + + ACTOR static Future getEncryptionKey(TenantAwareEncryptionKeyProvider* self, void* encodingHeader) { + BlobCipherEncryptHeader* header = reinterpret_cast(encodingHeader); + TextAndHeaderCipherKeys cipherKeys = + wait(getEncryptCipherKeys(self->db, *header, BlobCipherMetrics::KV_REDWOOD)); + EncryptionKey encryptionKey; + encryptionKey.aesKey = cipherKeys; + return encryptionKey; + } + + Future getEncryptionKey(void* encodingHeader) override { + return getEncryptionKey(this, encodingHeader); + } + + Future getLatestDefaultEncryptionKey() override { + return getLatestEncryptionKey(getDefaultEncryptionDomainId()); + } + + ACTOR static Future getLatestEncryptionKey(TenantAwareEncryptionKeyProvider* self, + int64_t domainId) { + + EncryptCipherDomainNameRef domainName = self->getDomainName(domainId); + TextAndHeaderCipherKeys cipherKeys = + wait(getLatestEncryptCipherKeysForDomain(self->db, domainId, domainName, BlobCipherMetrics::KV_REDWOOD)); + EncryptionKey encryptionKey; + encryptionKey.aesKey = cipherKeys; + return encryptionKey; + } + + Future getLatestEncryptionKey(int64_t domainId) override { + return getLatestEncryptionKey(this, domainId); + } + + int64_t getDefaultEncryptionDomainId() const override { return FDB_DEFAULT_ENCRYPT_DOMAIN_ID; } + + std::tuple getEncryptionDomain(const KeyRef& key, Optional possibleDomainId) override { + // System key. + if (key.startsWith("\xff\xff"_sr)) { + return { SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID, 2 }; + } + // Key smaller than tenant prefix in size belongs to the default domain. + if (key.size() < TENANT_PREFIX_SIZE) { + return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; + } + StringRef prefix = key.substr(0, TENANT_PREFIX_SIZE); + int64_t tenantId = TenantMapEntry::prefixToId(prefix); + // Tenant id must be non-negative. + if (tenantId < 0) { + return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; + } + // Optimization: Caller guarantee possibleDomainId is a valid domain id that we previously returned. + // We can return immediately without checking with tenant map. + if (possibleDomainId.present() && possibleDomainId.get() == tenantId) { + return { tenantId, TENANT_PREFIX_SIZE }; + } + if (tenantPrefixIndex.isValid()) { + auto view = tenantPrefixIndex->atLatest(); + auto itr = view.find(prefix); + if (itr != view.end()) { + // Tenant not found. Tenant must be disabled, or in optional mode. + return { tenantId, TENANT_PREFIX_SIZE }; + } + } + // The prefix does not belong to any tenant. The key belongs to the default domain. + return { FDB_DEFAULT_ENCRYPT_DOMAIN_ID, 0 }; + } + + int64_t getEncryptionDomain(void* encodingHeader) override { + BlobCipherEncryptHeader* header = reinterpret_cast(encodingHeader); + return header->cipherTextDetails.encryptDomainId; + } + + void setTenantPrefixIndex(Reference tenantPrefixIndex) override { + this->tenantPrefixIndex = tenantPrefixIndex; + } + +private: + EncryptCipherDomainNameRef getDomainName(int64_t domainId) { + if (domainId == SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID) { + return FDB_SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_NAME; + } + if (domainId == FDB_DEFAULT_ENCRYPT_DOMAIN_ID) { + return FDB_DEFAULT_ENCRYPT_DOMAIN_NAME; + } + if (tenantPrefixIndex.isValid()) { + StringRef prefix = TenantMapEntry::idToPrefix(domainId); + auto view = tenantPrefixIndex->atLatest(); + auto itr = view.find(prefix); + if (itr != view.end()) { + return *itr; + } + } + TraceEvent(SevWarn, "TenantAwareEncryptionKeyProvider_TenantNotFoundForDomain").detail("DomainId", domainId); + throw tenant_not_found(); + } + + Reference const> db; + Reference tenantPrefixIndex; +}; + +#include "flow/unactorcompiler.h" +#endif \ No newline at end of file diff --git a/fdbserver/include/fdbserver/IPager.h b/fdbserver/include/fdbserver/IPager.h index 440b034aa6..f3aa119afc 100644 --- a/fdbserver/include/fdbserver/IPager.h +++ b/fdbserver/include/fdbserver/IPager.h @@ -23,11 +23,11 @@ #define FDBSERVER_IPAGER_H #include #include +#include "fdbclient/BlobCipher.h" #include "fdbclient/FDBTypes.h" +#include "fdbclient/GetEncryptCipherKeys.actor.h" #include "fdbclient/Tenant.h" -#include "fdbserver/IEncryptionKeyProvider.actor.h" -#include "fdbserver/IKeyValueStore.h" -#include "flow/BlobCipher.h" +#include "fdbserver/IClosable.h" #include "flow/Error.h" #include "flow/FastAlloc.h" #include "flow/flow.h" @@ -220,6 +220,14 @@ public: uint8_t* rawData() { return buffer; } int rawSize() const { return bufferSize; } + // Encryption key used to encrypt a page. Different encoding types may use different structs to represent + // an encryption key, and EncryptionKeyRef is a union of these structs. + struct EncryptionKeyRef { + TextAndHeaderCipherKeys aesKey; // For AESEncryptionV1 + uint8_t xorKey; // For XOREncryption_TestOnly + }; + using EncryptionKey = Standalone; + #pragma pack(push, 1) // The next few structs describe the byte-packed physical structure. The fields of Page @@ -244,10 +252,7 @@ public: } // Get encoding header pointer, casting to its type - template - T* getEncodingHeader() const { - return (T*)((uint8_t*)this + encodingHeaderOffset); - } + void* getEncodingHeader() const { return (uint8_t*)this + encodingHeaderOffset; } // Get payload pointer uint8_t* getPayload() const { return (uint8_t*)this + payloadOffset; } @@ -303,12 +308,18 @@ public: // An encoding that validates the payload with an XXHash checksum struct XXHashEncoder { - XXH64_hash_t checksum; - void encode(uint8_t* payload, int len, PhysicalPageID seed) { - checksum = XXH3_64bits_withSeed(payload, len, seed); + struct Header { + XXH64_hash_t checksum; + }; + + static void encode(void* header, uint8_t* payload, int len, PhysicalPageID seed) { + Header* h = reinterpret_cast(header); + h->checksum = XXH3_64bits_withSeed(payload, len, seed); } - void decode(uint8_t* payload, int len, PhysicalPageID seed) { - if (checksum != XXH3_64bits_withSeed(payload, len, seed)) { + + static void decode(void* header, uint8_t* payload, int len, PhysicalPageID seed) { + Header* h = reinterpret_cast(header); + if (h->checksum != XXH3_64bits_withSeed(payload, len, seed)) { throw page_decoding_failed(); } } @@ -317,42 +328,61 @@ public: // A dummy "encrypting" encoding which uses XOR with a 1 byte secret key on // the payload to obfuscate it and protects the payload with an XXHash checksum. struct XOREncryptionEncoder { - // Checksum is on unencrypted payload - XXH64_hash_t checksum; - uint8_t keyID; + struct Header { + // Checksum is on unencrypted payload + XXH64_hash_t checksum; + uint8_t xorKey; + }; - void encode(uint8_t secret, uint8_t* payload, int len, PhysicalPageID seed) { - checksum = XXH3_64bits_withSeed(payload, len, seed); + static void encode(void* header, + const EncryptionKey& encryptionKey, + uint8_t* payload, + int len, + PhysicalPageID seed) { + Header* h = reinterpret_cast(header); + h->checksum = XXH3_64bits_withSeed(payload, len, seed); + h->xorKey = encryptionKey.xorKey; for (int i = 0; i < len; ++i) { - payload[i] ^= secret; + payload[i] ^= h->xorKey; } } - void decode(uint8_t secret, uint8_t* payload, int len, PhysicalPageID seed) { + + static void decode(void* header, + const EncryptionKey& encryptionKey, + uint8_t* payload, + int len, + PhysicalPageID seed) { + Header* h = reinterpret_cast(header); for (int i = 0; i < len; ++i) { - payload[i] ^= secret; + payload[i] ^= h->xorKey; } - if (checksum != XXH3_64bits_withSeed(payload, len, seed)) { + if (h->checksum != XXH3_64bits_withSeed(payload, len, seed)) { throw page_decoding_failed(); } } }; struct AESEncryptionV1Encoder { - BlobCipherEncryptHeader header; + using Header = BlobCipherEncryptHeader; - void encode(const TextAndHeaderCipherKeys& cipherKeys, uint8_t* payload, int len) { - EncryptBlobCipherAes265Ctr cipher( - cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE); + static void encode(void* header, const TextAndHeaderCipherKeys& cipherKeys, uint8_t* payload, int len) { + Header* h = reinterpret_cast(header); + EncryptBlobCipherAes265Ctr cipher(cipherKeys.cipherTextKey, + cipherKeys.cipherHeaderKey, + ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE, + BlobCipherMetrics::KV_REDWOOD); Arena arena; - StringRef ciphertext = cipher.encrypt(payload, len, &header, arena)->toStringRef(); + StringRef ciphertext = cipher.encrypt(payload, len, h, arena)->toStringRef(); ASSERT_EQ(len, ciphertext.size()); memcpy(payload, ciphertext.begin(), len); } - void decode(const TextAndHeaderCipherKeys& cipherKeys, uint8_t* payload, int len) { - DecryptBlobCipherAes256Ctr cipher(cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, header.iv); + static void decode(void* header, const TextAndHeaderCipherKeys& cipherKeys, uint8_t* payload, int len) { + Header* h = reinterpret_cast(header); + DecryptBlobCipherAes256Ctr cipher( + cipherKeys.cipherTextKey, cipherKeys.cipherHeaderKey, h->iv, BlobCipherMetrics::KV_REDWOOD); Arena arena; - StringRef plaintext = cipher.decrypt(payload, len, header, arena)->toStringRef(); + StringRef plaintext = cipher.decrypt(payload, len, *h, arena)->toStringRef(); ASSERT_EQ(len, plaintext.size()); memcpy(payload, plaintext.begin(), len); } @@ -364,11 +394,11 @@ public: // existing pages, the payload offset is stored in the page. static int encodingHeaderSize(EncodingType t) { if (t == EncodingType::XXHash64) { - return sizeof(XXHashEncoder); + return sizeof(XXHashEncoder::Header); } else if (t == EncodingType::XOREncryption_TestOnly) { - return sizeof(XOREncryptionEncoder); + return sizeof(XOREncryptionEncoder::Header); } else if (t == EncodingType::AESEncryptionV1) { - return sizeof(AESEncryptionV1Encoder); + return sizeof(AESEncryptionV1Encoder::Header); } else { throw page_encoding_not_supported(); } @@ -472,15 +502,11 @@ public: ASSERT(VALGRIND_CHECK_MEM_IS_DEFINED(pPayload, payloadSize) == 0); if (page->encodingType == EncodingType::XXHash64) { - page->getEncodingHeader()->encode(pPayload, payloadSize, pageID); + XXHashEncoder::encode(page->getEncodingHeader(), pPayload, payloadSize, pageID); } else if (page->encodingType == EncodingType::XOREncryption_TestOnly) { - ASSERT(encryptionKey.secret.size() == 1); - XOREncryptionEncoder* xh = page->getEncodingHeader(); - xh->keyID = encryptionKey.id.orDefault(0); - xh->encode(encryptionKey.secret[0], pPayload, payloadSize, pageID); + XOREncryptionEncoder::encode(page->getEncodingHeader(), encryptionKey, pPayload, payloadSize, pageID); } else if (page->encodingType == EncodingType::AESEncryptionV1) { - AESEncryptionV1Encoder* eh = page->getEncodingHeader(); - eh->encode(encryptionKey.cipherKeys, pPayload, payloadSize); + AESEncryptionV1Encoder::encode(page->getEncodingHeader(), encryptionKey.aesKey, pPayload, payloadSize); } else { throw page_encoding_not_supported(); } @@ -503,14 +529,6 @@ public: pPayload = page->getPayload(); payloadSize = logicalSize - (pPayload - buffer); - // Populate encryption key with relevant fields from page - if (page->encodingType == EncodingType::XOREncryption_TestOnly) { - encryptionKey.id = page->getEncodingHeader()->keyID; - } else if (page->encodingType == EncodingType::AESEncryptionV1) { - AESEncryptionV1Encoder* eh = page->getEncodingHeader(); - encryptionKey.cipherHeader = eh->header; - } - if (page->headerVersion == 1) { if (verify) { RedwoodHeaderV1* h = page->getMainHeader(); @@ -528,13 +546,11 @@ public: // Post: Payload has been verified and decrypted if necessary void postReadPayload(PhysicalPageID pageID) { if (page->encodingType == EncodingType::XXHash64) { - page->getEncodingHeader()->decode(pPayload, payloadSize, pageID); + XXHashEncoder::decode(page->getEncodingHeader(), pPayload, payloadSize, pageID); } else if (page->encodingType == EncodingType::XOREncryption_TestOnly) { - ASSERT(encryptionKey.secret.size() == 1); - page->getEncodingHeader()->decode( - encryptionKey.secret[0], pPayload, payloadSize, pageID); + XOREncryptionEncoder::decode(page->getEncodingHeader(), encryptionKey, pPayload, payloadSize, pageID); } else if (page->encodingType == EncodingType::AESEncryptionV1) { - page->getEncodingHeader()->decode(encryptionKey.cipherKeys, pPayload, payloadSize); + AESEncryptionV1Encoder::decode(page->getEncodingHeader(), encryptionKey.aesKey, pPayload, payloadSize); } else { throw page_encoding_not_supported(); } @@ -549,6 +565,8 @@ public: // Returns true if the page's encoding type employs encryption bool isEncrypted() const { return isEncodingTypeEncrypted(getEncodingType()); } + void* getEncodingHeader() { return page->getEncodingHeader(); } + private: Arena arena; diff --git a/fdbserver/include/fdbserver/KmsConnectorInterface.h b/fdbserver/include/fdbserver/KmsConnectorInterface.h index 73afc1cbc0..ac31bc5d37 100644 --- a/fdbserver/include/fdbserver/KmsConnectorInterface.h +++ b/fdbserver/include/fdbserver/KmsConnectorInterface.h @@ -132,7 +132,7 @@ struct KmsConnLookupKeyIdsReqInfoRef { EncryptCipherDomainNameRef domainName; KmsConnLookupKeyIdsReqInfoRef() - : domainId(ENCRYPT_INVALID_DOMAIN_ID), baseCipherId(ENCRYPT_INVALID_CIPHER_KEY_ID) {} + : domainId(INVALID_ENCRYPT_DOMAIN_ID), baseCipherId(INVALID_ENCRYPT_CIPHER_KEY_ID) {} explicit KmsConnLookupKeyIdsReqInfoRef(Arena& arena, const EncryptCipherDomainId dId, const EncryptCipherBaseKeyId bCId, @@ -185,7 +185,7 @@ struct KmsConnLookupDomainIdsReqInfoRef { EncryptCipherDomainId domainId; EncryptCipherDomainNameRef domainName; - KmsConnLookupDomainIdsReqInfoRef() : domainId(ENCRYPT_INVALID_DOMAIN_ID) {} + KmsConnLookupDomainIdsReqInfoRef() : domainId(INVALID_ENCRYPT_DOMAIN_ID) {} explicit KmsConnLookupDomainIdsReqInfoRef(Arena& arena, const EncryptCipherDomainId dId, StringRef name) : domainId(dId), domainName(StringRef(arena, name)) {} explicit KmsConnLookupDomainIdsReqInfoRef(const EncryptCipherDomainId dId, StringRef name) diff --git a/fdbserver/include/fdbserver/LocalConfiguration.h b/fdbserver/include/fdbserver/LocalConfiguration.h index 5458e9b928..f748408775 100644 --- a/fdbserver/include/fdbserver/LocalConfiguration.h +++ b/fdbserver/include/fdbserver/LocalConfiguration.h @@ -64,7 +64,8 @@ public: public: // Testing Future addChanges(Standalone> versionedMutations, - Version mostRecentVersion); + Version mostRecentVersion, + double restartDelay); void close(); Future onClosed(); }; diff --git a/fdbserver/include/fdbserver/MoveKeys.actor.h b/fdbserver/include/fdbserver/MoveKeys.actor.h index b809b02521..f0c142a9b4 100644 --- a/fdbserver/include/fdbserver/MoveKeys.actor.h +++ b/fdbserver/include/fdbserver/MoveKeys.actor.h @@ -56,6 +56,20 @@ public: bool setDDEnabled(bool status, UID snapUID); }; +struct MoveKeysParams { + UID dataMoveId; + KeyRange keys; + std::vector destinationTeam, healthyDestinations; + MoveKeysLock lock; + Promise dataMovementComplete; + FlowLock* startMoveKeysParallelismLock = nullptr; + FlowLock* finishMoveKeysParallelismLock = nullptr; + bool hasRemote; + UID relocationIntervalId; + const DDEnabledState* ddEnabledState = nullptr; + CancelConflictingDataMoves cancelConflictingDataMoves = CancelConflictingDataMoves::False; +}; + // Calling moveKeys, etc with the return value of this actor ensures that no movekeys, etc // has been executed by a different locker since takeMoveKeysLock(), as calling // takeMoveKeysLock() updates "moveKeysLockOwnerKey" to a random UID. @@ -74,19 +88,7 @@ void seedShardServers(Arena& trArena, CommitTransactionRef& tr, std::vector moveKeys(Database occ, - UID dataMoveId, - KeyRange keys, - std::vector destinationTeam, - std::vector healthyDestinations, - MoveKeysLock lock, - Promise dataMovementComplete, - FlowLock* startMoveKeysParallelismLock, - FlowLock* finishMoveKeysParallelismLock, - bool hasRemote, - UID relocationIntervalId, // for logging only - const DDEnabledState* ddEnabledState, - CancelConflictingDataMoves cancelConflictingDataMoves = CancelConflictingDataMoves::False); +ACTOR Future moveKeys(Database occ, MoveKeysParams params); // Cancels a data move designated by dataMoveId. ACTOR Future cleanUpDataMove(Database occ, diff --git a/fdbserver/include/fdbserver/PaxosConfigConsumer.h b/fdbserver/include/fdbserver/PaxosConfigConsumer.h index fc9e424d23..b3e01bc761 100644 --- a/fdbserver/include/fdbserver/PaxosConfigConsumer.h +++ b/fdbserver/include/fdbserver/PaxosConfigConsumer.h @@ -32,13 +32,17 @@ class PaxosConfigConsumer : public IConfigConsumer { public: PaxosConfigConsumer(ServerCoordinators const& coordinators, double pollingInterval, - Optional compactionInterval); + Optional compactionInterval, + bool readPreviousCoordinators); ~PaxosConfigConsumer(); + Future readSnapshot(ConfigBroadcaster& broadcaster) override; Future consume(ConfigBroadcaster& broadcaster) override; + void allowSpecialCaseRollforward() override; UID getID() const override; public: // Testing PaxosConfigConsumer(std::vector const& cfis, double pollingInterval, - Optional compactionInterval); + Optional compactionInterval, + bool readPreviousCoordinators); }; diff --git a/fdbserver/include/fdbserver/ProxyCommitData.actor.h b/fdbserver/include/fdbserver/ProxyCommitData.actor.h index c6e173387a..a6cd7c1575 100644 --- a/fdbserver/include/fdbserver/ProxyCommitData.actor.h +++ b/fdbserver/include/fdbserver/ProxyCommitData.actor.h @@ -20,6 +20,7 @@ #pragma once #include "fdbserver/EncryptionOpsUtils.h" +#include #if defined(NO_INTELLISENSE) && !defined(FDBSERVER_PROXYCOMMITDATA_ACTOR_G_H) #define FDBSERVER_PROXYCOMMITDATA_ACTOR_G_H #include "fdbserver/ProxyCommitData.actor.g.h" @@ -135,27 +136,17 @@ struct ProxyStats { SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, SERVER_KNOBS->LATENCY_SAMPLE_SIZE), maxComputeNS(0), minComputeNS(1e12), - commitBatchQueuingDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("CommitBatchQueuing"), - Histogram::Unit::microseconds)), - getCommitVersionDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("GetCommitVersion"), - Histogram::Unit::microseconds)), - resolutionDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("Resolution"), - Histogram::Unit::microseconds)), - postResolutionDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("PostResolutionQueuing"), - Histogram::Unit::microseconds)), - processingMutationDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("ProcessingMutation"), - Histogram::Unit::microseconds)), - tlogLoggingDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("TlogLogging"), - Histogram::Unit::microseconds)), - replyCommitDist(Histogram::getHistogram(LiteralStringRef("CommitProxy"), - LiteralStringRef("ReplyCommit"), - Histogram::Unit::microseconds)) { + commitBatchQueuingDist( + Histogram::getHistogram("CommitProxy"_sr, "CommitBatchQueuing"_sr, Histogram::Unit::microseconds)), + getCommitVersionDist( + Histogram::getHistogram("CommitProxy"_sr, "GetCommitVersion"_sr, Histogram::Unit::microseconds)), + resolutionDist(Histogram::getHistogram("CommitProxy"_sr, "Resolution"_sr, Histogram::Unit::microseconds)), + postResolutionDist( + Histogram::getHistogram("CommitProxy"_sr, "PostResolutionQueuing"_sr, Histogram::Unit::microseconds)), + processingMutationDist( + Histogram::getHistogram("CommitProxy"_sr, "ProcessingMutation"_sr, Histogram::Unit::microseconds)), + tlogLoggingDist(Histogram::getHistogram("CommitProxy"_sr, "TlogLogging"_sr, Histogram::Unit::microseconds)), + replyCommitDist(Histogram::getHistogram("CommitProxy"_sr, "ReplyCommit"_sr, Histogram::Unit::microseconds)) { specialCounter(cc, "LastAssignedCommitVersion", [this]() { return this->lastCommitVersionAssigned; }); specialCounter(cc, "Version", [pVersion]() { return pVersion->get(); }); specialCounter(cc, "CommittedVersion", [pCommittedVersion]() { return pCommittedVersion->get(); }); @@ -173,6 +164,7 @@ struct ProxyCommitData { UID dbgid; int64_t commitBatchesMemBytesCount; std::map tenantMap; + std::unordered_map tenantIdIndex; ProxyStats stats; MasterInterface master; std::vector resolvers; @@ -300,9 +292,9 @@ struct ProxyCommitData { commitBatchInterval(SERVER_KNOBS->COMMIT_TRANSACTION_BATCH_INTERVAL_MIN), localCommitBatchesStarted(0), getConsistentReadVersion(getConsistentReadVersion), commit(commit), cx(openDBOnServer(db, TaskPriority::DefaultEndpoint, LockAware::True)), db(db), - singleKeyMutationEvent(LiteralStringRef("SingleKeyMutation")), lastTxsPop(0), popRemoteTxs(false), - lastStartCommit(0), lastCommitLatency(SERVER_KNOBS->REQUIRED_MIN_RECOVERY_DURATION), lastCommitTime(0), - lastMasterReset(now()), lastResolverReset(now()), + singleKeyMutationEvent("SingleKeyMutation"_sr), lastTxsPop(0), popRemoteTxs(false), lastStartCommit(0), + lastCommitLatency(SERVER_KNOBS->REQUIRED_MIN_RECOVERY_DURATION), lastCommitTime(0), lastMasterReset(now()), + lastResolverReset(now()), isEncryptionEnabled(isEncryptionOpSupported(EncryptOperationType::TLOG_ENCRYPTION, db->get().client)) { commitComputePerOperation.resize(SERVER_KNOBS->PROXY_COMPUTE_BUCKETS, 0.0); } diff --git a/fdbserver/include/fdbserver/RestoreApplier.actor.h b/fdbserver/include/fdbserver/RestoreApplier.actor.h index 464b8fea8a..da370bc62c 100644 --- a/fdbserver/include/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/include/fdbserver/RestoreApplier.actor.h @@ -371,7 +371,7 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted ratekeeper; Optional blobManager; Optional encryptKeyProxy; + Optional consistencyScan; std::vector resolvers; DBRecoveryCount recoveryCount; // A recovery count from DBCoreState. A successful cluster recovery increments it twice; @@ -83,6 +85,7 @@ struct ServerDBInfo { ratekeeper, blobManager, encryptKeyProxy, + consistencyScan, resolvers, recoveryCount, recoveryState, diff --git a/fdbserver/include/fdbserver/SimKmsConnector.h b/fdbserver/include/fdbserver/SimKmsConnector.h index c26ba808b4..51c39ebf8f 100644 --- a/fdbserver/include/fdbserver/SimKmsConnector.h +++ b/fdbserver/include/fdbserver/SimKmsConnector.h @@ -22,8 +22,8 @@ #define SIM_KMS_CONNECTOR_H #pragma once +#include "fdbclient/BlobCipher.h" #include "fdbserver/KmsConnector.h" -#include "flow/BlobCipher.h" class SimKmsConnector : public KmsConnector { public: diff --git a/fdbserver/include/fdbserver/SimpleConfigConsumer.h b/fdbserver/include/fdbserver/SimpleConfigConsumer.h index 8701f51d60..a52d40f29d 100644 --- a/fdbserver/include/fdbserver/SimpleConfigConsumer.h +++ b/fdbserver/include/fdbserver/SimpleConfigConsumer.h @@ -37,7 +37,9 @@ public: double pollingInterval, Optional compactionInterval); ~SimpleConfigConsumer(); + Future readSnapshot(ConfigBroadcaster& broadcaster) override; Future consume(ConfigBroadcaster& broadcaster) override; + void allowSpecialCaseRollforward() override { ASSERT(false); } UID getID() const override; public: // Testing diff --git a/fdbserver/include/fdbserver/TesterInterface.actor.h b/fdbserver/include/fdbserver/TesterInterface.actor.h index 7d049eac3d..06da38d4a0 100644 --- a/fdbserver/include/fdbserver/TesterInterface.actor.h +++ b/fdbserver/include/fdbserver/TesterInterface.actor.h @@ -66,6 +66,7 @@ struct WorkloadRequest { double databasePingDelay; int64_t sharedRandomNumber; bool useDatabase; + bool runFailureWorkloads = true; Optional defaultTenant; // The vector of option lists are to construct compound workloads. If there @@ -98,6 +99,7 @@ struct WorkloadRequest { clientCount, reply, defaultTenant, + runFailureWorkloads, arena); } }; diff --git a/fdbserver/include/fdbserver/VFSAsync.h b/fdbserver/include/fdbserver/VFSAsync.h index d5568b41fe..2a0ac8caeb 100644 --- a/fdbserver/include/fdbserver/VFSAsync.h +++ b/fdbserver/include/fdbserver/VFSAsync.h @@ -68,7 +68,7 @@ struct VFSAsyncFile { // Error code is only checked for non-zero because the SQLite API error code after an injected error // may not match the error code returned by VFSAsyncFile when the inject error occurred. bool e = g_network->global(INetwork::enSQLiteInjectedError) != (flowGlobalType)0; - bool f = g_simulator.checkInjectedCorruption(); + bool f = g_simulator->checkInjectedCorruption(); TraceEvent("VFSCheckInjectedError") .detail("InjectedIOError", e) .detail("InjectedCorruption", f) diff --git a/fdbserver/include/fdbserver/WorkerInterface.actor.h b/fdbserver/include/fdbserver/WorkerInterface.actor.h index ca168fe96d..cd2207da60 100644 --- a/fdbserver/include/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/include/fdbserver/WorkerInterface.actor.h @@ -31,6 +31,7 @@ #include "fdbserver/MasterInterface.h" #include "fdbserver/TLogInterface.h" #include "fdbserver/RatekeeperInterface.h" +#include "fdbclient/ConsistencyScanInterface.h" #include "fdbserver/BlobManagerInterface.h" #include "fdbserver/ResolverInterface.h" #include "fdbclient/BlobWorkerInterface.h" @@ -57,6 +58,7 @@ struct WorkerInterface { RequestStream ratekeeper; RequestStream blobManager; RequestStream blobWorker; + RequestStream consistencyScan; RequestStream resolver; RequestStream storage; RequestStream logRouter; @@ -74,7 +76,6 @@ struct WorkerInterface { RequestStream workerSnapReq; RequestStream updateServerDBInfo; - ConfigBroadcastInterface configBroadcastInterface; TesterInterface testerInterface; UID id() const { return tLog.getEndpoint().token; } @@ -113,6 +114,7 @@ struct WorkerInterface { ratekeeper, blobManager, blobWorker, + consistencyScan, resolver, storage, logRouter, @@ -128,8 +130,7 @@ struct WorkerInterface { workerSnapReq, backup, encryptKeyProxy, - updateServerDBInfo, - configBroadcastInterface); + updateServerDBInfo); } }; @@ -430,14 +431,16 @@ struct RegisterWorkerRequest { Optional ratekeeperInterf; Optional blobManagerInterf; Optional encryptKeyProxyInterf; + Optional consistencyScanInterf; Standalone> issues; std::vector incompatiblePeers; ReplyPromise reply; bool degraded; - Version lastSeenKnobVersion; - ConfigClassSet knobConfigClassSet; + Optional lastSeenKnobVersion; + Optional knobConfigClassSet; bool requestDbInfo; bool recoveredDiskFiles; + ConfigBroadcastInterface configBroadcastInterface; RegisterWorkerRequest() : priorityInfo(ProcessClass::UnsetFit, false, ClusterControllerPriorityInfo::FitnessUnknown), degraded(false) {} @@ -450,14 +453,17 @@ struct RegisterWorkerRequest { Optional rkInterf, Optional bmInterf, Optional ekpInterf, + Optional csInterf, bool degraded, - Version lastSeenKnobVersion, - ConfigClassSet knobConfigClassSet, - bool recoveredDiskFiles) + Optional lastSeenKnobVersion, + Optional knobConfigClassSet, + bool recoveredDiskFiles, + ConfigBroadcastInterface configBroadcastInterface) : wi(wi), initialClass(initialClass), processClass(processClass), priorityInfo(priorityInfo), generation(generation), distributorInterf(ddInterf), ratekeeperInterf(rkInterf), blobManagerInterf(bmInterf), - encryptKeyProxyInterf(ekpInterf), degraded(degraded), lastSeenKnobVersion(lastSeenKnobVersion), - knobConfigClassSet(knobConfigClassSet), requestDbInfo(false), recoveredDiskFiles(recoveredDiskFiles) {} + encryptKeyProxyInterf(ekpInterf), consistencyScanInterf(csInterf), degraded(degraded), + lastSeenKnobVersion(lastSeenKnobVersion), knobConfigClassSet(knobConfigClassSet), requestDbInfo(false), + recoveredDiskFiles(recoveredDiskFiles), configBroadcastInterface(configBroadcastInterface) {} template void serialize(Ar& ar) { @@ -471,6 +477,7 @@ struct RegisterWorkerRequest { ratekeeperInterf, blobManagerInterf, encryptKeyProxyInterf, + consistencyScanInterf, issues, incompatiblePeers, reply, @@ -478,7 +485,8 @@ struct RegisterWorkerRequest { lastSeenKnobVersion, knobConfigClassSet, requestDbInfo, - recoveredDiskFiles); + recoveredDiskFiles, + configBroadcastInterface); } }; @@ -726,6 +734,19 @@ struct InitializeRatekeeperRequest { } }; +struct InitializeConsistencyScanRequest { + constexpr static FileIdentifier file_identifier = 3104275; + UID reqId; + ReplyPromise reply; + + InitializeConsistencyScanRequest() {} + explicit InitializeConsistencyScanRequest(UID uid) : reqId(uid) {} + template + void serialize(Ar& ar) { + serializer(ar, reqId, reply); + } +}; + struct InitializeBlobManagerRequest { constexpr static FileIdentifier file_identifier = 2567474; UID reqId; @@ -988,6 +1009,7 @@ struct Role { static const Role COORDINATOR; static const Role BACKUP; static const Role ENCRYPT_KEY_PROXY; + static const Role CONSISTENCYSCAN; std::string roleName; std::string abbreviation; @@ -1025,6 +1047,8 @@ struct Role { return BACKUP; case ProcessClass::EncryptKeyProxy: return ENCRYPT_KEY_PROXY; + case ProcessClass::ConsistencyScan: + return CONSISTENCYSCAN; case ProcessClass::Worker: return WORKER; case ProcessClass::NoRole: @@ -1092,7 +1116,7 @@ ACTOR Future encryptKeyProxyServer(EncryptKeyProxyInterface ei, Reference< class IKeyValueStore; class ServerCoordinators; class IDiskQueue; -class IEncryptionKeyProvider; +class IPageEncryptionKeyProvider; ACTOR Future storageServer(IKeyValueStore* persistentData, StorageServerInterface ssi, Tag seedTag, @@ -1102,7 +1126,7 @@ ACTOR Future storageServer(IKeyValueStore* persistentData, ReplyPromise recruitReply, Reference const> db, std::string folder, - Reference encryptionKeyProvider); + Reference encryptionKeyProvider); ACTOR Future storageServer( IKeyValueStore* persistentData, StorageServerInterface ssi, @@ -1111,7 +1135,7 @@ ACTOR Future storageServer( Promise recovered, Reference connRecord, // changes pssi->id() to be the recovered ID); // changes pssi->id() to be the recovered ID - Reference encryptionKeyProvider); + Reference encryptionKeyProvider); ACTOR Future masterServer(MasterInterface mi, Reference const> db, Reference> const> ccInterface, @@ -1146,6 +1170,7 @@ ACTOR Future logRouter(TLogInterface interf, Reference const> db); ACTOR Future dataDistributor(DataDistributorInterface ddi, Reference const> db); ACTOR Future ratekeeper(RatekeeperInterface rki, Reference const> db); +ACTOR Future consistencyScan(ConsistencyScanInterface csInterf, Reference const> dbInfo); ACTOR Future blobManager(BlobManagerInterface bmi, Reference const> db, int64_t epoch); ACTOR Future storageCacheServer(StorageServerInterface interf, uint16_t id, @@ -1210,7 +1235,7 @@ ACTOR template Future ioTimeoutError(Future what, double time) { // Before simulation is sped up, IO operations can take a very long time so limit timeouts // to not end until at least time after simulation is sped up. - if (g_network->isSimulated() && !g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation) { time += std::max(0.0, FLOW_KNOBS->SIM_SPEEDUP_AFTER_SECONDS - now()); } Future end = lowPriorityDelay(time); @@ -1218,7 +1243,7 @@ Future ioTimeoutError(Future what, double time) { when(T t = wait(what)) { return t; } when(wait(end)) { Error err = io_timeout(); - if (g_network->isSimulated() && !g_simulator.getCurrentProcess()->isReliable()) { + if (g_network->isSimulated() && !g_simulator->getCurrentProcess()->isReliable()) { err = err.asInjectedFault(); } TraceEvent(SevError, "IoTimeoutError").error(err); @@ -1234,7 +1259,7 @@ Future ioDegradedOrTimeoutError(Future what, double degradedTime) { // Before simulation is sped up, IO operations can take a very long time so limit timeouts // to not end until at least time after simulation is sped up. - if (g_network->isSimulated() && !g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation) { double timeShift = std::max(0.0, FLOW_KNOBS->SIM_SPEEDUP_AFTER_SECONDS - now()); errTime += timeShift; degradedTime += timeShift; @@ -1257,7 +1282,7 @@ Future ioDegradedOrTimeoutError(Future what, when(T t = wait(what)) { return t; } when(wait(end)) { Error err = io_timeout(); - if (g_network->isSimulated() && !g_simulator.getCurrentProcess()->isReliable()) { + if (g_network->isSimulated() && !g_simulator->getCurrentProcess()->isReliable()) { err = err.asInjectedFault(); } TraceEvent(SevError, "IoTimeoutError").error(err); diff --git a/fdbserver/include/fdbserver/workloads/ApiWorkload.h b/fdbserver/include/fdbserver/workloads/ApiWorkload.h index b7f823bc10..bc41d36798 100644 --- a/fdbserver/include/fdbserver/workloads/ApiWorkload.h +++ b/fdbserver/include/fdbserver/workloads/ApiWorkload.h @@ -273,24 +273,24 @@ struct ApiWorkload : TestWorkload { ApiWorkload(WorkloadContext const& wcx, int maxClients = -1) : TestWorkload(wcx), maxClients(maxClients), success(true), transactionFactory(nullptr) { - clientPrefixInt = getOption(options, LiteralStringRef("clientId"), clientId); + clientPrefixInt = getOption(options, "clientId"_sr, clientId); clientPrefix = format("%010d", clientPrefixInt); - numKeys = getOption(options, LiteralStringRef("numKeys"), 5000); - onlyLowerCase = getOption(options, LiteralStringRef("onlyLowerCase"), false); - shortKeysRatio = getOption(options, LiteralStringRef("shortKeysRatio"), 0.5); - minShortKeyLength = getOption(options, LiteralStringRef("minShortKeyLength"), 1); - maxShortKeyLength = getOption(options, LiteralStringRef("maxShortKeyLength"), 3); - minLongKeyLength = getOption(options, LiteralStringRef("minLongKeyLength"), 1); - maxLongKeyLength = getOption(options, LiteralStringRef("maxLongKeyLength"), 128); - minValueLength = getOption(options, LiteralStringRef("minValueLength"), 1); - maxValueLength = getOption(options, LiteralStringRef("maxValueLength"), 10000); + numKeys = getOption(options, "numKeys"_sr, 5000); + onlyLowerCase = getOption(options, "onlyLowerCase"_sr, false); + shortKeysRatio = getOption(options, "shortKeysRatio"_sr, 0.5); + minShortKeyLength = getOption(options, "minShortKeyLength"_sr, 1); + maxShortKeyLength = getOption(options, "maxShortKeyLength"_sr, 3); + minLongKeyLength = getOption(options, "minLongKeyLength"_sr, 1); + maxLongKeyLength = getOption(options, "maxLongKeyLength"_sr, 128); + minValueLength = getOption(options, "minValueLength"_sr, 1); + maxValueLength = getOption(options, "maxValueLength"_sr, 10000); - useExtraDB = g_network->isSimulated() && !g_simulator.extraDatabases.empty(); + useExtraDB = g_network->isSimulated() && !g_simulator->extraDatabases.empty(); if (useExtraDB) { - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } } diff --git a/fdbserver/include/fdbserver/workloads/ReadWriteWorkload.actor.h b/fdbserver/include/fdbserver/workloads/ReadWriteWorkload.actor.h index fe33be0213..89bc36f393 100644 --- a/fdbserver/include/fdbserver/workloads/ReadWriteWorkload.actor.h +++ b/fdbserver/include/fdbserver/workloads/ReadWriteWorkload.actor.h @@ -86,51 +86,50 @@ struct ReadWriteCommon : KVWorkload { double loadTime, clientBegin; explicit ReadWriteCommon(WorkloadContext const& wcx) - : KVWorkload(wcx), totalReadsMetric(LiteralStringRef("ReadWrite.TotalReads")), - totalRetriesMetric(LiteralStringRef("ReadWrite.TotalRetries")), aTransactions("A Transactions"), - bTransactions("B Transactions"), retries("Retries"), latencies(sampleSize), readLatencies(sampleSize), - commitLatencies(sampleSize), GRVLatencies(sampleSize), fullReadLatencies(sampleSize), readLatencyTotal(0), - readLatencyCount(0), loadTime(0.0), clientBegin(0) { + : KVWorkload(wcx), totalReadsMetric("ReadWrite.TotalReads"_sr), totalRetriesMetric("ReadWrite.TotalRetries"_sr), + aTransactions("A Transactions"), bTransactions("B Transactions"), retries("Retries"), latencies(sampleSize), + readLatencies(sampleSize), commitLatencies(sampleSize), GRVLatencies(sampleSize), fullReadLatencies(sampleSize), + readLatencyTotal(0), readLatencyCount(0), loadTime(0.0), clientBegin(0) { - transactionSuccessMetric.init(LiteralStringRef("ReadWrite.SuccessfulTransaction")); - transactionFailureMetric.init(LiteralStringRef("ReadWrite.FailedTransaction")); - readMetric.init(LiteralStringRef("ReadWrite.Read")); + transactionSuccessMetric.init("ReadWrite.SuccessfulTransaction"_sr); + transactionFailureMetric.init("ReadWrite.FailedTransaction"_sr); + readMetric.init("ReadWrite.Read"_sr); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; - double allowedLatency = getOption(options, LiteralStringRef("allowedLatency"), 0.250); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; + double allowedLatency = getOption(options, "allowedLatency"_sr, 0.250); actorCount = ceil(transactionsPerSecond * allowedLatency); - actorCount = getOption(options, LiteralStringRef("actorCountPerTester"), actorCount); + actorCount = getOption(options, "actorCountPerTester"_sr, actorCount); - readsPerTransactionA = getOption(options, LiteralStringRef("readsPerTransactionA"), 10); - writesPerTransactionA = getOption(options, LiteralStringRef("writesPerTransactionA"), 0); - readsPerTransactionB = getOption(options, LiteralStringRef("readsPerTransactionB"), 1); - writesPerTransactionB = getOption(options, LiteralStringRef("writesPerTransactionB"), 9); - alpha = getOption(options, LiteralStringRef("alpha"), 0.1); + readsPerTransactionA = getOption(options, "readsPerTransactionA"_sr, 10); + writesPerTransactionA = getOption(options, "writesPerTransactionA"_sr, 0); + readsPerTransactionB = getOption(options, "readsPerTransactionB"_sr, 1); + writesPerTransactionB = getOption(options, "writesPerTransactionB"_sr, 9); + alpha = getOption(options, "alpha"_sr, 0.1); valueString = std::string(maxValueBytes, '.'); if (nodePrefix > 0) { keyBytes += 16; } - metricsStart = getOption(options, LiteralStringRef("metricsStart"), 0.0); - metricsDuration = getOption(options, LiteralStringRef("metricsDuration"), testDuration); - if (getOption(options, LiteralStringRef("discardEdgeMeasurements"), true)) { + metricsStart = getOption(options, "metricsStart"_sr, 0.0); + metricsDuration = getOption(options, "metricsDuration"_sr, testDuration); + if (getOption(options, "discardEdgeMeasurements"_sr, true)) { // discardEdgeMeasurements keeps the metrics from the middle 3/4 of the test metricsStart += testDuration * 0.125; metricsDuration *= 0.75; } - warmingDelay = getOption(options, LiteralStringRef("warmingDelay"), 0.0); - maxInsertRate = getOption(options, LiteralStringRef("maxInsertRate"), 1e12); - debugInterval = getOption(options, LiteralStringRef("debugInterval"), 0.0); - debugTime = getOption(options, LiteralStringRef("debugTime"), 0.0); - enableReadLatencyLogging = getOption(options, LiteralStringRef("enableReadLatencyLogging"), false); - periodicLoggingInterval = getOption(options, LiteralStringRef("periodicLoggingInterval"), 5.0); - cancelWorkersAtDuration = getOption(options, LiteralStringRef("cancelWorkersAtDuration"), true); + warmingDelay = getOption(options, "warmingDelay"_sr, 0.0); + maxInsertRate = getOption(options, "maxInsertRate"_sr, 1e12); + debugInterval = getOption(options, "debugInterval"_sr, 0.0); + debugTime = getOption(options, "debugTime"_sr, 0.0); + enableReadLatencyLogging = getOption(options, "enableReadLatencyLogging"_sr, false); + periodicLoggingInterval = getOption(options, "periodicLoggingInterval"_sr, 5.0); + cancelWorkersAtDuration = getOption(options, "cancelWorkersAtDuration"_sr, true); - useRYW = getOption(options, LiteralStringRef("useRYW"), false); - doSetup = getOption(options, LiteralStringRef("setup"), true); + useRYW = getOption(options, "useRYW"_sr, false); + doSetup = getOption(options, "setup"_sr, true); // Validate that keyForIndex() is monotonic for (int i = 0; i < 30; i++) { @@ -144,7 +143,7 @@ struct ReadWriteCommon : KVWorkload { } std::vector insertionCountsToMeasureString = - getOption(options, LiteralStringRef("insertionCountsToMeasure"), std::vector()); + getOption(options, "insertionCountsToMeasure"_sr, std::vector()); for (int i = 0; i < insertionCountsToMeasureString.size(); i++) { try { uint64_t count = boost::lexical_cast(insertionCountsToMeasureString[i]); diff --git a/fdbserver/include/fdbserver/workloads/workloads.actor.h b/fdbserver/include/fdbserver/workloads/workloads.actor.h index 36d3112ae4..b65785ef28 100644 --- a/fdbserver/include/fdbserver/workloads/workloads.actor.h +++ b/fdbserver/include/fdbserver/workloads/workloads.actor.h @@ -67,7 +67,7 @@ struct TestWorkload : NonCopyable, WorkloadContext, ReferenceCounted): explicit TestWorkload(WorkloadContext const& wcx) : WorkloadContext(wcx) { - bool runSetup = getOption(options, LiteralStringRef("runSetup"), true); + bool runSetup = getOption(options, "runSetup"_sr, true); phases = TestWorkload::EXECUTION | TestWorkload::CHECK | TestWorkload::METRICS; if (runSetup) phases |= TestWorkload::SETUP; @@ -92,6 +92,63 @@ private: virtual void getMetrics(std::vector& m) = 0; }; +struct CompoundWorkload; +class DeterministicRandom; + +struct NoOptions {}; + +struct FailureInjectionWorkload : TestWorkload { + FailureInjectionWorkload(WorkloadContext const&); + virtual ~FailureInjectionWorkload() {} + virtual bool add(DeterministicRandom& random, WorkloadRequest const& work, CompoundWorkload const& workload); + virtual void initFailureInjectionMode(DeterministicRandom& random, unsigned count); + + Future setupInjectionWorkload(Database const& cx, Future done); + Future startInjectionWorkload(Database const& cx, Future done); + Future checkInjectionWorkload(Database const& cx, Future done); +}; + +struct IFailureInjectorFactory : ReferenceCounted { + virtual ~IFailureInjectorFactory() = default; + static std::vector>& factories() { + static std::vector> _factories; + return _factories; + } + virtual Reference create(WorkloadContext const& wcx) = 0; +}; + +template +struct FailureInjectorFactory : IFailureInjectorFactory { + static_assert(std::is_base_of::value); + FailureInjectorFactory() { + IFailureInjectorFactory::factories().push_back(Reference::addRef(this)); + } + Reference create(WorkloadContext const& wcx) override { + return makeReference(wcx, NoOptions()); + } +}; + +struct CompoundWorkload : TestWorkload { + bool runFailureWorkloads = true; + std::vector> workloads; + std::vector> failureInjection; + + CompoundWorkload(WorkloadContext& wcx); + CompoundWorkload* add(Reference&& w); + void addFailureInjection(WorkloadRequest& work); + + std::string description() const override; + + Future setup(Database const& cx) override; + Future start(Database const& cx) override; + Future check(Database const& cx) override; + + Future> getMetrics() override; + double getCheckTimeout() const override; + + void getMetrics(std::vector&) override; +}; + struct WorkloadProcess; struct ClientWorkload : TestWorkload { WorkloadProcess* impl; @@ -116,15 +173,15 @@ struct KVWorkload : TestWorkload { double absentFrac; explicit KVWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - nodeCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)100000); - nodePrefix = getOption(options, LiteralStringRef("nodePrefix"), (int64_t)-1); - actorCount = getOption(options, LiteralStringRef("actorCount"), 50); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 4); - maxValueBytes = getOption(options, LiteralStringRef("valueBytes"), 96); - minValueBytes = getOption(options, LiteralStringRef("minValueBytes"), maxValueBytes); + nodeCount = getOption(options, "nodeCount"_sr, (uint64_t)100000); + nodePrefix = getOption(options, "nodePrefix"_sr, (int64_t)-1); + actorCount = getOption(options, "actorCount"_sr, 50); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 4); + maxValueBytes = getOption(options, "valueBytes"_sr, 96); + minValueBytes = getOption(options, "minValueBytes"_sr, maxValueBytes); ASSERT(minValueBytes <= maxValueBytes); - absentFrac = getOption(options, LiteralStringRef("absentFrac"), 0.0); + absentFrac = getOption(options, "absentFrac"_sr, 0.0); } Key getRandomKey() const; Key getRandomKey(double absentFrac) const; @@ -223,6 +280,7 @@ public: bool dumpAfterTest; bool clearAfterTest; bool useDB; + bool runFailureWorkloads = true; double startDelay; int phases; Standalone>> options; diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index 0978f36d3b..9b793cce56 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -400,8 +400,8 @@ ACTOR Future masterServer(MasterInterface mi, state Future onDBChange = Void(); state PromiseStream> addActor; - state Reference self(new MasterData( - db, mi, coordinators, db->get().clusterInterface, LiteralStringRef(""), addActor, forceRecovery)); + state Reference self( + new MasterData(db, mi, coordinators, db->get().clusterInterface, ""_sr, addActor, forceRecovery)); state Future collection = actorCollection(addActor.getFuture()); addActor.send(traceRole(Role::MASTER, mi.id())); diff --git a/fdbserver/pubsub.actor.cpp b/fdbserver/pubsub.actor.cpp index 49a52b49ae..8338c22b62 100644 --- a/fdbserver/pubsub.actor.cpp +++ b/fdbserver/pubsub.actor.cpp @@ -89,7 +89,7 @@ Key keyForFeedWatcher(uint64_t feed, uint64_t inbox) { return StringRef(format("f/%016llx/watchers/%016llx", feed, inbox)); } -Standalone messagePrefix(LiteralStringRef("m/")); +Standalone messagePrefix("m/"_sr); Key keyForMessage(uint64_t message) { return StringRef(format("m/%016llx", message)); diff --git a/fdbserver/storageserver.actor.cpp b/fdbserver/storageserver.actor.cpp index 011f3b714e..e0b930ca98 100644 --- a/fdbserver/storageserver.actor.cpp +++ b/fdbserver/storageserver.actor.cpp @@ -23,6 +23,7 @@ #include #include +#include "fdbclient/BlobCipher.h" #include "fdbclient/BlobGranuleCommon.h" #include "flow/ApiVersion.h" #include "fmt/format.h" @@ -158,12 +159,11 @@ FDB_DECLARE_BOOLEAN_PARAM(UnlimitedCommitBytes); FDB_DEFINE_BOOLEAN_PARAM(UnlimitedCommitBytes); // Immutable -static const KeyValueRef persistFormat(LiteralStringRef(PERSIST_PREFIX "Format"), - LiteralStringRef("FoundationDB/StorageServer/1/4")); +static const KeyValueRef persistFormat(LiteralStringRef(PERSIST_PREFIX "Format"), "FoundationDB/StorageServer/1/4"_sr); static const KeyValueRef persistShardAwareFormat(LiteralStringRef(PERSIST_PREFIX "Format"), - LiteralStringRef("FoundationDB/StorageServer/1/5")); -static const KeyRangeRef persistFormatReadableRange(LiteralStringRef("FoundationDB/StorageServer/1/2"), - LiteralStringRef("FoundationDB/StorageServer/1/6")); + "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"); @@ -704,7 +704,7 @@ public: std::map> pendingRemoveRanges; // Pending requests to remove ranges from physical shards - Reference encryptionKeyProvider; + Reference encryptionKeyProvider; bool shardAware; // True if the storage server is aware of the physical shards. @@ -899,11 +899,11 @@ public: // Set up tss fault injection here, only if we are in simulated mode and with fault injection. // With fault injection enabled, the tss will start acting normal for a bit, then after the specified delay // start behaving incorrectly. - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && - g_simulator.tssMode >= ISimulator::TSSMode::EnabledAddDelay) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && + g_simulator->tssMode >= ISimulator::TSSMode::EnabledAddDelay) { tssFaultInjectTime = now() + deterministicRandom()->randomInt(60, 300); TraceEvent(SevWarnAlways, "TSSInjectFaultEnabled", thisServerID) - .detail("Mode", g_simulator.tssMode) + .detail("Mode", g_simulator->tssMode) .detail("At", tssFaultInjectTime.get()); } } @@ -1015,7 +1015,7 @@ public: // Extra lock that prevents too much post-initial-fetch work from building up, such as mutation applying and change // feed tail fetching FlowLock fetchKeysParallelismFullLock; - FlowLock fetchChangeFeedParallelismLock; + FlowLock changeFeedDiskReadsLock; int64_t fetchKeysBytesBudget; AsyncVar fetchKeysBudgetUsed; std::vector> readyFetchKeys; @@ -1056,7 +1056,8 @@ public: CounterCollection cc; Counter allQueries, getKeyQueries, getValueQueries, getRangeQueries, getMappedRangeQueries, getRangeStreamQueries, finishedQueries, lowPriorityQueries, rowsQueried, bytesQueried, watchQueries, - emptyQueries, feedRowsQueried, feedBytesQueried, feedStreamQueries, feedVersionQueries; + emptyQueries, feedRowsQueried, feedBytesQueried, feedStreamQueries, rejectedFeedStreamQueries, + feedVersionQueries; // Bytes of the mutations that have been added to the memory of the storage server. When the data is durable // and cleared from the memory, we do not subtract it but add it to bytesDurable. @@ -1121,6 +1122,12 @@ public: Counter changeFeedDiskReads; LatencySample readLatencySample; + LatencySample readKeyLatencySample; + LatencySample readValueLatencySample; + LatencySample readRangeLatencySample; + LatencySample readVersionWaitSample; + LatencySample readQueueWaitSample; + LatencyBands readLatencyBands; LatencySample mappedRangeSample; // Samples getMappedRange latency LatencySample mappedRangeRemoteSample; // Samples getMappedRange remote subquery latency @@ -1134,9 +1141,9 @@ public: lowPriorityQueries("LowPriorityQueries", cc), rowsQueried("RowsQueried", cc), bytesQueried("BytesQueried", cc), watchQueries("WatchQueries", cc), emptyQueries("EmptyQueries", cc), feedRowsQueried("FeedRowsQueried", cc), feedBytesQueried("FeedBytesQueried", cc), - feedStreamQueries("FeedStreamQueries", cc), feedVersionQueries("FeedVersionQueries", cc), - bytesInput("BytesInput", cc), logicalBytesInput("LogicalBytesInput", cc), - logicalBytesMoveInOverhead("LogicalBytesMoveInOverhead", cc), + feedStreamQueries("FeedStreamQueries", cc), rejectedFeedStreamQueries("RejectedFeedStreamQueries", cc), + feedVersionQueries("FeedVersionQueries", cc), bytesInput("BytesInput", cc), + logicalBytesInput("LogicalBytesInput", cc), logicalBytesMoveInOverhead("LogicalBytesMoveInOverhead", cc), kvCommitLogicalBytes("KVCommitLogicalBytes", cc), kvClearRanges("KVClearRanges", cc), kvSystemClearRanges("KVSystemClearRanges", cc), bytesDurable("BytesDurable", cc), bytesFetched("BytesFetched", cc), mutationBytes("MutationBytes", cc), @@ -1158,6 +1165,26 @@ public: self->thisServerID, SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + readKeyLatencySample("GetKeyMetrics", + self->thisServerID, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + readValueLatencySample("GetValueMetrics", + self->thisServerID, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + readRangeLatencySample("GetRangeMetrics", + self->thisServerID, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + readVersionWaitSample("ReadVersionWaitMetrics", + self->thisServerID, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), + readQueueWaitSample("ReadQueueWaitMetrics", + self->thisServerID, + SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL, + SERVER_KNOBS->LATENCY_SAMPLE_SIZE), readLatencyBands("ReadLatencyBands", self->thisServerID, SERVER_KNOBS->STORAGE_LOGGING_DELAY), mappedRangeSample("GetMappedRangeMetrics", self->thisServerID, @@ -1194,6 +1221,10 @@ public: specialCounter(cc, "ServeFetchCheckpointWaiting", [self]() { return self->serveFetchCheckpointParallelismLock.waiters(); }); + specialCounter( + cc, "ChangeFeedDiskReadsActive", [self]() { return self->changeFeedDiskReadsLock.activePermits(); }); + specialCounter( + cc, "ChangeFeedDiskReadsWaiting", [self]() { return self->changeFeedDiskReadsLock.waiters(); }); specialCounter(cc, "QueryQueueMax", [self]() { return self->getAndResetMaxQueryQueueSize(); }); specialCounter(cc, "BytesStored", [self]() { return self->metrics.byteSample.getEstimate(allKeys); }); specialCounter(cc, "ActiveWatches", [self]() { return self->numWatches; }); @@ -1218,7 +1249,7 @@ public: StorageServer(IKeyValueStore* storage, Reference const> const& db, StorageServerInterface const& ssi, - Reference encryptionKeyProvider) + Reference encryptionKeyProvider) : tenantPrefixIndex(makeReference()), encryptionKeyProvider(encryptionKeyProvider), shardAware(false), tlogCursorReadsLatencyHistogram(Histogram::getHistogram(STORAGESERVER_HISTOGRAM_GROUP, TLOG_CURSOR_READS_LATENCY_HISTOGRAM, @@ -1249,10 +1280,11 @@ public: prevVersion(0), rebootAfterDurableVersion(std::numeric_limits::max()), primaryLocality(tagLocalityInvalid), knownCommittedVersion(0), versionLag(0), logProtocol(0), thisServerID(ssi.id()), tssInQuarantine(false), db(db), actors(false), - byteSampleClears(false, LiteralStringRef("\xff\xff\xff")), durableInProgress(Void()), watchBytes(0), - numWatches(0), noRecentUpdates(false), lastUpdate(now()), updateEagerReads(nullptr), + byteSampleClears(false, "\xff\xff\xff"_sr), durableInProgress(Void()), watchBytes(0), numWatches(0), + noRecentUpdates(false), lastUpdate(now()), updateEagerReads(nullptr), fetchKeysParallelismLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM), fetchKeysParallelismFullLock(SERVER_KNOBS->FETCH_KEYS_PARALLELISM_FULL), + changeFeedDiskReadsLock(SERVER_KNOBS->CHANGE_FEED_DISK_READS_PARALLELISM), fetchKeysBytesBudget(SERVER_KNOBS->STORAGE_FETCH_BYTES), fetchKeysBudgetUsed(false), serveFetchCheckpointParallelismLock(SERVER_KNOBS->SERVE_FETCH_CHECKPOINT_PARALLELISM), instanceID(deterministicRandom()->randomUniqueID().first()), shuttingDown(false), behind(false), @@ -1262,10 +1294,10 @@ public: storageServerSourceTLogIDEventHolder( makeReference(ssi.id().toString() + "/StorageServerSourceTLogID")) { - version.initMetric(LiteralStringRef("StorageServer.Version"), counters.cc.id); - oldestVersion.initMetric(LiteralStringRef("StorageServer.OldestVersion"), counters.cc.id); - durableVersion.initMetric(LiteralStringRef("StorageServer.DurableVersion"), counters.cc.id); - desiredOldestVersion.initMetric(LiteralStringRef("StorageServer.DesiredOldestVersion"), counters.cc.id); + version.initMetric("StorageServer.Version"_sr, counters.cc.id); + oldestVersion.initMetric("StorageServer.OldestVersion"_sr, counters.cc.id); + durableVersion.initMetric("StorageServer.DurableVersion"_sr, counters.cc.id); + desiredOldestVersion.initMetric("StorageServer.DesiredOldestVersion"_sr, counters.cc.id); newestAvailableVersion.insert(allKeys, invalidVersion); newestDirtyVersion.insert(allKeys, invalidVersion); @@ -1468,28 +1500,28 @@ public: void maybeInjectTargetedRestart(Version v) { // inject an SS restart at most once per test - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && - now() > g_simulator.injectTargetedSSRestartTime && + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && + now() > g_simulator->injectTargetedSSRestartTime && rebootAfterDurableVersion == std::numeric_limits::max()) { CODE_PROBE(true, "Injecting SS targeted restart"); TraceEvent("SimSSInjectTargetedRestart", thisServerID).detail("Version", v); rebootAfterDurableVersion = v; - g_simulator.injectTargetedSSRestartTime = std::numeric_limits::max(); + g_simulator->injectTargetedSSRestartTime = std::numeric_limits::max(); } } bool maybeInjectDelay() { - if (g_network->isSimulated() && !g_simulator.speedUpSimulation && now() > g_simulator.injectSSDelayTime) { + if (g_network->isSimulated() && !g_simulator->speedUpSimulation && now() > g_simulator->injectSSDelayTime) { CODE_PROBE(true, "Injecting SS targeted delay"); - TraceEvent("SimSSInjectDelay", thisServerID); - g_simulator.injectSSDelayTime = std::numeric_limits::max(); + TraceEvent("SimSSInjectDelay", thisServerID).log(); + g_simulator->injectSSDelayTime = std::numeric_limits::max(); return true; } return false; } }; -const StringRef StorageServer::CurrentRunningFetchKeys::emptyString = LiteralStringRef(""); +const StringRef StorageServer::CurrentRunningFetchKeys::emptyString = ""_sr; const KeyRangeRef StorageServer::CurrentRunningFetchKeys::emptyKeyRange = KeyRangeRef(StorageServer::CurrentRunningFetchKeys::emptyString, StorageServer::CurrentRunningFetchKeys::emptyString); @@ -1836,6 +1868,10 @@ ACTOR Future getValueQ(StorageServer* data, GetValueRequest req) { // so we need to downgrade here wait(data->getQueryDelay()); + // Track time from requestTime through now as read queueing wait time + state double queueWaitEnd = g_network->timer(); + data->counters.readQueueWaitSample.addMeasurement(queueWaitEnd - req.requestTime()); + if (req.options.present() && req.options.get().debugID.present()) g_traceBatch.addEvent("GetValueDebug", req.options.get().debugID.get().first(), @@ -1844,6 +1880,8 @@ ACTOR Future getValueQ(StorageServer* data, GetValueRequest req) { state Optional v; Version commitVersion = getLatestCommitVersion(req.ssLatestCommitVersions, data->tag); state Version version = wait(waitForVersion(data, commitVersion, req.version, req.spanContext)); + data->counters.readVersionWaitSample.addMeasurement(g_network->timer() - queueWaitEnd); + if (req.options.present() && req.options.get().debugID.present()) g_traceBatch.addEvent("GetValueDebug", req.options.get().debugID.get().first(), @@ -1880,15 +1918,15 @@ ACTOR Future getValueQ(StorageServer* data, GetValueRequest req) { DEBUG_MUTATION("ShardGetValue", version, - MutationRef(MutationRef::DebugKey, req.key, v.present() ? v.get() : LiteralStringRef("")), + MutationRef(MutationRef::DebugKey, req.key, v.present() ? v.get() : ""_sr), data->thisServerID); DEBUG_MUTATION("ShardGetPath", version, MutationRef(MutationRef::DebugKey, req.key, - path == 0 ? LiteralStringRef("0") - : path == 1 ? LiteralStringRef("1") - : LiteralStringRef("2")), + path == 0 ? "0"_sr + : path == 1 ? "1"_sr + : "2"_sr), data->thisServerID); /* @@ -1939,6 +1977,7 @@ ACTOR Future getValueQ(StorageServer* data, GetValueRequest req) { double duration = g_network->timer() - req.requestTime(); data->counters.readLatencySample.addMeasurement(duration); + data->counters.readValueLatencySample.addMeasurement(duration); if (data->latencyBandConfig.present()) { int maxReadBytes = data->latencyBandConfig.get().readConfig.maxReadBytes.orDefault(std::numeric_limits::max()); @@ -1995,13 +2034,12 @@ ACTOR Future watchWaitForValueChange(StorageServer* data, SpanContext p throw transaction_too_old(); } - DEBUG_MUTATION( - "ShardWatchValue", - latest, - MutationRef(MutationRef::DebugKey, - metadata->key, - reply.value.present() ? StringRef(reply.value.get()) : LiteralStringRef("")), - data->thisServerID); + DEBUG_MUTATION("ShardWatchValue", + latest, + MutationRef(MutationRef::DebugKey, + metadata->key, + reply.value.present() ? StringRef(reply.value.get()) : ""_sr), + data->thisServerID); if (metadata->debugID.present()) g_traceBatch.addEvent( @@ -2244,8 +2282,9 @@ ACTOR Future fetchCheckpointKeyValuesQ(StorageServer* self, FetchCheckpoin return Void(); } + state ICheckpointReader* reader = nullptr; try { - state ICheckpointReader* reader = newCheckpointReader(it->second, self->thisServerID); + reader = newCheckpointReader(it->second, self->thisServerID); wait(reader->init(BinaryWriter::toValue(req.range, IncludeVersion()))); loop { @@ -2620,12 +2659,16 @@ ACTOR Future> getChangeFeedMutations(Stor // To let update storage finish wait(delay(0)); } + + wait(data->changeFeedDiskReadsLock.take(TaskPriority::DefaultYield)); + state FlowLock::Releaser holdingDiskReadsLock(data->changeFeedDiskReadsLock); RangeResult res = wait( data->storage.readRange(KeyRangeRef(changeFeedDurableKey(req.rangeID, std::max(req.begin, emptyVersion)), changeFeedDurableKey(req.rangeID, req.end)), 1 << 30, remainingDurableBytes, options)); + holdingDiskReadsLock.release(); data->counters.kvScanBytes += res.logicalSize(); ++data->counters.changeFeedDiskReads; @@ -2962,17 +3005,28 @@ ACTOR Future changeFeedStreamQ(StorageServer* data, ChangeFeedStreamReques state bool atLatest = false; state bool removeUID = false; state Optional blockedVersion; - if (req.replyBufferSize <= 0) { - req.reply.setByteLimit(SERVER_KNOBS->CHANGEFEEDSTREAM_LIMIT_BYTES); - } else { - req.reply.setByteLimit(std::min((int64_t)req.replyBufferSize, SERVER_KNOBS->CHANGEFEEDSTREAM_LIMIT_BYTES)); - } - - ++data->counters.feedStreamQueries; - - wait(delay(0, TaskPriority::DefaultEndpoint)); try { + ++data->counters.feedStreamQueries; + + // FIXME: do something more sophisticated here besides hard limit + if (data->activeFeedQueries >= SERVER_KNOBS->STORAGE_FEED_QUERY_HARD_LIMIT || + (g_network->isSimulated() && BUGGIFY_WITH_PROB(0.005))) { + req.reply.sendError(storage_too_many_feed_streams()); + ++data->counters.rejectedFeedStreamQueries; + return Void(); + } + + data->activeFeedQueries++; + + if (req.replyBufferSize <= 0) { + req.reply.setByteLimit(SERVER_KNOBS->CHANGEFEEDSTREAM_LIMIT_BYTES); + } else { + req.reply.setByteLimit(std::min((int64_t)req.replyBufferSize, SERVER_KNOBS->CHANGEFEEDSTREAM_LIMIT_BYTES)); + } + + wait(delay(0, TaskPriority::DefaultEndpoint)); + if (DEBUG_CF_TRACE) { TraceEvent(SevDebug, "TraceChangeFeedStreamStart", data->thisServerID) .detail("FeedID", req.rangeID) @@ -2983,7 +3037,6 @@ ACTOR Future changeFeedStreamQ(StorageServer* data, ChangeFeedStreamReques .detail("CanReadPopped", req.canReadPopped) .detail("PeerAddr", req.reply.getEndpoint().getPrimaryAddress()); } - data->activeFeedQueries++; wait(success(waitForVersionNoTooOld(data, req.begin))); @@ -3600,7 +3653,7 @@ ACTOR Future findKey(StorageServer* data, if (sel.offset <= 1 && sel.offset >= 0) maxBytes = std::numeric_limits::max(); else - maxBytes = (g_network->isSimulated() && g_simulator.tssMode == ISimulator::TSSMode::Disabled && BUGGIFY) + maxBytes = (g_network->isSimulated() && g_simulator->tssMode == ISimulator::TSSMode::Disabled && BUGGIFY) ? SERVER_KNOBS->BUGGIFY_LIMIT_BYTES : SERVER_KNOBS->STORAGE_LIMIT_BYTES; @@ -3731,6 +3784,10 @@ ACTOR Future getKeyValuesQ(StorageServer* data, GetKeyValuesRequest req) wait(data->getQueryDelay()); } + // Track time from requestTime through now as read queueing wait time + state double queueWaitEnd = g_network->timer(); + data->counters.readQueueWaitSample.addMeasurement(queueWaitEnd - req.requestTime()); + try { if (req.options.present() && req.options.get().debugID.present()) g_traceBatch.addEvent( @@ -3738,6 +3795,7 @@ ACTOR Future getKeyValuesQ(StorageServer* data, GetKeyValuesRequest req) Version commitVersion = getLatestCommitVersion(req.ssLatestCommitVersions, data->tag); state Version version = wait(waitForVersion(data, commitVersion, req.version, span.context)); + data->counters.readVersionWaitSample.addMeasurement(g_network->timer() - queueWaitEnd); state Optional tenantEntry = data->getTenantEntry(version, req.tenantInfo); state Optional tenantPrefix = tenantEntry.map([](TenantMapEntry e) { return e.prefix; }); @@ -3881,6 +3939,7 @@ ACTOR Future getKeyValuesQ(StorageServer* data, GetKeyValuesRequest req) double duration = g_network->timer() - req.requestTime(); data->counters.readLatencySample.addMeasurement(duration); + data->counters.readRangeLatencySample.addMeasurement(duration); if (data->latencyBandConfig.present()) { int maxReadBytes = data->latencyBandConfig.get().readConfig.maxReadBytes.orDefault(std::numeric_limits::max()); @@ -4068,14 +4127,12 @@ void preprocessMappedKey(Tuple& mappedKeyFormatTuple, std::vector>& vec, - Tuple& mappedKeyTuple, - Tuple& mappedKeyFormatTuple) { +Key constructMappedKey(KeyValueRef* keyValue, std::vector>& vec, Tuple& mappedKeyFormatTuple) { // Lazily parse key and/or value to tuple because they may not need to be a tuple if not used. Optional keyTuple; Optional valueTuple; - mappedKeyTuple.clear(); + Tuple mappedKeyTuple; + mappedKeyTuple.reserve(vec.size()); for (int i = 0; i < vec.size(); i++) { @@ -4122,12 +4179,11 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { Tuple mappedKeyFormatTuple = Tuple::makeTuple("normal"_sr, "{{escaped}}"_sr, "{K[2]}"_sr, "{V[0]}"_sr, "{...}"_sr); - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); Key expectedMappedKey = Tuple::makeTuple("normal"_sr, "{escaped}"_sr, "key-2"_sr, "value-0"_sr).getDataAsStandalone(); @@ -4139,11 +4195,10 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { { Tuple mappedKeyFormatTuple = Tuple::makeTuple("{{{{}}"_sr, "}}"_sr); - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); Key expectedMappedKey = Tuple::makeTuple("{{}"_sr, "}"_sr).getDataAsStandalone(); // std::cout << printable(mappedKey) << " == " << printable(expectedMappedKey) << std::endl; @@ -4153,11 +4208,10 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { { Tuple mappedKeyFormatTuple = Tuple::makeTuple("{{{{}}"_sr, "}}"_sr); - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); Key expectedMappedKey = Tuple::makeTuple("{{}"_sr, "}"_sr).getDataAsStandalone(); // std::cout << printable(mappedKey) << " == " << printable(expectedMappedKey) << std::endl; @@ -4168,12 +4222,11 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { Tuple mappedKeyFormatTuple = Tuple::makeTuple("{K[100]}"_sr); state bool throwException = false; try { - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); } catch (Error& e) { ASSERT(e.code() == error_code_mapper_bad_index); throwException = true; @@ -4184,12 +4237,11 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { Tuple mappedKeyFormatTuple = Tuple::makeTuple("{...}"_sr, "last-element"_sr); state bool throwException2 = false; try { - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); } catch (Error& e) { ASSERT(e.code() == error_code_mapper_bad_range_decriptor); throwException2 = true; @@ -4200,12 +4252,11 @@ TEST_CASE("/fdbserver/storageserver/constructMappedKey") { Tuple mappedKeyFormatTuple = Tuple::makeTuple("{K[not-a-number]}"_sr); state bool throwException3 = false; try { - Tuple mappedKeyTuple; std::vector> vt; bool isRangeQuery = false; preprocessMappedKey(mappedKeyFormatTuple, vt, isRangeQuery); - Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(&kvr, vt, mappedKeyFormatTuple); } catch (Error& e) { ASSERT(e.code() == error_code_mapper_bad_index); throwException3 = true; @@ -4263,7 +4314,6 @@ ACTOR Future mapKeyValues(StorageServer* data, g_traceBatch.addEvent( "TransactionDebug", pOriginalReq->options.get().debugID.get().first(), "storageserver.mapKeyValues.Start"); state Tuple mappedKeyFormatTuple; - state Tuple mappedKeyTuple; try { mappedKeyFormatTuple = Tuple::unpack(mapper); @@ -4300,7 +4350,7 @@ ACTOR Future mapKeyValues(StorageServer* data, kvm->value = ""_sr; } - Key mappedKey = constructMappedKey(it, vt, mappedKeyTuple, mappedKeyFormatTuple); + Key mappedKey = constructMappedKey(it, vt, mappedKeyFormatTuple); // Make sure the mappedKey is always available, so that it's good even we want to get key asynchronously. result.arena.dependsOn(mappedKey.arena()); @@ -4478,6 +4528,10 @@ ACTOR Future getMappedKeyValuesQ(StorageServer* data, GetMappedKeyValuesRe wait(data->getQueryDelay()); } + // Track time from requestTime through now as read queueing wait time + state double queueWaitEnd = g_network->timer(); + data->counters.readQueueWaitSample.addMeasurement(queueWaitEnd - req.requestTime()); + try { if (req.options.present() && req.options.get().debugID.present()) g_traceBatch.addEvent( @@ -4485,6 +4539,7 @@ ACTOR Future getMappedKeyValuesQ(StorageServer* data, GetMappedKeyValuesRe // VERSION_VECTOR change Version commitVersion = getLatestCommitVersion(req.ssLatestCommitVersions, data->tag); state Version version = wait(waitForVersion(data, commitVersion, req.version, span.context)); + data->counters.readVersionWaitSample.addMeasurement(g_network->timer() - queueWaitEnd); state Optional tenantEntry = data->getTenantEntry(req.version, req.tenantInfo); state Optional tenantPrefix = tenantEntry.map([](TenantMapEntry e) { return e.prefix; }); @@ -4782,10 +4837,15 @@ ACTOR Future getKeyValuesStreamQ(StorageServer* data, GetKeyValuesStreamRe // Even if TSS mode is Disabled, this may be the second test in a restarting test where the first run // had it enabled. - state int byteLimit = (BUGGIFY && g_simulator.tssMode == ISimulator::TSSMode::Disabled && + state int byteLimit = (BUGGIFY && g_simulator->tssMode == ISimulator::TSSMode::Disabled && !data->isTss() && !data->isSSWithTSSPair()) ? 1 : CLIENT_KNOBS->REPLY_BYTE_LIMIT; + TraceEvent(SevDebug, "SSGetKeyValueStreamLimits") + .detail("ByteLimit", byteLimit) + .detail("ReqLimit", req.limit) + .detail("Begin", begin.printable()) + .detail("End", end.printable()); GetKeyValuesReply _r = wait(readRange(data, version, KeyRangeRef(begin, end), @@ -4898,9 +4958,14 @@ ACTOR Future getKeyQ(StorageServer* data, GetKeyRequest req) { // so we need to downgrade here wait(data->getQueryDelay()); + // Track time from requestTime through now as read queueing wait time + state double queueWaitEnd = g_network->timer(); + data->counters.readQueueWaitSample.addMeasurement(queueWaitEnd - req.requestTime()); + try { Version commitVersion = getLatestCommitVersion(req.ssLatestCommitVersions, data->tag); state Version version = wait(waitForVersion(data, commitVersion, req.version, req.spanContext)); + data->counters.readVersionWaitSample.addMeasurement(g_network->timer() - queueWaitEnd); state Optional tenantEntry = data->getTenantEntry(version, req.tenantInfo); if (tenantEntry.present()) { @@ -4970,6 +5035,8 @@ ACTOR Future getKeyQ(StorageServer* data, GetKeyRequest req) { double duration = g_network->timer() - req.requestTime(); data->counters.readLatencySample.addMeasurement(duration); + data->counters.readKeyLatencySample.addMeasurement(duration); + if (data->latencyBandConfig.present()) { int maxReadBytes = data->latencyBandConfig.get().readConfig.maxReadBytes.orDefault(std::numeric_limits::max()); @@ -6036,7 +6103,7 @@ ACTOR Future fetchChangeFeed(StorageServer* data, .detail("Version", cleanupVersion); if (g_network->isSimulated()) { - ASSERT(g_simulator.validationData.allDestroyedChangeFeedIDs.count(changeFeedInfo->id.toString())); + ASSERT(g_simulator->validationData.allDestroyedChangeFeedIDs.count(changeFeedInfo->id.toString())); } Key beginClearKey = changeFeedInfo->id.withPrefix(persistChangeFeedKeys.begin); @@ -6266,7 +6333,7 @@ ACTOR Future> fetchChangeFeedMetadata(StorageServer* data, if (g_network->isSimulated()) { // verify that the feed was actually destroyed and it's not an error in this inference logic - ASSERT(g_simulator.validationData.allDestroyedChangeFeedIDs.count(feed.first.toString())); + ASSERT(g_simulator->validationData.allDestroyedChangeFeedIDs.count(feed.first.toString())); } Key beginClearKey = feed.first.withPrefix(persistChangeFeedKeys.begin); @@ -6829,8 +6896,9 @@ ACTOR Future fetchKeys(StorageServer* data, AddingShard* shard) { .detail("Version", feedTransferredVersion) .detail("StorageVersion", data->storageVersion()); + state StorageServerShard newShard; if (data->shardAware) { - state StorageServerShard newShard = data->shards[keys.begin]->toStorageServerShard(); + newShard = data->shards[keys.begin]->toStorageServerShard(); ASSERT(newShard.range == keys); ASSERT(newShard.getShardState() == StorageServerShard::ReadWritePending); updateStorageShard(data, newShard); @@ -7024,7 +7092,7 @@ ACTOR Future restoreShards(StorageServer* data, : availableShards[availableLoc + 1].key.removePrefix(persistShardAvailableKeys.begin)); ASSERT(!shardRange.empty()); - const bool nowAvailable = availableShards[availableLoc].value != LiteralStringRef("0"); + const bool nowAvailable = availableShards[availableLoc].value != "0"_sr; auto existingShards = data->shards.intersectingRanges(shardRange); for (auto it = existingShards.begin(); it != existingShards.end(); ++it) { TraceEvent(SevVerbose, "RestoreShardsValidateAvailable", data->thisServerID) @@ -7049,7 +7117,7 @@ ACTOR Future restoreShards(StorageServer* data, ? allKeys.end : assignedShards[assignedLoc + 1].key.removePrefix(persistShardAssignedKeys.begin)); ASSERT(!shardRange.empty()); - const bool nowAssigned = assignedShards[assignedLoc].value != LiteralStringRef("0"); + const bool nowAssigned = assignedShards[assignedLoc].value != "0"_sr; auto existingShards = data->shards.intersectingRanges(shardRange); for (auto it = existingShards.begin(); it != existingShards.end(); ++it) { @@ -8060,7 +8128,7 @@ void StorageServer::clearTenants(TenantNameRef startTenant, TenantNameRef endTen ACTOR Future tssDelayForever() { loop { wait(delay(5.0)); - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { return Void(); } } @@ -8079,7 +8147,7 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { // a very small value. state int64_t hardLimit = SERVER_KNOBS->STORAGE_HARD_LIMIT_BYTES; state int64_t hardLimitOverage = SERVER_KNOBS->STORAGE_HARD_LIMIT_BYTES_OVERAGE; - if (g_network->isSimulated() && g_simulator.speedUpSimulation) { + if (g_network->isSimulated() && g_simulator->speedUpSimulation) { hardLimit = SERVER_KNOBS->STORAGE_HARD_LIMIT_BYTES_SPEED_UP_SIM; hardLimitOverage = SERVER_KNOBS->STORAGE_HARD_LIMIT_BYTES_OVERAGE_SPEED_UP_SIM; } @@ -8108,8 +8176,8 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { data->lastDurableVersionEBrake = data->durableVersion.get(); } - if (g_network->isSimulated() && data->isTss() && g_simulator.tssMode == ISimulator::TSSMode::EnabledAddDelay && - !g_simulator.speedUpSimulation && data->tssFaultInjectTime.present() && + if (g_network->isSimulated() && data->isTss() && g_simulator->tssMode == ISimulator::TSSMode::EnabledAddDelay && + !g_simulator->speedUpSimulation && data->tssFaultInjectTime.present() && data->tssFaultInjectTime.get() < now()) { if (deterministicRandom()->random01() < 0.01) { TraceEvent(SevWarnAlways, "TSSInjectDelayForever", data->thisServerID).log(); @@ -8218,7 +8286,7 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { cipherDetails.insert(header->cipherHeaderDetails); collectingCipherKeys = true; } else { - msg = msg.decrypt(cipherKeys.get(), eager.arena); + msg = msg.decrypt(cipherKeys.get(), eager.arena, BlobCipherMetrics::TLOG); } } // TraceEvent(SevDebug, "SSReadingLog", data->thisServerID).detail("Mutation", msg); @@ -8241,7 +8309,7 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { if (collectingCipherKeys) { std::unordered_map> getCipherKeysResult = - wait(getEncryptCipherKeys(data->db, cipherDetails)); + wait(getEncryptCipherKeys(data->db, cipherDetails, BlobCipherMetrics::TLOG)); cipherKeys = getCipherKeysResult; collectingCipherKeys = false; eager = UpdateEagerReadInfo(); @@ -8366,7 +8434,7 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { rd >> msg; if (msg.isEncrypted()) { ASSERT(cipherKeys.present()); - msg = msg.decrypt(cipherKeys.get(), rd.arena()); + msg = msg.decrypt(cipherKeys.get(), rd.arena(), BlobCipherMetrics::TLOG); } Span span("SS:update"_loc, spanContext); @@ -8374,8 +8442,8 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { // Drop non-private mutations if TSS fault injection is enabled in simulation, or if this is a TSS in // quarantine. - if (g_network->isSimulated() && data->isTss() && !g_simulator.speedUpSimulation && - g_simulator.tssMode == ISimulator::TSSMode::EnabledDropMutations && + if (g_network->isSimulated() && data->isTss() && !g_simulator->speedUpSimulation && + g_simulator->tssMode == ISimulator::TSSMode::EnabledDropMutations && data->tssFaultInjectTime.present() && data->tssFaultInjectTime.get() < now() && (msg.type == MutationRef::SetValue || msg.type == MutationRef::ClearRange) && (msg.param1.size() < 2 || msg.param1[0] != 0xff || msg.param1[1] != 0xff) && @@ -8493,7 +8561,7 @@ ACTOR Future update(StorageServer* data, bool* pReceivedUpdate) { data->otherError.getFuture().get(); Version maxVersionsInMemory = - (g_network->isSimulated() && g_simulator.speedUpSimulation) + (g_network->isSimulated() && g_simulator->speedUpSimulation) ? std::max(5 * SERVER_KNOBS->VERSIONS_PER_SECOND, SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS) : SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS; for (int i = 0; i < data->recoveryVersionSkips.size(); i++) { @@ -8613,7 +8681,7 @@ ACTOR Future updateStorage(StorageServer* data) { ASSERT(data->durableVersion.get() == data->storageVersion()); if (g_network->isSimulated()) { double endTime = - g_simulator.checkDisabled(format("%s/updateStorage", data->thisServerID.toString().c_str())); + g_simulator->checkDisabled(format("%s/updateStorage", data->thisServerID.toString().c_str())); if (endTime > now()) { wait(delay(endTime - now(), TaskPriority::UpdateStorage)); } @@ -9015,8 +9083,8 @@ void StorageServerDisk::makeNewStorageServerDurable(const bool shardAware) { storage->set(KeyValueRef(persistStorageServerShardKeys.begin.toString(), ObjectWriter::toValue(StorageServerShard::notAssigned(allKeys, 0), IncludeVersion()))); } else { - storage->set(KeyValueRef(persistShardAssignedKeys.begin.toString(), LiteralStringRef("0"))); - storage->set(KeyValueRef(persistShardAvailableKeys.begin.toString(), LiteralStringRef("0"))); + storage->set(KeyValueRef(persistShardAssignedKeys.begin.toString(), "0"_sr)); + storage->set(KeyValueRef(persistShardAvailableKeys.begin.toString(), "0"_sr)); } auto view = data->tenantMap.atLatest(); @@ -9038,16 +9106,12 @@ void setAvailableStatus(StorageServer* self, KeyRangeRef keys, bool available) { self->addMutationToMutationLog(mLV, MutationRef(MutationRef::ClearRange, availableKeys.begin, availableKeys.end)); ++self->counters.kvSystemClearRanges; - self->addMutationToMutationLog(mLV, - MutationRef(MutationRef::SetValue, - availableKeys.begin, - available ? LiteralStringRef("1") : LiteralStringRef("0"))); + self->addMutationToMutationLog( + mLV, MutationRef(MutationRef::SetValue, availableKeys.begin, available ? "1"_sr : "0"_sr)); if (keys.end != allKeys.end) { bool endAvailable = self->shards.rangeContaining(keys.end)->value()->isCFInVersionedData(); - self->addMutationToMutationLog(mLV, - MutationRef(MutationRef::SetValue, - availableKeys.end, - endAvailable ? LiteralStringRef("1") : LiteralStringRef("0"))); + self->addMutationToMutationLog( + mLV, MutationRef(MutationRef::SetValue, availableKeys.end, endAvailable ? "1"_sr : "0"_sr)); } // When a shard is moved out, delete all related checkpoints created for data move. @@ -9113,16 +9177,12 @@ void setAssignedStatus(StorageServer* self, KeyRangeRef keys, bool nowAssigned) //TraceEvent("SetAssignedStatus", self->thisServerID).detail("Version", mLV.version).detail("RangeBegin", assignedKeys.begin).detail("RangeEnd", assignedKeys.end); self->addMutationToMutationLog(mLV, MutationRef(MutationRef::ClearRange, assignedKeys.begin, assignedKeys.end)); ++self->counters.kvSystemClearRanges; - self->addMutationToMutationLog(mLV, - MutationRef(MutationRef::SetValue, - assignedKeys.begin, - nowAssigned ? LiteralStringRef("1") : LiteralStringRef("0"))); + self->addMutationToMutationLog( + mLV, MutationRef(MutationRef::SetValue, assignedKeys.begin, nowAssigned ? "1"_sr : "0"_sr)); if (keys.end != allKeys.end) { bool endAssigned = self->shards.rangeContaining(keys.end)->value()->assigned(); - self->addMutationToMutationLog(mLV, - MutationRef(MutationRef::SetValue, - assignedKeys.end, - endAssigned ? LiteralStringRef("1") : LiteralStringRef("0"))); + self->addMutationToMutationLog( + mLV, MutationRef(MutationRef::SetValue, assignedKeys.end, endAssigned ? "1"_sr : "0"_sr)); } if (BUGGIFY) { @@ -9203,7 +9263,7 @@ void StorageServerDisk::makeVersionDurable(Version version) { // Update data->storage to persist tss quarantine state void StorageServerDisk::makeTssQuarantineDurable() { - storage->set(KeyValueRef(persistTssQuarantine, LiteralStringRef("1"))); + storage->set(KeyValueRef(persistTssQuarantine, "1"_sr)); } void StorageServerDisk::changeLogProtocol(Version version, ProtocolVersion protocol) { @@ -9252,7 +9312,7 @@ ACTOR Future applyByteSampleResult(StorageServer* data, } else { data->byteSampleClears.insert(KeyRangeRef(begin.removePrefix(persistByteSampleKeys.begin), end == persistByteSampleKeys.end - ? LiteralStringRef("\xff\xff\xff") + ? "\xff\xff\xff"_sr : end.removePrefix(persistByteSampleKeys.begin)), true); data->byteSampleClearsTooLarge.set(data->byteSampleClears.size() > @@ -9495,7 +9555,7 @@ ACTOR Future restoreDurableState(StorageServer* data, IKeyValueStore* stor : available[availableLoc + 1].key.removePrefix(persistShardAvailableKeys.begin)); ASSERT(!keys.empty()); - bool nowAvailable = available[availableLoc].value != LiteralStringRef("0"); + bool nowAvailable = available[availableLoc].value != "0"_sr; /*if(nowAvailable) TraceEvent("AvailableShard", data->thisServerID).detail("RangeBegin", keys.begin).detail("RangeEnd", keys.end);*/ data->newestAvailableVersion.insert(keys, nowAvailable ? latestVersion : invalidVersion); @@ -9515,7 +9575,7 @@ ACTOR Future restoreDurableState(StorageServer* data, IKeyValueStore* stor ? allKeys.end : assigned[assignedLoc + 1].key.removePrefix(persistShardAssignedKeys.begin)); ASSERT(!keys.empty()); - bool nowAssigned = assigned[assignedLoc].value != LiteralStringRef("0"); + bool nowAssigned = assigned[assignedLoc].value != "0"_sr; /*if(nowAssigned) TraceEvent("AssignedShard", data->thisServerID).detail("RangeBegin", keys.begin).detail("RangeEnd", keys.end);*/ changeServerKeys(data, keys, nowAssigned, version, CSK_RESTORE); @@ -10624,7 +10684,7 @@ ACTOR Future storageServer(IKeyValueStore* persistentData, ReplyPromise recruitReply, Reference const> db, std::string folder, - Reference encryptionKeyProvider) { + Reference encryptionKeyProvider) { state StorageServer self(persistentData, db, ssi, encryptionKeyProvider); self.shardAware = SERVER_KNOBS->SHARD_ENCODE_LOCATION_METADATA && persistentData->shardAware(); state Future ssCore; @@ -10715,7 +10775,7 @@ ACTOR Future storageServer(IKeyValueStore* persistentData, std::string folder, Promise recovered, Reference connRecord, - Reference encryptionKeyProvider) { + Reference encryptionKeyProvider) { state StorageServer self(persistentData, db, ssi, encryptionKeyProvider); state Future ssCore; self.folder = folder; diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index ea8a1cad82..dc34d32af4 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -26,6 +26,7 @@ #include #include "flow/ActorCollection.h" +#include "flow/DeterministicRandom.h" #include "fdbrpc/sim_validation.h" #include "fdbrpc/simulator.h" #include "fdbclient/ClusterInterface.h" @@ -153,7 +154,7 @@ Value getOption(VectorRef options, Key key, Value defaultValue) { for (int i = 0; i < options.size(); i++) if (options[i].key == key) { Value value = options[i].value; - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return value; } @@ -165,7 +166,7 @@ int getOption(VectorRef options, Key key, int defaultValue) { if (options[i].key == key) { int r; if (sscanf(options[i].value.toString().c_str(), "%d", &r)) { - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return r; } else { TraceEvent(SevError, "InvalidTestOption").detail("OptionName", key); @@ -181,7 +182,7 @@ uint64_t getOption(VectorRef options, Key key, uint64_t defaultValu if (options[i].key == key) { uint64_t r; if (sscanf(options[i].value.toString().c_str(), "%" SCNd64, &r)) { - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return r; } else { TraceEvent(SevError, "InvalidTestOption").detail("OptionName", key); @@ -197,7 +198,7 @@ int64_t getOption(VectorRef options, Key key, int64_t defaultValue) if (options[i].key == key) { int64_t r; if (sscanf(options[i].value.toString().c_str(), "%" SCNd64, &r)) { - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return r; } else { TraceEvent(SevError, "InvalidTestOption").detail("OptionName", key); @@ -213,7 +214,7 @@ double getOption(VectorRef options, Key key, double defaultValue) { if (options[i].key == key) { float r; if (sscanf(options[i].value.toString().c_str(), "%f", &r)) { - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return r; } } @@ -222,10 +223,10 @@ double getOption(VectorRef options, Key key, double defaultValue) { } bool getOption(VectorRef options, Key key, bool defaultValue) { - Value p = getOption(options, key, defaultValue ? LiteralStringRef("true") : LiteralStringRef("false")); - if (p == LiteralStringRef("true")) + Value p = getOption(options, key, defaultValue ? "true"_sr : "false"_sr); + if (p == "true"_sr) return true; - if (p == LiteralStringRef("false")) + if (p == "false"_sr) return false; ASSERT(false); return false; // Assure that compiler is fine with the function @@ -242,7 +243,7 @@ std::vector getOption(VectorRef options, Key key, std: begin = c + 1; } v.push_back(options[i].value.substr(begin).toString()); - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; return v; } return defaultValue; @@ -259,7 +260,7 @@ bool hasOption(VectorRef options, Key key) { // returns unconsumed options Standalone> checkAllOptionsConsumed(VectorRef options) { - static StringRef nothing = LiteralStringRef(""); + static StringRef nothing = ""_sr; Standalone> unconsumed; for (int i = 0; i < options.size(); i++) if (!(options[i].value == nothing)) { @@ -271,111 +272,192 @@ Standalone> checkAllOptionsConsumed(VectorRef> workloads; +CompoundWorkload::CompoundWorkload(WorkloadContext& wcx) : TestWorkload(wcx) {} - CompoundWorkload(WorkloadContext& wcx) : TestWorkload(wcx) {} - CompoundWorkload* add(Reference&& w) { - workloads.push_back(std::move(w)); - return this; - } +CompoundWorkload* CompoundWorkload::add(Reference&& w) { + workloads.push_back(std::move(w)); + return this; +} - std::string description() const override { - std::string d; - for (int w = 0; w < workloads.size(); w++) - d += workloads[w]->description() + (w == workloads.size() - 1 ? "" : ";"); - return d; +std::string CompoundWorkload::description() const { + std::vector names; + names.reserve(workloads.size()); + for (auto const& w : workloads) { + names.push_back(w->description()); } - Future setup(Database const& cx) override { - std::vector> all; - all.reserve(workloads.size()); - for (int w = 0; w < workloads.size(); w++) - all.push_back(workloads[w]->setup(cx)); - return waitForAll(all); + return fmt::format("{}", fmt::join(std::move(names), ";")); +} +Future CompoundWorkload::setup(Database const& cx) { + std::vector> all; + all.reserve(workloads.size()); + for (int w = 0; w < workloads.size(); w++) + all.push_back(workloads[w]->setup(cx)); + auto done = waitForAll(all); + if (failureInjection.empty()) { + return done; } - Future start(Database const& cx) override { - std::vector> all; - all.reserve(workloads.size()); - auto wCount = std::make_shared(0); - for (int i = 0; i < workloads.size(); i++) { - std::string workloadName = workloads[i]->description(); - ++(*wCount); - TraceEvent("WorkloadRunStatus") - .detail("Name", workloadName) - .detail("Count", *wCount) - .detail("Phase", "Start"); - all.push_back(fmap( - [workloadName, wCount](Void value) { - --(*wCount); - TraceEvent("WorkloadRunStatus") - .detail("Name", workloadName) - .detail("Remaining", *wCount) - .detail("Phase", "End"); - return Void(); - }, - workloads[i]->start(cx))); + std::vector> res; + res.reserve(failureInjection.size()); + for (auto& f : failureInjection) { + res.push_back(f->setupInjectionWorkload(cx, done)); + } + return waitForAll(res); +} + +Future CompoundWorkload::start(Database const& cx) { + std::vector> all; + all.reserve(workloads.size() + failureInjection.size()); + auto wCount = std::make_shared(0); + auto startWorkload = [&](TestWorkload& workload) -> Future { + auto workloadName = workload.description(); + ++(*wCount); + TraceEvent("WorkloadRunStatus").detail("Name", workloadName).detail("Count", *wCount).detail("Phase", "Start"); + return fmap( + [workloadName, wCount](Void value) { + --(*wCount); + TraceEvent("WorkloadRunStatus") + .detail("Name", workloadName) + .detail("Remaining", *wCount) + .detail("Phase", "End"); + return Void(); + }, + workload.start(cx)); + }; + for (auto& workload : workloads) { + all.push_back(startWorkload(*workload)); + } + for (auto& workload : failureInjection) { + all.push_back(startWorkload(*workload)); + } + return waitForAll(all); +} + +Future CompoundWorkload::check(Database const& cx) { + std::vector> all; + all.reserve(workloads.size() + failureInjection.size()); + auto wCount = std::make_shared(0); + auto starter = [&](TestWorkload& workload) -> Future { + ++(*wCount); + std::string workloadName = workload.description(); + TraceEvent("WorkloadCheckStatus") + .detail("Name", workloadName) + .detail("Count", *wCount) + .detail("Phase", "Start"); + return fmap( + [workloadName, wCount](bool ret) { + --(*wCount); + TraceEvent("WorkloadCheckStatus") + .detail("Name", workloadName) + .detail("Remaining", *wCount) + .detail("Phase", "End"); + return true; + }, + workload.check(cx)); + }; + for (auto& workload : workloads) { + all.push_back(starter(*workload)); + } + for (auto& workload : failureInjection) { + all.push_back(starter(*workload)); + } + return allTrue(all); +} + +ACTOR Future> getMetricsCompoundWorkload(CompoundWorkload* self) { + state std::vector>> results; + for (int w = 0; w < self->workloads.size(); w++) { + std::vector p; + results.push_back(self->workloads[w]->getMetrics()); + } + wait(waitForAll(results)); + std::vector res; + for (int i = 0; i < results.size(); ++i) { + auto const& p = results[i].get(); + for (auto const& m : p) { + res.push_back(m.withPrefix(self->workloads[i]->description() + ".")); } - return waitForAll(all); - } - Future check(Database const& cx) override { - std::vector> all; - all.reserve(workloads.size()); - auto wCount = std::make_shared(0); - for (int i = 0; i < workloads.size(); i++) { - ++(*wCount); - std::string workloadName = workloads[i]->description(); - TraceEvent("WorkloadCheckStatus") - .detail("Name", workloadName) - .detail("Count", *wCount) - .detail("Phase", "Start"); - all.push_back(fmap( - [workloadName, wCount](bool ret) { - --(*wCount); - TraceEvent("WorkloadCheckStatus") - .detail("Name", workloadName) - .detail("Remaining", *wCount) - .detail("Phase", "End"); - return true; - }, - workloads[i]->check(cx))); - } - return allTrue(all); } + return res; +} - ACTOR static Future> getMetrics(CompoundWorkload* self) { - state std::vector>> results; - for (int w = 0; w < self->workloads.size(); w++) { - std::vector p; - results.push_back(self->workloads[w]->getMetrics()); - } - wait(waitForAll(results)); - std::vector res; - for (int i = 0; i < results.size(); ++i) { - auto const& p = results[i].get(); - for (auto const& m : p) { - res.push_back(m.withPrefix(self->workloads[i]->description() + ".")); - } - } - return res; +void CompoundWorkload::addFailureInjection(WorkloadRequest& work) { + if (!work.runFailureWorkloads || !FLOW_KNOBS->ENABLE_SIMULATION_IMPROVEMENTS) { + return; } - - Future> getMetrics() override { return getMetrics(this); } - double getCheckTimeout() const override { - double m = 0; - for (int w = 0; w < workloads.size(); w++) - m = std::max(workloads[w]->getCheckTimeout(), m); - return m; + // Some common workloads won't work with failure injection workloads + for (auto const& w : workloads) { + auto desc = w->description(); + if (desc == "ChangeConfig") { + return; + } else if (desc == "SaveAndKill") { + return; + } } + auto& factories = IFailureInjectorFactory::factories(); + DeterministicRandom random(sharedRandomNumber); + for (auto& factory : factories) { + auto workload = factory->create(*this); + while (workload->add(random, work, *this)) { + failureInjection.push_back(workload); + workload = factory->create(*this); + } + } +} - void getMetrics(std::vector&) override { ASSERT(false); } -}; +Future> CompoundWorkload::getMetrics() { + return getMetricsCompoundWorkload(this); +} + +double CompoundWorkload::getCheckTimeout() const { + double m = 0; + for (int w = 0; w < workloads.size(); w++) + m = std::max(workloads[w]->getCheckTimeout(), m); + return m; +} + +void CompoundWorkload::getMetrics(std::vector&) { + ASSERT(false); +} + +FailureInjectionWorkload::FailureInjectionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {} + +bool FailureInjectionWorkload::add(DeterministicRandom& random, + const WorkloadRequest& work, + const CompoundWorkload& workload) { + auto desc = description(); + unsigned alreadyAdded = std::count_if(workload.workloads.begin(), workload.workloads.end(), [&desc](auto const& w) { + return w->description() == desc; + }); + alreadyAdded += std::count_if(workload.failureInjection.begin(), + workload.failureInjection.end(), + [&desc](auto const& w) { return w->description() == desc; }); + bool willAdd = alreadyAdded < 3 && work.useDatabase && 0.1 / (1 + alreadyAdded) > random.random01(); + if (willAdd) { + initFailureInjectionMode(random, alreadyAdded); + } + return willAdd; +} + +void FailureInjectionWorkload::initFailureInjectionMode(DeterministicRandom& random, unsigned count) {} + +Future FailureInjectionWorkload::setupInjectionWorkload(const Database& cx, Future done) { + return holdWhile(this->setup(cx), done); +} + +Future FailureInjectionWorkload::startInjectionWorkload(const Database& cx, Future done) { + return holdWhile(this->start(cx), done); +} + +Future FailureInjectionWorkload::checkInjectionWorkload(const Database& cx, Future done) { + return holdWhile(this->check(cx), done); +} ACTOR Future> getWorkloadIface(WorkloadRequest work, Reference ccr, VectorRef options, Reference const> dbInfo) { state Reference workload; - state Value testName = getOption(options, LiteralStringRef("testName"), LiteralStringRef("no-test-specified")); + state Value testName = getOption(options, "testName"_sr, "no-test-specified"_sr); WorkloadContext wcx; wcx.clientId = work.clientId; wcx.clientCount = work.clientCount; @@ -422,10 +504,6 @@ ACTOR Future> getWorkloadIface(WorkloadRequest work, fprintf(stderr, "ERROR: No options were provided for workload.\n"); throw test_specification_invalid(); } - if (work.options.size() == 1) { - Reference res = wait(getWorkloadIface(work, ccr, work.options[0], dbInfo)); - return res; - } wcx.clientId = work.clientId; wcx.clientCount = work.clientCount; @@ -440,6 +518,7 @@ ACTOR Future> getWorkloadIface(WorkloadRequest work, for (int i = 0; i < work.options.size(); i++) { compound->add(ifaces[i].getValue()); } + compound->addFailureInjection(work); return compound; } @@ -451,7 +530,7 @@ void printSimulatedTopology() { if (!g_network->isSimulated()) { return; } - auto processes = g_simulator.getAllProcesses(); + auto processes = g_simulator->getAllProcesses(); std::sort(processes.begin(), processes.end(), [](ISimulator::ProcessInfo* lhs, ISimulator::ProcessInfo* rhs) { auto l = lhs->locality; auto r = rhs->locality; @@ -736,7 +815,7 @@ ACTOR Future testerServerCore(TesterInterface interf, state PromiseStream> addWorkload; state Future workerFatalError = actorCollection(addWorkload.getFuture()); - TraceEvent("StartingTesterServerCore", interf.id()); + TraceEvent("StartingTesterServerCore", interf.id()).log(); loop choose { when(wait(workerFatalError)) {} when(WorkloadRequest work = waitNext(interf.recruitments.getFuture())) { @@ -883,6 +962,7 @@ ACTOR Future runWorkload(Database cx, WorkloadRequest req; req.title = spec.title; req.useDatabase = spec.useDB; + req.runFailureWorkloads = spec.runFailureWorkloads; req.timeout = spec.timeout; req.databasePingDelay = spec.useDB ? spec.databasePingDelay : 0.0; req.options = spec.options; @@ -975,10 +1055,10 @@ ACTOR Future runWorkload(Database cx, ACTOR Future changeConfiguration(Database cx, std::vector testers, StringRef configMode) { state TestSpec spec; Standalone> options; - spec.title = LiteralStringRef("ChangeConfig"); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("testName"), LiteralStringRef("ChangeConfig"))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("configMode"), configMode)); + spec.title = "ChangeConfig"_sr; + spec.runFailureWorkloads = false; + options.push_back_deep(options.arena(), KeyValueRef("testName"_sr, "ChangeConfig"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("configMode"_sr, configMode)); spec.options.push_back_deep(spec.options.arena(), options); DistributedTestResults testResults = wait(runWorkload(cx, testers, spec, Optional())); @@ -1001,37 +1081,37 @@ ACTOR Future checkConsistency(Database cx, state double connectionFailures; if (g_network->isSimulated()) { // NOTE: the value will be reset after consistency check - connectionFailures = g_simulator.connectionFailuresDisableDuration; - g_simulator.connectionFailuresDisableDuration = 1e6; - g_simulator.speedUpSimulation = true; + connectionFailures = g_simulator->connectionFailuresDisableDuration; + g_simulator->connectionFailuresDisableDuration = 1e6; + g_simulator->speedUpSimulation = true; } Standalone> options; - StringRef performQuiescent = LiteralStringRef("false"); - StringRef performCacheCheck = LiteralStringRef("false"); - StringRef performTSSCheck = LiteralStringRef("false"); + StringRef performQuiescent = "false"_sr; + StringRef performCacheCheck = "false"_sr; + StringRef performTSSCheck = "false"_sr; if (doQuiescentCheck) { - performQuiescent = LiteralStringRef("true"); + performQuiescent = "true"_sr; spec.restorePerpetualWiggleSetting = false; } if (doCacheCheck) { - performCacheCheck = LiteralStringRef("true"); + performCacheCheck = "true"_sr; } if (doTSSCheck) { - performTSSCheck = LiteralStringRef("true"); + performTSSCheck = "true"_sr; } - spec.title = LiteralStringRef("ConsistencyCheck"); + spec.title = "ConsistencyCheck"_sr; spec.databasePingDelay = databasePingDelay; + spec.runFailureWorkloads = false; spec.timeout = 32000; - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("testName"), LiteralStringRef("ConsistencyCheck"))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("performQuiescentChecks"), performQuiescent)); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("performCacheCheck"), performCacheCheck)); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("performTSSCheck"), performTSSCheck)); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("quiescentWaitTimeout"), - ValueRef(options.arena(), format("%f", quiescentWaitTimeout)))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("distributed"), LiteralStringRef("false"))); + options.push_back_deep(options.arena(), KeyValueRef("testName"_sr, "ConsistencyCheck"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("performQuiescentChecks"_sr, performQuiescent)); + options.push_back_deep(options.arena(), KeyValueRef("performCacheCheck"_sr, performCacheCheck)); + options.push_back_deep(options.arena(), KeyValueRef("performTSSCheck"_sr, performTSSCheck)); + options.push_back_deep( + options.arena(), + KeyValueRef("quiescentWaitTimeout"_sr, ValueRef(options.arena(), format("%f", quiescentWaitTimeout)))); + options.push_back_deep(options.arena(), KeyValueRef("distributed"_sr, "false"_sr)); spec.options.push_back_deep(spec.options.arena(), options); state double start = now(); @@ -1040,13 +1120,12 @@ ACTOR Future checkConsistency(Database cx, DistributedTestResults testResults = wait(runWorkload(cx, testers, spec, Optional())); if (testResults.ok() || lastRun) { if (g_network->isSimulated()) { - g_simulator.connectionFailuresDisableDuration = connectionFailures; + g_simulator->connectionFailuresDisableDuration = connectionFailures; } return Void(); } if (now() - start > softTimeLimit) { - spec.options[0].push_back_deep(spec.options.arena(), - KeyValueRef(LiteralStringRef("failureIsError"), LiteralStringRef("true"))); + spec.options[0].push_back_deep(spec.options.arena(), KeyValueRef("failureIsError"_sr, "true"_sr)); lastRun = true; } @@ -1297,7 +1376,7 @@ std::map= 0); spec->simConnectionFailuresDisableDuration = connectionFailuresDisableDuration; if (g_network->isSimulated()) - g_simulator.connectionFailuresDisableDuration = spec->simConnectionFailuresDisableDuration; + g_simulator->connectionFailuresDisableDuration = spec->simConnectionFailuresDisableDuration; TraceEvent("TestParserTest") .detail("ParsedSimConnectionFailuresDisableDuration", spec->simConnectionFailuresDisableDuration); } }, @@ -1325,6 +1404,8 @@ std::maprestorePerpetualWiggleSetting = false; } }, + { "runFailureWorkloads", + [](const std::string& value, TestSpec* spec) { spec->runFailureWorkloads = (value == "true"); } }, }; std::vector readTests(std::ifstream& ifs) { @@ -1549,6 +1630,24 @@ ACTOR Future monitorServerDBInfo(Reference initializeSimConfig(Database db) { + state Transaction tr(db); + ASSERT(g_network->isSimulated()); + loop { + try { + DatabaseConfiguration dbConfig = wait(getDatabaseConfiguration(&tr)); + g_simulator->storagePolicy = dbConfig.storagePolicy; + g_simulator->tLogPolicy = dbConfig.tLogPolicy; + g_simulator->tLogWriteAntiQuorum = dbConfig.tLogWriteAntiQuorum; + g_simulator->remoteTLogPolicy = dbConfig.getRemoteTLogPolicy(); + g_simulator->usableRegions = dbConfig.usableRegions; + return Void(); + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + /** * \brief Test orchestrator: sends test specification to testers in the right order and collects the results. * @@ -1608,12 +1707,12 @@ ACTOR Future runTests(ReferencesimDrAgents != ISimulator::BackupAgentType::NoBackupAgents) { simDrAgents = iter->simDrAgents; } - enableDD = enableDD || getOption(iter->options[0], LiteralStringRef("enableDD"), false); + enableDD = enableDD || getOption(iter->options[0], "enableDD"_sr, false); } if (g_network->isSimulated()) { - g_simulator.backupAgents = simBackupAgents; - g_simulator.drAgents = simDrAgents; + g_simulator->backupAgents = simBackupAgents; + g_simulator->drAgents = simDrAgents; } // turn off the database ping functionality if the suite of tests are not going to be using the database @@ -1629,6 +1728,7 @@ ACTOR Future runTests(Reference runTests(ReferenceisSimulated()) { + wait(initializeSimConfig(cx)); + } } if (useDB && waitForQuiescenceBegin) { @@ -1682,6 +1785,7 @@ ACTOR Future runTests(Reference runTests(Reference connRecord, if (whatToRun == TEST_TYPE_CONSISTENCY_CHECK) { TestSpec spec; Standalone> options; - spec.title = LiteralStringRef("ConsistencyCheck"); + spec.title = "ConsistencyCheck"_sr; + spec.runFailureWorkloads = false; spec.databasePingDelay = 0; spec.timeout = 0; spec.waitForQuiescenceBegin = false; spec.waitForQuiescenceEnd = false; std::string rateLimitMax = format("%d", CLIENT_KNOBS->CONSISTENCY_CHECK_RATE_LIMIT_MAX); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("testName"), LiteralStringRef("ConsistencyCheck"))); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("performQuiescentChecks"), LiteralStringRef("false"))); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("distributed"), LiteralStringRef("false"))); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("failureIsError"), LiteralStringRef("true"))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("indefinite"), LiteralStringRef("true"))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("rateLimitMax"), StringRef(rateLimitMax))); - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("shuffleShards"), LiteralStringRef("true"))); + options.push_back_deep(options.arena(), KeyValueRef("testName"_sr, "ConsistencyCheck"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("performQuiescentChecks"_sr, "false"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("distributed"_sr, "false"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("failureIsError"_sr, "true"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("indefinite"_sr, "true"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("rateLimitMax"_sr, StringRef(rateLimitMax))); + options.push_back_deep(options.arena(), KeyValueRef("shuffleShards"_sr, "true"_sr)); spec.options.push_back_deep(spec.options.arena(), options); testSet.testSpecs.push_back(spec); } else if (whatToRun == TEST_TYPE_UNIT_TESTS) { TestSpec spec; Standalone> options; - spec.title = LiteralStringRef("UnitTests"); + spec.title = "UnitTests"_sr; spec.startDelay = 0; spec.useDB = false; spec.timeout = 0; - options.push_back_deep(options.arena(), - KeyValueRef(LiteralStringRef("testName"), LiteralStringRef("UnitTests"))); - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("testsMatching"), fileName)); + options.push_back_deep(options.arena(), KeyValueRef("testName"_sr, "UnitTests"_sr)); + options.push_back_deep(options.arena(), KeyValueRef("testsMatching"_sr, fileName)); // Add unit test options as test spec options for (auto& kv : testOptions.params) { options.push_back_deep(options.arena(), KeyValueRef(kv.first, kv.second)); diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 3ba0e371e8..43072f0132 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -191,7 +191,7 @@ Error checkIOTimeout(Error const& e) { // In simulation, have to check global timed out flag for both this process and the machine process on which IO is // done if (g_network->isSimulated() && !timeoutOccurred) - timeoutOccurred = g_pSimulator->getCurrentProcess()->machine->machineProcess->global(INetwork::enASIOTimedOut); + timeoutOccurred = g_simulator->getCurrentProcess()->machine->machineProcess->global(INetwork::enASIOTimedOut); if (timeoutOccurred) { CODE_PROBE(true, "Timeout occurred"); @@ -271,7 +271,9 @@ ACTOR Future workerHandleErrors(FutureStream errors) { if (err.error.code() == error_code_please_reboot || (err.role == Role::SHARED_TRANSACTION_LOG && - (err.error.code() == error_code_io_error || err.error.code() == error_code_io_timeout))) + (err.error.code() == error_code_io_error || err.error.code() == error_code_io_timeout)) || + (SERVER_KNOBS->STORAGE_SERVER_REBOOT_ON_IO_TIMEOUT && err.role == Role::STORAGE_SERVER && + err.error.code() == error_code_io_timeout)) throw err.error; } } @@ -298,18 +300,18 @@ ACTOR Future loadedPonger(FutureStream pings) { loop { LoadedPingRequest pong = waitNext(pings); LoadedReply rep; - rep.payload = (pong.loadReply ? payloadBack : LiteralStringRef("")); + rep.payload = (pong.loadReply ? payloadBack : ""_sr); rep.id = pong.id; pong.reply.send(rep); } } -StringRef fileStoragePrefix = LiteralStringRef("storage-"); -StringRef testingStoragePrefix = LiteralStringRef("testingstorage-"); -StringRef fileLogDataPrefix = LiteralStringRef("log-"); -StringRef fileVersionedLogDataPrefix = LiteralStringRef("log2-"); -StringRef fileLogQueuePrefix = LiteralStringRef("logqueue-"); -StringRef tlogQueueExtension = LiteralStringRef("fdq"); +StringRef fileStoragePrefix = "storage-"_sr; +StringRef testingStoragePrefix = "testingstorage-"_sr; +StringRef fileLogDataPrefix = "log-"_sr; +StringRef fileVersionedLogDataPrefix = "log2-"_sr; +StringRef fileLogQueuePrefix = "logqueue-"_sr; +StringRef tlogQueueExtension = "fdq"_sr; enum class FilesystemCheck { FILES_ONLY, @@ -386,12 +388,12 @@ struct TLogOptions { if (key.size() != 0 && value.size() == 0) return default_error_or(); - if (key == LiteralStringRef("V")) { + if (key == "V"_sr) { ErrorOr tLogVersion = TLogVersion::FromStringRef(value); if (tLogVersion.isError()) return tLogVersion.getError(); options.version = tLogVersion.get(); - } else if (key == LiteralStringRef("LS")) { + } else if (key == "LS"_sr) { ErrorOr tLogSpillType = TLogSpillType::FromStringRef(value); if (tLogSpillType.isError()) return tLogSpillType.getError(); @@ -560,11 +562,13 @@ ACTOR Future registrationClient( Reference> const> rkInterf, Reference>> const> bmInterf, Reference> const> ekpInterf, + Reference> const> csInterf, Reference const> degraded, Reference connRecord, Reference> const> issues, Reference configNode, Reference localConfig, + ConfigBroadcastInterface configBroadcastInterface, Reference> dbInfo, Promise recoveredDiskFiles) { // Keeps the cluster controller (as it may be re-elected) informed that this worker exists @@ -589,27 +593,29 @@ ACTOR Future registrationClient( incorrectTime = Optional(); } - RegisterWorkerRequest request(interf, - initialClass, - processClass, - asyncPriorityInfo->get(), - requestGeneration++, - ddInterf->get(), - rkInterf->get(), - bmInterf->get().present() ? bmInterf->get().get().second - : Optional(), - ekpInterf->get(), - degraded->get(), - localConfig->lastSeenVersion(), - localConfig->configClassSet(), - recoveredDiskFiles.isSet()); + RegisterWorkerRequest request( + interf, + initialClass, + processClass, + asyncPriorityInfo->get(), + requestGeneration++, + ddInterf->get(), + rkInterf->get(), + bmInterf->get().present() ? bmInterf->get().get().second : Optional(), + ekpInterf->get(), + csInterf->get(), + degraded->get(), + localConfig.isValid() ? localConfig->lastSeenVersion() : Optional(), + localConfig.isValid() ? localConfig->configClassSet() : Optional(), + recoveredDiskFiles.isSet(), + configBroadcastInterface); for (auto const& i : issues->get()) { request.issues.push_back_deep(request.issues.arena(), i); } if (!upToDate) { - request.issues.push_back_deep(request.issues.arena(), LiteralStringRef("incorrect_cluster_file_contents")); + request.issues.push_back_deep(request.issues.arena(), "incorrect_cluster_file_contents"_sr); std::string connectionString = connRecord->getConnectionString().toString(); if (!incorrectTime.present()) { incorrectTime = now(); @@ -666,6 +672,7 @@ ACTOR Future registrationClient( when(wait(ccInterface->onChange())) { break; } when(wait(ddInterf->onChange())) { break; } when(wait(rkInterf->onChange())) { break; } + when(wait(csInterf->onChange())) { break; } when(wait(bmInterf->onChange())) { break; } when(wait(ekpInterf->onChange())) { break; } when(wait(degraded->onChange())) { break; } @@ -692,6 +699,10 @@ bool addressInDbAndPrimaryDc(const NetworkAddress& address, Reference(Endpoint({ remoteTLogAddress }, UID(1, 2))); @@ -914,7 +925,7 @@ TEST_CASE("/fdbserver/worker/addressInDbAndRemoteDc") { // Setup a ServerDBInfo for test. ServerDBInfo testDbInfo; LocalityData testLocal; - testLocal.set(LiteralStringRef("dcid"), StringRef(std::to_string(1))); + testLocal.set("dcid"_sr, StringRef(std::to_string(1))); testDbInfo.master.locality = testLocal; // First, create an empty TLogInterface, and check that it shouldn't be considered as in remote DC. @@ -930,7 +941,7 @@ TEST_CASE("/fdbserver/worker/addressInDbAndRemoteDc") { // Create a remote TLog, and it should be considered as in remote DC. LocalityData fakeRemote; - fakeRemote.set(LiteralStringRef("dcid"), StringRef(std::to_string(2))); + fakeRemote.set("dcid"_sr, StringRef(std::to_string(2))); TLogInterface remoteTlog(fakeRemote); remoteTlog.initEndpoints(); @@ -1255,7 +1266,7 @@ ACTOR Future storageServerRollbackRebooter(std::set* rebootKVStore, - Reference encryptionKeyProvider) { + Reference encryptionKeyProvider) { state TrackRunningStorage _(id, storeType, runningStorages); loop { ErrorOr e = wait(errorOr(prevStorageServer)); @@ -1409,10 +1420,10 @@ void startRole(const Role& role, // Update roles map, log Roles metrics g_roles.insert({ role.roleName, roleId.shortString() }); - StringMetricHandle(LiteralStringRef("Roles")) = roleString(g_roles, false); - StringMetricHandle(LiteralStringRef("RolesWithIDs")) = roleString(g_roles, true); + StringMetricHandle("Roles"_sr) = roleString(g_roles, false); + StringMetricHandle("RolesWithIDs"_sr) = roleString(g_roles, true); if (g_network->isSimulated()) - g_simulator.addRole(g_network->getLocalAddress(), role.roleName); + g_simulator->addRole(g_network->getLocalAddress(), role.roleName); } void endRole(const Role& role, UID id, std::string reason, bool ok, Error e) { @@ -1439,10 +1450,10 @@ void endRole(const Role& role, UID id, std::string reason, bool ok, Error e) { // Update roles map, log Roles metrics g_roles.erase({ role.roleName, id.shortString() }); - StringMetricHandle(LiteralStringRef("Roles")) = roleString(g_roles, false); - StringMetricHandle(LiteralStringRef("RolesWithIDs")) = roleString(g_roles, true); + StringMetricHandle("Roles"_sr) = roleString(g_roles, false); + StringMetricHandle("RolesWithIDs"_sr) = roleString(g_roles, true); if (g_network->isSimulated()) - g_simulator.removeRole(g_network->getLocalAddress(), role.roleName); + g_simulator->removeRole(g_network->getLocalAddress(), role.roleName); if (role.includeInTraceRoles) { removeTraceRole(role.abbreviation); @@ -1616,6 +1627,8 @@ ACTOR Future workerServer(Reference connRecord, state UID lastBMRecruitRequestId; state Reference>> ekpInterf( new AsyncVar>()); + state Reference>> csInterf( + new AsyncVar>()); state Future handleErrors = workerHandleErrors(errors.getFuture()); // Needs to be stopped last state ActorCollection errorForwarders(false); state Future loggingTrigger = Void(); @@ -1647,7 +1660,6 @@ ACTOR Future workerServer(Reference connRecord, state std::string coordFolder = abspath(_coordFolder); state WorkerInterface interf(locality); - interf.configBroadcastInterface = configBroadcastInterface; state std::set> runningStorages; interf.initEndpoints(); @@ -1725,7 +1737,7 @@ ACTOR Future workerServer(Reference connRecord, if (s.storedComponent == DiskStore::Storage) { LocalLineage _; getCurrentLineage()->modify(&RoleLineage::role) = ProcessClass::ClusterRole::Storage; - Reference encryptionKeyProvider = + Reference encryptionKeyProvider = makeReference(dbInfo); IKeyValueStore* kv = openKVStore( s.storeType, @@ -1939,16 +1951,18 @@ ACTOR Future workerServer(Reference connRecord, rkInterf, bmEpochAndInterf, ekpInterf, + csInterf, degraded, connRecord, issues, configNode, localConfig, + configBroadcastInterface, dbInfo, recoveredDiskFiles)); if (configNode.isValid()) { - errorForwarders.add(localConfig->consume(interf.configBroadcastInterface)); + errorForwarders.add(brokenPromiseToNever(localConfig->consume(configBroadcastInterface))); } if (SERVER_KNOBS->ENABLE_WORKER_HEALTH_MONITOR) { @@ -2132,6 +2146,31 @@ ACTOR Future workerServer(Reference connRecord, TraceEvent("Ratekeeper_InitRequest", req.reqId).detail("RatekeeperId", recruited.id()); req.reply.send(recruited); } + when(InitializeConsistencyScanRequest req = waitNext(interf.consistencyScan.getFuture())) { + LocalLineage _; + getCurrentLineage()->modify(&RoleLineage::role) = ProcessClass::ClusterRole::ConsistencyScan; + ConsistencyScanInterface recruited(locality, req.reqId); + recruited.initEndpoints(); + + if (csInterf->get().present()) { + recruited = csInterf->get().get(); + CODE_PROBE(true, "Recovered while already a consistencyscan"); + } else { + startRole(Role::CONSISTENCYSCAN, recruited.id(), interf.id()); + DUMPTOKEN(recruited.waitFailure); + DUMPTOKEN(recruited.haltConsistencyScan); + + Future consistencyScanProcess = consistencyScan(recruited, dbInfo); + errorForwarders.add(forwardError( + errors, + Role::CONSISTENCYSCAN, + recruited.id(), + setWhenDoneOrError(consistencyScanProcess, csInterf, Optional()))); + csInterf->set(Optional(recruited)); + } + TraceEvent("ConsistencyScanReceived", req.reqId).detail("ConsistencyScanId", recruited.id()); + req.reply.send(recruited); + } when(InitializeBlobManagerRequest req = waitNext(interf.blobManager.getFuture())) { LocalLineage _; getCurrentLineage()->modify(&RoleLineage::role) = ProcessClass::ClusterRole::BlobManager; @@ -2341,7 +2380,7 @@ ACTOR Future workerServer(Reference connRecord, folder, isTss ? testingStoragePrefix.toString() : fileStoragePrefix.toString(), recruited.id()); - Reference encryptionKeyProvider = + Reference encryptionKeyProvider = makeReference(dbInfo); IKeyValueStore* data = openKVStore( req.storeType, @@ -3319,8 +3358,11 @@ ACTOR Future fdbd(Reference connRecord, state std::vector> actors; state Promise recoveredDiskFiles; state Reference configNode; - state Reference localConfig = - makeReference(dataFolder, configPath, manualKnobOverrides); + state Reference localConfig; + if (configDBType != ConfigDBType::DISABLED) { + localConfig = makeReference( + dataFolder, configPath, manualKnobOverrides, IsTest(g_network->isSimulated())); + } // setupStackSignal(); getCurrentLineage()->modify(&RoleLineage::role) = ProcessClass::Worker; @@ -3328,18 +3370,11 @@ ACTOR Future fdbd(Reference connRecord, configNode = makeReference(dataFolder); } - // FIXME: Initializing here causes simulation issues, these must be fixed - /* - if (configDBType != ConfigDBType::DISABLED) { - wait(localConfig->initialize()); - } - */ - actors.push_back(serveProtocolInfo()); actors.push_back(serveProcess()); try { - ServerCoordinators coordinators(connRecord); + ServerCoordinators coordinators(connRecord, configDBType); if (g_network->isSimulated()) { whitelistBinPaths = ",, random_path, /bin/snap_create.sh,,"; } @@ -3348,7 +3383,8 @@ ACTOR Future fdbd(Reference connRecord, .detail("MachineId", localities.machineId()) .detail("DiskPath", dataFolder) .detail("CoordPath", coordFolder) - .detail("WhiteListBinPath", whitelistBinPaths); + .detail("WhiteListBinPath", whitelistBinPaths) + .detail("ConfigDBType", configDBType); state ConfigBroadcastInterface configBroadcastInterface; // SOMEDAY: start the services on the machine in a staggered fashion in simulation? @@ -3367,6 +3403,10 @@ ACTOR Future fdbd(Reference connRecord, wait(testAndUpdateSoftwareVersionCompatibility(dataFolder, processIDUid)); + if (configDBType != ConfigDBType::DISABLED) { + wait(localConfig->initialize()); + } + std::string fitnessFilePath = joinPath(dataFolder, "fitness"); auto cc = makeReference>>(); auto ci = makeReference>>(); @@ -3454,3 +3494,4 @@ const Role Role::STORAGE_CACHE("StorageCache", "SC"); const Role Role::COORDINATOR("Coordinator", "CD"); const Role Role::BACKUP("Backup", "BK"); const Role Role::ENCRYPT_KEY_PROXY("EncryptKeyProxy", "EP"); +const Role Role::CONSISTENCYSCAN("ConsistencyScan", "CS"); diff --git a/fdbserver/workloads/ApiCorrectness.actor.cpp b/fdbserver/workloads/ApiCorrectness.actor.cpp index 55eebfd0f6..3feaebe539 100644 --- a/fdbserver/workloads/ApiCorrectness.actor.cpp +++ b/fdbserver/workloads/ApiCorrectness.actor.cpp @@ -99,21 +99,21 @@ public: ApiCorrectnessWorkload(WorkloadContext const& wcx) : ApiWorkload(wcx), numRandomOperations("Num Random Operations") { - numGets = getOption(options, LiteralStringRef("numGets"), 1000); - numGetRanges = getOption(options, LiteralStringRef("numGetRanges"), 100); - numGetRangeSelectors = getOption(options, LiteralStringRef("numGetRangeSelectors"), 100); - numGetKeys = getOption(options, LiteralStringRef("numGetKeys"), 100); - numClears = getOption(options, LiteralStringRef("numClears"), 100); - numClearRanges = getOption(options, LiteralStringRef("numClearRanges"), 100); - minSizeAfterClear = getOption(options, LiteralStringRef("minSizeAfterClear"), (int)(0.1 * numKeys)); + numGets = getOption(options, "numGets"_sr, 1000); + numGetRanges = getOption(options, "numGetRanges"_sr, 100); + numGetRangeSelectors = getOption(options, "numGetRangeSelectors"_sr, 100); + numGetKeys = getOption(options, "numGetKeys"_sr, 100); + numClears = getOption(options, "numClears"_sr, 100); + numClearRanges = getOption(options, "numClearRanges"_sr, 100); + minSizeAfterClear = getOption(options, "minSizeAfterClear"_sr, (int)(0.1 * numKeys)); - maxRandomTestKeys = getOption(options, LiteralStringRef("maxRandomTestKeys"), numKeys); - randomTestDuration = getOption(options, LiteralStringRef("randomTestDuration"), 60.0); + maxRandomTestKeys = getOption(options, "maxRandomTestKeys"_sr, numKeys); + randomTestDuration = getOption(options, "randomTestDuration"_sr, 60.0); - int maxTransactionBytes = getOption(options, LiteralStringRef("maxTransactionBytes"), 500000); + int maxTransactionBytes = getOption(options, "maxTransactionBytes"_sr, 500000); maxKeysPerTransaction = std::max(1, maxTransactionBytes / (maxValueLength + maxLongKeyLength)); - resetDBTimeout = getOption(options, LiteralStringRef("resetDBTimeout"), 1800.0); + resetDBTimeout = getOption(options, "resetDBTimeout"_sr, 1800.0); if (maxTransactionBytes > 500000) { TraceEvent("RemapEventSeverity") @@ -502,7 +502,7 @@ public: if (keys[i].startsWith(StringRef(self->clientPrefix)) || (keys[i].size() == 0 && self->clientPrefixInt == 0) || - (keys[i].startsWith(LiteralStringRef("\xff")) && self->clientPrefixInt == self->clientCount - 1)) { + (keys[i].startsWith("\xff"_sr) && self->clientPrefixInt == self->clientCount - 1)) { break; } @@ -553,7 +553,7 @@ public: if (endKey == self->store.endKey()) { for (int i = 0; i < range.size(); i++) { // Don't include results in the 0xFF key-space - if (!range[i].key.startsWith(LiteralStringRef("\xff"))) + if (!range[i].key.startsWith("\xff"_sr)) dbResults.push_back_deep(dbResults.arena(), range[i]); } if (reverse && dbResults.size() < storeResults.size()) { diff --git a/fdbserver/workloads/AsyncFile.cpp b/fdbserver/workloads/AsyncFile.cpp index 96c7c239e1..0dae37423f 100644 --- a/fdbserver/workloads/AsyncFile.cpp +++ b/fdbserver/workloads/AsyncFile.cpp @@ -62,11 +62,11 @@ const int AsyncFileWorkload::_PAGE_SIZE = 4096; AsyncFileWorkload::AsyncFileWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), fileHandle(nullptr) { // Only run on one client enabled = clientId == 0; - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - unbufferedIO = getOption(options, LiteralStringRef("unbufferedIO"), false); - uncachedIO = getOption(options, LiteralStringRef("uncachedIO"), false); - fillRandom = getOption(options, LiteralStringRef("fillRandom"), false); - path = getOption(options, LiteralStringRef("fileName"), LiteralStringRef("")).toString(); + testDuration = getOption(options, "testDuration"_sr, 10.0); + unbufferedIO = getOption(options, "unbufferedIO"_sr, false); + uncachedIO = getOption(options, "uncachedIO"_sr, false); + fillRandom = getOption(options, "fillRandom"_sr, false); + path = getOption(options, "fileName"_sr, ""_sr).toString(); } Reference AsyncFileWorkload::allocateBuffer(size_t size) { diff --git a/fdbserver/workloads/AsyncFileCorrectness.actor.cpp b/fdbserver/workloads/AsyncFileCorrectness.actor.cpp index cbf47d47dc..5dd5137819 100644 --- a/fdbserver/workloads/AsyncFileCorrectness.actor.cpp +++ b/fdbserver/workloads/AsyncFileCorrectness.actor.cpp @@ -75,9 +75,9 @@ struct AsyncFileCorrectnessWorkload : public AsyncFileWorkload { AsyncFileCorrectnessWorkload(WorkloadContext const& wcx) : AsyncFileWorkload(wcx), memoryFile(nullptr), success(true), numOperations("Num Operations") { - maxOperationSize = getOption(options, LiteralStringRef("maxOperationSize"), 4096); - numSimultaneousOperations = getOption(options, LiteralStringRef("numSimultaneousOperations"), 10); - targetFileSize = getOption(options, LiteralStringRef("targetFileSize"), (uint64_t)163840); + maxOperationSize = getOption(options, "maxOperationSize"_sr, 4096); + numSimultaneousOperations = getOption(options, "numSimultaneousOperations"_sr, 10); + targetFileSize = getOption(options, "targetFileSize"_sr, (uint64_t)163840); if (unbufferedIO) maxOperationSize = std::max(_PAGE_SIZE, maxOperationSize); diff --git a/fdbserver/workloads/AsyncFileRead.actor.cpp b/fdbserver/workloads/AsyncFileRead.actor.cpp index f4321ca2f8..17267adf6c 100644 --- a/fdbserver/workloads/AsyncFileRead.actor.cpp +++ b/fdbserver/workloads/AsyncFileRead.actor.cpp @@ -173,15 +173,14 @@ struct AsyncFileReadWorkload : public AsyncFileWorkload { AsyncFileReadWorkload(WorkloadContext const& wcx) : AsyncFileWorkload(wcx), bytesRead("Bytes Read"), ioLog(0) { // Only run on one client - numParallelReads = getOption(options, LiteralStringRef("numParallelReads"), 0); - readSize = getOption(options, LiteralStringRef("readSize"), _PAGE_SIZE); - fileSize = - getOption(options, LiteralStringRef("fileSize"), (int64_t)0); // 0 = use existing, else, change file size - unbatched = getOption(options, LiteralStringRef("unbatched"), false); - sequential = getOption(options, LiteralStringRef("sequential"), true); - writeFraction = getOption(options, LiteralStringRef("writeFraction"), 0.0); - randomData = getOption(options, LiteralStringRef("randomData"), true); - fixedRate = getOption(options, LiteralStringRef("fixedRate"), 0.0); + numParallelReads = getOption(options, "numParallelReads"_sr, 0); + readSize = getOption(options, "readSize"_sr, _PAGE_SIZE); + fileSize = getOption(options, "fileSize"_sr, (int64_t)0); // 0 = use existing, else, change file size + unbatched = getOption(options, "unbatched"_sr, false); + sequential = getOption(options, "sequential"_sr, true); + writeFraction = getOption(options, "writeFraction"_sr, 0.0); + randomData = getOption(options, "randomData"_sr, true); + fixedRate = getOption(options, "fixedRate"_sr, 0.0); } ~AsyncFileReadWorkload() override {} diff --git a/fdbserver/workloads/AsyncFileWrite.actor.cpp b/fdbserver/workloads/AsyncFileWrite.actor.cpp index fe6a1448ac..45352c946d 100644 --- a/fdbserver/workloads/AsyncFileWrite.actor.cpp +++ b/fdbserver/workloads/AsyncFileWrite.actor.cpp @@ -47,10 +47,10 @@ struct AsyncFileWriteWorkload : public AsyncFileWorkload { AsyncFileWriteWorkload(WorkloadContext const& wcx) : AsyncFileWorkload(wcx), writeBuffer(nullptr), bytesWritten("Bytes Written") { - numParallelWrites = getOption(options, LiteralStringRef("numParallelWrites"), 0); - writeSize = getOption(options, LiteralStringRef("writeSize"), _PAGE_SIZE); - fileSize = getOption(options, LiteralStringRef("fileSize"), 10002432); - sequential = getOption(options, LiteralStringRef("sequential"), true); + numParallelWrites = getOption(options, "numParallelWrites"_sr, 0); + writeSize = getOption(options, "writeSize"_sr, _PAGE_SIZE); + fileSize = getOption(options, "fileSize"_sr, 10002432); + sequential = getOption(options, "sequential"_sr, true); } std::string description() const override { return "AsyncFileWrite"; } diff --git a/fdbserver/workloads/AtomicOps.actor.cpp b/fdbserver/workloads/AtomicOps.actor.cpp index e9fbadc383..cbde2560d8 100644 --- a/fdbserver/workloads/AtomicOps.actor.cpp +++ b/fdbserver/workloads/AtomicOps.actor.cpp @@ -39,11 +39,11 @@ struct AtomicOpsWorkload : TestWorkload { uint64_t lbsum, ubsum; // The lower bound and upper bound sum of operations when opType = AddValue AtomicOpsWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), opNum(0) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), transactionsPerSecond / 5); - opType = getOption(options, LiteralStringRef("opType"), -1); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), 1000); + testDuration = getOption(options, "testDuration"_sr, 600.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; + actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5); + opType = getOption(options, "opType"_sr, -1); + nodeCount = getOption(options, "nodeCount"_sr, 1000); // Atomic OPs Min and And have modified behavior from api version 510. Hence allowing testing for older version // (500) with a 10% probability Actual change of api Version happens in setup apiVersion500 = ((sharedRandomNumber % 10) == 0); @@ -319,7 +319,7 @@ struct AtomicOpsWorkload : TestWorkload { for (auto& kv : log) { uint64_t intValue = 0; memcpy(&intValue, kv.value.begin(), kv.value.size()); - logVal[kv.key.removePrefix(LiteralStringRef("log")).withPrefix(LiteralStringRef("debug"))] = intValue; + logVal[kv.key.removePrefix("log"_sr).withPrefix("debug"_sr)] = intValue; } // Get opsKeys and validate if it has correct value @@ -372,12 +372,11 @@ struct AtomicOpsWorkload : TestWorkload { RangeResult log_ = wait(tr.getRange(KeyRangeRef(begin, strinc(begin)), CLIENT_KNOBS->TOO_MANY)); log = log_; uint64_t zeroValue = 0; - tr.set(LiteralStringRef("xlogResult"), - StringRef((const uint8_t*)&zeroValue, sizeof(zeroValue))); + tr.set("xlogResult"_sr, StringRef((const uint8_t*)&zeroValue, sizeof(zeroValue))); for (auto& kv : log) { uint64_t intValue = 0; memcpy(&intValue, kv.value.begin(), kv.value.size()); - tr.atomicOp(LiteralStringRef("xlogResult"), kv.value, self->opType); + tr.atomicOp("xlogResult"_sr, kv.value, self->opType); } } @@ -386,18 +385,16 @@ struct AtomicOpsWorkload : TestWorkload { Key begin(format("ops%08x", g)); RangeResult ops = wait(tr.getRange(KeyRangeRef(begin, strinc(begin)), CLIENT_KNOBS->TOO_MANY)); uint64_t zeroValue = 0; - tr.set(LiteralStringRef("xopsResult"), - StringRef((const uint8_t*)&zeroValue, sizeof(zeroValue))); + tr.set("xopsResult"_sr, StringRef((const uint8_t*)&zeroValue, sizeof(zeroValue))); for (auto& kv : ops) { uint64_t intValue = 0; memcpy(&intValue, kv.value.begin(), kv.value.size()); - tr.atomicOp(LiteralStringRef("xopsResult"), kv.value, self->opType); + tr.atomicOp("xopsResult"_sr, kv.value, self->opType); } - if (tr.get(LiteralStringRef("xlogResult")).get() != - tr.get(LiteralStringRef("xopsResult")).get()) { - Optional> logResult = tr.get(LiteralStringRef("xlogResult")).get(); - Optional> opsResult = tr.get(LiteralStringRef("xopsResult")).get(); + if (tr.get("xlogResult"_sr).get() != tr.get("xopsResult"_sr).get()) { + Optional> logResult = tr.get("xlogResult"_sr).get(); + Optional> opsResult = tr.get("xopsResult"_sr).get(); ASSERT(logResult.present()); ASSERT(opsResult.present()); TraceEvent(SevError, "LogMismatch") @@ -408,7 +405,7 @@ struct AtomicOpsWorkload : TestWorkload { if (self->opType == MutationRef::AddValue) { uint64_t opsResult = 0; - Key opsResultStr = tr.get(LiteralStringRef("xopsResult")).get().get(); + Key opsResultStr = tr.get("xopsResult"_sr).get().get(); memcpy(&opsResult, opsResultStr.begin(), opsResultStr.size()); uint64_t logResult = 0; for (auto& kv : log) { diff --git a/fdbserver/workloads/AtomicOpsApiCorrectness.actor.cpp b/fdbserver/workloads/AtomicOpsApiCorrectness.actor.cpp index 05cdb18985..bd10514d5d 100644 --- a/fdbserver/workloads/AtomicOpsApiCorrectness.actor.cpp +++ b/fdbserver/workloads/AtomicOpsApiCorrectness.actor.cpp @@ -40,7 +40,7 @@ private: public: AtomicOpsApiCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - opType = getOption(options, LiteralStringRef("opType"), -1); + opType = getOption(options, "opType"_sr, -1); } std::string description() const override { return "AtomicOpsApiCorrectness"; } diff --git a/fdbserver/workloads/AtomicRestore.actor.cpp b/fdbserver/workloads/AtomicRestore.actor.cpp index 86d90e1093..d03a90fa73 100644 --- a/fdbserver/workloads/AtomicRestore.actor.cpp +++ b/fdbserver/workloads/AtomicRestore.actor.cpp @@ -37,15 +37,15 @@ struct AtomicRestoreWorkload : TestWorkload { AtomicRestoreWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - startAfter = getOption(options, LiteralStringRef("startAfter"), 10.0); - restoreAfter = getOption(options, LiteralStringRef("restoreAfter"), 20.0); - fastRestore = getOption(options, LiteralStringRef("fastRestore"), false); + startAfter = getOption(options, "startAfter"_sr, 10.0); + restoreAfter = getOption(options, "restoreAfter"_sr, 20.0); + fastRestore = getOption(options, "fastRestore"_sr, false); backupRanges.push_back_deep(backupRanges.arena(), normalKeys); - usePartitionedLogs.set(getOption( - options, LiteralStringRef("usePartitionedLogs"), deterministicRandom()->random01() < 0.5 ? true : false)); + usePartitionedLogs.set( + getOption(options, "usePartitionedLogs"_sr, deterministicRandom()->random01() < 0.5 ? true : false)); - addPrefix = getOption(options, LiteralStringRef("addPrefix"), LiteralStringRef("")); - removePrefix = getOption(options, LiteralStringRef("removePrefix"), LiteralStringRef("")); + addPrefix = getOption(options, "addPrefix"_sr, ""_sr); + removePrefix = getOption(options, "removePrefix"_sr, ""_sr); // Correctness is not clean for addPrefix feature yet. Uncomment below to enable the test // Generate addPrefix @@ -81,7 +81,7 @@ struct AtomicRestoreWorkload : TestWorkload { void getMetrics(std::vector& m) override {} - bool hasPrefix() const { return addPrefix != LiteralStringRef("") || removePrefix != LiteralStringRef(""); } + bool hasPrefix() const { return addPrefix != ""_sr || removePrefix != ""_sr; } ACTOR static Future _start(Database cx, AtomicRestoreWorkload* self) { state FileBackupAgent backupAgent; @@ -138,8 +138,8 @@ struct AtomicRestoreWorkload : TestWorkload { } // SOMEDAY: Remove after backup agents can exist quiescently - if (g_simulator.backupAgents == ISimulator::BackupAgentType::BackupToFile) { - g_simulator.backupAgents = ISimulator::BackupAgentType::NoBackupAgents; + if (g_simulator->backupAgents == ISimulator::BackupAgentType::BackupToFile) { + g_simulator->backupAgents = ISimulator::BackupAgentType::NoBackupAgents; } TraceEvent("AtomicRestore_Done").log(); diff --git a/fdbserver/workloads/AtomicSwitchover.actor.cpp b/fdbserver/workloads/AtomicSwitchover.actor.cpp index cfd16c017b..a42d7b51f2 100644 --- a/fdbserver/workloads/AtomicSwitchover.actor.cpp +++ b/fdbserver/workloads/AtomicSwitchover.actor.cpp @@ -33,15 +33,15 @@ struct AtomicSwitchoverWorkload : TestWorkload { AtomicSwitchoverWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - switch1delay = getOption(options, LiteralStringRef("switch1delay"), 50.0); - switch2delay = getOption(options, LiteralStringRef("switch2delay"), 50.0); - stopDelay = getOption(options, LiteralStringRef("stopDelay"), 50.0); + switch1delay = getOption(options, "switch1delay"_sr, 50.0); + switch2delay = getOption(options, "switch2delay"_sr, 50.0); + stopDelay = getOption(options, "stopDelay"_sr, 50.0); backupRanges.push_back_deep(backupRanges.arena(), normalKeys); - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } @@ -193,8 +193,8 @@ struct AtomicSwitchoverWorkload : TestWorkload { TraceEvent("AS_Done").log(); // SOMEDAY: Remove after backup agents can exist quiescently - if (g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) { - g_simulator.drAgents = ISimulator::BackupAgentType::NoBackupAgents; + if (g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) { + g_simulator->drAgents = ISimulator::BackupAgentType::NoBackupAgents; } return Void(); diff --git a/fdbserver/workloads/BackgroundSelectors.actor.cpp b/fdbserver/workloads/BackgroundSelectors.actor.cpp index 702b40054a..63ee6511cf 100644 --- a/fdbserver/workloads/BackgroundSelectors.actor.cpp +++ b/fdbserver/workloads/BackgroundSelectors.actor.cpp @@ -39,13 +39,12 @@ struct BackgroundSelectorWorkload : TestWorkload { BackgroundSelectorWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operations("Operations"), checks("Checks"), retries("Retries") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - actorsPerClient = std::max(getOption(options, LiteralStringRef("actorsPerClient"), 1), 1); - maxDiff = std::max(getOption(options, LiteralStringRef("maxDiff"), 100), 2); - minDrift = getOption(options, LiteralStringRef("minDiff"), -10); - maxDrift = getOption(options, LiteralStringRef("minDiff"), 100); - transactionsPerSecond = - getOption(options, LiteralStringRef("transactionsPerSecond"), 10.0) / (clientCount * actorsPerClient); + testDuration = getOption(options, "testDuration"_sr, 10.0); + actorsPerClient = std::max(getOption(options, "actorsPerClient"_sr, 1), 1); + maxDiff = std::max(getOption(options, "maxDiff"_sr, 100), 2); + minDrift = getOption(options, "minDiff"_sr, -10); + maxDrift = getOption(options, "minDiff"_sr, 100); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 10.0) / (clientCount * actorsPerClient); resultLimit = 10 * maxDiff; } diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 749a652907..552b6da1d5 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -51,34 +51,33 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { BackupAndParallelRestoreCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { locked.set(sharedRandomNumber % 2); - backupAfter = getOption(options, LiteralStringRef("backupAfter"), 10.0); - restoreAfter = getOption(options, LiteralStringRef("restoreAfter"), 35.0); - performRestore = getOption(options, LiteralStringRef("performRestore"), true); - backupTag = getOption(options, LiteralStringRef("backupTag"), BackupAgentBase::getDefaultTag()); - backupRangesCount = getOption(options, LiteralStringRef("backupRangesCount"), 5); - backupRangeLengthMax = getOption(options, LiteralStringRef("backupRangeLengthMax"), 1); + backupAfter = getOption(options, "backupAfter"_sr, 10.0); + restoreAfter = getOption(options, "restoreAfter"_sr, 35.0); + performRestore = getOption(options, "performRestore"_sr, true); + backupTag = getOption(options, "backupTag"_sr, BackupAgentBase::getDefaultTag()); + backupRangesCount = getOption(options, "backupRangesCount"_sr, 5); + backupRangeLengthMax = getOption(options, "backupRangeLengthMax"_sr, 1); abortAndRestartAfter = getOption(options, - LiteralStringRef("abortAndRestartAfter"), + "abortAndRestartAfter"_sr, deterministicRandom()->random01() < 0.5 ? deterministicRandom()->random01() * (restoreAfter - backupAfter) + backupAfter : 0.0); - differentialBackup = getOption( - options, LiteralStringRef("differentialBackup"), deterministicRandom()->random01() < 0.5 ? true : false); + differentialBackup = + getOption(options, "differentialBackup"_sr, deterministicRandom()->random01() < 0.5 ? true : false); stopDifferentialAfter = getOption(options, - LiteralStringRef("stopDifferentialAfter"), + "stopDifferentialAfter"_sr, differentialBackup ? deterministicRandom()->random01() * (restoreAfter - std::max(abortAndRestartAfter, backupAfter)) + std::max(abortAndRestartAfter, backupAfter) : 0.0); - agentRequest = getOption(options, LiteralStringRef("simBackupAgents"), true); - allowPauses = getOption(options, LiteralStringRef("allowPauses"), true); - shareLogRange = getOption(options, LiteralStringRef("shareLogRange"), false); - usePartitionedLogs.set( - getOption(options, LiteralStringRef("usePartitionedLogs"), deterministicRandom()->coinflip())); - addPrefix = getOption(options, LiteralStringRef("addPrefix"), LiteralStringRef("")); - removePrefix = getOption(options, LiteralStringRef("removePrefix"), LiteralStringRef("")); + agentRequest = getOption(options, "simBackupAgents"_sr, true); + allowPauses = getOption(options, "allowPauses"_sr, true); + shareLogRange = getOption(options, "shareLogRange"_sr, false); + usePartitionedLogs.set(getOption(options, "usePartitionedLogs"_sr, deterministicRandom()->coinflip())); + addPrefix = getOption(options, "addPrefix"_sr, ""_sr); + removePrefix = getOption(options, "removePrefix"_sr, ""_sr); KeyRef beginRange; KeyRef endRange; @@ -108,11 +107,10 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { if (shareLogRange) { bool beforePrefix = sharedRandomNumber & 1; if (beforePrefix) - backupRanges.push_back_deep(backupRanges.arena(), - KeyRangeRef(normalKeys.begin, LiteralStringRef("\xfe\xff\xfe"))); + backupRanges.push_back_deep(backupRanges.arena(), KeyRangeRef(normalKeys.begin, "\xfe\xff\xfe"_sr)); else backupRanges.push_back_deep(backupRanges.arena(), - KeyRangeRef(strinc(LiteralStringRef("\x00\x00\x01")), normalKeys.end)); + KeyRangeRef(strinc("\x00\x00\x01"_sr), normalKeys.end)); } else if (backupRangesCount <= 0) { backupRanges.push_back_deep(backupRanges.arena(), normalKeys); } else { @@ -161,7 +159,7 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { return _start(cx, this); } - bool hasPrefix() const { return addPrefix != LiteralStringRef("") || removePrefix != LiteralStringRef(""); } + bool hasPrefix() const { return addPrefix != ""_sr || removePrefix != ""_sr; } Future check(Database const& cx) override { return true; } @@ -480,7 +478,7 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { // Note the "partitionedLog" must be false, because we change // the configuration to disable backup workers before restore. extraBackup = backupAgent.submitBackup(cx, - LiteralStringRef("file://simfdb/backups/"), + "file://simfdb/backups/"_sr, {}, deterministicRandom()->randomInt(0, 60), deterministicRandom()->randomInt(0, 100), @@ -766,7 +764,7 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { } if (displaySystemKeys) { - wait(TaskBucket::debugPrintRange(cx, LiteralStringRef("\xff"), StringRef())); + wait(TaskBucket::debugPrintRange(cx, "\xff"_sr, StringRef())); } TraceEvent("BARW_Complete", randomID).detail("BackupTag", printable(self->backupTag)); @@ -777,9 +775,9 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { } // SOMEDAY: Remove after backup agents can exist quiescently - if ((g_simulator.backupAgents == ISimulator::BackupAgentType::BackupToFile) && + if ((g_simulator->backupAgents == ISimulator::BackupAgentType::BackupToFile) && (!BackupAndParallelRestoreCorrectnessWorkload::backupAgentRequests)) { - g_simulator.backupAgents = ISimulator::BackupAgentType::NoBackupAgents; + g_simulator->backupAgents = ISimulator::BackupAgentType::NoBackupAgents; } } catch (Error& e) { TraceEvent(SevError, "BackupAndParallelRestoreCorrectness").error(e).GetLastError(); diff --git a/fdbserver/workloads/BackupCorrectness.actor.cpp b/fdbserver/workloads/BackupCorrectness.actor.cpp index 5f2e7b120c..a0e2decf0a 100644 --- a/fdbserver/workloads/BackupCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupCorrectness.actor.cpp @@ -220,6 +220,8 @@ struct BackupAndRestoreCorrectnessWorkload : TestWorkload { ACTOR static Future statusLoop(Database cx, std::string tag) { state FileBackupAgent agent; loop { + bool active = wait(agent.checkActive(cx)); + TraceEvent("BARW_AgentActivityCheck").detail("IsActive", active); std::string status = wait(agent.getStatus(cx, ShowErrors::True, tag)); puts(status.c_str()); std::string statusJSON = wait(agent.getStatusJSON(cx, tag)); @@ -869,9 +871,9 @@ struct BackupAndRestoreCorrectnessWorkload : TestWorkload { } // SOMEDAY: Remove after backup agents can exist quiescently - if ((g_simulator.backupAgents == ISimulator::BackupAgentType::BackupToFile) && + if ((g_simulator->backupAgents == ISimulator::BackupAgentType::BackupToFile) && (!BackupAndRestoreCorrectnessWorkload::backupAgentRequests)) { - g_simulator.backupAgents = ISimulator::BackupAgentType::NoBackupAgents; + g_simulator->backupAgents = ISimulator::BackupAgentType::NoBackupAgents; } } catch (Error& e) { TraceEvent(SevError, "BackupAndRestoreCorrectness").error(e).GetLastError(); diff --git a/fdbserver/workloads/BackupToBlob.actor.cpp b/fdbserver/workloads/BackupToBlob.actor.cpp index 480ae62466..0ec4dbc5e0 100644 --- a/fdbserver/workloads/BackupToBlob.actor.cpp +++ b/fdbserver/workloads/BackupToBlob.actor.cpp @@ -35,15 +35,12 @@ struct BackupToBlobWorkload : TestWorkload { static constexpr const char* DESCRIPTION = "BackupToBlob"; BackupToBlobWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - backupAfter = getOption(options, LiteralStringRef("backupAfter"), 10.0); - backupTag = getOption(options, LiteralStringRef("backupTag"), BackupAgentBase::getDefaultTag()); - auto backupURLString = - getOption(options, LiteralStringRef("backupURL"), LiteralStringRef("http://0.0.0.0:10000")).toString(); - auto accessKeyEnvVar = - getOption(options, LiteralStringRef("accessKeyVar"), LiteralStringRef("BLOB_ACCESS_KEY")).toString(); - auto secretKeyEnvVar = - getOption(options, LiteralStringRef("secretKeyVar"), LiteralStringRef("BLOB_SECRET_KEY")).toString(); - bool provideKeys = getOption(options, LiteralStringRef("provideKeys"), false); + backupAfter = getOption(options, "backupAfter"_sr, 10.0); + backupTag = getOption(options, "backupTag"_sr, BackupAgentBase::getDefaultTag()); + auto backupURLString = getOption(options, "backupURL"_sr, "http://0.0.0.0:10000"_sr).toString(); + auto accessKeyEnvVar = getOption(options, "accessKeyVar"_sr, "BLOB_ACCESS_KEY"_sr).toString(); + auto secretKeyEnvVar = getOption(options, "secretKeyVar"_sr, "BLOB_SECRET_KEY"_sr).toString(); + bool provideKeys = getOption(options, "provideKeys"_sr, false); if (provideKeys) { updateBackupURL(backupURLString, accessKeyEnvVar, "", secretKeyEnvVar, ""); } diff --git a/fdbserver/workloads/BackupToDBAbort.actor.cpp b/fdbserver/workloads/BackupToDBAbort.actor.cpp index 55888fcbfb..92db4ecc33 100644 --- a/fdbserver/workloads/BackupToDBAbort.actor.cpp +++ b/fdbserver/workloads/BackupToDBAbort.actor.cpp @@ -33,13 +33,13 @@ struct BackupToDBAbort : TestWorkload { UID lockid; explicit BackupToDBAbort(const WorkloadContext& wcx) : TestWorkload(wcx) { - abortDelay = getOption(options, LiteralStringRef("abortDelay"), 50.0); + abortDelay = getOption(options, "abortDelay"_sr, 50.0); backupRanges.push_back_deep(backupRanges.arena(), normalKeys); - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); lockid = UID(0xbeeffeed, 0xdecaf00d); @@ -94,8 +94,8 @@ struct BackupToDBAbort : TestWorkload { TraceEvent("BDBA_End").log(); // SOMEDAY: Remove after backup agents can exist quiescently - if (g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) { - g_simulator.drAgents = ISimulator::BackupAgentType::NoBackupAgents; + if (g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) { + g_simulator->drAgents = ISimulator::BackupAgentType::NoBackupAgents; } return Void(); diff --git a/fdbserver/workloads/BackupToDBCorrectness.actor.cpp b/fdbserver/workloads/BackupToDBCorrectness.actor.cpp index b64ee9c0d9..b02684c284 100644 --- a/fdbserver/workloads/BackupToDBCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupToDBCorrectness.actor.cpp @@ -46,44 +46,44 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { BackupToDBCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { locked.set(sharedRandomNumber % 2); - backupAfter = getOption(options, LiteralStringRef("backupAfter"), 10.0); - restoreAfter = getOption(options, LiteralStringRef("restoreAfter"), 35.0); - performRestore = getOption(options, LiteralStringRef("performRestore"), true); - backupTag = getOption(options, LiteralStringRef("backupTag"), BackupAgentBase::getDefaultTag()); - restoreTag = getOption(options, LiteralStringRef("restoreTag"), LiteralStringRef("restore")); - backupPrefix = getOption(options, LiteralStringRef("backupPrefix"), StringRef()); + backupAfter = getOption(options, "backupAfter"_sr, 10.0); + restoreAfter = getOption(options, "restoreAfter"_sr, 35.0); + performRestore = getOption(options, "performRestore"_sr, true); + backupTag = getOption(options, "backupTag"_sr, BackupAgentBase::getDefaultTag()); + restoreTag = getOption(options, "restoreTag"_sr, "restore"_sr); + backupPrefix = getOption(options, "backupPrefix"_sr, StringRef()); backupRangesCount = getOption(options, - LiteralStringRef("backupRangesCount"), + "backupRangesCount"_sr, 5); // tests can hangs if set higher than 1 + BACKUP_MAP_KEY_LOWER_LIMIT - backupRangeLengthMax = getOption(options, LiteralStringRef("backupRangeLengthMax"), 1); + backupRangeLengthMax = getOption(options, "backupRangeLengthMax"_sr, 1); abortAndRestartAfter = getOption(options, - LiteralStringRef("abortAndRestartAfter"), + "abortAndRestartAfter"_sr, (!locked && deterministicRandom()->random01() < 0.5) ? deterministicRandom()->random01() * (restoreAfter - backupAfter) + backupAfter : 0.0); - differentialBackup = getOption( - options, LiteralStringRef("differentialBackup"), deterministicRandom()->random01() < 0.5 ? true : false); + differentialBackup = + getOption(options, "differentialBackup"_sr, deterministicRandom()->random01() < 0.5 ? true : false); stopDifferentialAfter = getOption(options, - LiteralStringRef("stopDifferentialAfter"), + "stopDifferentialAfter"_sr, differentialBackup ? deterministicRandom()->random01() * (restoreAfter - std::max(abortAndRestartAfter, backupAfter)) + std::max(abortAndRestartAfter, backupAfter) : 0.0); - agentRequest = getOption(options, LiteralStringRef("simDrAgents"), true); - shareLogRange = getOption(options, LiteralStringRef("shareLogRange"), false); + agentRequest = getOption(options, "simDrAgents"_sr, true); + shareLogRange = getOption(options, "shareLogRange"_sr, false); // Use sharedRandomNumber if shareLogRange is true so that we can ensure backup and DR both backup the same // range beforePrefix = shareLogRange ? (sharedRandomNumber & 1) : (deterministicRandom()->random01() < 0.5); if (beforePrefix) { - extraPrefix = backupPrefix.withPrefix(LiteralStringRef("\xfe\xff\xfe")); - backupPrefix = backupPrefix.withPrefix(LiteralStringRef("\xfe\xff\xff")); + extraPrefix = backupPrefix.withPrefix("\xfe\xff\xfe"_sr); + backupPrefix = backupPrefix.withPrefix("\xfe\xff\xff"_sr); } else { - extraPrefix = backupPrefix.withPrefix(LiteralStringRef("\x00\x00\x01")); - backupPrefix = backupPrefix.withPrefix(LiteralStringRef("\x00\x00\00")); + extraPrefix = backupPrefix.withPrefix("\x00\x00\x01"_sr); + backupPrefix = backupPrefix.withPrefix("\x00\x00\00"_sr); } ASSERT(backupPrefix != StringRef()); @@ -94,11 +94,10 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { if (shareLogRange) { if (beforePrefix) - backupRanges.push_back_deep(backupRanges.arena(), - KeyRangeRef(normalKeys.begin, LiteralStringRef("\xfe\xff\xfe"))); + backupRanges.push_back_deep(backupRanges.arena(), KeyRangeRef(normalKeys.begin, "\xfe\xff\xfe"_sr)); else backupRanges.push_back_deep(backupRanges.arena(), - KeyRangeRef(strinc(LiteralStringRef("\x00\x00\x01")), normalKeys.end)); + KeyRangeRef(strinc("\x00\x00\x01"_sr), normalKeys.end)); } else if (backupRangesCount <= 0) { if (beforePrefix) backupRanges.push_back_deep(backupRanges.arena(), @@ -129,9 +128,9 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { } } - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); TraceEvent("BARW_Start").detail("Locked", locked); @@ -441,7 +440,7 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { .detail("TaskCount", taskCount) .detail("WaitCycles", waitCycles); printf("EndingNonZeroTasks: %ld\n", (long)taskCount); - wait(TaskBucket::debugPrintRange(cx, LiteralStringRef("\xff"), StringRef())); + wait(TaskBucket::debugPrintRange(cx, "\xff"_sr, StringRef())); } loop { @@ -549,7 +548,7 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { } if (displaySystemKeys) { - wait(TaskBucket::debugPrintRange(cx, LiteralStringRef("\xff"), StringRef())); + wait(TaskBucket::debugPrintRange(cx, "\xff"_sr, StringRef())); } return Void(); } @@ -737,9 +736,9 @@ struct BackupToDBCorrectnessWorkload : TestWorkload { } // SOMEDAY: Remove after backup agents can exist quiescently - if ((g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) && + if ((g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) && (!BackupToDBCorrectnessWorkload::drAgentRequests)) { - g_simulator.drAgents = ISimulator::BackupAgentType::NoBackupAgents; + g_simulator->drAgents = ISimulator::BackupAgentType::NoBackupAgents; } } catch (Error& e) { TraceEvent(SevError, "BackupAndRestoreCorrectness").error(e); diff --git a/fdbserver/workloads/BackupToDBUpgrade.actor.cpp b/fdbserver/workloads/BackupToDBUpgrade.actor.cpp index 44e76f025c..ed32897dd0 100644 --- a/fdbserver/workloads/BackupToDBUpgrade.actor.cpp +++ b/fdbserver/workloads/BackupToDBUpgrade.actor.cpp @@ -36,15 +36,15 @@ struct BackupToDBUpgradeWorkload : TestWorkload { Database extraDB; BackupToDBUpgradeWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - backupAfter = getOption(options, LiteralStringRef("backupAfter"), deterministicRandom()->random01() * 10.0); - backupPrefix = getOption(options, LiteralStringRef("backupPrefix"), StringRef()); - backupRangeLengthMax = getOption(options, LiteralStringRef("backupRangeLengthMax"), 1); - stopDifferentialAfter = getOption(options, LiteralStringRef("stopDifferentialAfter"), 60.0); - backupTag = getOption(options, LiteralStringRef("backupTag"), BackupAgentBase::getDefaultTag()); - restoreTag = getOption(options, LiteralStringRef("restoreTag"), LiteralStringRef("restore")); - backupRangesCount = getOption(options, LiteralStringRef("backupRangesCount"), 5); - extraPrefix = backupPrefix.withPrefix(LiteralStringRef("\xfe\xff\xfe")); - backupPrefix = backupPrefix.withPrefix(LiteralStringRef("\xfe\xff\xff")); + backupAfter = getOption(options, "backupAfter"_sr, deterministicRandom()->random01() * 10.0); + backupPrefix = getOption(options, "backupPrefix"_sr, StringRef()); + backupRangeLengthMax = getOption(options, "backupRangeLengthMax"_sr, 1); + stopDifferentialAfter = getOption(options, "stopDifferentialAfter"_sr, 60.0); + backupTag = getOption(options, "backupTag"_sr, BackupAgentBase::getDefaultTag()); + restoreTag = getOption(options, "restoreTag"_sr, "restore"_sr); + backupRangesCount = getOption(options, "backupRangesCount"_sr, 5); + extraPrefix = backupPrefix.withPrefix("\xfe\xff\xfe"_sr); + backupPrefix = backupPrefix.withPrefix("\xfe\xff\xff"_sr); ASSERT(backupPrefix != StringRef()); @@ -77,9 +77,9 @@ struct BackupToDBUpgradeWorkload : TestWorkload { } } - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); TraceEvent("DRU_Start").log(); @@ -178,7 +178,7 @@ struct BackupToDBUpgradeWorkload : TestWorkload { .detail("TaskCount", taskCount) .detail("WaitCycles", waitCycles); printf("EndingNonZeroTasks: %ld\n", (long)taskCount); - wait(TaskBucket::debugPrintRange(cx, LiteralStringRef("\xff"), StringRef())); + wait(TaskBucket::debugPrintRange(cx, "\xff"_sr, StringRef())); } loop { @@ -282,7 +282,7 @@ struct BackupToDBUpgradeWorkload : TestWorkload { } if (displaySystemKeys) { - wait(TaskBucket::debugPrintRange(cx, LiteralStringRef("\xff"), StringRef())); + wait(TaskBucket::debugPrintRange(cx, "\xff"_sr, StringRef())); } return Void(); @@ -520,8 +520,8 @@ struct BackupToDBUpgradeWorkload : TestWorkload { TraceEvent("DRU_Complete").detail("BackupTag", printable(self->backupTag)); - if (g_simulator.drAgents == ISimulator::BackupAgentType::BackupToDB) { - g_simulator.drAgents = ISimulator::BackupAgentType::NoBackupAgents; + if (g_simulator->drAgents == ISimulator::BackupAgentType::BackupToDB) { + g_simulator->drAgents = ISimulator::BackupAgentType::NoBackupAgents; } } catch (Error& e) { TraceEvent(SevError, "BackupAndRestoreCorrectnessError").error(e); diff --git a/fdbserver/workloads/BlobGranuleCorrectnessWorkload.actor.cpp b/fdbserver/workloads/BlobGranuleCorrectnessWorkload.actor.cpp index 5b73712fd3..b94b50ffe7 100644 --- a/fdbserver/workloads/BlobGranuleCorrectnessWorkload.actor.cpp +++ b/fdbserver/workloads/BlobGranuleCorrectnessWorkload.actor.cpp @@ -193,7 +193,7 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload { BlobGranuleCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { doSetup = !clientId; // only do this on the "first" client - testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0); + testDuration = getOption(options, "testDuration"_sr, 120.0); // randomize global test settings based on shared parameter to get similar workload across tests, but then vary // different parameters within those constraints @@ -475,7 +475,7 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload { beginVersionByChunk.insert(normalKeys, 0); int beginCollapsed = 0; int beginNotCollapsed = 0; - Key lastBeginKey = LiteralStringRef(""); + Key lastBeginKey = ""_sr; for (auto& chunk : blob.second) { KeyRange beginVersionRange; if (chunk.tenantPrefix.present()) { diff --git a/fdbserver/workloads/BlobGranuleRangesWorkload.actor.cpp b/fdbserver/workloads/BlobGranuleRangesWorkload.actor.cpp index b72b884656..42e87398d0 100644 --- a/fdbserver/workloads/BlobGranuleRangesWorkload.actor.cpp +++ b/fdbserver/workloads/BlobGranuleRangesWorkload.actor.cpp @@ -401,64 +401,70 @@ struct BlobGranuleRangesWorkload : TestWorkload { return Void(); } - ACTOR Future rangesMisalignedUnit(Database cx, BlobGranuleRangesWorkload* self, KeyRange range) { - // FIXME: parts of this don't work yet - bool setSuccess = wait(self->setRange(cx, range, true)); - ASSERT(setSuccess); - state KeyRange subRange(KeyRangeRef(range.begin.withSuffix("A"_sr), range.begin.withSuffix("B"_sr))); - - // getBlobGranules and getBlobRanges on subRange - should return actual granules instead of clipped to subRange - Standalone> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000)); + ACTOR Future checkRangesMisaligned(Database cx, + BlobGranuleRangesWorkload* self, + KeyRange expectedRange, + KeyRange queryRange) { + Standalone> blobRanges = wait(cx->listBlobbifiedRanges(queryRange, 1000000)); ASSERT(blobRanges.size() == 1); - ASSERT(blobRanges[0] == range); + ASSERT(blobRanges[0] == expectedRange); state Transaction tr(cx); loop { try { - Standalone> granules = wait(tr.getBlobGranuleRanges(range, 1000000)); + Standalone> granules = wait(tr.getBlobGranuleRanges(queryRange, 1000000)); ASSERT(granules.size() == 1); - ASSERT(granules[0] == range); + ASSERT(granules[0] == expectedRange); break; } catch (Error& e) { wait(tr.onError(e)); } } - Key purgeKey = wait(cx->purgeBlobGranules(subRange, 1, {}, false)); - wait(cx->waitPurgeGranulesComplete(purgeKey)); - - // should still be active after normal purge - bool success1 = wait(self->isRangeActive(cx, subRange)); - ASSERT(success1); - - bool success2 = wait(self->isRangeActive(cx, range)); - ASSERT(success2); - - Key forcePurgeKey = wait(cx->purgeBlobGranules(subRange, 1, {}, false)); - wait(cx->waitPurgeGranulesComplete(forcePurgeKey)); - - // should NOT still be active after force purge - bool fail1 = wait(self->isRangeActive(cx, subRange)); - ASSERT(!fail1); - - bool fail2 = wait(self->isRangeActive(cx, range)); - ASSERT(!fail2); - - // getBlobGranules should return nothing here after purge - - tr.reset(); - loop { - try { - Standalone> granules2 = wait(tr.getBlobGranuleRanges(range, 1000000)); - ASSERT(granules2.empty()); - break; - } catch (Error& e) { - wait(tr.onError(e)); - } - } return Void(); + } - // TODO - also test purging larger range than blob range + ACTOR Future rangesMisalignedUnit(Database cx, BlobGranuleRangesWorkload* self, KeyRange range) { + bool setSuccess = wait(self->setRange(cx, range, true)); + ASSERT(setSuccess); + state KeyRange subRange(KeyRangeRef(range.begin.withSuffix("A"_sr), range.begin.withSuffix("B"_sr))); + + // validate range set up correctly + wait(self->checkRange(cx, self, range, true)); + wait(self->checkRangesMisaligned(cx, self, range, range)); + + // getBlobGranules and getBlobRanges on sub ranges- should return actual granule/range instead of clipped + wait(self->checkRange(cx, self, subRange, true)); + wait(self->checkRangesMisaligned(cx, self, range, subRange)); + wait(self->checkRangesMisaligned(cx, self, range, KeyRangeRef(range.begin, subRange.end))); + wait(self->checkRangesMisaligned(cx, self, range, KeyRangeRef(subRange.begin, range.end))); + + try { + wait(success(cx->purgeBlobGranules(subRange, 1, {}, false))); + ASSERT(false); + } catch (Error& e) { + if (e.code() == error_code_operation_cancelled) { + throw e; + } + ASSERT(e.code() == error_code_unsupported_operation); + } + + try { + wait(success(cx->purgeBlobGranules(subRange, 1, {}, true))); + ASSERT(false); + } catch (Error& e) { + if (e.code() == error_code_operation_cancelled) { + throw e; + } + ASSERT(e.code() == error_code_unsupported_operation); + } + + // ensure range still there after unaligned purges + wait(self->checkRange(cx, self, range, true)); + wait(self->checkRangesMisaligned(cx, self, range, range)); + + wait(self->tearDownRangeAfterUnit(cx, self, range)); + return Void(); } ACTOR Future blobbifyIdempotentUnit(Database cx, BlobGranuleRangesWorkload* self, KeyRange range) { @@ -527,9 +533,20 @@ struct BlobGranuleRangesWorkload : TestWorkload { } // tear down + check that un-blobbifying at a non-aligned range also doesn't work - Key purgeKey = wait(cx->purgeBlobGranules(range, 1, {}, true)); + Key purgeKey = wait(cx->purgeBlobGranules(activeRange, 1, {}, true)); 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> blobRanges = wait(cx->listBlobbifiedRanges(range, 1000000)); + ASSERT(blobRanges.size() == 1); + ASSERT(blobRanges[0] == activeRange); + bool unblobbifyFail1 = wait(self->setRange(cx, range, false)); ASSERT(!unblobbifyFail1); @@ -604,7 +621,6 @@ struct BlobGranuleRangesWorkload : TestWorkload { excludedTypes.insert(OP_COUNT); // FIXME: fix bugs and enable these tests! - excludedTypes.insert(RANGES_MISALIGNED); // TODO - fix in blob manager excludedTypes.insert(RE_BLOBBIFY); // TODO - fix is non-trivial, is desired behavior eventually std::string nextRangeKey = "U_" + self->newKey(); @@ -620,7 +636,6 @@ struct BlobGranuleRangesWorkload : TestWorkload { loopTries--; ASSERT(loopTries >= 0); } - if (BGRW_DEBUG) { fmt::print( "Selected range [{0} - {1}) for unit {2}.\n", range.begin.printable(), range.end.printable(), op); diff --git a/fdbserver/workloads/BlobGranuleVerifier.actor.cpp b/fdbserver/workloads/BlobGranuleVerifier.actor.cpp index 11913dc6c1..3ab9b2ebfd 100644 --- a/fdbserver/workloads/BlobGranuleVerifier.actor.cpp +++ b/fdbserver/workloads/BlobGranuleVerifier.actor.cpp @@ -87,30 +87,29 @@ struct BlobGranuleVerifierWorkload : TestWorkload { BlobGranuleVerifierWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { doSetup = !clientId; // only do this on the "first" client - testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0); - timeTravelLimit = getOption(options, LiteralStringRef("timeTravelLimit"), testDuration); - timeTravelBufferSize = getOption(options, LiteralStringRef("timeTravelBufferSize"), 100000000); - threads = getOption(options, LiteralStringRef("threads"), 1); + testDuration = getOption(options, "testDuration"_sr, 120.0); + timeTravelLimit = getOption(options, "timeTravelLimit"_sr, testDuration); + timeTravelBufferSize = getOption(options, "timeTravelBufferSize"_sr, 100000000); + threads = getOption(options, "threads"_sr, 1); - enablePurging = getOption(options, LiteralStringRef("enablePurging"), sharedRandomNumber % 3 == 0); + enablePurging = getOption(options, "enablePurging"_sr, sharedRandomNumber % 3 == 0); sharedRandomNumber /= 3; // FIXME: re-enable this! There exist several bugs with purging active granules where a small amount of state // won't be cleaned up. - strictPurgeChecking = - getOption(options, LiteralStringRef("strictPurgeChecking"), false /*sharedRandomNumber % 2 == 0*/); + strictPurgeChecking = getOption(options, "strictPurgeChecking"_sr, false /*sharedRandomNumber % 2 == 0*/); sharedRandomNumber /= 2; - doForcePurge = getOption(options, LiteralStringRef("doForcePurge"), sharedRandomNumber % 3 == 0); + doForcePurge = getOption(options, "doForcePurge"_sr, sharedRandomNumber % 3 == 0); sharedRandomNumber /= 3; - purgeAtLatest = getOption(options, LiteralStringRef("purgeAtLatest"), sharedRandomNumber % 3 == 0); + purgeAtLatest = getOption(options, "purgeAtLatest"_sr, sharedRandomNumber % 3 == 0); sharedRandomNumber /= 3; // randomly some tests write data first and then turn on blob granules later, to test conversion of existing DB initAtEnd = !enablePurging && sharedRandomNumber % 10 == 0; sharedRandomNumber /= 10; - clearAndMergeCheck = getOption(options, LiteralStringRef("clearAndMergeCheck"), sharedRandomNumber % 10 == 0); + clearAndMergeCheck = getOption(options, "clearAndMergeCheck"_sr, sharedRandomNumber % 10 == 0); sharedRandomNumber /= 10; // don't do strictPurgeChecking or forcePurge if !enablePurging @@ -160,7 +159,7 @@ struct BlobGranuleVerifierWorkload : TestWorkload { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); tr->set(blobRangeChangeKey, deterministicRandom()->randomUniqueID().toString()); - wait(krmSetRange(tr, blobRangeKeys.begin, KeyRange(normalKeys), LiteralStringRef("1"))); + wait(krmSetRange(tr, blobRangeKeys.begin, KeyRange(normalKeys), "1"_sr)); wait(tr->commit()); if (BGV_DEBUG) { printf("Successfully set up blob granule range for normalKeys\n"); diff --git a/fdbserver/workloads/BulkLoad.actor.cpp b/fdbserver/workloads/BulkLoad.actor.cpp index d10a909a04..e5726cdfcb 100644 --- a/fdbserver/workloads/BulkLoad.actor.cpp +++ b/fdbserver/workloads/BulkLoad.actor.cpp @@ -38,13 +38,13 @@ struct BulkLoadWorkload : TestWorkload { BulkLoadWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), clientCount(wcx.clientCount), transactions("Transactions"), retries("Retries"), latencies(2000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - actorCount = getOption(options, LiteralStringRef("actorCount"), 20); - writesPerTransaction = getOption(options, LiteralStringRef("writesPerTransaction"), 10); - valueBytes = std::max(getOption(options, LiteralStringRef("valueBytes"), 96), 16); + testDuration = getOption(options, "testDuration"_sr, 10.0); + actorCount = getOption(options, "actorCount"_sr, 20); + writesPerTransaction = getOption(options, "writesPerTransaction"_sr, 10); + valueBytes = std::max(getOption(options, "valueBytes"_sr, 96), 16); value = Value(std::string(valueBytes, '.')); - targetBytes = getOption(options, LiteralStringRef("targetBytes"), std::numeric_limits::max()); - keyPrefix = getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("")); + targetBytes = getOption(options, "targetBytes"_sr, std::numeric_limits::max()); + keyPrefix = getOption(options, "keyPrefix"_sr, ""_sr); keyPrefix = unprintable(keyPrefix.toString()); } diff --git a/fdbserver/workloads/Cache.actor.cpp b/fdbserver/workloads/Cache.actor.cpp index 2b107a07e1..23bd9e3d92 100644 --- a/fdbserver/workloads/Cache.actor.cpp +++ b/fdbserver/workloads/Cache.actor.cpp @@ -27,7 +27,7 @@ struct CacheWorkload : TestWorkload { Key keyPrefix; CacheWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - keyPrefix = unprintable(getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("")).toString()); + keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, ""_sr).toString()); } std::string description() const override { return "CacheWorkload"; } diff --git a/fdbserver/workloads/ChangeConfig.actor.cpp b/fdbserver/workloads/ChangeConfig.actor.cpp index 840e8d2448..0d36ebd409 100644 --- a/fdbserver/workloads/ChangeConfig.actor.cpp +++ b/fdbserver/workloads/ChangeConfig.actor.cpp @@ -33,13 +33,18 @@ struct ChangeConfigWorkload : TestWorkload { double minDelayBeforeChange, maxDelayBeforeChange; std::string configMode; //<\"single\"|\"double\"|\"triple\"> std::string networkAddresses; // comma separated list e.g. "127.0.0.1:4000,127.0.0.1:4001" + int coordinatorChanges; // number of times to change coordinators. Only applied if `coordinators` is set to `auto` ChangeConfigWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - minDelayBeforeChange = getOption(options, LiteralStringRef("minDelayBeforeChange"), 0); - maxDelayBeforeChange = getOption(options, LiteralStringRef("maxDelayBeforeChange"), 0); + minDelayBeforeChange = getOption(options, "minDelayBeforeChange"_sr, 0); + maxDelayBeforeChange = getOption(options, "maxDelayBeforeChange"_sr, 0); ASSERT(maxDelayBeforeChange >= minDelayBeforeChange); - configMode = getOption(options, LiteralStringRef("configMode"), StringRef()).toString(); - networkAddresses = getOption(options, LiteralStringRef("coordinators"), StringRef()).toString(); + configMode = getOption(options, "configMode"_sr, StringRef()).toString(); + networkAddresses = getOption(options, "coordinators"_sr, StringRef()).toString(); + coordinatorChanges = getOption(options, "coordinatorChanges"_sr, 1); + if (networkAddresses != "auto") { + coordinatorChanges = 1; + } } std::string description() const override { return "ChangeConfig"; } @@ -57,11 +62,11 @@ struct ChangeConfigWorkload : TestWorkload { ACTOR Future configureExtraDatabase(ChangeConfigWorkload* self, Database db) { wait(delay(5 * deterministicRandom()->random01())); if (self->configMode.size()) { - if (g_simulator.startingDisabledConfiguration != "") { + if (g_simulator->startingDisabledConfiguration != "") { // It is not safe to allow automatic failover to a region which is not fully replicated, // so wait for both regions to be fully replicated before enabling failover wait(success( - ManagementAPI::changeConfig(db.getReference(), g_simulator.startingDisabledConfiguration, true))); + ManagementAPI::changeConfig(db.getReference(), g_simulator->startingDisabledConfiguration, true))); TraceEvent("WaitForReplicasExtra").log(); wait(waitForFullReplication(db)); TraceEvent("WaitForReplicasExtraEnd").log(); @@ -84,7 +89,7 @@ struct ChangeConfigWorkload : TestWorkload { Future configureExtraDatabases(ChangeConfigWorkload* self) { std::vector> futures; if (g_network->isSimulated()) { - for (auto extraDatabase : g_simulator.extraDatabases) { + for (auto extraDatabase : g_simulator->extraDatabases) { auto extraFile = makeReference(ClusterConnectionString(extraDatabase)); Database db = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); futures.push_back(configureExtraDatabase(self, db)); @@ -106,22 +111,28 @@ struct ChangeConfigWorkload : TestWorkload { } if (self->configMode.size()) { - if (g_network->isSimulated() && g_simulator.startingDisabledConfiguration != "") { + if (g_network->isSimulated() && g_simulator->startingDisabledConfiguration != "") { // It is not safe to allow automatic failover to a region which is not fully replicated, // so wait for both regions to be fully replicated before enabling failover wait(success( - ManagementAPI::changeConfig(cx.getReference(), g_simulator.startingDisabledConfiguration, true))); + ManagementAPI::changeConfig(cx.getReference(), g_simulator->startingDisabledConfiguration, true))); TraceEvent("WaitForReplicas").log(); wait(waitForFullReplication(cx)); TraceEvent("WaitForReplicasEnd").log(); } wait(success(ManagementAPI::changeConfig(cx.getReference(), self->configMode, true))); } - if (self->networkAddresses.size()) { - if (self->networkAddresses == "auto") - wait(CoordinatorsChangeActor(cx, self, true)); - else - wait(CoordinatorsChangeActor(cx, self)); + if ((g_network->isSimulated() && g_simulator->configDBType != ConfigDBType::SIMPLE) || + !g_network->isSimulated()) { + if (self->networkAddresses.size()) { + state int i; + for (i = 0; i < self->coordinatorChanges; ++i) { + if (i > 0) { + wait(delay(20)); + } + wait(CoordinatorsChangeActor(cx, self, self->networkAddresses == "auto")); + } + } } if (!extraConfigureBefore) { @@ -140,9 +151,11 @@ struct ChangeConfigWorkload : TestWorkload { if (autoChange) { // if auto, we first get the desired addresses by read \xff\xff/management/auto_coordinators loop { try { - Optional newCoordinatorsKey = wait(tr.get( - LiteralStringRef("auto_coordinators") - .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); + // Set RAW_ACCESS to explicitly avoid using tenants because + // access to management keys is denied for tenant transactions + tr.setOption(FDBTransactionOptions::RAW_ACCESS); + Optional newCoordinatorsKey = wait(tr.get("auto_coordinators"_sr.withPrefix( + SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))); ASSERT(newCoordinatorsKey.present()); desiredCoordinatorsKey = newCoordinatorsKey.get().toString(); tr.reset(); @@ -179,8 +192,7 @@ struct ChangeConfigWorkload : TestWorkload { loop { try { tr.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); - tr.set(LiteralStringRef("processes") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), + tr.set("processes"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), Value(desiredCoordinatorsKey)); TraceEvent(SevDebug, "CoordinatorsChangeBeforeCommit") .detail("Auto", autoChange) diff --git a/fdbserver/workloads/ChangeFeedOperations.actor.cpp b/fdbserver/workloads/ChangeFeedOperations.actor.cpp index d6fd92bc5f..a0b1bf9923 100644 --- a/fdbserver/workloads/ChangeFeedOperations.actor.cpp +++ b/fdbserver/workloads/ChangeFeedOperations.actor.cpp @@ -78,7 +78,7 @@ struct FeedTestData : ReferenceCounted, NonCopyable { NotifiedVersion checkVersion; FeedTestData(Key key, bool doPops) - : key(key), keyRange(KeyRangeRef(key, keyAfter(key))), feedID(key.withPrefix(LiteralStringRef("CF"))), nextVal(0), + : key(key), keyRange(KeyRangeRef(key, keyAfter(key))), feedID(key.withPrefix("CF"_sr)), nextVal(0), lastCleared(false), poppingVersion(0), poppedVersion(0), destroying(false), destroyed(false), complete(false), checkVersion(0) { if (doPops) { diff --git a/fdbserver/workloads/ChangeFeeds.actor.cpp b/fdbserver/workloads/ChangeFeeds.actor.cpp index 530aa54e33..580a07072a 100644 --- a/fdbserver/workloads/ChangeFeeds.actor.cpp +++ b/fdbserver/workloads/ChangeFeeds.actor.cpp @@ -108,7 +108,7 @@ Standalone> advanceData(Standalone Standalone> mutations, Version begin, Version end) { - StringRef dbgKey = LiteralStringRef(""); + StringRef dbgKey = ""_sr; std::map data; for (auto& kv : source) { if (kv.key == dbgKey) diff --git a/fdbserver/workloads/ClearSingleRange.actor.cpp b/fdbserver/workloads/ClearSingleRange.actor.cpp index bc4ab66d34..38565bed4c 100644 --- a/fdbserver/workloads/ClearSingleRange.actor.cpp +++ b/fdbserver/workloads/ClearSingleRange.actor.cpp @@ -30,9 +30,9 @@ struct ClearSingleRange : TestWorkload { double startDelay; ClearSingleRange(WorkloadContext const& wcx) : TestWorkload(wcx) { - begin = getOption(options, LiteralStringRef("begin"), normalKeys.begin); - end = getOption(options, LiteralStringRef("end"), normalKeys.end); - startDelay = getOption(options, LiteralStringRef("beginClearRange"), 10.0); + begin = getOption(options, "begin"_sr, normalKeys.begin); + end = getOption(options, "end"_sr, normalKeys.end); + startDelay = getOption(options, "beginClearRange"_sr, 10.0); } std::string description() const override { return "ClearSingleRangeWorkload"; } diff --git a/fdbserver/workloads/ClientTransactionProfileCorrectness.actor.cpp b/fdbserver/workloads/ClientTransactionProfileCorrectness.actor.cpp index de9ef2a60a..6366d0060b 100644 --- a/fdbserver/workloads/ClientTransactionProfileCorrectness.actor.cpp +++ b/fdbserver/workloads/ClientTransactionProfileCorrectness.actor.cpp @@ -26,8 +26,8 @@ #include "fdbclient/Tuple.h" #include "flow/actorcompiler.h" // has to be last include -static const Key CLIENT_LATENCY_INFO_PREFIX = LiteralStringRef("client_latency/"); -static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = LiteralStringRef("client_latency_counter/"); +static const Key CLIENT_LATENCY_INFO_PREFIX = "client_latency/"_sr; +static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = "client_latency_counter/"_sr; /* FF - 2 bytes \xff\x02 @@ -37,8 +37,7 @@ NNNN - 4 Bytes Chunk number (Big Endian) TTTT - 4 Bytes Total number of chunks (Big Endian) XXXX - Variable length user provided transaction identifier */ -StringRef sampleTrInfoKey = - LiteralStringRef("\xff\x02/fdbClientInfo/client_latency/SSSSSSSSSS/RRRRRRRRRRRRRRRR/NNNNTTTT/XXXX/"); +StringRef sampleTrInfoKey = "\xff\x02/fdbClientInfo/client_latency/SSSSSSSSSS/RRRRRRRRRRRRRRRR/NNNNTTTT/XXXX/"_sr; static const auto chunkNumStartIndex = sampleTrInfoKey.toString().find('N'); static const auto numChunksStartIndex = sampleTrInfoKey.toString().find('T'); static const int chunkFormatSize = 4; @@ -203,10 +202,10 @@ struct ClientTransactionProfileCorrectnessWorkload : TestWorkload { ClientTransactionProfileCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { samplingProbability = getOption(options, - LiteralStringRef("samplingProbability"), + "samplingProbability"_sr, deterministicRandom()->random01() / 10); // rand range 0 - 0.1 trInfoSizeLimit = getOption(options, - LiteralStringRef("trInfoSizeLimit"), + "trInfoSizeLimit"_sr, deterministicRandom()->randomInt(100 * 1024, 10 * 1024 * 1024)); // 100 KB - 10 MB TraceEvent(SevInfo, "ClientTransactionProfilingSetup") .detail("ClientId", clientId) diff --git a/fdbserver/workloads/ClientWorkload.actor.cpp b/fdbserver/workloads/ClientWorkload.actor.cpp index 25ad2e8d3d..73dbb0ea75 100644 --- a/fdbserver/workloads/ClientWorkload.actor.cpp +++ b/fdbserver/workloads/ClientWorkload.actor.cpp @@ -37,11 +37,11 @@ class WorkloadProcessState { ~WorkloadProcessState() { TraceEvent("ShutdownClientForWorkload", id).log(); - g_simulator.destroyProcess(childProcess); + g_simulator->destroyProcess(childProcess); } ACTOR static Future initializationDone(WorkloadProcessState* self, ISimulator::ProcessInfo* parent) { - wait(g_simulator.onProcess(parent, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(parent, TaskPriority::DefaultYield)); self->init.send(Void()); wait(Never()); ASSERT(false); // does not happen @@ -49,7 +49,7 @@ class WorkloadProcessState { } ACTOR static Future processStart(WorkloadProcessState* self) { - state ISimulator::ProcessInfo* parent = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* parent = g_simulator->getCurrentProcess(); state std::vector> futures; if (parent->address.isV6()) { self->childAddress = @@ -65,22 +65,22 @@ class WorkloadProcessState { TraceEvent("StartingClientWorkloadProcess", self->id) .detail("Name", self->processName) .detail("Address", self->childAddress); - self->childProcess = g_simulator.newProcess(self->processName.c_str(), - self->childAddress, - 1, - parent->address.isTLS(), - 1, - locality, - ProcessClass(ProcessClass::TesterClass, ProcessClass::AutoSource), - dataFolder.c_str(), - parent->coordinationFolder.c_str(), - parent->protocolVersion); + self->childProcess = g_simulator->newProcess(self->processName.c_str(), + self->childAddress, + 1, + parent->address.isTLS(), + 1, + locality, + ProcessClass(ProcessClass::TesterClass, ProcessClass::AutoSource), + dataFolder.c_str(), + parent->coordinationFolder.c_str(), + parent->protocolVersion); self->childProcess->excludeFromRestarts = true; - wait(g_simulator.onProcess(self->childProcess, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(self->childProcess, TaskPriority::DefaultYield)); try { FlowTransport::createInstance(true, 1, WLTOKEN_RESERVED_COUNT); Sim2FileSystem::newFileSystem(); - auto addr = g_simulator.getCurrentProcess()->address; + auto addr = g_simulator->getCurrentProcess()->address; futures.push_back(FlowTransport::transport().bind(addr, addr)); futures.push_back(success((self->childProcess->onShutdown()))); TraceEvent("ClientWorkloadProcessInitialized", self->id).log(); @@ -143,18 +143,18 @@ struct WorkloadProcess { ACTOR static Future openDatabase(WorkloadProcess* self, ClientWorkload::CreateWorkload childCreator, WorkloadContext wcx) { - state ISimulator::ProcessInfo* parent = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* parent = g_simulator->getCurrentProcess(); state Optional err; wcx.dbInfo = Reference const>(); wait(self->processState->initialized()); - wait(g_simulator.onProcess(self->childProcess(), TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(self->childProcess(), TaskPriority::DefaultYield)); try { self->createDatabase(childCreator, wcx); } catch (Error& e) { ASSERT(e.code() != error_code_actor_cancelled); err = e; } - wait(g_simulator.onProcess(parent, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(parent, TaskPriority::DefaultYield)); if (err.present()) { throw err.get(); } @@ -177,11 +177,11 @@ struct WorkloadProcess { } ACTOR static void destroy(WorkloadProcess* self) { - state ISimulator::ProcessInfo* parent = g_simulator.getCurrentProcess(); - wait(g_simulator.onProcess(self->childProcess(), TaskPriority::DefaultYield)); + state ISimulator::ProcessInfo* parent = g_simulator->getCurrentProcess(); + wait(g_simulator->onProcess(self->childProcess(), TaskPriority::DefaultYield)); TraceEvent("DeleteWorkloadProcess").backtrace(); delete self; - wait(g_simulator.onProcess(parent, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(parent, TaskPriority::DefaultYield)); } std::string description() { return desc; } @@ -190,7 +190,7 @@ struct WorkloadProcess { // count of `f` is 1, this will cause the future to be destroyed in the process `process` ACTOR template static void cancelChild(ISimulator::ProcessInfo* process, Future f) { - wait(g_simulator.onProcess(process, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(process, TaskPriority::DefaultYield)); } ACTOR template @@ -198,9 +198,9 @@ struct WorkloadProcess { state Optional err; state Ret res; state Future fut; - state ISimulator::ProcessInfo* parent = g_simulator.getCurrentProcess(); + state ISimulator::ProcessInfo* parent = g_simulator->getCurrentProcess(); wait(self->databaseOpened); - wait(g_simulator.onProcess(self->childProcess(), TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(self->childProcess(), TaskPriority::DefaultYield)); self->cx->defaultTenant = defaultTenant; try { fut = f(self->cx); @@ -218,7 +218,7 @@ struct WorkloadProcess { err = e; } fut = Future(); - wait(g_simulator.onProcess(parent, TaskPriority::DefaultYield)); + wait(g_simulator->onProcess(parent, TaskPriority::DefaultYield)); if (err.present()) { throw err.get(); } diff --git a/fdbserver/workloads/ClogSingleConnection.actor.cpp b/fdbserver/workloads/ClogSingleConnection.actor.cpp index 0fe828ca6a..0852a562e0 100644 --- a/fdbserver/workloads/ClogSingleConnection.actor.cpp +++ b/fdbserver/workloads/ClogSingleConnection.actor.cpp @@ -60,10 +60,10 @@ public: void getMetrics(std::vector& m) override {} void clogRandomPair() { - auto m1 = deterministicRandom()->randomChoice(g_simulator.getAllProcesses()); - auto m2 = deterministicRandom()->randomChoice(g_simulator.getAllProcesses()); + auto m1 = deterministicRandom()->randomChoice(g_simulator->getAllProcesses()); + auto m2 = deterministicRandom()->randomChoice(g_simulator->getAllProcesses()); if (m1->address.ip != m2->address.ip) { - g_simulator.clogPair(m1->address.ip, m2->address.ip, clogDuration.orDefault(10000)); + g_simulator->clogPair(m1->address.ip, m2->address.ip, clogDuration.orDefault(10000)); } } }; diff --git a/fdbserver/workloads/CommitBugCheck.actor.cpp b/fdbserver/workloads/CommitBugCheck.actor.cpp index d5a5f22c11..89a6f3b67e 100644 --- a/fdbserver/workloads/CommitBugCheck.actor.cpp +++ b/fdbserver/workloads/CommitBugCheck.actor.cpp @@ -36,8 +36,8 @@ struct CommitBugWorkload : TestWorkload { ACTOR Future bug1(Database cx, CommitBugWorkload* self) { state Key key = StringRef(format("B1Key%d", self->clientId)); - state Value val1 = LiteralStringRef("Value1"); - state Value val2 = LiteralStringRef("Value2"); + state Value val1 = "Value1"_sr; + state Value val2 = "Value2"_sr; loop { state Transaction tr(cx); diff --git a/fdbserver/workloads/ConfigIncrement.actor.cpp b/fdbserver/workloads/ConfigIncrement.actor.cpp index e1961b598f..c759055a31 100644 --- a/fdbserver/workloads/ConfigIncrement.actor.cpp +++ b/fdbserver/workloads/ConfigIncrement.actor.cpp @@ -39,12 +39,14 @@ class ConfigIncrementWorkload : public TestWorkload { static Key getConfigKey() { return Tuple::makeTuple(/* config class */ nullptr, testKnobName).pack(); } ACTOR static Future get(Reference tr) { - TraceEvent(SevDebug, "ConfigIncrementGet"); + state TraceEvent te(SevDebug, "ConfigIncrementGet"); Optional serializedValue = wait(tr->get(getConfigKey())); if (!serializedValue.present()) { return 0; } else { - return BinaryReader::fromStringRef(serializedValue.get(), Unversioned()); + int value = BinaryReader::fromStringRef(serializedValue.get(), Unversioned()); + te.detail("Value", value); + return value; } } @@ -98,9 +100,9 @@ class ConfigIncrementWorkload : public TestWorkload { } ACTOR static Future check(ConfigIncrementWorkload* self, Database cx) { - state Reference tr = self->getTransaction(cx); loop { try { + state Reference tr = self->getTransaction(cx); state int currentValue = wait(get(tr)); auto expectedValue = self->incrementActors * self->incrementsPerActor; TraceEvent("ConfigIncrementCheck") @@ -115,9 +117,9 @@ class ConfigIncrementWorkload : public TestWorkload { Reference getTransaction(Database cx) const { ASSERT(g_network->isSimulated()); // TODO: Enforce elsewhere - ASSERT(g_simulator.configDBType != ConfigDBType::DISABLED); - auto type = (g_simulator.configDBType == ConfigDBType::SIMPLE) ? ISingleThreadTransaction::Type::SIMPLE_CONFIG - : ISingleThreadTransaction::Type::PAXOS_CONFIG; + ASSERT(g_simulator->configDBType != ConfigDBType::DISABLED); + auto type = (g_simulator->configDBType == ConfigDBType::SIMPLE) ? ISingleThreadTransaction::Type::SIMPLE_CONFIG + : ISingleThreadTransaction::Type::PAXOS_CONFIG; return ISingleThreadTransaction::create(type, cx); } diff --git a/fdbserver/workloads/ConfigureDatabase.actor.cpp b/fdbserver/workloads/ConfigureDatabase.actor.cpp index f7a538f591..79d2fcbdfd 100644 --- a/fdbserver/workloads/ConfigureDatabase.actor.cpp +++ b/fdbserver/workloads/ConfigureDatabase.actor.cpp @@ -52,9 +52,9 @@ static const char* backupTypes[] = { "backup_worker_enabled:=0", "backup_worker_ std::string generateRegions() { std::string result; - if (g_simulator.physicalDatacenters == 1 || - (g_simulator.physicalDatacenters == 2 && deterministicRandom()->random01() < 0.25) || - g_simulator.physicalDatacenters == 3) { + if (g_simulator->physicalDatacenters == 1 || + (g_simulator->physicalDatacenters == 2 && deterministicRandom()->random01() < 0.25) || + g_simulator->physicalDatacenters == 3) { return " usable_regions=1 regions=\"\""; } @@ -87,7 +87,7 @@ std::string generateRegions() { StatusArray remoteDcArr; remoteDcArr.push_back(remoteDcObj); - if (g_simulator.physicalDatacenters > 3 && deterministicRandom()->random01() < 0.5) { + if (g_simulator->physicalDatacenters > 3 && deterministicRandom()->random01() < 0.5) { StatusObject primarySatelliteObj; primarySatelliteObj["id"] = "2"; primarySatelliteObj["priority"] = 1; @@ -104,7 +104,7 @@ std::string generateRegions() { remoteSatelliteObj["satellite_logs"] = deterministicRandom()->randomInt(1, 7); remoteDcArr.push_back(remoteSatelliteObj); - if (g_simulator.physicalDatacenters > 5 && deterministicRandom()->random01() < 0.5) { + if (g_simulator->physicalDatacenters > 5 && deterministicRandom()->random01() < 0.5) { StatusObject primarySatelliteObjB; primarySatelliteObjB["id"] = "4"; primarySatelliteObjB["priority"] = 1; @@ -235,15 +235,15 @@ struct ConfigureDatabaseWorkload : TestWorkload { PerfIntCounter retries; ConfigureDatabaseWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), retries("Retries") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 200.0); + testDuration = getOption(options, "testDuration"_sr, 200.0); allowDescriptorChange = - getOption(options, LiteralStringRef("allowDescriptorChange"), SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT); + getOption(options, "allowDescriptorChange"_sr, SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT); allowTestStorageMigration = - getOption(options, "allowTestStorageMigration"_sr, false) && g_simulator.allowStorageMigrationTypeChange; + getOption(options, "allowTestStorageMigration"_sr, false) && g_simulator->allowStorageMigrationTypeChange; storageMigrationCompatibleConf = getOption(options, "storageMigrationCompatibleConf"_sr, false); waitStoreTypeCheck = getOption(options, "waitStoreTypeCheck"_sr, false); downgradeTest1 = getOption(options, "downgradeTest1"_sr, false); - g_simulator.usableRegions = 1; + g_simulator->usableRegions = 1; } std::string description() const override { return "DestroyDatabaseWorkload"; } @@ -347,7 +347,7 @@ struct ConfigureDatabaseWorkload : TestWorkload { ACTOR Future singleDB(ConfigureDatabaseWorkload* self, Database cx) { state Transaction tr; loop { - if (g_simulator.speedUpSimulation) { + if (g_simulator->speedUpSimulation) { return Void(); } state int randomChoice; @@ -363,7 +363,7 @@ struct ConfigureDatabaseWorkload : TestWorkload { if (randomChoice == 0) { wait(success( runRYWTransaction(cx, [=](Reference tr) -> Future> { - return tr->get(LiteralStringRef("This read is only to ensure that the database recovered")); + return tr->get("This read is only to ensure that the database recovered"_sr); }))); wait(delay(20 + 10 * deterministicRandom()->random01())); } else if (randomChoice < 3) { @@ -373,14 +373,14 @@ struct ConfigureDatabaseWorkload : TestWorkload { } else if (randomChoice == 3) { //TraceEvent("ConfigureTestConfigureBegin").detail("NewConfig", newConfig); int maxRedundancies = sizeof(redundancies) / sizeof(redundancies[0]); - if (g_simulator.physicalDatacenters == 2 || g_simulator.physicalDatacenters > 3) { + if (g_simulator->physicalDatacenters == 2 || g_simulator->physicalDatacenters > 3) { maxRedundancies--; // There are not enough machines for triple replication in fearless // configurations } int redundancy = deterministicRandom()->randomInt(0, maxRedundancies); std::string config = redundancies[redundancy]; - if (config == "triple" && g_simulator.physicalDatacenters == 3) { + if (config == "triple" && g_simulator->physicalDatacenters == 3) { config = "three_data_hall "; } diff --git a/fdbserver/workloads/ConflictRange.actor.cpp b/fdbserver/workloads/ConflictRange.actor.cpp index c95ad02320..daf45740fc 100644 --- a/fdbserver/workloads/ConflictRange.actor.cpp +++ b/fdbserver/workloads/ConflictRange.actor.cpp @@ -39,12 +39,12 @@ struct ConflictRangeWorkload : TestWorkload { ConflictRangeWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), withConflicts("WithConflicts"), withoutConflicts("withoutConflicts"), retries("Retries") { - minOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 2); - maxOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 4); - maxKeySpace = getOption(options, LiteralStringRef("maxKeySpace"), 100); - maxOffset = getOption(options, LiteralStringRef("maxOffset"), 5); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - testReadYourWrites = getOption(options, LiteralStringRef("testReadYourWrites"), false); + minOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 2); + maxOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 4); + maxKeySpace = getOption(options, "maxKeySpace"_sr, 100); + maxOffset = getOption(options, "maxOffset"_sr, 5); + testDuration = getOption(options, "testDuration"_sr, 10.0); + testReadYourWrites = getOption(options, "testReadYourWrites"_sr, false); } std::string description() const override { return "ConflictRange"; } diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index d6d2da101a..b159905480 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -98,16 +98,16 @@ struct ConsistencyCheckWorkload : TestWorkload { Future monitorConsistencyCheckSettingsActor; ConsistencyCheckWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - performQuiescentChecks = getOption(options, LiteralStringRef("performQuiescentChecks"), false); - performCacheCheck = getOption(options, LiteralStringRef("performCacheCheck"), false); - performTSSCheck = getOption(options, LiteralStringRef("performTSSCheck"), true); - quiescentWaitTimeout = getOption(options, LiteralStringRef("quiescentWaitTimeout"), 600.0); - distributed = getOption(options, LiteralStringRef("distributed"), true); - shardSampleFactor = std::max(getOption(options, LiteralStringRef("shardSampleFactor"), 1), 1); - failureIsError = getOption(options, LiteralStringRef("failureIsError"), false); - rateLimitMax = getOption(options, LiteralStringRef("rateLimitMax"), 0); - shuffleShards = getOption(options, LiteralStringRef("shuffleShards"), false); - indefinite = getOption(options, LiteralStringRef("indefinite"), false); + performQuiescentChecks = getOption(options, "performQuiescentChecks"_sr, false); + performCacheCheck = getOption(options, "performCacheCheck"_sr, false); + performTSSCheck = getOption(options, "performTSSCheck"_sr, true); + quiescentWaitTimeout = getOption(options, "quiescentWaitTimeout"_sr, 600.0); + distributed = getOption(options, "distributed"_sr, true); + shardSampleFactor = std::max(getOption(options, "shardSampleFactor"_sr, 1), 1); + failureIsError = getOption(options, "failureIsError"_sr, false); + rateLimitMax = getOption(options, "rateLimitMax"_sr, 0); + shuffleShards = getOption(options, "shuffleShards"_sr, false); + indefinite = getOption(options, "indefinite"_sr, false); suspendConsistencyCheck.set(true); success = true; @@ -321,19 +321,41 @@ struct ConsistencyCheckWorkload : TestWorkload { // Get a list of key servers; verify that the TLogs and master all agree about who the key servers are state Promise>>> keyServerPromise; - bool keyServerResult = wait(self->getKeyServers(cx, self, keyServerPromise, keyServersKeys)); + bool keyServerResult = + wait(getKeyServers(cx, keyServerPromise, keyServersKeys, self->performQuiescentChecks)); if (keyServerResult) { state std::vector>> keyServers = keyServerPromise.getFuture().get(); // Get the locations of all the shards in the database state Promise>> keyLocationPromise; - bool keyLocationResult = wait(self->getKeyLocations(cx, keyServers, self, keyLocationPromise)); + bool keyLocationResult = + wait(getKeyLocations(cx, keyServers, keyLocationPromise, self->performQuiescentChecks)); if (keyLocationResult) { state Standalone> keyLocations = keyLocationPromise.getFuture().get(); // Check that each shard has the same data on all storage servers that it resides on - wait(::success(self->checkDataConsistency(cx, keyLocations, configuration, tssMapping, self))); + wait(::success( + checkDataConsistency(cx, + keyLocations, + configuration, + tssMapping, + self->performQuiescentChecks, + self->performTSSCheck, + self->firstClient, + self->failureIsError, + self->clientId, + self->clientCount, + self->distributed, + self->shuffleShards, + self->shardSampleFactor, + self->sharedRandomNumber, + self->repetitions, + &(self->bytesReadInPreviousRound), + true, + self->rateLimitMax, + CLIENT_KNOBS->CONSISTENCY_CHECK_ONE_ROUND_TARGET_COMPLETION_TIME, + KeyRef()))); // Cache consistency check if (self->performCacheCheck) @@ -343,11 +365,12 @@ struct ConsistencyCheckWorkload : TestWorkload { } catch (Error& e) { if (e.code() == error_code_transaction_too_old || e.code() == error_code_future_version || e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed || - e.code() == error_code_process_behind) + e.code() == error_code_process_behind || e.code() == error_code_actor_cancelled) { TraceEvent("ConsistencyCheck_Retry") .error(e); // FIXME: consistency check does not retry in this case - else + } else { self->testFailure(format("Error %d - %s", e.code(), e.name())); + } } } @@ -526,7 +549,7 @@ struct ConsistencyCheckWorkload : TestWorkload { lastSampleKey = lastStartSampleKey; // Get the min version of the storage servers - Version version = wait(self->getVersion(cx, self)); + Version version = wait(getVersion(cx)); state GetKeyValuesRequest req; req.begin = begin; @@ -744,7 +767,7 @@ struct ConsistencyCheckWorkload : TestWorkload { bool removePrefix) { // get shards paired with corresponding storage servers state Promise>>> keyServerPromise; - bool keyServerResult = wait(self->getKeyServers(cx, self, keyServerPromise, range)); + bool keyServerResult = wait(getKeyServers(cx, keyServerPromise, range, self->performQuiescentChecks)); if (!keyServerResult) return false; state std::vector>> shards = @@ -762,7 +785,7 @@ struct ConsistencyCheckWorkload : TestWorkload { for (i = 0; i < shards.size(); i++) { while (beginKey < std::min(shards[i].first.end, endKey)) { try { - Version version = wait(self->getVersion(cx, self)); + Version version = wait(getVersion(cx)); GetKeyValuesRequest req; req.begin = firstGreaterOrEqual(beginKey); @@ -846,879 +869,12 @@ struct ConsistencyCheckWorkload : TestWorkload { return true; } - // Gets a version at which to read from the storage servers - ACTOR Future getVersion(Database cx, ConsistencyCheckWorkload* self) { - loop { - state Transaction tr(cx); - tr.setOption(FDBTransactionOptions::LOCK_AWARE); - try { - Version version = wait(tr.getReadVersion()); - return version; - } catch (Error& e) { - wait(tr.onError(e)); - } - } - } - - // Get a list of storage servers(persisting keys within range "kr") from the master and compares them with the - // TLogs. If this is a quiescent check, then each commit proxy needs to respond, otherwise only one needs to - // respond. Returns false if there is a failure (in this case, keyServersPromise will never be set) - ACTOR Future getKeyServers( - Database cx, - ConsistencyCheckWorkload* self, - Promise>>> keyServersPromise, - KeyRangeRef kr) { - state std::vector>> keyServers; - - // Try getting key server locations from the master proxies - state std::vector>> keyServerLocationFutures; - state Key begin = kr.begin; - state Key end = kr.end; - state int limitKeyServers = BUGGIFY ? 1 : 100; - state Span span(SpanContext(deterministicRandom()->randomUniqueID(), deterministicRandom()->randomUInt64()), - "WL:ConsistencyCheck"_loc); - - while (begin < end) { - state Reference commitProxyInfo = - wait(cx->getCommitProxiesFuture(UseProvisionalProxies::False)); - keyServerLocationFutures.clear(); - for (int i = 0; i < commitProxyInfo->size(); i++) - keyServerLocationFutures.push_back( - commitProxyInfo->get(i, &CommitProxyInterface::getKeyServersLocations) - .getReplyUnlessFailedFor( - GetKeyServerLocationsRequest( - span.context, TenantInfo(), begin, end, limitKeyServers, false, latestVersion, Arena()), - 2, - 0)); - - state bool keyServersInsertedForThisIteration = false; - choose { - when(wait(waitForAll(keyServerLocationFutures))) { - // Read the key server location results - for (int i = 0; i < keyServerLocationFutures.size(); i++) { - ErrorOr shards = keyServerLocationFutures[i].get(); - - // If performing quiescent check, then all master proxies should be reachable. Otherwise, only - // one needs to be reachable - if (self->performQuiescentChecks && !shards.present()) { - TraceEvent("ConsistencyCheck_CommitProxyUnavailable") - .detail("CommitProxyID", commitProxyInfo->getId(i)); - self->testFailure("Commit proxy unavailable"); - return false; - } - - // Get the list of shards if one was returned. If not doing a quiescent check, we can break if - // it is. If we are doing a quiescent check, then we only need to do this for the first shard. - if (shards.present() && !keyServersInsertedForThisIteration) { - keyServers.insert( - keyServers.end(), shards.get().results.begin(), shards.get().results.end()); - keyServersInsertedForThisIteration = true; - begin = shards.get().results.back().first.end; - - if (!self->performQuiescentChecks) - break; - } - } // End of For - } - when(wait(cx->onProxiesChanged())) {} - } // End of choose - - if (!keyServersInsertedForThisIteration) // Retry the entire workflow - wait(delay(1.0)); - - } // End of while - - keyServersPromise.send(keyServers); - return true; - } - - // Retrieves the locations of all shards in the database - // Returns false if there is a failure (in this case, keyLocationPromise will never be set) - ACTOR Future getKeyLocations(Database cx, - std::vector>> shards, - ConsistencyCheckWorkload* self, - Promise>> keyLocationPromise) { - state Standalone> keyLocations; - state Key beginKey = allKeys.begin.withPrefix(keyServersPrefix); - state Key endKey = allKeys.end.withPrefix(keyServersPrefix); - state int i = 0; - state Transaction onErrorTr(cx); // This transaction exists only to access onError and its backoff behavior - - // If the responses are too big, we may use multiple requests to get the key locations. Each request begins - // where the last left off - for (; i < shards.size(); i++) { - while (beginKey < std::min(shards[i].first.end, endKey)) { - try { - Version version = wait(self->getVersion(cx, self)); - - GetKeyValuesRequest req; - req.begin = firstGreaterOrEqual(beginKey); - req.end = firstGreaterOrEqual(std::min(shards[i].first.end, endKey)); - req.limit = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT; - req.limitBytes = SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES; - req.version = version; - req.tags = TagSet(); - - // Try getting the shard locations from the key servers - state std::vector>> keyValueFutures; - for (const auto& kv : shards[i].second) { - resetReply(req); - keyValueFutures.push_back(kv.getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); - } - - wait(waitForAll(keyValueFutures)); - - int firstValidStorageServer = -1; - - // Read the shard location results - for (int j = 0; j < keyValueFutures.size(); j++) { - ErrorOr reply = keyValueFutures[j].get(); - - if (!reply.present() || reply.get().error.present()) { - // If the storage server didn't reply in a quiescent database, then the check fails - if (self->performQuiescentChecks) { - TraceEvent("ConsistencyCheck_KeyServerUnavailable") - .detail("StorageServer", shards[i].second[j].id().toString().c_str()); - self->testFailure("Key server unavailable"); - return false; - } - - // If no storage servers replied, then throw all_alternatives_failed to force a retry - else if (firstValidStorageServer < 0 && j == keyValueFutures.size() - 1) - throw all_alternatives_failed(); - } - - // If this is the first storage server, store the locations to send back to the caller - else if (firstValidStorageServer < 0) { - firstValidStorageServer = j; - - // Otherwise, compare the data to the results from the first storage server. If they are - // different, then the check fails - } else if (reply.get().data != keyValueFutures[firstValidStorageServer].get().get().data || - reply.get().more != keyValueFutures[firstValidStorageServer].get().get().more) { - TraceEvent("ConsistencyCheck_InconsistentKeyServers") - .detail("StorageServer1", shards[i].second[firstValidStorageServer].id()) - .detail("StorageServer2", shards[i].second[j].id()); - self->testFailure("Key servers inconsistent", true); - return false; - } - } - - auto keyValueResponse = keyValueFutures[firstValidStorageServer].get().get(); - RangeResult currentLocations = krmDecodeRanges( - keyServersPrefix, - KeyRangeRef(beginKey.removePrefix(keyServersPrefix), - std::min(shards[i].first.end, endKey).removePrefix(keyServersPrefix)), - RangeResultRef(keyValueResponse.data, keyValueResponse.more)); - - if (keyValueResponse.data.size() && beginKey == keyValueResponse.data[0].key) { - keyLocations.push_back_deep(keyLocations.arena(), currentLocations[0]); - } - - if (currentLocations.size() > 2) { - keyLocations.append_deep( - keyLocations.arena(), ¤tLocations[1], currentLocations.size() - 2); - } - - // Next iteration should pick up where we left off - ASSERT(currentLocations.size() > 1); - if (!keyValueResponse.more) { - beginKey = shards[i].first.end; - } else { - beginKey = keyValueResponse.data.end()[-1].key; - } - - // If this is the last iteration, then push the allKeys.end KV pair - if (beginKey >= endKey) - keyLocations.push_back_deep(keyLocations.arena(), currentLocations.end()[-1]); - } catch (Error& e) { - state Error err = e; - wait(onErrorTr.onError(err)); - TraceEvent("ConsistencyCheck_RetryGetKeyLocations").error(err); - } - } - } - - keyLocationPromise.send(keyLocations); - return true; - } - - // Retrieves a vector of the storage servers' estimates for the size of a particular shard - // If a storage server can't be reached, its estimate will be -1 - // If there is an error, then the returned vector will have 0 size - ACTOR Future> getStorageSizeEstimate(std::vector storageServers, - KeyRangeRef shard) { - state std::vector estimatedBytes; - - state WaitMetricsRequest req; - req.keys = shard; - req.max.bytes = -1; - req.min.bytes = 0; - - state std::vector>> metricFutures; - - try { - // Check the size of the shard on each storage server - for (int i = 0; i < storageServers.size(); i++) { - resetReply(req); - metricFutures.push_back(storageServers[i].waitMetrics.getReplyUnlessFailedFor(req, 2, 0)); - } - - // Wait for the storage servers to respond - wait(waitForAll(metricFutures)); - - int firstValidStorageServer = -1; - - // Retrieve the size from the storage server responses - for (int i = 0; i < storageServers.size(); i++) { - ErrorOr reply = metricFutures[i].get(); - - // If the storage server doesn't reply, then return -1 - if (!reply.present()) { - TraceEvent("ConsistencyCheck_FailedToFetchMetrics") - .error(reply.getError()) - .detail("Begin", printable(shard.begin)) - .detail("End", printable(shard.end)) - .detail("StorageServer", storageServers[i].id()) - .detail("IsTSS", storageServers[i].isTss() ? "True" : "False"); - estimatedBytes.push_back(-1); - } - - // Add the result to the list of estimates - else if (reply.present()) { - int64_t numBytes = reply.get().bytes; - estimatedBytes.push_back(numBytes); - if (firstValidStorageServer < 0) - firstValidStorageServer = i; - else if (estimatedBytes[firstValidStorageServer] != numBytes) { - TraceEvent("ConsistencyCheck_InconsistentStorageMetrics") - .detail("ByteEstimate1", estimatedBytes[firstValidStorageServer]) - .detail("ByteEstimate2", numBytes) - .detail("Begin", shard.begin) - .detail("End", shard.end) - .detail("StorageServer1", storageServers[firstValidStorageServer].id()) - .detail("StorageServer2", storageServers[i].id()) - .detail("IsTSS", - storageServers[i].isTss() || storageServers[firstValidStorageServer].isTss() - ? "True" - : "False"); - } - } - } - } catch (Error& e) { - TraceEvent("ConsistencyCheck_ErrorFetchingMetrics") - .error(e) - .detail("Begin", printable(shard.begin)) - .detail("End", printable(shard.end)); - estimatedBytes.clear(); - } - - return estimatedBytes; - } - // Comparison function used to compare map elements by value template static bool compareByValue(std::pair a, std::pair b) { return a.second < b.second; } - ACTOR Future getDatabaseSize(Database cx) { - state Transaction tr(cx); - tr.setOption(FDBTransactionOptions::LOCK_AWARE); - loop { - try { - StorageMetrics metrics = - wait(tr.getDatabase()->getStorageMetrics(KeyRangeRef(allKeys.begin, keyServersPrefix), 100000)); - return metrics.bytes; - } catch (Error& e) { - wait(tr.onError(e)); - } - } - } - - // Checks that the data in each shard is the same on each storage server that it resides on. Also performs some - // sanity checks on the sizes of shards and storage servers. Returns false if there is a failure - ACTOR Future checkDataConsistency(Database cx, - VectorRef keyLocations, - DatabaseConfiguration configuration, - std::map tssMapping, - ConsistencyCheckWorkload* self) { - // Stores the total number of bytes on each storage server - // In a distributed test, this will be an estimated size - state std::map storageServerSizes; - - // Iterate through each shard, checking its values on all of its storage servers - // If shardSampleFactor > 1, then not all shards are processed - // Also, in a distributed data consistency check, each client processes a subset of the shards - // Note: this may cause some shards to be processed more than once or not at all in a non-quiescent database - state int effectiveClientCount = (self->distributed) ? self->clientCount : 1; - state int i = self->clientId * (self->shardSampleFactor + 1); - state int increment = - (self->distributed && !self->firstClient) ? effectiveClientCount * self->shardSampleFactor : 1; - state int rateLimitForThisRound = - self->bytesReadInPreviousRound == 0 - ? self->rateLimitMax - : std::min( - self->rateLimitMax, - static_cast(ceil(self->bytesReadInPreviousRound / - (float)CLIENT_KNOBS->CONSISTENCY_CHECK_ONE_ROUND_TARGET_COMPLETION_TIME))); - ASSERT(rateLimitForThisRound >= 0 && rateLimitForThisRound <= self->rateLimitMax); - TraceEvent("ConsistencyCheck_RateLimitForThisRound").detail("RateLimit", rateLimitForThisRound); - state Reference rateLimiter = Reference(new SpeedLimit(rateLimitForThisRound, 1)); - state double rateLimiterStartTime = now(); - state int64_t bytesReadInthisRound = 0; - - state double dbSize = 100e12; - if (g_network->isSimulated()) { - // This call will get all shard ranges in the database, which is too expensive on real clusters. - int64_t _dbSize = wait(self->getDatabaseSize(cx)); - dbSize = _dbSize; - } - - state std::vector ranges; - - for (int k = 0; k < keyLocations.size() - 1; k++) { - KeyRangeRef range(keyLocations[k].key, keyLocations[k + 1].key); - ranges.push_back(range); - } - - state std::vector shardOrder; - shardOrder.reserve(ranges.size()); - for (int k = 0; k < ranges.size(); k++) - shardOrder.push_back(k); - if (self->shuffleShards) { - uint32_t seed = self->sharedRandomNumber + self->repetitions; - DeterministicRandom sharedRandom(seed == 0 ? 1 : seed); - sharedRandom.randomShuffle(shardOrder); - } - - for (; i < ranges.size(); i += increment) { - state int shard = shardOrder[i]; - - state KeyRangeRef range = ranges[shard]; - state std::vector sourceStorageServers; - state std::vector destStorageServers; - state Transaction tr(cx); - tr.setOption(FDBTransactionOptions::LOCK_AWARE); - state int bytesReadInRange = 0; - - RangeResult UIDtoTagMap = wait(tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY)); - ASSERT(!UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY); - decodeKeyServersValue( - UIDtoTagMap, keyLocations[shard].value, sourceStorageServers, destStorageServers, false); - - // If the destStorageServers is non-empty, then this shard is being relocated - state bool isRelocating = destStorageServers.size() > 0; - - // This check was disabled because we now disable data distribution during the consistency check, - // which can leave shards with dest storage servers. - - // Disallow relocations in a quiescent database - /*if(self->firstClient && self->performQuiescentChecks && isRelocating) - { - TraceEvent("ConsistencyCheck_QuiescentShardRelocation").detail("ShardBegin", printable(range.start)).detail("ShardEnd", printable(range.end)); - self->testFailure("Shard is being relocated in quiescent database"); - return false; - }*/ - - // In a quiescent database, check that the team size is the same as the desired team size - if (self->firstClient && self->performQuiescentChecks && - sourceStorageServers.size() != configuration.usableRegions * configuration.storageTeamSize) { - TraceEvent("ConsistencyCheck_InvalidTeamSize") - .detail("ShardBegin", printable(range.begin)) - .detail("ShardEnd", printable(range.end)) - .detail("SourceTeamSize", sourceStorageServers.size()) - .detail("DestServerSize", destStorageServers.size()) - .detail("ConfigStorageTeamSize", configuration.storageTeamSize) - .detail("UsableRegions", configuration.usableRegions); - // Record the server reponsible for the problematic shards - int i = 0; - for (auto& id : sourceStorageServers) { - TraceEvent("IncorrectSizeTeamInfo").detail("ServerUID", id).detail("TeamIndex", i++); - } - self->testFailure("Invalid team size"); - return false; - } - - state std::vector storageServers = (isRelocating) ? destStorageServers : sourceStorageServers; - state std::vector storageServerInterfaces; - - //TraceEvent("ConsistencyCheck_GetStorageInfo").detail("StorageServers", storageServers.size()); - loop { - try { - std::vector>> serverListEntries; - serverListEntries.reserve(storageServers.size()); - for (int s = 0; s < storageServers.size(); s++) - serverListEntries.push_back(tr.get(serverListKeyFor(storageServers[s]))); - state std::vector> serverListValues = wait(getAll(serverListEntries)); - for (int s = 0; s < serverListValues.size(); s++) { - if (serverListValues[s].present()) - storageServerInterfaces.push_back(decodeServerListValue(serverListValues[s].get())); - else if (self->performQuiescentChecks) - self->testFailure("/FF/serverList changing in a quiescent database"); - } - - break; - } catch (Error& e) { - wait(tr.onError(e)); - } - } - - // add TSS to end of list, if configured and if not relocating - if (!isRelocating && self->performTSSCheck) { - int initialSize = storageServers.size(); - for (int i = 0; i < initialSize; i++) { - auto tssPair = tssMapping.find(storageServers[i]); - if (tssPair != tssMapping.end()) { - CODE_PROBE(true, "TSS checked in consistency check"); - storageServers.push_back(tssPair->second.id()); - storageServerInterfaces.push_back(tssPair->second); - } - } - } - - state std::vector estimatedBytes = - wait(self->getStorageSizeEstimate(storageServerInterfaces, range)); - - // Gets permitted size range of shard - int64_t maxShardSize = getMaxShardSize(dbSize); - state ShardSizeBounds shardBounds = getShardSizeBounds(range, maxShardSize); - - if (self->firstClient) { - // If there was an error retrieving shard estimated size - if (self->performQuiescentChecks && estimatedBytes.size() == 0) - self->testFailure("Error fetching storage metrics"); - - // If running a distributed test, storage server size is an accumulation of shard estimates - else if (self->distributed && self->firstClient) - for (int j = 0; j < storageServers.size(); j++) - storageServerSizes[storageServers[j]] += std::max(estimatedBytes[j], (int64_t)0); - } - - // The first client may need to skip the rest of the loop contents if it is just processing this shard to - // get a size estimate - if (!self->firstClient || shard % (effectiveClientCount * self->shardSampleFactor) == 0) { - state int shardKeys = 0; - state int shardBytes = 0; - state int sampledBytes = 0; - state int splitBytes = 0; - state int firstKeySampledBytes = 0; - state int sampledKeys = 0; - state int sampledKeysWithProb = 0; - state double shardVariance = 0; - state bool canSplit = false; - state Key lastSampleKey; - state Key lastStartSampleKey; - state int64_t totalReadAmount = 0; - - state KeySelector begin = firstGreaterOrEqual(range.begin); - state Transaction onErrorTr( - cx); // This transaction exists only to access onError and its backoff behavior - - // Read a limited number of entries at a time, repeating until all keys in the shard have been read - loop { - try { - lastSampleKey = lastStartSampleKey; - - // Get the min version of the storage servers - Version version = wait(self->getVersion(cx, self)); - - state GetKeyValuesRequest req; - req.begin = begin; - req.end = firstGreaterOrEqual(range.end); - req.limit = 1e4; - req.limitBytes = CLIENT_KNOBS->REPLY_BYTE_LIMIT; - req.version = version; - req.tags = TagSet(); - - // Try getting the entries in the specified range - state std::vector>> keyValueFutures; - state int j = 0; - for (j = 0; j < storageServerInterfaces.size(); j++) { - resetReply(req); - keyValueFutures.push_back( - storageServerInterfaces[j].getKeyValues.getReplyUnlessFailedFor(req, 2, 0)); - } - - wait(waitForAll(keyValueFutures)); - - // Read the resulting entries - state int firstValidServer = -1; - totalReadAmount = 0; - for (j = 0; j < keyValueFutures.size(); j++) { - ErrorOr rangeResult = keyValueFutures[j].get(); - - // Compare the results with other storage servers - if (rangeResult.present() && !rangeResult.get().error.present()) { - state GetKeyValuesReply current = rangeResult.get(); - totalReadAmount += current.data.expectedSize(); - // If we haven't encountered a valid storage server yet, then mark this as the baseline - // to compare against - if (firstValidServer == -1) - firstValidServer = j; - - // Compare this shard against the first - else { - GetKeyValuesReply reference = keyValueFutures[firstValidServer].get().get(); - - if (current.data != reference.data || current.more != reference.more) { - // Be especially verbose if in simulation - if (g_network->isSimulated()) { - int invalidIndex = -1; - printf("\n%sSERVER %d (%s); shard = %s - %s:\n", - storageServerInterfaces[j].isTss() ? "TSS " : "", - j, - storageServerInterfaces[j].address().toString().c_str(), - printable(req.begin.getKey()).c_str(), - printable(req.end.getKey()).c_str()); - for (int k = 0; k < current.data.size(); k++) { - printf("%d. %s => %s\n", - k, - printable(current.data[k].key).c_str(), - printable(current.data[k].value).c_str()); - if (invalidIndex < 0 && - (k >= reference.data.size() || - current.data[k].key != reference.data[k].key || - current.data[k].value != reference.data[k].value)) - invalidIndex = k; - } - - printf( - "\n%sSERVER %d (%s); shard = %s - %s:\n", - storageServerInterfaces[firstValidServer].isTss() ? "TSS " : "", - firstValidServer, - storageServerInterfaces[firstValidServer].address().toString().c_str(), - printable(req.begin.getKey()).c_str(), - printable(req.end.getKey()).c_str()); - for (int k = 0; k < reference.data.size(); k++) { - printf("%d. %s => %s\n", - k, - printable(reference.data[k].key).c_str(), - printable(reference.data[k].value).c_str()); - if (invalidIndex < 0 && - (k >= current.data.size() || - reference.data[k].key != current.data[k].key || - reference.data[k].value != current.data[k].value)) - invalidIndex = k; - } - - printf("\nMISMATCH AT %d\n\n", invalidIndex); - } - - // Data for trace event - // The number of keys unique to the current shard - int currentUniques = 0; - // The number of keys unique to the reference shard - int referenceUniques = 0; - // The number of keys in both shards with conflicting values - int valueMismatches = 0; - // The number of keys in both shards with matching values - int matchingKVPairs = 0; - // Last unique key on the current shard - KeyRef currentUniqueKey; - // Last unique key on the reference shard - KeyRef referenceUniqueKey; - // Last value mismatch - KeyRef valueMismatchKey; - - // Loop indeces - int currentI = 0; - int referenceI = 0; - while (currentI < current.data.size() || referenceI < reference.data.size()) { - if (currentI >= current.data.size()) { - referenceUniqueKey = reference.data[referenceI].key; - referenceUniques++; - referenceI++; - } else if (referenceI >= reference.data.size()) { - currentUniqueKey = current.data[currentI].key; - currentUniques++; - currentI++; - } else { - KeyValueRef currentKV = current.data[currentI]; - KeyValueRef referenceKV = reference.data[referenceI]; - - if (currentKV.key == referenceKV.key) { - if (currentKV.value == referenceKV.value) - matchingKVPairs++; - else { - valueMismatchKey = currentKV.key; - valueMismatches++; - } - - currentI++; - referenceI++; - } else if (currentKV.key < referenceKV.key) { - currentUniqueKey = currentKV.key; - currentUniques++; - currentI++; - } else { - referenceUniqueKey = referenceKV.key; - referenceUniques++; - referenceI++; - } - } - } - - TraceEvent("ConsistencyCheck_DataInconsistent") - .detail(format("StorageServer%d", j).c_str(), storageServers[j].toString()) - .detail(format("StorageServer%d", firstValidServer).c_str(), - storageServers[firstValidServer].toString()) - .detail("ShardBegin", req.begin.getKey()) - .detail("ShardEnd", req.end.getKey()) - .detail("VersionNumber", req.version) - .detail(format("Server%dUniques", j).c_str(), currentUniques) - .detail(format("Server%dUniqueKey", j).c_str(), currentUniqueKey) - .detail(format("Server%dUniques", firstValidServer).c_str(), - referenceUniques) - .detail(format("Server%dUniqueKey", firstValidServer).c_str(), - referenceUniqueKey) - .detail("ValueMismatches", valueMismatches) - .detail("ValueMismatchKey", valueMismatchKey) - .detail("MatchingKVPairs", matchingKVPairs) - .detail("IsTSS", - storageServerInterfaces[j].isTss() || - storageServerInterfaces[firstValidServer].isTss() - ? "True" - : "False"); - - if ((g_network->isSimulated() && - g_simulator.tssMode != ISimulator::TSSMode::EnabledDropMutations) || - (!storageServerInterfaces[j].isTss() && - !storageServerInterfaces[firstValidServer].isTss())) { - self->testFailure("Data inconsistent", true); - return false; - } - } - } - } - - // If the data is not available and we aren't relocating this shard - else if (!isRelocating) { - Error e = - rangeResult.isError() ? rangeResult.getError() : rangeResult.get().error.get(); - - TraceEvent("ConsistencyCheck_StorageServerUnavailable") - .errorUnsuppressed(e) - .suppressFor(1.0) - .detail("StorageServer", storageServers[j]) - .detail("ShardBegin", printable(range.begin)) - .detail("ShardEnd", printable(range.end)) - .detail("Address", storageServerInterfaces[j].address()) - .detail("UID", storageServerInterfaces[j].id()) - .detail("GetKeyValuesToken", - storageServerInterfaces[j].getKeyValues.getEndpoint().token) - .detail("IsTSS", storageServerInterfaces[j].isTss() ? "True" : "False"); - - // All shards should be available in quiscence - if (self->performQuiescentChecks && !storageServerInterfaces[j].isTss()) { - self->testFailure("Storage server unavailable"); - return false; - } - } - } - - if (firstValidServer >= 0) { - VectorRef data = keyValueFutures[firstValidServer].get().get().data; - // Calculate the size of the shard, the variance of the shard size estimate, and the correct - // shard size estimate - for (int k = 0; k < data.size(); k++) { - ByteSampleInfo sampleInfo = isKeyValueInSample(data[k]); - shardBytes += sampleInfo.size; - double itemProbability = ((double)sampleInfo.size) / sampleInfo.sampledSize; - if (itemProbability < 1) - shardVariance += itemProbability * (1 - itemProbability) * - pow((double)sampleInfo.sampledSize, 2); - - if (sampleInfo.inSample) { - sampledBytes += sampleInfo.sampledSize; - if (!canSplit && sampledBytes >= shardBounds.min.bytes && - data[k].key.size() <= CLIENT_KNOBS->SPLIT_KEY_SIZE_LIMIT && - sampledBytes <= shardBounds.max.bytes * - CLIENT_KNOBS->STORAGE_METRICS_UNFAIR_SPLIT_LIMIT / 2) { - canSplit = true; - splitBytes = sampledBytes; - } - - /*TraceEvent("ConsistencyCheck_ByteSample").detail("ShardBegin", printable(range.begin)).detail("ShardEnd", printable(range.end)) - .detail("SampledBytes", sampleInfo.sampledSize).detail("Key", - printable(data[k].key)).detail("KeySize", data[k].key.size()).detail("ValueSize", - data[k].value.size());*/ - - // In data distribution, the splitting process ignores the first key in a shard. - // Thus, we shouldn't consider it when validating the upper bound of estimated shard - // sizes - if (k == 0) - firstKeySampledBytes += sampleInfo.sampledSize; - - sampledKeys++; - if (itemProbability < 1) { - sampledKeysWithProb++; - } - } - } - - // Accumulate number of keys in this shard - shardKeys += data.size(); - } - // after requesting each shard, enforce rate limit based on how much data will likely be read - if (rateLimitForThisRound > 0) { - wait(rateLimiter->getAllowance(totalReadAmount)); - // Set ratelimit to max allowed if current round has been going on for a while - if (now() - rateLimiterStartTime > - 1.1 * CLIENT_KNOBS->CONSISTENCY_CHECK_ONE_ROUND_TARGET_COMPLETION_TIME && - rateLimitForThisRound != self->rateLimitMax) { - rateLimitForThisRound = self->rateLimitMax; - rateLimiter = Reference(new SpeedLimit(rateLimitForThisRound, 1)); - rateLimiterStartTime = now(); - TraceEvent(SevInfo, "ConsistencyCheck_RateLimitSetMaxForThisRound") - .detail("RateLimit", rateLimitForThisRound); - } - } - bytesReadInRange += totalReadAmount; - bytesReadInthisRound += totalReadAmount; - - // Advance to the next set of entries - if (firstValidServer >= 0 && keyValueFutures[firstValidServer].get().get().more) { - VectorRef result = keyValueFutures[firstValidServer].get().get().data; - ASSERT(result.size() > 0); - begin = firstGreaterThan(result[result.size() - 1].key); - ASSERT(begin.getKey() != allKeys.end); - lastStartSampleKey = lastSampleKey; - } else - break; - } catch (Error& e) { - state Error err = e; - wait(onErrorTr.onError(err)); - TraceEvent("ConsistencyCheck_RetryDataConsistency").error(err); - } - } - - canSplit = canSplit && sampledBytes - splitBytes >= shardBounds.min.bytes && sampledBytes > splitBytes; - - // Update the size of all storage servers containing this shard - // This is only done in a non-distributed consistency check; the distributed check uses shard size - // estimates - if (!self->distributed) - for (int j = 0; j < storageServers.size(); j++) - storageServerSizes[storageServers[j]] += shardBytes; - - // FIXME: Where is this intended to be used? - [[maybe_unused]] bool hasValidEstimate = estimatedBytes.size() > 0; - - // If the storage servers' sampled estimate of shard size is different from ours - if (self->performQuiescentChecks) { - for (int j = 0; j < estimatedBytes.size(); j++) { - if (estimatedBytes[j] >= 0 && estimatedBytes[j] != sampledBytes) { - TraceEvent("ConsistencyCheck_IncorrectEstimate") - .detail("EstimatedBytes", estimatedBytes[j]) - .detail("CorrectSampledBytes", sampledBytes) - .detail("StorageServer", storageServers[j]) - .detail("IsTSS", storageServerInterfaces[j].isTss() ? "True" : "False"); - - if (!storageServerInterfaces[j].isTss()) { - self->testFailure("Storage servers had incorrect sampled estimate"); - } - - hasValidEstimate = false; - - break; - } else if (estimatedBytes[j] < 0 && - ((g_network->isSimulated() && - g_simulator.tssMode <= ISimulator::TSSMode::EnabledNormal) || - !storageServerInterfaces[j].isTss())) { - // Ignore a non-responding TSS outside of simulation, or if tss fault injection is enabled - hasValidEstimate = false; - break; - } - } - } - - // Compute the difference between the shard size estimate and its actual size. If it is sufficiently - // large, then fail - double stdDev = sqrt(shardVariance); - - double failErrorNumStdDev = 7; - int estimateError = abs(shardBytes - sampledBytes); - - // Only perform the check if there are sufficient keys to get a distribution that should resemble a - // normal distribution - if (sampledKeysWithProb > 30 && estimateError > failErrorNumStdDev * stdDev) { - double numStdDev = estimateError / sqrt(shardVariance); - TraceEvent("ConsistencyCheck_InaccurateShardEstimate") - .detail("Min", shardBounds.min.bytes) - .detail("Max", shardBounds.max.bytes) - .detail("Estimate", sampledBytes) - .detail("Actual", shardBytes) - .detail("NumStdDev", numStdDev) - .detail("Variance", shardVariance) - .detail("StdDev", stdDev) - .detail("ShardBegin", printable(range.begin)) - .detail("ShardEnd", printable(range.end)) - .detail("NumKeys", shardKeys) - .detail("NumSampledKeys", sampledKeys) - .detail("NumSampledKeysWithProb", sampledKeysWithProb); - - self->testFailure(format("Shard size is more than %f std dev from estimate", failErrorNumStdDev)); - } - - // In a quiescent database, check that the (estimated) size of the shard is within permitted bounds - // Min and max shard sizes have a 3 * shardBounds.permittedError.bytes cushion for error since shard - // sizes are not precise Shard splits ignore the first key in a shard, so its size shouldn't be - // considered when checking the upper bound 0xff shards are not checked - if (canSplit && sampledKeys > 5 && self->performQuiescentChecks && - !range.begin.startsWith(keyServersPrefix) && - (sampledBytes < shardBounds.min.bytes - 3 * shardBounds.permittedError.bytes || - sampledBytes - firstKeySampledBytes > - shardBounds.max.bytes + 3 * shardBounds.permittedError.bytes)) { - TraceEvent("ConsistencyCheck_InvalidShardSize") - .detail("Min", shardBounds.min.bytes) - .detail("Max", shardBounds.max.bytes) - .detail("Size", shardBytes) - .detail("EstimatedSize", sampledBytes) - .detail("ShardBegin", printable(range.begin)) - .detail("ShardEnd", printable(range.end)) - .detail("ShardCount", ranges.size()) - .detail("SampledKeys", sampledKeys); - self->testFailure(format("Shard size in quiescent database is too %s", - (sampledBytes < shardBounds.min.bytes) ? "small" : "large")); - return false; - } - } - - if (bytesReadInRange > 0) { - TraceEvent("ConsistencyCheck_ReadRange") - .suppressFor(1.0) - .detail("Range", range) - .detail("BytesRead", bytesReadInRange); - } - } - - // SOMEDAY: when background data distribution is implemented, include this test - // In a quiescent database, check that the sizes of storage servers are roughly the same - /*if(self->performQuiescentChecks) - { - auto minStorageServer = std::min_element(storageServerSizes.begin(), storageServerSizes.end(), - ConsistencyCheckWorkload::compareByValue); auto maxStorageServer = - std::max_element(storageServerSizes.begin(), storageServerSizes.end(), - ConsistencyCheckWorkload::compareByValue); - - int bias = SERVER_KNOBS->MIN_SHARD_BYTES; - if(1.1 * (minStorageServer->second + SERVER_KNOBS->MIN_SHARD_BYTES) < maxStorageServer->second + - SERVER_KNOBS->MIN_SHARD_BYTES) - { - TraceEvent("ConsistencyCheck_InconsistentStorageServerSizes").detail("MinSize", minStorageServer->second).detail("MaxSize", maxStorageServer->second) - .detail("MinStorageServer", minStorageServer->first).detail("MaxStorageServer", - maxStorageServer->first); - - self->testFailure(format("Storage servers differ significantly in size by a factor of %f", - ((double)maxStorageServer->second) / minStorageServer->second)); return false; - } - }*/ - - self->bytesReadInPreviousRound = bytesReadInthisRound; - return true; - } - // Returns true if any storage servers have the exact same network address or are not using the correct key value // store type ACTOR Future checkForUndesirableServers(Database cx, @@ -1928,7 +1084,7 @@ struct ConsistencyCheckWorkload : TestWorkload { // FIXME: this is hiding the fact that we can recruit a new storage server on a location the has // files left behind by a previous failure // this means that the process is wasting disk space until the process is rebooting - ISimulator::ProcessInfo* p = g_simulator.getProcessByAddress(itr->interf.address()); + ISimulator::ProcessInfo* p = g_simulator->getProcessByAddress(itr->interf.address()); // Note: itr->interf.address() may not equal to p->address() because role's endpoint's primary // addr can be swapped by choosePrimaryAddress() based on its peer's tls config. TraceEvent("ConsistencyCheck_RebootProcess") @@ -1937,14 +1093,14 @@ struct ConsistencyCheckWorkload : TestWorkload { .detail("ProcessPrimaryAddress", p->address) .detail("ProcessAddresses", p->addresses.toString()) .detail("DataStoreID", id) - .detail("Protected", g_simulator.protectedAddresses.count(itr->interf.address())) + .detail("Protected", g_simulator->protectedAddresses.count(itr->interf.address())) .detail("Reliable", p->isReliable()) .detail("ReliableInfo", p->getReliableInfo()) .detail("KillOrRebootProcess", p->address); if (p->isReliable()) { - g_simulator.rebootProcess(p, ISimulator::RebootProcess); + g_simulator->rebootProcess(p, ISimulator::RebootProcess); } else { - g_simulator.killProcess(p, ISimulator::KillInstantly); + g_simulator->killProcess(p, ISimulator::KillInstantly); } } @@ -2034,7 +1190,7 @@ struct ConsistencyCheckWorkload : TestWorkload { } ACTOR Future checkWorkerList(Database cx, ConsistencyCheckWorkload* self) { - if (!g_simulator.extraDatabases.empty()) { + if (!g_simulator->extraDatabases.empty()) { return true; } @@ -2043,7 +1199,7 @@ struct ConsistencyCheckWorkload : TestWorkload { for (const auto& it : workers) { NetworkAddress addr = it.interf.tLog.getEndpoint().addresses.getTLSAddress(); - ISimulator::ProcessInfo* info = g_simulator.getProcessByAddress(addr); + ISimulator::ProcessInfo* info = g_simulator->getProcessByAddress(addr); if (!info || info->failed) { TraceEvent("ConsistencyCheck_FailedWorkerInList").detail("Addr", it.interf.address()); return false; @@ -2051,7 +1207,7 @@ struct ConsistencyCheckWorkload : TestWorkload { workerAddresses.insert(NetworkAddress(addr.ip, addr.port, true, addr.isTLS())); } - std::vector all = g_simulator.getAllProcesses(); + std::vector all = g_simulator->getAllProcesses(); for (int i = 0; i < all.size(); i++) { if (all[i]->isReliable() && all[i]->name == std::string("Server") && all[i]->startingClass != ProcessClass::TesterClass && @@ -2181,10 +1337,10 @@ struct ConsistencyCheckWorkload : TestWorkload { } // Check if master and cluster controller are in the desired DC for fearless cluster when running under // simulation - // FIXME: g_simulator.datacenterDead could return false positives. Relaxing checks until it is fixed. - if (g_network->isSimulated() && config.usableRegions > 1 && g_simulator.primaryDcId.present() && - !g_simulator.datacenterDead(g_simulator.primaryDcId) && - !g_simulator.datacenterDead(g_simulator.remoteDcId)) { + // FIXME: g_simulator->datacenterDead could return false positives. Relaxing checks until it is fixed. + if (g_network->isSimulated() && config.usableRegions > 1 && g_simulator->primaryDcId.present() && + !g_simulator->datacenterDead(g_simulator->primaryDcId) && + !g_simulator->datacenterDead(g_simulator->remoteDcId)) { expectedPrimaryDcId = config.regions[0].dcId; expectedRemoteDcId = config.regions[1].dcId; // If the priorities are equal, either could be the primary @@ -2303,9 +1459,9 @@ struct ConsistencyCheckWorkload : TestWorkload { } // Check LogRouter - if (g_network->isSimulated() && config.usableRegions > 1 && g_simulator.primaryDcId.present() && - !g_simulator.datacenterDead(g_simulator.primaryDcId) && - !g_simulator.datacenterDead(g_simulator.remoteDcId)) { + if (g_network->isSimulated() && config.usableRegions > 1 && g_simulator->primaryDcId.present() && + !g_simulator->datacenterDead(g_simulator->primaryDcId) && + !g_simulator->datacenterDead(g_simulator->remoteDcId)) { for (auto& tlogSet : db.logSystemConfig.tLogs) { if (!tlogSet.isLocal && tlogSet.logRouters.size()) { for (auto& logRouter : tlogSet.logRouters) { diff --git a/fdbserver/workloads/CpuProfiler.actor.cpp b/fdbserver/workloads/CpuProfiler.actor.cpp index bc7cbfea15..f5ebbf2174 100644 --- a/fdbserver/workloads/CpuProfiler.actor.cpp +++ b/fdbserver/workloads/CpuProfiler.actor.cpp @@ -42,9 +42,9 @@ struct CpuProfilerWorkload : TestWorkload { std::vector profilingWorkers; CpuProfilerWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - initialDelay = getOption(options, LiteralStringRef("initialDelay"), 0.0); - duration = getOption(options, LiteralStringRef("duration"), -1.0); - roles = getOption(options, LiteralStringRef("roles"), std::vector()); + initialDelay = getOption(options, "initialDelay"_sr, 0.0); + duration = getOption(options, "duration"_sr, -1.0); + roles = getOption(options, "roles"_sr, std::vector()); success = true; } diff --git a/fdbserver/workloads/Cycle.actor.cpp b/fdbserver/workloads/Cycle.actor.cpp index f6834d1fe1..3ceaead9f0 100644 --- a/fdbserver/workloads/Cycle.actor.cpp +++ b/fdbserver/workloads/Cycle.actor.cpp @@ -63,13 +63,13 @@ struct CycleWorkload : TestWorkload, CycleMembers { transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5); nodeCount = getOption(options, "nodeCount"_sr, transactionsPerSecond * clientCount); - keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, LiteralStringRef("")).toString()); + keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, ""_sr).toString()); traceParentProbability = getOption(options, "traceParentProbability"_sr, 0.01); minExpectedTransactionsPerSecond = transactionsPerSecond * getOption(options, "expectedRate"_sr, 0.7); if constexpr (MultiTenancy) { ASSERT(g_network->isSimulated()); this->useToken = getOption(options, "useToken"_sr, true); - auto k = g_simulator.authKeys.begin(); + auto k = g_simulator->authKeys.begin(); this->tenant = getOption(options, "tenant"_sr, "CycleTenant"_sr); // make it comfortably longer than the timeout of the workload auto currentTime = uint64_t(lround(g_network->timer())); @@ -327,7 +327,7 @@ struct CycleWorkload : TestWorkload, CycleMembers { if (g_network->isSimulated() && retryCount > 50) { CODE_PROBE(true, "Cycle check enable speedUpSimulation because too many transaction_too_old()"); // try to make the read window back to normal size (5 * version_per_sec) - g_simulator.speedUpSimulation = true; + g_simulator->speedUpSimulation = true; } wait(tr.onError(e)); } diff --git a/fdbserver/workloads/DDBalance.actor.cpp b/fdbserver/workloads/DDBalance.actor.cpp index 62b9a03ccd..1b7b992e4a 100644 --- a/fdbserver/workloads/DDBalance.actor.cpp +++ b/fdbserver/workloads/DDBalance.actor.cpp @@ -36,17 +36,17 @@ struct DDBalanceWorkload : TestWorkload { DDBalanceWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), bin_shifts("Bin_Shifts"), operations("Operations"), retries("Retries"), latencies(2000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - binCount = getOption(options, LiteralStringRef("binCount"), 1000); - writesPerTransaction = getOption(options, LiteralStringRef("writesPerTransaction"), 1); - keySpaceDriftFactor = getOption(options, LiteralStringRef("keySpaceDriftFactor"), 1); - moversPerClient = std::max(getOption(options, LiteralStringRef("moversPerClient"), 10), 1); - actorsPerClient = std::max(getOption(options, LiteralStringRef("actorsPerClient"), 100), 1); - int nodes = getOption(options, LiteralStringRef("nodes"), 10000); - discardEdgeMeasurements = getOption(options, LiteralStringRef("discardEdgeMeasurements"), true); - warmingDelay = getOption(options, LiteralStringRef("warmingDelay"), 0.0); + testDuration = getOption(options, "testDuration"_sr, 10.0); + binCount = getOption(options, "binCount"_sr, 1000); + writesPerTransaction = getOption(options, "writesPerTransaction"_sr, 1); + keySpaceDriftFactor = getOption(options, "keySpaceDriftFactor"_sr, 1); + moversPerClient = std::max(getOption(options, "moversPerClient"_sr, 10), 1); + actorsPerClient = std::max(getOption(options, "actorsPerClient"_sr, 100), 1); + int nodes = getOption(options, "nodes"_sr, 10000); + discardEdgeMeasurements = getOption(options, "discardEdgeMeasurements"_sr, true); + warmingDelay = getOption(options, "warmingDelay"_sr, 0.0); transactionsPerSecond = - getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / (clientCount * moversPerClient); + getOption(options, "transactionsPerSecond"_sr, 5000.0) / (clientCount * moversPerClient); nodesPerActor = nodes / (actorsPerClient * clientCount); diff --git a/fdbserver/workloads/DDMetrics.actor.cpp b/fdbserver/workloads/DDMetrics.actor.cpp index 360ec4e0ac..540868a3c8 100644 --- a/fdbserver/workloads/DDMetrics.actor.cpp +++ b/fdbserver/workloads/DDMetrics.actor.cpp @@ -30,7 +30,7 @@ struct DDMetricsWorkload : TestWorkload { double startDelay, ddDone; DDMetricsWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ddDone(0.0) { - startDelay = getOption(options, LiteralStringRef("beginPoll"), 10.0); + startDelay = getOption(options, "beginPoll"_sr, 10.0); } std::string description() const override { return "Data Distribution Metrics"; } @@ -39,8 +39,8 @@ struct DDMetricsWorkload : TestWorkload { WorkerInterface masterWorker = wait(getMasterWorker(cx, self->dbInfo)); TraceEvent("GetHighPriorityReliocationsInFlight").detail("Stage", "ContactingMaster"); - TraceEventFields md = wait( - timeoutError(masterWorker.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("MovingData"))), 1.0)); + TraceEventFields md = + wait(timeoutError(masterWorker.eventLogRequest.getReply(EventLogRequest("MovingData"_sr)), 1.0)); int relocations; sscanf(md.getValue("UnhealthyRelocations").c_str(), "%d", &relocations); return relocations; diff --git a/fdbserver/workloads/DDMetricsExclude.actor.cpp b/fdbserver/workloads/DDMetricsExclude.actor.cpp index ff35a73f21..9aca58b570 100644 --- a/fdbserver/workloads/DDMetricsExclude.actor.cpp +++ b/fdbserver/workloads/DDMetricsExclude.actor.cpp @@ -38,8 +38,8 @@ struct DDMetricsExcludeWorkload : TestWorkload { DDMetricsExcludeWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ddDone(0.0), peakMovingData(0.0), peakInQueue(0.0), peakInFlight(0.0), movingDataPerSec(0.0) { - excludeIp = getOption(options, LiteralStringRef("excludeIp"), Value(LiteralStringRef("127.0.0.1"))); - excludePort = getOption(options, LiteralStringRef("excludePort"), 4500); + excludeIp = getOption(options, "excludeIp"_sr, Value("127.0.0.1"_sr)); + excludePort = getOption(options, "excludePort"_sr, 4500); } static Value getRandomValue() { diff --git a/fdbserver/workloads/DataDistributionMetrics.actor.cpp b/fdbserver/workloads/DataDistributionMetrics.actor.cpp index 63b1cde4ff..d15fcc0ba1 100644 --- a/fdbserver/workloads/DataDistributionMetrics.actor.cpp +++ b/fdbserver/workloads/DataDistributionMetrics.actor.cpp @@ -37,11 +37,11 @@ struct DataDistributionMetricsWorkload : KVWorkload { DataDistributionMetricsWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), numShards(0), avgBytes(0), commits("Commits"), errors("Errors") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - keyPrefix = getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("DDMetrics")).toString(); - readPerTx = getOption(options, LiteralStringRef("readPerTransaction"), 1); - writePerTx = getOption(options, LiteralStringRef("writePerTransaction"), 5 * readPerTx); - delayPerLoop = getOption(options, LiteralStringRef("delayPerLoop"), 0.1); // throttling dd rpc calls + testDuration = getOption(options, "testDuration"_sr, 10.0); + keyPrefix = getOption(options, "keyPrefix"_sr, "DDMetrics"_sr).toString(); + readPerTx = getOption(options, "readPerTransaction"_sr, 1); + writePerTx = getOption(options, "writePerTransaction"_sr, 5 * readPerTx); + delayPerLoop = getOption(options, "delayPerLoop"_sr, 0.1); // throttling dd rpc calls ASSERT(nodeCount > 1); } diff --git a/fdbserver/workloads/DataLossRecovery.actor.cpp b/fdbserver/workloads/DataLossRecovery.actor.cpp index 048b6d4bec..86b7f9de3f 100644 --- a/fdbserver/workloads/DataLossRecovery.actor.cpp +++ b/fdbserver/workloads/DataLossRecovery.actor.cpp @@ -188,7 +188,7 @@ struct DataLossRecoveryWorkload : TestWorkload { std::vector interfs = wait(getStorageServers(cx)); if (!interfs.empty()) { const auto& interf = interfs[deterministicRandom()->randomInt(0, interfs.size())]; - if (g_simulator.protectedAddresses.count(interf.address()) == 0) { + if (g_simulator->protectedAddresses.count(interf.address()) == 0) { dest.push_back(interf.uniqueID); addr = interf.address(); } @@ -213,18 +213,18 @@ struct DataLossRecoveryWorkload : TestWorkload { TraceEvent("DataLossRecovery").detail("Phase", "StartMoveKeys"); wait(moveKeys(cx, - deterministicRandom()->randomUniqueID(), - keys, - dest, - dest, - moveKeysLock, - Promise(), - &self->startMoveKeysParallelismLock, - &self->finishMoveKeysParallelismLock, - false, - UID(), // for logging only - &ddEnabledState, - CancelConflictingDataMoves::True)); + MoveKeysParams{ deterministicRandom()->randomUniqueID(), + keys, + dest, + dest, + moveKeysLock, + Promise(), + &self->startMoveKeysParallelismLock, + &self->finishMoveKeysParallelismLock, + false, + UID(), // for logging only + &ddEnabledState, + CancelConflictingDataMoves::True })); break; } catch (Error& e) { TraceEvent("DataLossRecovery").error(e).detail("Phase", "MoveRangeError"); @@ -256,9 +256,9 @@ struct DataLossRecoveryWorkload : TestWorkload { } void killProcess(DataLossRecoveryWorkload* self, const NetworkAddress& addr) { - ISimulator::ProcessInfo* process = g_simulator.getProcessByAddress(addr); + ISimulator::ProcessInfo* process = g_simulator->getProcessByAddress(addr); ASSERT(process->addresses.contains(addr)); - g_simulator.killProcess(process, ISimulator::KillInstantly); + g_simulator->killProcess(process, ISimulator::KillInstantly); TraceEvent("TestTeamKilled").detail("Address", addr); } @@ -267,4 +267,4 @@ struct DataLossRecoveryWorkload : TestWorkload { void getMetrics(std::vector& m) override {} }; -WorkloadFactory DataLossRecoveryWorkloadFactory("DataLossRecovery"); \ No newline at end of file +WorkloadFactory DataLossRecoveryWorkloadFactory("DataLossRecovery"); diff --git a/fdbserver/workloads/DifferentClustersSameRV.actor.cpp b/fdbserver/workloads/DifferentClustersSameRV.actor.cpp index bed2d27c2b..a3edd32df5 100644 --- a/fdbserver/workloads/DifferentClustersSameRV.actor.cpp +++ b/fdbserver/workloads/DifferentClustersSameRV.actor.cpp @@ -38,14 +38,14 @@ struct DifferentClustersSameRVWorkload : TestWorkload { bool switchComplete = false; DifferentClustersSameRVWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); - testDuration = getOption(options, LiteralStringRef("testDuration"), 100.0); - switchAfter = getOption(options, LiteralStringRef("switchAfter"), 50.0); - keyToRead = getOption(options, LiteralStringRef("keyToRead"), LiteralStringRef("someKey")); - keyToWatch = getOption(options, LiteralStringRef("keyToWatch"), LiteralStringRef("anotherKey")); + testDuration = getOption(options, "testDuration"_sr, 100.0); + switchAfter = getOption(options, "switchAfter"_sr, 50.0); + keyToRead = getOption(options, "keyToRead"_sr, "someKey"_sr); + keyToWatch = getOption(options, "keyToWatch"_sr, "anotherKey"_sr); } std::string description() const override { return "DifferentClustersSameRV"; } @@ -165,7 +165,7 @@ struct DifferentClustersSameRVWorkload : TestWorkload { wait(unlockDatabase(self->extraDB, lockUid)); TraceEvent("DifferentClusters_UnlockedExtraDB").log(); ASSERT(!watchFuture.isReady() || watchFuture.isError()); - wait(doWrite(self->extraDB, self->keyToWatch, Optional{ LiteralStringRef("") })); + wait(doWrite(self->extraDB, self->keyToWatch, Optional{ ""_sr })); TraceEvent("DifferentClusters_WaitingForWatch").log(); try { wait(timeoutError(watchFuture, (self->testDuration - self->switchAfter) / 2)); diff --git a/fdbserver/workloads/DiskDurability.actor.cpp b/fdbserver/workloads/DiskDurability.actor.cpp index bbbbbf0b15..0d5048e6b4 100644 --- a/fdbserver/workloads/DiskDurability.actor.cpp +++ b/fdbserver/workloads/DiskDurability.actor.cpp @@ -87,14 +87,14 @@ struct DiskDurabilityWorkload : public AsyncFileWorkload { double syncInterval; DiskDurabilityWorkload(WorkloadContext const& wcx) : AsyncFileWorkload(wcx) { - writers = getOption(options, LiteralStringRef("writers"), 1); - filePages = getOption(options, LiteralStringRef("filePages"), 1000000); + writers = getOption(options, "writers"_sr, 1); + filePages = getOption(options, "filePages"_sr, 1000000); fileSize = filePages * _PAGE_SIZE; unbufferedIO = true; uncachedIO = true; fillRandom = false; - pagesPerWrite = getOption(options, LiteralStringRef("pagesPerWrite"), 1); - syncInterval = (double)(getOption(options, LiteralStringRef("syncIntervalMs"), 2000)) / 1000; + pagesPerWrite = getOption(options, "pagesPerWrite"_sr, 1); + syncInterval = (double)(getOption(options, "syncIntervalMs"_sr, 2000)) / 1000; } ~DiskDurabilityWorkload() override {} diff --git a/fdbserver/workloads/DiskDurabilityTest.actor.cpp b/fdbserver/workloads/DiskDurabilityTest.actor.cpp index 4752e8a7b1..836f7217f2 100644 --- a/fdbserver/workloads/DiskDurabilityTest.actor.cpp +++ b/fdbserver/workloads/DiskDurabilityTest.actor.cpp @@ -32,9 +32,9 @@ struct DiskDurabilityTest : TestWorkload { DiskDurabilityTest(WorkloadContext const& wcx) : TestWorkload(wcx) { enabled = !clientId; // only do this on the "first" client - filename = getOption(options, LiteralStringRef("filename"), LiteralStringRef("durability_test.bin")).toString(); - auto prefix = getOption(options, LiteralStringRef("prefix"), LiteralStringRef("/DiskDurabilityTest/")); - range = prefixRange(LiteralStringRef("S").withPrefix(prefix)); + filename = getOption(options, "filename"_sr, "durability_test.bin"_sr).toString(); + auto prefix = getOption(options, "prefix"_sr, "/DiskDurabilityTest/"_sr); + range = prefixRange("S"_sr.withPrefix(prefix)); metrics = prefixRange(prefix); } @@ -142,10 +142,10 @@ struct DiskDurabilityTest : TestWorkload { tr.clear(self->encodeKey(targetPages[i])); if (!first) { - Optional v = wait(tr.get(LiteralStringRef("syncs").withPrefix(self->metrics.begin))); + Optional v = wait(tr.get("syncs"_sr.withPrefix(self->metrics.begin))); int64_t count = v.present() ? self->decodeValue(v.get()) : 0; count++; - tr.set(LiteralStringRef("syncs").withPrefix(self->metrics.begin), self->encodeValue(count)); + tr.set("syncs"_sr.withPrefix(self->metrics.begin), self->encodeValue(count)); } wait(tr.commit()); diff --git a/fdbserver/workloads/DiskFailureInjection.actor.cpp b/fdbserver/workloads/DiskFailureInjection.actor.cpp index 098d3f141a..62573a086c 100644 --- a/fdbserver/workloads/DiskFailureInjection.actor.cpp +++ b/fdbserver/workloads/DiskFailureInjection.actor.cpp @@ -28,19 +28,19 @@ #include "fdbserver/Status.h" #include "flow/actorcompiler.h" // This must be the last #include. -struct DiskFailureInjectionWorkload : TestWorkload { +struct DiskFailureInjectionWorkload : FailureInjectionWorkload { bool enabled; - double testDuration; - double startDelay; - bool throttleDisk; - int workersToThrottle; - double stallInterval; - double stallPeriod; - double throttlePeriod; - bool corruptFile; - int workersToCorrupt; - double percentBitFlips; - double periodicBroadcastInterval; + double testDuration = 60.0; + double startDelay = 0.0; + bool throttleDisk = false; + int workersToThrottle = 3; + double stallInterval = 0.0; + double stallPeriod = 60.0; + double throttlePeriod = 60.0; + bool corruptFile = false; + int workersToCorrupt = 1; + double percentBitFlips = 10; + double periodicBroadcastInterval = 5.0; std::vector chosenWorkers; std::vector> clients; // Verification Mode: We run the workload indefinitely in this mode. @@ -48,24 +48,28 @@ struct DiskFailureInjectionWorkload : TestWorkload { // that we haven't lost the chaos event. testDuration is ignored in this mode bool verificationMode; - DiskFailureInjectionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { + DiskFailureInjectionWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) {} + + DiskFailureInjectionWorkload(WorkloadContext const& wcx) : FailureInjectionWorkload(wcx) { enabled = !clientId; // only do this on the "first" client - startDelay = getOption(options, LiteralStringRef("startDelay"), 0.0); - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); - verificationMode = getOption(options, LiteralStringRef("verificationMode"), false); - throttleDisk = getOption(options, LiteralStringRef("throttleDisk"), false); - workersToThrottle = getOption(options, LiteralStringRef("workersToThrottle"), 3); - stallInterval = getOption(options, LiteralStringRef("stallInterval"), 0.0); - stallPeriod = getOption(options, LiteralStringRef("stallPeriod"), 60.0); - throttlePeriod = getOption(options, LiteralStringRef("throttlePeriod"), 60.0); - corruptFile = getOption(options, LiteralStringRef("corruptFile"), false); - workersToCorrupt = getOption(options, LiteralStringRef("workersToCorrupt"), 1); - percentBitFlips = getOption(options, LiteralStringRef("percentBitFlips"), 10.0); - periodicBroadcastInterval = getOption(options, LiteralStringRef("periodicBroadcastInterval"), 5.0); + startDelay = getOption(options, "startDelay"_sr, startDelay); + testDuration = getOption(options, "testDuration"_sr, testDuration); + verificationMode = getOption(options, "verificationMode"_sr, verificationMode); + throttleDisk = getOption(options, "throttleDisk"_sr, throttleDisk); + workersToThrottle = getOption(options, "workersToThrottle"_sr, workersToThrottle); + stallInterval = getOption(options, "stallInterval"_sr, stallInterval); + stallPeriod = getOption(options, "stallPeriod"_sr, stallPeriod); + throttlePeriod = getOption(options, "throttlePeriod"_sr, throttlePeriod); + corruptFile = getOption(options, "corruptFile"_sr, corruptFile); + workersToCorrupt = getOption(options, "workersToCorrupt"_sr, workersToCorrupt); + percentBitFlips = getOption(options, "percentBitFlips"_sr, percentBitFlips); + periodicBroadcastInterval = getOption(options, "periodicBroadcastInterval"_sr, periodicBroadcastInterval); } + void initFailureInjectionMode(DeterministicRandom& random, unsigned count) override { enabled = clientId == 0; } + std::string description() const override { - if (&g_simulator == g_network) + if (g_simulator == g_network) return "DiskFailureInjection"; else return "NoSimDiskFailureInjection"; @@ -177,8 +181,8 @@ struct DiskFailureInjectionWorkload : TestWorkload { if (self->throttleDisk && (throttledWorkers++ < self->workersToThrottle)) self->injectDiskDelays(machine, self->stallInterval, self->stallPeriod, self->throttlePeriod); if (self->corruptFile && (corruptedWorkers++ < self->workersToCorrupt)) { - if (&g_simulator == g_network) - g_simulator.corruptWorkerMap[machine.address()] = true; + if (g_simulator == g_network) + g_simulator->corruptWorkerMap[machine.address()] = true; self->injectBitFlips(machine, self->percentBitFlips); } } @@ -200,8 +204,8 @@ struct DiskFailureInjectionWorkload : TestWorkload { if (self->throttleDisk && (throttledWorkers++ < self->workersToThrottle)) self->injectDiskDelays(itr->second, self->stallInterval, self->stallPeriod, self->throttlePeriod); if (self->corruptFile && (corruptedWorkers++ < self->workersToCorrupt)) { - if (&g_simulator == g_network) - g_simulator.corruptWorkerMap[workerAddress] = true; + if (g_simulator == g_network) + g_simulator->corruptWorkerMap[workerAddress] = true; self->injectBitFlips(itr->second, self->percentBitFlips); } } @@ -275,3 +279,4 @@ struct DiskFailureInjectionWorkload : TestWorkload { } }; WorkloadFactory DiskFailureInjectionWorkloadFactory("DiskFailureInjection"); +FailureInjectorFactory DiskFailureInjectionWorkloadFailureInjectionFactory; diff --git a/fdbserver/workloads/DummyWorkload.actor.cpp b/fdbserver/workloads/DummyWorkload.actor.cpp index 99af0a2947..a908085b3e 100644 --- a/fdbserver/workloads/DummyWorkload.actor.cpp +++ b/fdbserver/workloads/DummyWorkload.actor.cpp @@ -27,8 +27,8 @@ struct DummyWorkload : TestWorkload { double displayDelay; DummyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - displayWorkers = getOption(options, LiteralStringRef("displayWorkers"), true); - displayDelay = getOption(options, LiteralStringRef("displayDelay"), 0.0); + displayWorkers = getOption(options, "displayWorkers"_sr, true); + displayDelay = getOption(options, "displayDelay"_sr, 0.0); } std::string description() const override { return "DummyWorkload"; } @@ -43,7 +43,7 @@ struct DummyWorkload : TestWorkload { ACTOR static Future _start(DummyWorkload* self, Database cx) { if (self->displayDelay > 0.0) wait(delay(self->displayDelay)); - g_simulator.displayWorkers(); + g_simulator->displayWorkers(); return Void(); } diff --git a/fdbserver/workloads/EncryptionOps.actor.cpp b/fdbserver/workloads/EncryptionOps.actor.cpp index 6c49efec8d..285af5ecd6 100644 --- a/fdbserver/workloads/EncryptionOps.actor.cpp +++ b/fdbserver/workloads/EncryptionOps.actor.cpp @@ -18,12 +18,12 @@ * limitations under the License. */ +#include "fdbclient/BlobCipher.h" #include "fdbclient/DatabaseContext.h" #include "fdbclient/NativeAPI.actor.h" #include "flow/EncryptUtils.h" #include "flow/Error.h" #include "flow/IRandom.h" -#include "flow/BlobCipher.h" #include "fdbserver/workloads/workloads.actor.h" #include "flow/flow.h" #include "flow/ITrace.h" @@ -126,10 +126,10 @@ struct EncryptionOpsWorkload : TestWorkload { EncryptCipherRandomSalt headerRandomSalt; EncryptionOpsWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), enableTTLTest(false) { - mode = getOption(options, LiteralStringRef("fixedSize"), 1); - numIterations = getOption(options, LiteralStringRef("numIterations"), 10); - pageSize = getOption(options, LiteralStringRef("pageSize"), 4096); - maxBufSize = getOption(options, LiteralStringRef("maxBufSize"), 512 * 1024); + mode = getOption(options, "fixedSize"_sr, 1); + numIterations = getOption(options, "numIterations"_sr, 10); + pageSize = getOption(options, "pageSize"_sr, 4096); + maxBufSize = getOption(options, "maxBufSize"_sr, 512 * 1024); buff = std::make_unique(maxBufSize); // assign unique encryptionDomainId range per workload clients @@ -221,6 +221,7 @@ struct EncryptionOpsWorkload : TestWorkload { cipherKeyCache->resetEncryptDomainId(id); } + cipherKeyCache->resetEncryptDomainId(FDB_DEFAULT_ENCRYPT_DOMAIN_ID); cipherKeyCache->resetEncryptDomainId(SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID); cipherKeyCache->resetEncryptDomainId(ENCRYPT_HEADER_DOMAIN_ID); @@ -274,7 +275,8 @@ struct EncryptionOpsWorkload : TestWorkload { BlobCipherEncryptHeader* header) { uint8_t iv[AES_256_IV_LENGTH]; deterministicRandom()->randomBytes(&iv[0], AES_256_IV_LENGTH); - EncryptBlobCipherAes265Ctr encryptor(textCipherKey, headerCipherKey, &iv[0], AES_256_IV_LENGTH, authMode); + EncryptBlobCipherAes265Ctr encryptor( + textCipherKey, headerCipherKey, &iv[0], AES_256_IV_LENGTH, authMode, BlobCipherMetrics::TEST); auto start = std::chrono::high_resolution_clock::now(); Reference encrypted = encryptor.encrypt(payload, len, header, arena); @@ -306,7 +308,7 @@ struct EncryptionOpsWorkload : TestWorkload { ASSERT(cipherKey.isValid()); ASSERT(cipherKey->isEqual(orgCipherKey)); - DecryptBlobCipherAes256Ctr decryptor(cipherKey, headerCipherKey, header.iv); + DecryptBlobCipherAes256Ctr decryptor(cipherKey, headerCipherKey, header.iv, BlobCipherMetrics::TEST); const bool validateHeaderAuthToken = deterministicRandom()->randomInt(0, 100) < 65; auto start = std::chrono::high_resolution_clock::now(); diff --git a/fdbserver/workloads/ExternalWorkload.actor.cpp b/fdbserver/workloads/ExternalWorkload.actor.cpp index e652674347..dde3c04c47 100644 --- a/fdbserver/workloads/ExternalWorkload.actor.cpp +++ b/fdbserver/workloads/ExternalWorkload.actor.cpp @@ -128,9 +128,9 @@ struct ExternalWorkload : TestWorkload, FDBWorkloadContext { } explicit ExternalWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - libraryName = ::getOption(options, LiteralStringRef("libraryName"), LiteralStringRef("")).toString(); - libraryPath = ::getOption(options, LiteralStringRef("libraryPath"), Value(getDefaultLibraryPath())).toString(); - auto wName = ::getOption(options, LiteralStringRef("workloadName"), LiteralStringRef("")); + libraryName = ::getOption(options, "libraryName"_sr, ""_sr).toString(); + libraryPath = ::getOption(options, "libraryPath"_sr, Value(getDefaultLibraryPath())).toString(); + auto wName = ::getOption(options, "workloadName"_sr, ""_sr); auto fullPath = joinPath(libraryPath, toLibName(libraryName)); TraceEvent("ExternalWorkloadLoad") .detail("LibraryName", libraryName) @@ -185,7 +185,7 @@ struct ExternalWorkload : TestWorkload, FDBWorkloadContext { keepAlive(f, database); workloadImpl->setup(reinterpret_cast(database.getPtr()), GenericPromise(new FDBPromiseImpl(promise))); - return assertTrue(LiteralStringRef("setup"), f); + return assertTrue("setup"_sr, f); } Future start(Database const& cx) override { @@ -200,7 +200,7 @@ struct ExternalWorkload : TestWorkload, FDBWorkloadContext { keepAlive(f, database); workloadImpl->start(reinterpret_cast(database.getPtr()), GenericPromise(new FDBPromiseImpl(promise))); - return assertTrue(LiteralStringRef("start"), f); + return assertTrue("start"_sr, f); } Future check(Database const& cx) override { if (!success) { @@ -242,14 +242,14 @@ struct ExternalWorkload : TestWorkload, FDBWorkloadContext { } uint64_t getProcessID() const override { if (g_network->isSimulated()) { - return reinterpret_cast(g_simulator.getCurrentProcess()); + return reinterpret_cast(g_simulator->getCurrentProcess()); } else { return 0ul; } } void setProcessID(uint64_t processID) override { if (g_network->isSimulated()) { - g_simulator.currentProcess = reinterpret_cast(processID); + g_simulator->currentProcess = reinterpret_cast(processID); } } double now() const override { return g_network->now(); } diff --git a/fdbserver/workloads/FastTriggeredWatches.actor.cpp b/fdbserver/workloads/FastTriggeredWatches.actor.cpp index 8d1e635a47..cafdda2fc8 100644 --- a/fdbserver/workloads/FastTriggeredWatches.actor.cpp +++ b/fdbserver/workloads/FastTriggeredWatches.actor.cpp @@ -36,10 +36,10 @@ struct FastTriggeredWatchesWorkload : TestWorkload { FastTriggeredWatchesWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operations("Operations"), retries("Retries") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - nodes = getOption(options, LiteralStringRef("nodes"), 100); + testDuration = getOption(options, "testDuration"_sr, 600.0); + nodes = getOption(options, "nodes"_sr, 100); defaultValue = StringRef(format("%010d", deterministicRandom()->randomInt(0, 1000))); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); } std::string description() const override { return "Watches"; } diff --git a/fdbserver/workloads/FileSystem.actor.cpp b/fdbserver/workloads/FileSystem.actor.cpp index a5fa90af4e..b5340e9c12 100644 --- a/fdbserver/workloads/FileSystem.actor.cpp +++ b/fdbserver/workloads/FileSystem.actor.cpp @@ -44,23 +44,21 @@ struct FileSystemWorkload : TestWorkload { FileSystemWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), queries("Queries"), writes("Latency"), latencies(2500), writeLatencies(1000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; - double allowedLatency = getOption(options, LiteralStringRef("allowedLatency"), 0.250); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; + double allowedLatency = getOption(options, "allowedLatency"_sr, 0.250); actorCount = transactionsPerSecond * allowedLatency; - fileCount = getOption(options, LiteralStringRef("fileCount"), 100000); - pathMinChars = std::max(getOption(options, LiteralStringRef("pathMinChars"), 32), 8); - pathCharRange = - std::max(getOption(options, LiteralStringRef("pathMaxChars"), 128), pathMinChars) - pathMinChars; - discardEdgeMeasurements = getOption(options, LiteralStringRef("discardEdgeMeasurements"), true); - deletedFilesRatio = getOption(options, LiteralStringRef("deletedFilesRatio"), 0.01); - serverCount = getOption(options, LiteralStringRef("serverCount"), 32); - userIDCount = getOption(options, LiteralStringRef("userIDCount"), std::max(100, fileCount / 3000)); - operationName = - getOption(options, LiteralStringRef("operationName"), LiteralStringRef("modificationQuery")).toString(); - performingWrites = getOption(options, LiteralStringRef("performingWrites"), false); - writeActorCount = getOption(options, LiteralStringRef("writeActorCount"), 4); - loggingQueries = getOption(options, LiteralStringRef("loggingQueries"), false); + fileCount = getOption(options, "fileCount"_sr, 100000); + pathMinChars = std::max(getOption(options, "pathMinChars"_sr, 32), 8); + pathCharRange = std::max(getOption(options, "pathMaxChars"_sr, 128), pathMinChars) - pathMinChars; + discardEdgeMeasurements = getOption(options, "discardEdgeMeasurements"_sr, true); + deletedFilesRatio = getOption(options, "deletedFilesRatio"_sr, 0.01); + serverCount = getOption(options, "serverCount"_sr, 32); + userIDCount = getOption(options, "userIDCount"_sr, std::max(100, fileCount / 3000)); + operationName = getOption(options, "operationName"_sr, "modificationQuery"_sr).toString(); + performingWrites = getOption(options, "performingWrites"_sr, false); + writeActorCount = getOption(options, "writeActorCount"_sr, 4); + loggingQueries = getOption(options, "loggingQueries"_sr, false); } std::string description() const override { return "ReadWrite"; } @@ -105,7 +103,7 @@ struct FileSystemWorkload : TestWorkload { std::string keyStr(key.toString()); tr->set(keyStr + "/size", format("%d", deterministicRandom()->randomInt(0, std::numeric_limits::max()))); tr->set(keyStr + "/server", format("%d", deterministicRandom()->randomInt(0, self->serverCount))); - tr->set(keyStr + "/deleted", deleted ? LiteralStringRef("1") : LiteralStringRef("0")); + tr->set(keyStr + "/deleted", deleted ? "1"_sr : "0"_sr); tr->set(keyStr + "/server", format("%d", serverID)); tr->set(keyStr + "/created", doubleToTestKey(time)); tr->set(keyStr + "/lastupdated", doubleToTestKey(time)); @@ -250,10 +248,10 @@ struct FileSystemWorkload : TestWorkload { ASSERT(serverStr.present()); int serverID = testKeyToInt(serverStr.get()); if (deleted.get().toString() == "1") { - tr.set(keyStr + "/deleted", LiteralStringRef("0")); + tr.set(keyStr + "/deleted", "0"_sr); tr.clear(format("/files/server/%08x/deleted/%016llx", serverID, fileID)); } else { - tr.set(keyStr + "/deleted", LiteralStringRef("1")); + tr.set(keyStr + "/deleted", "1"_sr); tr.set(format("/files/server/%08x/deleted/%016llx", serverID, fileID), doubleToTestKey(time)); } diff --git a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp index eff62949de..8342e35f9f 100644 --- a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp +++ b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp @@ -140,11 +140,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { FuzzApiCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operationId(0), success(true) { std::call_once(onceFlag, [&]() { addTestCases(); }); - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); - numOps = getOption(options, LiteralStringRef("numOps"), 21); - rarelyCommit = getOption(options, LiteralStringRef("rarelyCommit"), false); - maximumTotalData = getOption(options, LiteralStringRef("maximumTotalData"), 15e6); - minNode = getOption(options, LiteralStringRef("minNode"), 0); + testDuration = getOption(options, "testDuration"_sr, 60.0); + numOps = getOption(options, "numOps"_sr, 21); + rarelyCommit = getOption(options, "rarelyCommit"_sr, false); + maximumTotalData = getOption(options, "maximumTotalData"_sr, 15e6); + minNode = getOption(options, "minNode"_sr, 0); adjacentKeys = deterministicRandom()->coinflip(); useSystemKeys = deterministicRandom()->coinflip(); initialKeyDensity = deterministicRandom()->random01(); // This fraction of keys are present before the first @@ -181,7 +181,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { } maxClearSize = 1 << deterministicRandom()->randomInt(0, 20); - conflictRange = KeyRangeRef(LiteralStringRef("\xfe"), LiteralStringRef("\xfe\x00")); + conflictRange = KeyRangeRef("\xfe"_sr, "\xfe\x00"_sr); TraceEvent("FuzzApiCorrectnessConfiguration") .detail("Nodes", nodes) .detail("NumTenants", numTenants) @@ -737,18 +737,18 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { error_code_special_keys_no_module_found, ExceptionContract::possibleIf(specialKeys.contains(key) && !workload->specialKeysRelaxed)), // Read this particular special key may throw timed_out - std::make_pair(error_code_timed_out, - ExceptionContract::possibleIf(key == LiteralStringRef("\xff\xff/status/json"))), + std::make_pair(error_code_timed_out, ExceptionContract::possibleIf(key == "\xff\xff/status/json"_sr)), // Read this particular special key may throw special_keys_api_failure std::make_pair( error_code_special_keys_api_failure, ExceptionContract::possibleIf( - key == - LiteralStringRef("auto_coordinators") - .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))), + key == "auto_coordinators"_sr.withPrefix( + SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))), std::make_pair(error_code_tenant_not_found, ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))), std::make_pair(error_code_invalid_option, + ExceptionContract::possibleIf(tr->getTenant().present() && specialKeys.contains(key))), + std::make_pair(error_code_illegal_tenant_access, ExceptionContract::possibleIf(tr->getTenant().present() && specialKeys.contains(key))) }; } @@ -829,6 +829,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { std::make_pair(error_code_tenant_not_found, ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))), std::make_pair(error_code_invalid_option, + ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)), + std::make_pair(error_code_illegal_tenant_access, ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)) }; } @@ -875,7 +877,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible), std::make_pair(error_code_tenant_not_found, ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))), - std::make_pair(error_code_invalid_option, + std::make_pair(error_code_illegal_tenant_access, ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)) }; } @@ -915,12 +917,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { bool isSpecialKeyRange = specialKeys.contains(key1) && specialKeys.begin <= key2 && key2 <= specialKeys.end; // Read this particular special key may throw special_keys_api_failure - Key autoCoordinatorSpecialKey = - LiteralStringRef("auto_coordinators") - .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); + Key autoCoordinatorSpecialKey = "auto_coordinators"_sr.withPrefix( + SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); KeyRangeRef actorLineageRange = SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::ACTORLINEAGE); // Read this particular special key may throw timed_out - Key statusJsonSpecialKey = LiteralStringRef("\xff\xff/status/json"); + Key statusJsonSpecialKey = "\xff\xff/status/json"_sr; contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), @@ -945,6 +946,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { std::make_pair(error_code_tenant_not_found, ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))), std::make_pair(error_code_invalid_option, + ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)), + std::make_pair(error_code_illegal_tenant_access, ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)) }; } @@ -972,11 +975,10 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { limits = makeRangeLimits(); bool isSpecialKeyRange = specialKeys.contains(key1) && specialKeys.begin <= key2 && key2 <= specialKeys.end; - Key autoCoordinatorSpecialKey = - LiteralStringRef("auto_coordinators") - .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); + Key autoCoordinatorSpecialKey = "auto_coordinators"_sr.withPrefix( + SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin); KeyRangeRef actorLineageRange = SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::ACTORLINEAGE); - Key statusJsonSpecialKey = LiteralStringRef("\xff\xff/status/json"); + Key statusJsonSpecialKey = "\xff\xff/status/json"_sr; contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), @@ -1002,6 +1004,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { std::make_pair(error_code_tenant_not_found, ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))), std::make_pair(error_code_invalid_option, + ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)), + std::make_pair(error_code_illegal_tenant_access, ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange)) }; } diff --git a/fdbserver/workloads/GetRangeStream.actor.cpp b/fdbserver/workloads/GetRangeStream.actor.cpp index 8d476069f1..68ec509de7 100644 --- a/fdbserver/workloads/GetRangeStream.actor.cpp +++ b/fdbserver/workloads/GetRangeStream.actor.cpp @@ -32,10 +32,10 @@ struct GetRangeStream : TestWorkload { bool printKVPairs; GetRangeStream(WorkloadContext const& wcx) : TestWorkload(wcx), bytesRead("BytesRead") { - useGetRange = getOption(options, LiteralStringRef("useGetRange"), false); - begin = getOption(options, LiteralStringRef("begin"), normalKeys.begin); - end = getOption(options, LiteralStringRef("end"), normalKeys.end); - printKVPairs = getOption(options, LiteralStringRef("printKVPairs"), false); + useGetRange = getOption(options, "useGetRange"_sr, false); + begin = getOption(options, "begin"_sr, normalKeys.begin); + end = getOption(options, "end"_sr, normalKeys.end); + printKVPairs = getOption(options, "printKVPairs"_sr, false); } std::string description() const override { return "GetRangeStreamWorkload"; } diff --git a/fdbserver/workloads/HealthMetricsApi.actor.cpp b/fdbserver/workloads/HealthMetricsApi.actor.cpp index 29fb0a0b64..988c3970c7 100644 --- a/fdbserver/workloads/HealthMetricsApi.actor.cpp +++ b/fdbserver/workloads/HealthMetricsApi.actor.cpp @@ -49,10 +49,10 @@ struct HealthMetricsApiWorkload : TestWorkload { static constexpr const char* NAME = "HealthMetricsApi"; HealthMetricsApiWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0); - healthMetricsCheckInterval = getOption(options, LiteralStringRef("healthMetricsCheckInterval"), 1.0); - sendDetailedHealthMetrics = getOption(options, LiteralStringRef("sendDetailedHealthMetrics"), true); - maxAllowedStaleness = getOption(options, LiteralStringRef("maxAllowedStaleness"), 60.0); + testDuration = getOption(options, "testDuration"_sr, 120.0); + healthMetricsCheckInterval = getOption(options, "healthMetricsCheckInterval"_sr, 1.0); + sendDetailedHealthMetrics = getOption(options, "sendDetailedHealthMetrics"_sr, true); + maxAllowedStaleness = getOption(options, "maxAllowedStaleness"_sr, 60.0); } std::string description() const override { return HealthMetricsApiWorkload::NAME; } diff --git a/fdbserver/workloads/HighContentionPrefixAllocatorWorkload.actor.cpp b/fdbserver/workloads/HighContentionPrefixAllocatorWorkload.actor.cpp index c48e2ebc02..b5becdabd7 100644 --- a/fdbserver/workloads/HighContentionPrefixAllocatorWorkload.actor.cpp +++ b/fdbserver/workloads/HighContentionPrefixAllocatorWorkload.actor.cpp @@ -38,9 +38,9 @@ struct HighContentionPrefixAllocatorWorkload : TestWorkload { HighContentionPrefixAllocatorWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), allocatorSubspace("test_subspace"_sr), allocator(allocatorSubspace) { - numRounds = getOption(options, LiteralStringRef("numRounds"), 500); - maxTransactionsPerRound = getOption(options, LiteralStringRef("maxTransactionsPerRound"), 20); - maxAllocationsPerTransaction = getOption(options, LiteralStringRef("maxAllocationsPerTransaction"), 20); + numRounds = getOption(options, "numRounds"_sr, 500); + maxTransactionsPerRound = getOption(options, "maxTransactionsPerRound"_sr, 20); + maxAllocationsPerTransaction = getOption(options, "maxAllocationsPerTransaction"_sr, 20); } std::string description() const override { return HighContentionPrefixAllocatorWorkload::NAME; } diff --git a/fdbserver/workloads/Increment.actor.cpp b/fdbserver/workloads/Increment.actor.cpp index a6a4c2d668..0bd1504e51 100644 --- a/fdbserver/workloads/Increment.actor.cpp +++ b/fdbserver/workloads/Increment.actor.cpp @@ -35,12 +35,11 @@ struct Increment : TestWorkload { Increment(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries"), tooOldRetries("Retries.too_old"), commitFailedRetries("Retries.commit_failed"), totalLatency("Latency") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0); - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), transactionsPerSecond / 5); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), transactionsPerSecond * clientCount); - minExpectedTransactionsPerSecond = - transactionsPerSecond * getOption(options, LiteralStringRef("expectedRate"), 0.7); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0); + actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5); + nodeCount = getOption(options, "nodeCount"_sr, transactionsPerSecond * clientCount); + minExpectedTransactionsPerSecond = transactionsPerSecond * getOption(options, "expectedRate"_sr, 0.7); } std::string description() const override { return "IncrementWorkload"; } @@ -84,11 +83,11 @@ struct Increment : TestWorkload { while (true) { try { tr.atomicOp(intToTestKey(deterministicRandom()->randomInt(0, self->nodeCount / 2)), - LiteralStringRef("\x01"), + "\x01"_sr, MutationRef::AddValue); tr.atomicOp( intToTestKey(deterministicRandom()->randomInt(self->nodeCount / 2, self->nodeCount)), - LiteralStringRef("\x01"), + "\x01"_sr, MutationRef::AddValue); wait(tr.commit()); break; diff --git a/fdbserver/workloads/IncrementalBackup.actor.cpp b/fdbserver/workloads/IncrementalBackup.actor.cpp index e40133ffd0..31c8c5e636 100644 --- a/fdbserver/workloads/IncrementalBackup.actor.cpp +++ b/fdbserver/workloads/IncrementalBackup.actor.cpp @@ -44,15 +44,15 @@ struct IncrementalBackupWorkload : TestWorkload { bool clearBackupAgentKeys; IncrementalBackupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - backupDir = getOption(options, LiteralStringRef("backupDir"), LiteralStringRef("file://simfdb/backups/")); - tag = getOption(options, LiteralStringRef("tag"), LiteralStringRef("default")); - submitOnly = getOption(options, LiteralStringRef("submitOnly"), false); - restoreOnly = getOption(options, LiteralStringRef("restoreOnly"), false); - waitForBackup = getOption(options, LiteralStringRef("waitForBackup"), false); - waitRetries = getOption(options, LiteralStringRef("waitRetries"), -1); - stopBackup = getOption(options, LiteralStringRef("stopBackup"), false); - checkBeginVersion = getOption(options, LiteralStringRef("checkBeginVersion"), false); - clearBackupAgentKeys = getOption(options, LiteralStringRef("clearBackupAgentKeys"), false); + backupDir = getOption(options, "backupDir"_sr, "file://simfdb/backups/"_sr); + tag = getOption(options, "tag"_sr, "default"_sr); + submitOnly = getOption(options, "submitOnly"_sr, false); + restoreOnly = getOption(options, "restoreOnly"_sr, false); + waitForBackup = getOption(options, "waitForBackup"_sr, false); + waitRetries = getOption(options, "waitRetries"_sr, -1); + stopBackup = getOption(options, "stopBackup"_sr, false); + checkBeginVersion = getOption(options, "checkBeginVersion"_sr, false); + clearBackupAgentKeys = getOption(options, "clearBackupAgentKeys"_sr, false); } std::string description() const override { return "IncrementalBackup"; } diff --git a/fdbserver/workloads/IndexScan.actor.cpp b/fdbserver/workloads/IndexScan.actor.cpp index ca844c980c..2e39470c52 100644 --- a/fdbserver/workloads/IndexScan.actor.cpp +++ b/fdbserver/workloads/IndexScan.actor.cpp @@ -34,11 +34,11 @@ struct IndexScanWorkload : KVWorkload { IndexScanWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), rowsRead(0), chunks(0), failedTransactions(0), scans(0) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - bytesPerRead = getOption(options, LiteralStringRef("bytesPerRead"), 80000); - transactionDuration = getOption(options, LiteralStringRef("transactionDuration"), 1.0); - singleProcess = getOption(options, LiteralStringRef("singleProcess"), true); - readYourWrites = getOption(options, LiteralStringRef("readYourWrites"), true); + testDuration = getOption(options, "testDuration"_sr, 10.0); + bytesPerRead = getOption(options, "bytesPerRead"_sr, 80000); + transactionDuration = getOption(options, "transactionDuration"_sr, 1.0); + singleProcess = getOption(options, "singleProcess"_sr, true); + readYourWrites = getOption(options, "readYourWrites"_sr, true); } std::string description() const override { return "SimpleRead"; } diff --git a/fdbserver/workloads/Inventory.actor.cpp b/fdbserver/workloads/Inventory.actor.cpp index c5f8074625..03106cc805 100644 --- a/fdbserver/workloads/Inventory.actor.cpp +++ b/fdbserver/workloads/Inventory.actor.cpp @@ -38,12 +38,12 @@ struct InventoryTestWorkload : TestWorkload { InventoryTestWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries"), totalLatency("Latency") { - actorCount = getOption(options, LiteralStringRef("actorCount"), 500); - nProducts = getOption(options, LiteralStringRef("nProducts"), 100000); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 10000); - fractionWriteTransactions = getOption(options, LiteralStringRef("fractionWriteTransactions"), 0.01); - productsPerWrite = getOption(options, LiteralStringRef("productsPerWrite"), 2); + actorCount = getOption(options, "actorCount"_sr, 500); + nProducts = getOption(options, "nProducts"_sr, 100000); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 10000); + fractionWriteTransactions = getOption(options, "fractionWriteTransactions"_sr, 0.01); + productsPerWrite = getOption(options, "productsPerWrite"_sr, 2); } std::string description() const override { return "InventoryTest"; } diff --git a/fdbserver/workloads/KVStoreTest.actor.cpp b/fdbserver/workloads/KVStoreTest.actor.cpp index afa9c3dbd6..4b8cd62e58 100644 --- a/fdbserver/workloads/KVStoreTest.actor.cpp +++ b/fdbserver/workloads/KVStoreTest.actor.cpp @@ -210,19 +210,19 @@ struct KVStoreTestWorkload : TestWorkload { KVStoreTestWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), reads("Reads"), sets("Sets"), commits("Commits"), setupTook(0) { enabled = !clientId; // only do this on the "first" client - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - operationsPerSecond = getOption(options, LiteralStringRef("operationsPerSecond"), 100e3); - commitFraction = getOption(options, LiteralStringRef("commitFraction"), .001); - setFraction = getOption(options, LiteralStringRef("setFraction"), .1); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), 100000); - keyBytes = getOption(options, LiteralStringRef("keyBytes"), 8); - valueBytes = getOption(options, LiteralStringRef("valueBytes"), 8); - doSetup = getOption(options, LiteralStringRef("setup"), false); - doClear = getOption(options, LiteralStringRef("clear"), false); - doCount = getOption(options, LiteralStringRef("count"), false); - filename = getOption(options, LiteralStringRef("filename"), Value()).toString(); - saturation = getOption(options, LiteralStringRef("saturation"), false); - storeType = getOption(options, LiteralStringRef("storeType"), LiteralStringRef("ssd")).toString(); + testDuration = getOption(options, "testDuration"_sr, 10.0); + operationsPerSecond = getOption(options, "operationsPerSecond"_sr, 100e3); + commitFraction = getOption(options, "commitFraction"_sr, .001); + setFraction = getOption(options, "setFraction"_sr, .1); + nodeCount = getOption(options, "nodeCount"_sr, 100000); + keyBytes = getOption(options, "keyBytes"_sr, 8); + valueBytes = getOption(options, "valueBytes"_sr, 8); + doSetup = getOption(options, "setup"_sr, false); + doClear = getOption(options, "clear"_sr, false); + doCount = getOption(options, "count"_sr, false); + filename = getOption(options, "filename"_sr, Value()).toString(); + saturation = getOption(options, "saturation"_sr, false); + storeType = getOption(options, "storeType"_sr, "ssd"_sr).toString(); } std::string description() const override { return "KVStoreTest"; } Future setup(Database const& cx) override { return Void(); } @@ -271,7 +271,7 @@ ACTOR Future testKVStoreMain(KVStoreTestWorkload* workload, KVTest* ptest) state Key k; state double cst = timer(); while (true) { - RangeResult kv = wait(test.store->readRange(KeyRangeRef(k, LiteralStringRef("\xff\xff\xff\xff")), 1000)); + RangeResult kv = wait(test.store->readRange(KeyRangeRef(k, "\xff\xff\xff\xff"_sr), 1000)); count += kv.size(); if (kv.size() < 1000) break; diff --git a/fdbserver/workloads/KillRegion.actor.cpp b/fdbserver/workloads/KillRegion.actor.cpp index 08b3f62946..63d2774fe6 100644 --- a/fdbserver/workloads/KillRegion.actor.cpp +++ b/fdbserver/workloads/KillRegion.actor.cpp @@ -35,8 +35,8 @@ struct KillRegionWorkload : TestWorkload { KillRegionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client, and only when in simulation - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - g_simulator.usableRegions = 1; + testDuration = getOption(options, "testDuration"_sr, 10.0); + g_simulator->usableRegions = 1; } std::string description() const override { return "KillRegionWorkload"; } @@ -57,9 +57,9 @@ struct KillRegionWorkload : TestWorkload { ACTOR static Future _setup(KillRegionWorkload* self, Database cx) { TraceEvent("ForceRecovery_DisablePrimaryBegin").log(); - wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator.disablePrimary, true))); + wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator->disablePrimary, true))); TraceEvent("ForceRecovery_WaitForRemote").log(); - wait(waitForPrimaryDC(cx, LiteralStringRef("1"))); + wait(waitForPrimaryDC(cx, "1"_sr)); TraceEvent("ForceRecovery_DisablePrimaryComplete").log(); return Void(); } @@ -75,33 +75,33 @@ struct KillRegionWorkload : TestWorkload { ASSERT(g_network->isSimulated()); if (deterministicRandom()->random01() < 0.5) { TraceEvent("ForceRecovery_DisableRemoteBegin").log(); - wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator.disableRemote, true))); + wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator->disableRemote, true))); TraceEvent("ForceRecovery_WaitForPrimary").log(); - wait(waitForPrimaryDC(cx, LiteralStringRef("0"))); + wait(waitForPrimaryDC(cx, "0"_sr)); TraceEvent("ForceRecovery_DisableRemoteComplete").log(); - wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator.originalRegions, true))); + wait(success(ManagementAPI::changeConfig(cx.getReference(), g_simulator->originalRegions, true))); } TraceEvent("ForceRecovery_Wait").log(); wait(delay(deterministicRandom()->random01() * self->testDuration)); // FIXME: killDataCenter breaks simulation if forceKill=false, since some processes can survive and // partially complete a recovery - g_simulator.killDataCenter(LiteralStringRef("0"), - deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly - : ISimulator::RebootAndDelete, - true); - g_simulator.killDataCenter(LiteralStringRef("2"), - deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly - : ISimulator::RebootAndDelete, - true); - g_simulator.killDataCenter(LiteralStringRef("4"), - deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly - : ISimulator::RebootAndDelete, - true); + g_simulator->killDataCenter("0"_sr, + deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly + : ISimulator::RebootAndDelete, + true); + g_simulator->killDataCenter("2"_sr, + deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly + : ISimulator::RebootAndDelete, + true); + g_simulator->killDataCenter("4"_sr, + deterministicRandom()->random01() < 0.5 ? ISimulator::KillInstantly + : ISimulator::RebootAndDelete, + true); TraceEvent("ForceRecovery_Begin").log(); - wait(forceRecovery(cx->getConnectionRecord(), LiteralStringRef("1"))); + wait(forceRecovery(cx->getConnectionRecord(), "1"_sr)); TraceEvent("ForceRecovery_UsableRegions").log(); @@ -116,7 +116,7 @@ struct KillRegionWorkload : TestWorkload { loop { // only needed if force recovery was unnecessary and we killed the secondary wait(success(ManagementAPI::changeConfig( - cx.getReference(), g_simulator.disablePrimary + " repopulate_anti_quorum=1", true))); + cx.getReference(), g_simulator->disablePrimary + " repopulate_anti_quorum=1", true))); choose { when(wait(waitForStorageRecovered(self))) { break; } when(wait(delay(300.0))) {} diff --git a/fdbserver/workloads/LocalRatekeeper.actor.cpp b/fdbserver/workloads/LocalRatekeeper.actor.cpp index 67d84d309f..ef54b1e116 100644 --- a/fdbserver/workloads/LocalRatekeeper.actor.cpp +++ b/fdbserver/workloads/LocalRatekeeper.actor.cpp @@ -51,10 +51,9 @@ struct LocalRatekeeperWorkload : TestWorkload { bool testFailed = false; LocalRatekeeperWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - startAfter = getOption(options, LiteralStringRef("startAfter"), startAfter); - blockWritesFor = getOption(options, - LiteralStringRef("blockWritesFor"), - double(SERVER_KNOBS->STORAGE_DURABILITY_LAG_HARD_MAX) / double(1e6)); + startAfter = getOption(options, "startAfter"_sr, startAfter); + blockWritesFor = getOption( + options, "blockWritesFor"_sr, double(SERVER_KNOBS->STORAGE_DURABILITY_LAG_HARD_MAX) / double(1e6)); } std::string description() const override { return "LocalRatekeeperWorkload"; } @@ -99,7 +98,7 @@ struct LocalRatekeeperWorkload : TestWorkload { GetValueRequest req; req.version = readVersion; // we don't care about the value - req.key = LiteralStringRef("/lkfs"); + req.key = "/lkfs"_sr; requests.emplace_back(brokenPromiseToNever(ssi.getValue.getReply(req))); } wait(waitForAllReady(requests)); @@ -128,7 +127,7 @@ struct LocalRatekeeperWorkload : TestWorkload { ACTOR static Future _start(LocalRatekeeperWorkload* self, Database cx) { wait(delay(self->startAfter)); state StorageServerInterface ssi = wait(getRandomStorage(cx)); - g_simulator.disableFor(format("%s/updateStorage", ssi.id().toString().c_str()), now() + self->blockWritesFor); + g_simulator->disableFor(format("%s/updateStorage", ssi.id().toString().c_str()), now() + self->blockWritesFor); state Future done = delay(self->blockWritesFor); // not much will happen until the storage goes over the soft limit wait(delay(double(SERVER_KNOBS->STORAGE_DURABILITY_LAG_SOFT_MAX / 1e6))); diff --git a/fdbserver/workloads/LockDatabase.actor.cpp b/fdbserver/workloads/LockDatabase.actor.cpp index 535f0c17cb..81f46f8c32 100644 --- a/fdbserver/workloads/LockDatabase.actor.cpp +++ b/fdbserver/workloads/LockDatabase.actor.cpp @@ -31,9 +31,9 @@ struct LockDatabaseWorkload : TestWorkload { bool onlyCheckLocked; LockDatabaseWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), ok(true) { - lockAfter = getOption(options, LiteralStringRef("lockAfter"), 0.0); - unlockAfter = getOption(options, LiteralStringRef("unlockAfter"), 10.0); - onlyCheckLocked = getOption(options, LiteralStringRef("onlyCheckLocked"), false); + lockAfter = getOption(options, "lockAfter"_sr, 0.0); + unlockAfter = getOption(options, "unlockAfter"_sr, 10.0); + onlyCheckLocked = getOption(options, "onlyCheckLocked"_sr, false); ASSERT(unlockAfter > lockAfter); } diff --git a/fdbserver/workloads/LockDatabaseFrequently.actor.cpp b/fdbserver/workloads/LockDatabaseFrequently.actor.cpp index bfab394031..3e4d6a6c04 100644 --- a/fdbserver/workloads/LockDatabaseFrequently.actor.cpp +++ b/fdbserver/workloads/LockDatabaseFrequently.actor.cpp @@ -30,8 +30,8 @@ struct LockDatabaseFrequentlyWorkload : TestWorkload { PerfIntCounter lockCount{ "LockCount" }; LockDatabaseFrequentlyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - delayBetweenLocks = getOption(options, LiteralStringRef("delayBetweenLocks"), 0.1); - testDuration = getOption(options, LiteralStringRef("testDuration"), 60); + delayBetweenLocks = getOption(options, "delayBetweenLocks"_sr, 0.1); + testDuration = getOption(options, "testDuration"_sr, 60); } std::string description() const override { return "LockDatabaseFrequently"; } diff --git a/fdbserver/workloads/LogMetrics.actor.cpp b/fdbserver/workloads/LogMetrics.actor.cpp index 1ebb9c8e98..5950af5af6 100644 --- a/fdbserver/workloads/LogMetrics.actor.cpp +++ b/fdbserver/workloads/LogMetrics.actor.cpp @@ -35,10 +35,10 @@ struct LogMetricsWorkload : TestWorkload { double logAt, logDuration, logsPerSecond; LogMetricsWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - logAt = getOption(options, LiteralStringRef("logAt"), 0.0); - logDuration = getOption(options, LiteralStringRef("logDuration"), 30.0); - logsPerSecond = getOption(options, LiteralStringRef("logsPerSecond"), 20); - dataFolder = getOption(options, LiteralStringRef("dataFolder"), LiteralStringRef("")).toString(); + logAt = getOption(options, "logAt"_sr, 0.0); + logDuration = getOption(options, "logDuration"_sr, 30.0); + logsPerSecond = getOption(options, "logsPerSecond"_sr, 20); + dataFolder = getOption(options, "dataFolder"_sr, ""_sr).toString(); } std::string description() const override { return "LogMetricsWorkload"; } diff --git a/fdbserver/workloads/LowLatency.actor.cpp b/fdbserver/workloads/LowLatency.actor.cpp index dc7dacc7b5..46bd365c95 100644 --- a/fdbserver/workloads/LowLatency.actor.cpp +++ b/fdbserver/workloads/LowLatency.actor.cpp @@ -39,12 +39,12 @@ struct LowLatencyWorkload : TestWorkload { LowLatencyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operations("Operations"), retries("Retries"), ok(true) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - maxGRVLatency = getOption(options, LiteralStringRef("maxGRVLatency"), 20.0); - maxCommitLatency = getOption(options, LiteralStringRef("maxCommitLatency"), 30.0); - checkDelay = getOption(options, LiteralStringRef("checkDelay"), 1.0); - testWrites = getOption(options, LiteralStringRef("testWrites"), true); - testKey = getOption(options, LiteralStringRef("testKey"), LiteralStringRef("testKey")); + testDuration = getOption(options, "testDuration"_sr, 600.0); + maxGRVLatency = getOption(options, "maxGRVLatency"_sr, 20.0); + maxCommitLatency = getOption(options, "maxCommitLatency"_sr, 30.0); + checkDelay = getOption(options, "checkDelay"_sr, 1.0); + testWrites = getOption(options, "testWrites"_sr, true); + testKey = getOption(options, "testKey"_sr, "testKey"_sr); } std::string description() const override { return "LowLatency"; } @@ -81,7 +81,7 @@ struct LowLatencyWorkload : TestWorkload { tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); tr.setOption(FDBTransactionOptions::LOCK_AWARE); if (doCommit) { - tr.set(self->testKey, LiteralStringRef("")); + tr.set(self->testKey, ""_sr); wait(tr.commit()); } else { wait(success(tr.getReadVersion())); diff --git a/fdbserver/workloads/MachineAttrition.actor.cpp b/fdbserver/workloads/MachineAttrition.actor.cpp index fb272fcf07..fb6679feb3 100644 --- a/fdbserver/workloads/MachineAttrition.actor.cpp +++ b/fdbserver/workloads/MachineAttrition.actor.cpp @@ -27,6 +27,7 @@ #include "fdbrpc/simulator.h" #include "fdbclient/ManagementAPI.actor.h" #include "flow/FaultInjection.h" +#include "flow/DeterministicRandom.h" #include "flow/actorcompiler.h" // This must be the last #include. static std::set const& normalAttritionErrors() { @@ -60,55 +61,102 @@ ACTOR Future ignoreSSFailuresForDuration(Database cx, double duration) { } } -struct MachineAttritionWorkload : TestWorkload { +struct MachineAttritionWorkload : FailureInjectionWorkload { bool enabled; - int machinesToKill, machinesToLeave, workersToKill, workersToLeave; - double testDuration, suspendDuration, liveDuration; - bool reboot; - bool killDc; - bool killMachine; - bool killDatahall; - bool killProcess; - bool killZone; - bool killSelf; + int machinesToKill = 2, machinesToLeave = 1, workersToKill = 2, workersToLeave = 1; + double testDuration = 10.0, suspendDuration = 1.0, liveDuration = 5.0; + bool iterate = false; + bool reboot = false; + bool killDc = false; + bool killMachine = false; + bool killDatahall = false; + bool killProcess = false; + bool killZone = false; + bool killSelf = false; std::vector targetIds; - bool replacement; - bool waitForVersion; - bool allowFaultInjection; - Future ignoreSSFailures; + bool replacement = false; + bool waitForVersion = false; + bool allowFaultInjection = true; + Future ignoreSSFailures = true; + double maxRunDuration = 60.0, backoff = 1.5; // This is set in setup from the list of workers when the cluster is started std::vector machines; - MachineAttritionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { + MachineAttritionWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) { + enabled = !clientId && g_network->isSimulated() && faultInjectionActivated; + suspendDuration = 10.0; + iterate = true; + } + + MachineAttritionWorkload(WorkloadContext const& wcx) : FailureInjectionWorkload(wcx) { // only do this on the "first" client, and only when in simulation and only when fault injection is enabled enabled = !clientId && g_network->isSimulated() && faultInjectionActivated; - machinesToKill = getOption(options, LiteralStringRef("machinesToKill"), 2); - machinesToLeave = getOption(options, LiteralStringRef("machinesToLeave"), 1); - workersToKill = getOption(options, LiteralStringRef("workersToKill"), 2); - workersToLeave = getOption(options, LiteralStringRef("workersToLeave"), 1); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - suspendDuration = getOption(options, LiteralStringRef("suspendDuration"), 1.0); - liveDuration = getOption(options, LiteralStringRef("liveDuration"), 5.0); - reboot = getOption(options, LiteralStringRef("reboot"), false); - killDc = getOption( - options, LiteralStringRef("killDc"), g_network->isSimulated() && deterministicRandom()->random01() < 0.25); - killMachine = getOption(options, LiteralStringRef("killMachine"), false); - killDatahall = getOption(options, LiteralStringRef("killDatahall"), false); - killProcess = getOption(options, LiteralStringRef("killProcess"), false); - killZone = getOption(options, LiteralStringRef("killZone"), false); - killSelf = getOption(options, LiteralStringRef("killSelf"), false); - targetIds = getOption(options, LiteralStringRef("targetIds"), std::vector()); - replacement = - getOption(options, LiteralStringRef("replacement"), reboot && deterministicRandom()->random01() < 0.5); - waitForVersion = getOption(options, LiteralStringRef("waitForVersion"), false); - allowFaultInjection = getOption(options, LiteralStringRef("allowFaultInjection"), true); - ignoreSSFailures = true; + machinesToKill = getOption(options, "machinesToKill"_sr, machinesToKill); + machinesToLeave = getOption(options, "machinesToLeave"_sr, machinesToLeave); + workersToKill = getOption(options, "workersToKill"_sr, workersToKill); + workersToLeave = getOption(options, "workersToLeave"_sr, workersToLeave); + testDuration = getOption(options, "testDuration"_sr, testDuration); + suspendDuration = getOption(options, "suspendDuration"_sr, suspendDuration); + liveDuration = getOption(options, "liveDuration"_sr, liveDuration); + reboot = getOption(options, "reboot"_sr, reboot); + killDc = getOption(options, "killDc"_sr, g_network->isSimulated() && deterministicRandom()->random01() < 0.25); + killMachine = getOption(options, "killMachine"_sr, killMachine); + killDatahall = getOption(options, "killDatahall"_sr, killDatahall); + killProcess = getOption(options, "killProcess"_sr, killProcess); + killZone = getOption(options, "killZone"_sr, killZone); + killSelf = getOption(options, "killSelf"_sr, killSelf); + targetIds = getOption(options, "targetIds"_sr, std::vector()); + replacement = getOption(options, "replacement"_sr, reboot && deterministicRandom()->random01() < 0.5); + waitForVersion = getOption(options, "waitForVersion"_sr, waitForVersion); + allowFaultInjection = getOption(options, "allowFaultInjection"_sr, allowFaultInjection); + } + + bool add(DeterministicRandom& random, WorkloadRequest const& work, CompoundWorkload const& workload) override { + auto desc = this->description(); + unsigned alreadyAdded = std::count_if(workload.workloads.begin(), + workload.workloads.end(), + [&desc](auto const& w) { return w->description() == desc; }); + alreadyAdded += std::count_if(workload.failureInjection.begin(), + workload.failureInjection.end(), + [&desc](auto const& w) { return w->description() == desc; }); + auto res = work.useDatabase && random.random01() < 1.0 / (2.0 + alreadyAdded); + if (res) { + initializeForInjection(random); + } + TraceEvent("AddingFailureInjection") + .detail("Reboot", reboot) + .detail("Replacement", replacement) + .detail("AllowFaultInjection", allowFaultInjection) + .detail("KillDC", killDc) + .detail("KillDataHall", killDatahall) + .detail("KillZone", killZone); + return res; + } + + void initializeForInjection(DeterministicRandom& random) { + reboot = random.random01() < 0.25; + replacement = random.random01() < 0.25; + allowFaultInjection = random.random01() < 0.5; + suspendDuration = 10.0 * random.random01(); + if (g_network->isSimulated()) { + std::set> dataCenters; + std::set> dataHalls; + std::set> zones; + for (auto process : g_simulator->getAllProcesses()) { + dataCenters.emplace(process->locality.dcId().castTo()); + dataHalls.emplace(process->locality.dataHallId().castTo()); + zones.emplace(process->locality.zoneId().castTo()); + } + killDc = dataCenters.size() > 0 && random.random01() > (dataHalls.size() < 0 ? 0.1 : 0.25); + killDatahall = dataHalls.size() > 0 && killDc && random.random01() < 0.5; + killZone = zones.size() > 0 && random.random01() < 0.2; + } } static std::vector getServers() { std::vector machines; - std::vector all = g_simulator.getAllProcesses(); + std::vector all = g_simulator->getAllProcesses(); for (int i = 0; i < all.size(); i++) if (!all[i]->failed && all[i]->name == std::string("Server") && all[i]->startingClass != ProcessClass::TesterClass) @@ -198,7 +246,8 @@ struct MachineAttritionWorkload : TestWorkload { } deterministicRandom()->randomShuffle(workers); wait(delay(self->liveDuration)); - // if a specific kill is requested, it must be accompanied by a set of target IDs otherwise no kills will occur + // if a specific kill is requested, it must be accompanied by a set of target IDs otherwise no kills will + // occur if (self->killDc) { TraceEvent("Assassination").detail("TargetDataCenterIds", describe(self->targetIds)); sendRebootRequests(workers, @@ -275,140 +324,150 @@ struct MachineAttritionWorkload : TestWorkload { ACTOR static Future machineKillWorker(MachineAttritionWorkload* self, double meanDelay, Database cx) { ASSERT(g_network->isSimulated()); state double delayBeforeKill; + state double suspendDuration = self->suspendDuration; + state double startTime = now(); - if (self->killDc) { - delayBeforeKill = deterministicRandom()->random01() * meanDelay; - wait(delay(delayBeforeKill)); - - // decide on a machine to kill - ASSERT(self->machines.size()); - Optional> target = self->machines.back().dcId(); - - ISimulator::KillType kt = ISimulator::Reboot; - if (!self->reboot) { - int killType = deterministicRandom()->randomInt(0, 3); // FIXME: enable disk stalls - if (killType == 0) - kt = ISimulator::KillInstantly; - else if (killType == 1) - kt = ISimulator::InjectFaults; - else if (killType == 2) - kt = ISimulator::RebootAndDelete; - else - kt = ISimulator::FailDisk; - } - TraceEvent("Assassination") - .detail("TargetDatacenter", target) - .detail("Reboot", self->reboot) - .detail("KillType", kt); - - g_simulator.killDataCenter(target, kt); - } else if (self->killDatahall) { - delayBeforeKill = deterministicRandom()->random01() * meanDelay; - wait(delay(delayBeforeKill)); - - // It only makes sense to kill a single data hall. - ASSERT(self->targetIds.size() == 1); - auto target = self->targetIds.front(); - - auto kt = ISimulator::KillInstantly; - TraceEvent("Assassination").detail("TargetDataHall", target).detail("KillType", kt); - - g_simulator.killDataHall(target, kt); - } else { - state int killedMachines = 0; - while (killedMachines < self->machinesToKill && self->machines.size() > self->machinesToLeave) { - TraceEvent("WorkerKillBegin") - .detail("KilledMachines", killedMachines) - .detail("MachinesToKill", self->machinesToKill) - .detail("MachinesToLeave", self->machinesToLeave) - .detail("Machines", self->machines.size()); - CODE_PROBE(true, "Killing a machine"); - + loop { + if (self->killDc) { delayBeforeKill = deterministicRandom()->random01() * meanDelay; wait(delay(delayBeforeKill)); - TraceEvent("WorkerKillAfterDelay").log(); - - if (self->waitForVersion) { - state Transaction tr(cx); - loop { - try { - tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); - tr.setOption(FDBTransactionOptions::LOCK_AWARE); - wait(success(tr.getReadVersion())); - break; - } catch (Error& e) { - wait(tr.onError(e)); - } - } - } // decide on a machine to kill - state LocalityData targetMachine = self->machines.back(); - if (BUGGIFY_WITH_PROB(0.01)) { - CODE_PROBE(true, "Marked a zone for maintenance before killing it"); - wait(success( - setHealthyZone(cx, targetMachine.zoneId().get(), deterministicRandom()->random01() * 20))); - } else if (BUGGIFY_WITH_PROB(0.005)) { - CODE_PROBE(true, "Disable DD for all storage server failures"); - self->ignoreSSFailures = - uncancellable(ignoreSSFailuresForDuration(cx, deterministicRandom()->random01() * 5)); - } + ASSERT(self->machines.size()); + Optional> target = self->machines.back().dcId(); + ISimulator::KillType kt = ISimulator::Reboot; + if (!self->reboot) { + int killType = deterministicRandom()->randomInt(0, 3); // FIXME: enable disk stalls + if (killType == 0) + kt = ISimulator::KillInstantly; + else if (killType == 1) + kt = ISimulator::InjectFaults; + else if (killType == 2) + kt = ISimulator::RebootAndDelete; + else + kt = ISimulator::FailDisk; + } TraceEvent("Assassination") - .detail("TargetMachine", targetMachine.toString()) - .detail("ZoneId", targetMachine.zoneId()) + .detail("TargetDatacenter", target) .detail("Reboot", self->reboot) - .detail("KilledMachines", killedMachines) - .detail("MachinesToKill", self->machinesToKill) - .detail("MachinesToLeave", self->machinesToLeave) - .detail("Machines", self->machines.size()) - .detail("Replace", self->replacement); - - if (self->reboot) { - if (deterministicRandom()->random01() > 0.5) { - g_simulator.rebootProcess(targetMachine.zoneId(), deterministicRandom()->random01() > 0.5); - } else { - g_simulator.killZone(targetMachine.zoneId(), ISimulator::Reboot); - } - } else { - auto randomDouble = deterministicRandom()->random01(); - TraceEvent("WorkerKill") - .detail("MachineCount", self->machines.size()) - .detail("RandomValue", randomDouble); - if (randomDouble < 0.33) { - TraceEvent("RebootAndDelete").detail("TargetMachine", targetMachine.toString()); - g_simulator.killZone(targetMachine.zoneId(), ISimulator::RebootAndDelete); - } else { - auto kt = ISimulator::KillInstantly; - if (self->allowFaultInjection) { - if (randomDouble < 0.50) { - kt = ISimulator::InjectFaults; - } - // FIXME: enable disk stalls - /* - if( randomDouble < 0.56 ) { - kt = ISimulator::InjectFaults; - } else if( randomDouble < 0.66 ) { - kt = ISimulator::FailDisk; - } - */ - } - g_simulator.killZone(targetMachine.zoneId(), kt); - } - } - - killedMachines++; - if (self->replacement) { - // Replace by reshuffling, since we always pick from the back. - deterministicRandom()->randomShuffle(self->machines); - } else { - self->machines.pop_back(); - } - - wait(delay(meanDelay - delayBeforeKill) && success(self->ignoreSSFailures)); + .detail("KillType", kt); + g_simulator->killDataCenter(target, kt); + } else if (self->killDatahall) { delayBeforeKill = deterministicRandom()->random01() * meanDelay; - TraceEvent("WorkerKillAfterMeanDelay").detail("DelayBeforeKill", delayBeforeKill); + wait(delay(delayBeforeKill)); + + // It only makes sense to kill a single data hall. + ASSERT(self->targetIds.size() == 1); + auto target = self->targetIds.front(); + + auto kt = ISimulator::KillInstantly; + TraceEvent("Assassination").detail("TargetDataHall", target).detail("KillType", kt); + + g_simulator->killDataHall(target, kt); + } else { + state int killedMachines = 0; + while (killedMachines < self->machinesToKill && self->machines.size() > self->machinesToLeave) { + TraceEvent("WorkerKillBegin") + .detail("KilledMachines", killedMachines) + .detail("MachinesToKill", self->machinesToKill) + .detail("MachinesToLeave", self->machinesToLeave) + .detail("Machines", self->machines.size()); + CODE_PROBE(true, "Killing a machine"); + + delayBeforeKill = deterministicRandom()->random01() * meanDelay; + wait(delay(delayBeforeKill)); + TraceEvent("WorkerKillAfterDelay").log(); + + if (self->waitForVersion) { + state Transaction tr(cx); + loop { + try { + tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + wait(success(tr.getReadVersion())); + break; + } catch (Error& e) { + wait(tr.onError(e)); + } + } + } + + // decide on a machine to kill + state LocalityData targetMachine = self->machines.back(); + if (BUGGIFY_WITH_PROB(0.01)) { + CODE_PROBE(true, "Marked a zone for maintenance before killing it"); + wait(success( + setHealthyZone(cx, targetMachine.zoneId().get(), deterministicRandom()->random01() * 20))); + } else if (BUGGIFY_WITH_PROB(0.005)) { + CODE_PROBE(true, "Disable DD for all storage server failures"); + self->ignoreSSFailures = + uncancellable(ignoreSSFailuresForDuration(cx, deterministicRandom()->random01() * 5)); + } + + TraceEvent("Assassination") + .detail("TargetMachine", targetMachine.toString()) + .detail("ZoneId", targetMachine.zoneId()) + .detail("Reboot", self->reboot) + .detail("KilledMachines", killedMachines) + .detail("MachinesToKill", self->machinesToKill) + .detail("MachinesToLeave", self->machinesToLeave) + .detail("Machines", self->machines.size()) + .detail("Replace", self->replacement); + + if (self->reboot) { + if (deterministicRandom()->random01() > 0.5) { + g_simulator->rebootProcess(targetMachine.zoneId(), deterministicRandom()->random01() > 0.5); + } else { + g_simulator->killZone(targetMachine.zoneId(), ISimulator::Reboot); + } + } else { + auto randomDouble = deterministicRandom()->random01(); + TraceEvent("WorkerKill") + .detail("MachineCount", self->machines.size()) + .detail("RandomValue", randomDouble); + if (randomDouble < 0.33) { + TraceEvent("RebootAndDelete").detail("TargetMachine", targetMachine.toString()); + g_simulator->killZone(targetMachine.zoneId(), ISimulator::RebootAndDelete); + } else { + auto kt = ISimulator::KillInstantly; + if (self->allowFaultInjection) { + if (randomDouble < 0.50) { + kt = ISimulator::InjectFaults; + } + // FIXME: enable disk stalls + /* + if( randomDouble < 0.56 ) { + kt = ISimulator::InjectFaults; + } else if( randomDouble < 0.66 ) { + kt = ISimulator::FailDisk; + } + */ + } + g_simulator->killZone(targetMachine.zoneId(), kt); + } + } + + killedMachines++; + if (self->replacement) { + // Replace by reshuffling, since we always pick from the back. + deterministicRandom()->randomShuffle(self->machines); + } else { + self->machines.pop_back(); + } + + wait(delay(meanDelay - delayBeforeKill) && success(self->ignoreSSFailures)); + + delayBeforeKill = deterministicRandom()->random01() * meanDelay; + TraceEvent("WorkerKillAfterMeanDelay").detail("DelayBeforeKill", delayBeforeKill); + } + } + if (!self->iterate || now() - startTime > self->maxRunDuration) { + break; + } else { + wait(delay(suspendDuration)); + suspendDuration *= self->backoff; } } @@ -419,3 +478,4 @@ struct MachineAttritionWorkload : TestWorkload { }; WorkloadFactory MachineAttritionWorkloadFactory("Attrition"); +FailureInjectorFactory MachineAttritionFailureWorkloadFactory; diff --git a/fdbserver/workloads/Mako.actor.cpp b/fdbserver/workloads/Mako.actor.cpp index d6597674c4..140d7fda45 100644 --- a/fdbserver/workloads/Mako.actor.cpp +++ b/fdbserver/workloads/Mako.actor.cpp @@ -76,39 +76,39 @@ struct MakoWorkload : TestWorkload { commits("Commits"), totalOps("Operations") { // init parameters from test file // Number of rows populated - rowCount = getOption(options, LiteralStringRef("rows"), (uint64_t)10000); + rowCount = getOption(options, "rows"_sr, (uint64_t)10000); // Test duration in seconds - testDuration = getOption(options, LiteralStringRef("testDuration"), 30.0); - warmingDelay = getOption(options, LiteralStringRef("warmingDelay"), 0.0); - maxInsertRate = getOption(options, LiteralStringRef("maxInsertRate"), 1e12); + testDuration = getOption(options, "testDuration"_sr, 30.0); + warmingDelay = getOption(options, "warmingDelay"_sr, 0.0); + maxInsertRate = getOption(options, "maxInsertRate"_sr, 1e12); // Flag to control whether to populate data into database - populateData = getOption(options, LiteralStringRef("populateData"), true); + populateData = getOption(options, "populateData"_sr, true); // Flag to control whether to run benchmark - runBenchmark = getOption(options, LiteralStringRef("runBenchmark"), true); + runBenchmark = getOption(options, "runBenchmark"_sr, true); // Flag to control whether to clean data in the database - preserveData = getOption(options, LiteralStringRef("preserveData"), true); + preserveData = getOption(options, "preserveData"_sr, true); // If true, force commit for read-only transactions - commitGet = getOption(options, LiteralStringRef("commitGet"), false); + commitGet = getOption(options, "commitGet"_sr, false); // If true, log latency for set, clear and clearrange - latencyForLocalOperation = getOption(options, LiteralStringRef("latencyForLocalOperation"), false); + latencyForLocalOperation = getOption(options, "latencyForLocalOperation"_sr, false); // Target total transaction-per-second (TPS) of all clients - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 100000.0) / clientCount; - actorCountPerClient = getOption(options, LiteralStringRef("actorCountPerClient"), 16); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 100000.0) / clientCount; + actorCountPerClient = getOption(options, "actorCountPerClient"_sr, 16); // Sampling rate (1 sample / ops) for latency stats - sampleSize = getOption(options, LiteralStringRef("sampleSize"), rowCount / 100); + sampleSize = getOption(options, "sampleSize"_sr, rowCount / 100); // If true, record latency metrics per periodicLoggingInterval; For details, see tracePeriodically() - enableLogging = getOption(options, LiteralStringRef("enableLogging"), false); - periodicLoggingInterval = getOption(options, LiteralStringRef("periodicLoggingInterval"), 5.0); + enableLogging = getOption(options, "enableLogging"_sr, false); + periodicLoggingInterval = getOption(options, "periodicLoggingInterval"_sr, 5.0); // All the generated keys will start with the specified prefix - keyPrefix = getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("mako")).toString(); + keyPrefix = getOption(options, "keyPrefix"_sr, "mako"_sr).toString(); KEYPREFIXLEN = keyPrefix.size(); // If true, the workload will picking up keys which are zipfian distributed - zipf = getOption(options, LiteralStringRef("zipf"), false); - zipfConstant = getOption(options, LiteralStringRef("zipfConstant"), 0.99); + zipf = getOption(options, "zipf"_sr, false); + zipfConstant = getOption(options, "zipfConstant"_sr, 0.99); // Specified length of keys and length range of values - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); - maxValueBytes = getOption(options, LiteralStringRef("valueBytes"), 16); - minValueBytes = getOption(options, LiteralStringRef("minValueBytes"), maxValueBytes); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); + maxValueBytes = getOption(options, "valueBytes"_sr, 16); + minValueBytes = getOption(options, "minValueBytes"_sr, maxValueBytes); ASSERT(minValueBytes <= maxValueBytes); // The inserted key is formatted as: fixed prefix('mako') + sequential number + padding('x') // assume we want to insert 10000 rows with keyBytes set to 16, @@ -135,8 +135,7 @@ struct MakoWorkload : TestWorkload { // scr – SET & CLEAR RANGE // grv – GetReadVersion() // Every transaction is committed unless it contains only GET / GET RANGE operations. - operationsSpec = - getOption(options, LiteralStringRef("operations"), LiteralStringRef("g100")).contents().toString(); + operationsSpec = getOption(options, "operations"_sr, "g100"_sr).contents().toString(); // parse the sequence and extract operations to be executed parseOperationsSpec(); for (int i = 0; i < MAX_OP; ++i) { @@ -149,11 +148,11 @@ struct MakoWorkload : TestWorkload { zipfian_generator3(0, (int)rowCount - 1, zipfConstant); } // Added for checksum verification - csSize = getOption(options, LiteralStringRef("csSize"), rowCount / 100); + csSize = getOption(options, "csSize"_sr, rowCount / 100); ASSERT(csSize <= rowCount); - csCount = getOption(options, LiteralStringRef("csCount"), 0); + csCount = getOption(options, "csCount"_sr, 0); checksumVerification = (csCount != 0); - doChecksumVerificationOnly = getOption(options, LiteralStringRef("doChecksumVerificationOnly"), false); + doChecksumVerificationOnly = getOption(options, "doChecksumVerificationOnly"_sr, false); if (doChecksumVerificationOnly) ASSERT(checksumVerification); // csCount should be non-zero when you do checksum verification only if (csCount) { diff --git a/fdbserver/workloads/MemoryKeyValueStore.cpp b/fdbserver/workloads/MemoryKeyValueStore.cpp index 8ac088f210..b59ec9ced7 100644 --- a/fdbserver/workloads/MemoryKeyValueStore.cpp +++ b/fdbserver/workloads/MemoryKeyValueStore.cpp @@ -126,12 +126,12 @@ uint64_t MemoryKeyValueStore::size() const { // The first key in the database; returned by key selectors that choose a key off the front Key MemoryKeyValueStore::startKey() const { - return LiteralStringRef(""); + return ""_sr; } // The last key in the database; returned by key selectors that choose a key off the back Key MemoryKeyValueStore::endKey() const { - return LiteralStringRef("\xff"); + return "\xff"_sr; } // Debugging function that prints all key-value pairs diff --git a/fdbserver/workloads/MemoryLifetime.actor.cpp b/fdbserver/workloads/MemoryLifetime.actor.cpp index 3b0ca269a5..ffa80a8dd6 100644 --- a/fdbserver/workloads/MemoryLifetime.actor.cpp +++ b/fdbserver/workloads/MemoryLifetime.actor.cpp @@ -34,7 +34,7 @@ struct MemoryLifetime : KVWorkload { std::string valueString; MemoryLifetime(WorkloadContext const& wcx) : KVWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); + testDuration = getOption(options, "testDuration"_sr, 60.0); valueString = std::string(maxValueBytes, '.'); } diff --git a/fdbserver/workloads/MetaclusterManagementWorkload.actor.cpp b/fdbserver/workloads/MetaclusterManagementWorkload.actor.cpp index 816c3a9d45..6779acf3c1 100644 --- a/fdbserver/workloads/MetaclusterManagementWorkload.actor.cpp +++ b/fdbserver/workloads/MetaclusterManagementWorkload.actor.cpp @@ -99,8 +99,8 @@ struct MetaclusterManagementWorkload : TestWorkload { MultiVersionApi::api->selectApiVersion(cx->apiVersion.version()); self->managementDb = MultiVersionDatabase::debugCreateFromExistingDatabase(threadSafeHandle); - ASSERT(g_simulator.extraDatabases.size() > 0); - for (auto connectionString : g_simulator.extraDatabases) { + ASSERT(g_simulator->extraDatabases.size() > 0); + for (auto connectionString : g_simulator->extraDatabases) { ClusterConnectionString ccs(connectionString); auto extraFile = makeReference(ccs); self->dataDbIndex.push_back(ClusterName(format("cluster_%08d", self->dataDbs.size()))); diff --git a/fdbserver/workloads/MetricLogging.actor.cpp b/fdbserver/workloads/MetricLogging.actor.cpp index 866ba522c7..92e0ccbae8 100644 --- a/fdbserver/workloads/MetricLogging.actor.cpp +++ b/fdbserver/workloads/MetricLogging.actor.cpp @@ -36,17 +36,17 @@ struct MetricLoggingWorkload : TestWorkload { std::vector int64Metrics; MetricLoggingWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), changes("Changes") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - actorCount = getOption(options, LiteralStringRef("actorCount"), 1); - metricCount = getOption(options, LiteralStringRef("metricCount"), 1); - testBool = getOption(options, LiteralStringRef("testBool"), true); - enabled = getOption(options, LiteralStringRef("enabled"), true); + testDuration = getOption(options, "testDuration"_sr, 10.0); + actorCount = getOption(options, "actorCount"_sr, 1); + metricCount = getOption(options, "metricCount"_sr, 1); + testBool = getOption(options, "testBool"_sr, true); + enabled = getOption(options, "enabled"_sr, true); for (int i = 0; i < metricCount; i++) { if (testBool) { - boolMetrics.push_back(BoolMetricHandle(LiteralStringRef("TestBool"), format("%d", i))); + boolMetrics.push_back(BoolMetricHandle("TestBool"_sr, format("%d", i))); } else { - int64Metrics.push_back(Int64MetricHandle(LiteralStringRef("TestInt"), format("%d", i))); + int64Metrics.push_back(Int64MetricHandle("TestInt"_sr, format("%d", i))); } } } diff --git a/fdbserver/workloads/MiniCycle.actor.cpp b/fdbserver/workloads/MiniCycle.actor.cpp index 5b9b48ab2c..f427dbaae5 100644 --- a/fdbserver/workloads/MiniCycle.actor.cpp +++ b/fdbserver/workloads/MiniCycle.actor.cpp @@ -47,7 +47,7 @@ struct MiniCycleWorkload : TestWorkload { transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5); nodeCount = getOption(options, "nodeCount"_sr, transactionsPerSecond * clientCount); - keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, LiteralStringRef("")).toString()); + keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, ""_sr).toString()); traceParentProbability = getOption(options, "traceParentProbability "_sr, 0.01); minExpectedTransactionsPerSecond = transactionsPerSecond * getOption(options, "expectedRate"_sr, 0.7); } diff --git a/fdbserver/workloads/Performance.actor.cpp b/fdbserver/workloads/Performance.actor.cpp index 0cfc16497a..0119d3bae4 100644 --- a/fdbserver/workloads/Performance.actor.cpp +++ b/fdbserver/workloads/Performance.actor.cpp @@ -34,7 +34,7 @@ struct PerformanceWorkload : TestWorkload { PerfMetric maxAchievedTPS; PerformanceWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - probeWorkload = getOption(options, LiteralStringRef("probeWorkload"), LiteralStringRef("ReadWrite")); + probeWorkload = getOption(options, "probeWorkload"_sr, "ReadWrite"_sr); // "Consume" all options and save for later tests for (int i = 0; i < options.size(); i++) { @@ -44,7 +44,7 @@ struct PerformanceWorkload : TestWorkload { i, printable(options[i].key).c_str(), printable(options[i].value).c_str()); - options[i].value = LiteralStringRef(""); + options[i].value = ""_sr; } } printf("saved %d options\n", savedOptions.size()); @@ -78,10 +78,9 @@ struct PerformanceWorkload : TestWorkload { Standalone>> getOpts(double transactionsPerSecond) { Standalone> options; Standalone>> opts; - options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("testName"), probeWorkload)); - options.push_back_deep( - options.arena(), - KeyValueRef(LiteralStringRef("transactionsPerSecond"), format("%f", transactionsPerSecond))); + options.push_back_deep(options.arena(), KeyValueRef("testName"_sr, probeWorkload)); + options.push_back_deep(options.arena(), + KeyValueRef("transactionsPerSecond"_sr, format("%f", transactionsPerSecond))); for (int i = 0; i < savedOptions.size(); i++) { options.push_back_deep(options.arena(), savedOptions[i]); printf("option [%d]: '%s'='%s'\n", @@ -134,7 +133,7 @@ struct PerformanceWorkload : TestWorkload { std::vector testers = wait(self->getTesters(self)); self->testers = testers; - TestSpec spec(LiteralStringRef("PerformanceSetup"), false, false); + TestSpec spec("PerformanceSetup"_sr, false, false); spec.options = options; spec.phases = TestWorkload::SETUP; DistributedTestResults results = wait(runWorkload(cx, testers, spec, Optional())); @@ -169,7 +168,7 @@ struct PerformanceWorkload : TestWorkload { } state DistributedTestResults results; try { - TestSpec spec(LiteralStringRef("PerformanceRun"), false, false); + TestSpec spec("PerformanceRun"_sr, false, false); spec.phases = TestWorkload::EXECUTION | TestWorkload::METRICS; spec.options = options; DistributedTestResults r = wait(runWorkload(cx, self->testers, spec, Optional())); diff --git a/fdbserver/workloads/PhysicalShardMove.actor.cpp b/fdbserver/workloads/PhysicalShardMove.actor.cpp index 9fa605a167..80bdcddbee 100644 --- a/fdbserver/workloads/PhysicalShardMove.actor.cpp +++ b/fdbserver/workloads/PhysicalShardMove.actor.cpp @@ -328,17 +328,17 @@ struct PhysicalShardMoveWorkLoad : TestWorkload { TraceEvent("TestMoveShardStartMoveKeys").detail("DataMove", dataMoveId); wait(moveKeys(cx, - dataMoveId, - keys, - dests, - dests, - moveKeysLock, - Promise(), - &self->startMoveKeysParallelismLock, - &self->finishMoveKeysParallelismLock, - false, - deterministicRandom()->randomUniqueID(), // for logging only - &ddEnabledState)); + MoveKeysParams{ dataMoveId, + keys, + dests, + dests, + moveKeysLock, + Promise(), + &self->startMoveKeysParallelismLock, + &self->finishMoveKeysParallelismLock, + false, + deterministicRandom()->randomUniqueID(), // for logging only + &ddEnabledState })); break; } catch (Error& e) { if (e.code() == error_code_movekeys_conflict) { diff --git a/fdbserver/workloads/Ping.actor.cpp b/fdbserver/workloads/Ping.actor.cpp index f939d2525b..c8c67a275f 100644 --- a/fdbserver/workloads/Ping.actor.cpp +++ b/fdbserver/workloads/Ping.actor.cpp @@ -53,20 +53,20 @@ struct PingWorkload : TestWorkload { PingWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), messages("Messages"), totalMessageLatency("TotalLatency"), maxMessageLatency("Max Latency (ms)") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - operationsPerSecond = getOption(options, LiteralStringRef("operationsPerSecondPerClient"), 50.0); - usePayload = getOption(options, LiteralStringRef("usePayload"), false); - logging = getOption(options, LiteralStringRef("logging"), false); - pingWorkers = getOption(options, LiteralStringRef("pingWorkers"), false); - registerInterface = getOption(options, LiteralStringRef("registerInterface"), true); - broadcastTest = getOption(options, LiteralStringRef("broadcastTest"), false); - parallelBroadcast = getOption(options, LiteralStringRef("parallelBroadcast"), false); - workerBroadcast = getOption(options, LiteralStringRef("workerBroadcast"), false); - int payloadSize = getOption(options, LiteralStringRef("payloadSizeOut"), 1024); + testDuration = getOption(options, "testDuration"_sr, 10.0); + operationsPerSecond = getOption(options, "operationsPerSecondPerClient"_sr, 50.0); + usePayload = getOption(options, "usePayload"_sr, false); + logging = getOption(options, "logging"_sr, false); + pingWorkers = getOption(options, "pingWorkers"_sr, false); + registerInterface = getOption(options, "registerInterface"_sr, true); + broadcastTest = getOption(options, "broadcastTest"_sr, false); + parallelBroadcast = getOption(options, "parallelBroadcast"_sr, false); + workerBroadcast = getOption(options, "workerBroadcast"_sr, false); + int payloadSize = getOption(options, "payloadSizeOut"_sr, 1024); payloadOut = std::string(payloadSize, '.'); - payloadSize = getOption(options, LiteralStringRef("payloadSizeBack"), 1024); + payloadSize = getOption(options, "payloadSizeBack"_sr, 1024); payloadBack = std::string(payloadSize, '.'); - actorCount = getOption(options, LiteralStringRef("actorCount"), 1); + actorCount = getOption(options, "actorCount"_sr, 1); } std::string description() const override { return "PingWorkload"; } @@ -155,7 +155,7 @@ struct PingWorkload : TestWorkload { LoadedPingRequest req; req.id = deterministicRandom()->randomUniqueID(); - req.payload = self->usePayload ? self->payloadOut : LiteralStringRef(""); + req.payload = self->usePayload ? self->payloadOut : ""_sr; req.loadReply = self->usePayload; LoadedReply rep = wait(peer.getReply(req)); @@ -275,7 +275,7 @@ struct PingWorkload : TestWorkload { // LoadedReply rep; // rep.id = req.id; - // rep.payload = req.loadReply ? self->payloadBack : LiteralStringRef(""); + // rep.payload = req.loadReply ? self->payloadBack : ""_sr; // req.reply.send( rep ); // return Void(); @@ -296,7 +296,7 @@ struct PingWorkload : TestWorkload { LoadedReply rep; rep.id = req.id; - rep.payload = req.loadReply ? self->payloadBack : LiteralStringRef(""); + rep.payload = req.loadReply ? self->payloadBack : ""_sr; req.reply.send(rep); // addActor.send( self->packetPonger( self, req ) ); diff --git a/fdbserver/workloads/ProtocolVersion.actor.cpp b/fdbserver/workloads/ProtocolVersion.actor.cpp index dc027758e1..037b9be1be 100644 --- a/fdbserver/workloads/ProtocolVersion.actor.cpp +++ b/fdbserver/workloads/ProtocolVersion.actor.cpp @@ -29,7 +29,7 @@ struct ProtocolVersionWorkload : TestWorkload { Future start(Database const& cx) override { return _start(this, cx); } ACTOR Future _start(ProtocolVersionWorkload* self, Database cx) { - state std::vector allProcesses = g_pSimulator->getAllProcesses(); + state std::vector allProcesses = g_simulator->getAllProcesses(); state std::vector::iterator diffVersionProcess = find_if(allProcesses.begin(), allProcesses.end(), [](const ISimulator::ProcessInfo* p) { return p->protocolVersion != currentProtocolVersion(); diff --git a/fdbserver/workloads/PubSubMultiples.actor.cpp b/fdbserver/workloads/PubSubMultiples.actor.cpp index a3c27ae512..270a9af347 100644 --- a/fdbserver/workloads/PubSubMultiples.actor.cpp +++ b/fdbserver/workloads/PubSubMultiples.actor.cpp @@ -32,10 +32,10 @@ struct PubSubMultiplesWorkload : TestWorkload { PerfIntCounter messages; PubSubMultiplesWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), messages("Messages") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - messagesPerSecond = getOption(options, LiteralStringRef("messagesPerSecond"), 500.0) / clientCount; - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), 20); - inboxesPerActor = getOption(options, LiteralStringRef("inboxesPerActor"), 20); + testDuration = getOption(options, "testDuration"_sr, 10.0); + messagesPerSecond = getOption(options, "messagesPerSecond"_sr, 500.0) / clientCount; + actorCount = getOption(options, "actorsPerClient"_sr, 20); + inboxesPerActor = getOption(options, "inboxesPerActor"_sr, 20); } std::string description() const override { return "PubSubMultiplesWorkload"; } diff --git a/fdbserver/workloads/QueuePush.actor.cpp b/fdbserver/workloads/QueuePush.actor.cpp index bc9c192c6d..3eccbb5422 100644 --- a/fdbserver/workloads/QueuePush.actor.cpp +++ b/fdbserver/workloads/QueuePush.actor.cpp @@ -40,16 +40,16 @@ struct QueuePushWorkload : TestWorkload { QueuePushWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries"), commitLatencies(2000), GRVLatencies(2000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - actorCount = getOption(options, LiteralStringRef("actorCount"), 50); + testDuration = getOption(options, "testDuration"_sr, 10.0); + actorCount = getOption(options, "actorCount"_sr, 50); - valueBytes = getOption(options, LiteralStringRef("valueBytes"), 96); + valueBytes = getOption(options, "valueBytes"_sr, 96); valueString = std::string(valueBytes, 'x'); - forward = getOption(options, LiteralStringRef("forward"), true); + forward = getOption(options, "forward"_sr, true); - endingKey = LiteralStringRef("9999999900000001"); - startingKey = LiteralStringRef("0000000000000001"); + endingKey = "9999999900000001"_sr; + startingKey = "0000000000000001"_sr; } std::string description() const override { return "QueuePush"; } diff --git a/fdbserver/workloads/RYWDisable.actor.cpp b/fdbserver/workloads/RYWDisable.actor.cpp index 2586118cb6..b1b3ca7684 100644 --- a/fdbserver/workloads/RYWDisable.actor.cpp +++ b/fdbserver/workloads/RYWDisable.actor.cpp @@ -31,9 +31,9 @@ struct RYWDisableWorkload : TestWorkload { std::vector> clients; RYWDisableWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - nodes = getOption(options, LiteralStringRef("nodes"), 100); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); + testDuration = getOption(options, "testDuration"_sr, 600.0); + nodes = getOption(options, "nodes"_sr, 100); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); } std::string description() const override { return "RYWDisable"; } diff --git a/fdbserver/workloads/RYWPerformance.actor.cpp b/fdbserver/workloads/RYWPerformance.actor.cpp index 22f9cffb7c..584e07f84c 100644 --- a/fdbserver/workloads/RYWPerformance.actor.cpp +++ b/fdbserver/workloads/RYWPerformance.actor.cpp @@ -28,9 +28,9 @@ struct RYWPerformanceWorkload : TestWorkload { int keyBytes, nodes, ranges; RYWPerformanceWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - nodes = getOption(options, LiteralStringRef("nodes"), 10000); - ranges = getOption(options, LiteralStringRef("ranges"), 10); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); + nodes = getOption(options, "nodes"_sr, 10000); + ranges = getOption(options, "ranges"_sr, 10); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); } std::string description() const override { return "RYWPerformance"; } @@ -47,7 +47,7 @@ struct RYWPerformanceWorkload : TestWorkload { loop { try { for (int i = 0; i < self->nodes; i++) - tr.set(self->keyForIndex(i), LiteralStringRef("bar")); + tr.set(self->keyForIndex(i), "bar"_sr); wait(tr.commit()); break; @@ -69,7 +69,7 @@ struct RYWPerformanceWorkload : TestWorkload { state int i; if (type == 0) { for (i = 0; i < self->nodes; i++) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 1) { std::vector>> gets; @@ -84,7 +84,7 @@ struct RYWPerformanceWorkload : TestWorkload { } wait(waitForAll(gets)); for (i = 0; i < self->nodes; i++) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 3) { std::vector>> gets; @@ -93,19 +93,19 @@ struct RYWPerformanceWorkload : TestWorkload { } wait(waitForAll(gets)); for (i = 1; i < self->nodes; i += 2) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 4) { wait(success(tr->getRange(KeyRangeRef(self->keyForIndex(0), self->keyForIndex(self->nodes)), self->nodes))); } else if (type == 5) { wait(success(tr->getRange(KeyRangeRef(self->keyForIndex(0), self->keyForIndex(self->nodes)), self->nodes))); for (i = 0; i < self->nodes; i++) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 6) { wait(success(tr->getRange(KeyRangeRef(self->keyForIndex(0), self->keyForIndex(self->nodes)), self->nodes))); for (i = 0; i < self->nodes; i += 2) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 7) { wait(success(tr->getRange(KeyRangeRef(self->keyForIndex(0), self->keyForIndex(self->nodes)), self->nodes))); @@ -130,7 +130,7 @@ struct RYWPerformanceWorkload : TestWorkload { } wait(waitForAll(gets)); for (i = 0; i < self->nodes; i++) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 11) { std::vector> gets; @@ -139,7 +139,7 @@ struct RYWPerformanceWorkload : TestWorkload { } wait(waitForAll(gets)); for (i = 0; i < self->nodes; i += 2) { - tr->set(self->keyForIndex(i), LiteralStringRef("foo")); + tr->set(self->keyForIndex(i), "foo"_sr); } } else if (type == 12) { std::vector> gets; diff --git a/fdbserver/workloads/RandomClogging.actor.cpp b/fdbserver/workloads/RandomClogging.actor.cpp index 7afa89e93b..f842983234 100644 --- a/fdbserver/workloads/RandomClogging.actor.cpp +++ b/fdbserver/workloads/RandomClogging.actor.cpp @@ -18,55 +18,93 @@ * limitations under the License. */ +#include "flow/DeterministicRandom.h" +#include "fdbrpc/simulator.h" #include "fdbclient/NativeAPI.actor.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" -#include "fdbrpc/simulator.h" #include "flow/actorcompiler.h" // This must be the last #include. -struct RandomCloggingWorkload : TestWorkload { +struct RandomCloggingWorkload : FailureInjectionWorkload { bool enabled; - double testDuration; - double scale, clogginess; - int swizzleClog; + double testDuration = 10.0; + double scale = 1.0, clogginess = 1.0; + int swizzleClog = 0; + bool iterate = false; + double maxRunDuration = 60.0, backoff = 1.5, suspend = 10.0; - RandomCloggingWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { + RandomCloggingWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) {} + + RandomCloggingWorkload(WorkloadContext const& wcx) : FailureInjectionWorkload(wcx) { enabled = !clientId; // only do this on the "first" client - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - scale = getOption(options, LiteralStringRef("scale"), 1.0); - clogginess = getOption(options, LiteralStringRef("clogginess"), 1.0); - swizzleClog = getOption(options, LiteralStringRef("swizzle"), 0); + testDuration = getOption(options, "testDuration"_sr, testDuration); + scale = getOption(options, "scale"_sr, scale); + clogginess = getOption(options, "clogginess"_sr, clogginess); + swizzleClog = getOption(options, "swizzle"_sr, swizzleClog); + } + + bool add(DeterministicRandom& random, WorkloadRequest const& work, CompoundWorkload const& workload) override { + auto desc = description(); + unsigned alreadyAdded = std::count_if(workload.workloads.begin(), + workload.workloads.end(), + [&desc](auto const& w) { return w->description() == desc; }); + alreadyAdded += std::count_if(workload.failureInjection.begin(), + workload.failureInjection.end(), + [&desc](auto const& w) { return w->description() == desc; }); + bool willAdd = work.useDatabase && 0.25 / (1 + alreadyAdded) > random.random01(); + if (willAdd) { + enabled = this->clientId == 0; + scale = std::max(random.random01(), 0.1); + clogginess = std::max(random.random01(), 0.1); + swizzleClog = random.random01() < 0.3; + iterate = random.random01() < 0.5; + } + return willAdd; } std::string description() const override { - if (&g_simulator == g_network) + if (g_simulator == g_network) return "RandomClogging"; else return "NoRC"; } Future setup(Database const& cx) override { return Void(); } Future start(Database const& cx) override { - if (&g_simulator == g_network && enabled) - return timeout( - reportErrors(swizzleClog ? swizzleClogClient(this) : clogClient(this), "RandomCloggingError"), - testDuration, - Void()); - else - return Void(); + if (g_network->isSimulated() && enabled) { + return _start(this); + } + return Void(); } Future check(Database const& cx) override { return true; } void getMetrics(std::vector& m) override {} + ACTOR static Future _start(RandomCloggingWorkload* self) { + state Future done = delay(self->maxRunDuration); + loop { + wait(done || + timeout(reportErrors(self->swizzleClog ? self->swizzleClogClient(self) : self->clogClient(self), + "RandomCloggingError"), + self->testDuration, + Void())); + if (!done.isReady() && self->iterate) { + wait(delay(self->suspend)); + self->suspend *= self->backoff; + } else { + return Void(); + } + } + } + ACTOR void doClog(ISimulator::ProcessInfo* machine, double t, double delay = 0.0) { wait(::delay(delay)); - g_simulator.clogInterface(machine->address.ip, t); + g_simulator->clogInterface(machine->address.ip, t); } void clogRandomPair(double t) { - auto m1 = deterministicRandom()->randomChoice(g_simulator.getAllProcesses()); - auto m2 = deterministicRandom()->randomChoice(g_simulator.getAllProcesses()); + auto m1 = deterministicRandom()->randomChoice(g_simulator->getAllProcesses()); + auto m2 = deterministicRandom()->randomChoice(g_simulator->getAllProcesses()); if (m1->address.ip != m2->address.ip) - g_simulator.clogPair(m1->address.ip, m2->address.ip, t); + g_simulator->clogPair(m1->address.ip, m2->address.ip, t); } ACTOR Future clogClient(RandomCloggingWorkload* self) { @@ -74,7 +112,7 @@ struct RandomCloggingWorkload : TestWorkload { state double workloadEnd = now() + self->testDuration; loop { wait(poisson(&lastTime, self->scale / self->clogginess)); - auto machine = deterministicRandom()->randomChoice(g_simulator.getAllProcesses()); + auto machine = deterministicRandom()->randomChoice(g_simulator->getAllProcesses()); double t = self->scale * 10.0 * exp(-10.0 * deterministicRandom()->random01()); t = std::max(0.0, std::min(t, workloadEnd - now())); self->doClog(machine, t); @@ -97,9 +135,9 @@ struct RandomCloggingWorkload : TestWorkload { // then unclog in a different order over the course of t seconds std::vector swizzled; std::vector starts, ends; - for (int m = 0; m < g_simulator.getAllProcesses().size(); m++) + for (int m = 0; m < g_simulator->getAllProcesses().size(); m++) if (deterministicRandom()->random01() < 0.5) { - swizzled.push_back(g_simulator.getAllProcesses()[m]); + swizzled.push_back(g_simulator->getAllProcesses()[m]); starts.push_back(deterministicRandom()->random01() * t / 2); ends.push_back(deterministicRandom()->random01() * t / 2 + t / 2); } @@ -114,3 +152,4 @@ struct RandomCloggingWorkload : TestWorkload { }; WorkloadFactory RandomCloggingWorkloadFactory("RandomClogging"); +FailureInjectorFactory RandomCloggingFailureInjectionFactory; diff --git a/fdbserver/workloads/RandomMoveKeys.actor.cpp b/fdbserver/workloads/RandomMoveKeys.actor.cpp index 6ec85f8381..d41fdcf8b5 100644 --- a/fdbserver/workloads/RandomMoveKeys.actor.cpp +++ b/fdbserver/workloads/RandomMoveKeys.actor.cpp @@ -29,17 +29,21 @@ #include "fdbserver/QuietDatabase.h" #include "flow/actorcompiler.h" // This must be the last #include. -struct MoveKeysWorkload : TestWorkload { +struct MoveKeysWorkload : FailureInjectionWorkload { bool enabled; - double testDuration, meanDelay; - double maxKeyspace; + double testDuration = 10.0, meanDelay = 0.05; + double maxKeyspace = 0.1; DatabaseConfiguration configuration; - MoveKeysWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { + MoveKeysWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) { enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client - meanDelay = getOption(options, LiteralStringRef("meanDelay"), 0.05); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - maxKeyspace = getOption(options, LiteralStringRef("maxKeyspace"), 0.1); + } + + MoveKeysWorkload(WorkloadContext const& wcx) : FailureInjectionWorkload(wcx) { + enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client + meanDelay = getOption(options, "meanDelay"_sr, meanDelay); + testDuration = getOption(options, "testDuration"_sr, testDuration); + maxKeyspace = getOption(options, "maxKeyspace"_sr, maxKeyspace); } std::string description() const override { return "MoveKeysWorkload"; } @@ -143,18 +147,18 @@ struct MoveKeysWorkload : TestWorkload { state Promise signal; state DDEnabledState ddEnabledState; wait(moveKeys(cx, - deterministicRandom()->randomUniqueID(), - keys, - destinationTeamIDs, - destinationTeamIDs, - lock, - signal, - &fl1, - &fl2, - false, - relocateShardInterval.pairID, - &ddEnabledState, - CancelConflictingDataMoves::True)); + MoveKeysParams{ deterministicRandom()->randomUniqueID(), + keys, + destinationTeamIDs, + destinationTeamIDs, + lock, + signal, + &fl1, + &fl2, + false, + relocateShardInterval.pairID, + &ddEnabledState, + CancelConflictingDataMoves::True })); TraceEvent(relocateShardInterval.end()).detail("Result", "Success"); return Void(); } catch (Error& e) { @@ -181,7 +185,7 @@ struct MoveKeysWorkload : TestWorkload { ACTOR Future forceMasterFailure(Database cx, MoveKeysWorkload* self) { ASSERT(g_network->isSimulated()); loop { - if (g_simulator.killZone(self->dbInfo->get().master.locality.zoneId(), ISimulator::Reboot, true)) + if (g_simulator->killZone(self->dbInfo->get().master.locality.zoneId(), ISimulator::Reboot, true)) return Void(); wait(delay(1.0)); } @@ -232,3 +236,4 @@ struct MoveKeysWorkload : TestWorkload { }; WorkloadFactory MoveKeysWorkloadFactory("RandomMoveKeys"); +FailureInjectorFactory MoveKeysFailureInjectionFactory; diff --git a/fdbserver/workloads/RandomSelector.actor.cpp b/fdbserver/workloads/RandomSelector.actor.cpp index 1573e6f848..d5d8e23f16 100644 --- a/fdbserver/workloads/RandomSelector.actor.cpp +++ b/fdbserver/workloads/RandomSelector.actor.cpp @@ -36,13 +36,13 @@ struct RandomSelectorWorkload : TestWorkload { RandomSelectorWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries") { - minOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 10); - maxOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 50); - maxKeySpace = getOption(options, LiteralStringRef("maxKeySpace"), 20); - maxOffset = getOption(options, LiteralStringRef("maxOffset"), 5); - minInitialAmount = getOption(options, LiteralStringRef("minInitialAmount"), 5); - maxInitialAmount = getOption(options, LiteralStringRef("maxInitialAmount"), 10); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); + minOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 10); + maxOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 50); + maxKeySpace = getOption(options, "maxKeySpace"_sr, 20); + maxOffset = getOption(options, "maxOffset"_sr, 5); + minInitialAmount = getOption(options, "minInitialAmount"_sr, 5); + maxInitialAmount = getOption(options, "maxInitialAmount"_sr, 10); + testDuration = getOption(options, "testDuration"_sr, 10.0); fail = false; } diff --git a/fdbserver/workloads/ReadAfterWrite.actor.cpp b/fdbserver/workloads/ReadAfterWrite.actor.cpp index ecbd97410a..96a95db040 100644 --- a/fdbserver/workloads/ReadAfterWrite.actor.cpp +++ b/fdbserver/workloads/ReadAfterWrite.actor.cpp @@ -53,7 +53,7 @@ struct ReadAfterWriteWorkload : KVWorkload { ContinuousSample propagationLatency; ReadAfterWriteWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), propagationLatency(SAMPLE_SIZE) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); + testDuration = getOption(options, "testDuration"_sr, 10.0); } std::string description() const override { return "ReadAfterWriteWorkload"; } diff --git a/fdbserver/workloads/ReadHotDetection.actor.cpp b/fdbserver/workloads/ReadHotDetection.actor.cpp index 251c1f90f4..81be6220d6 100644 --- a/fdbserver/workloads/ReadHotDetection.actor.cpp +++ b/fdbserver/workloads/ReadHotDetection.actor.cpp @@ -37,10 +37,10 @@ struct ReadHotDetectionWorkload : TestWorkload { bool passed; ReadHotDetectionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 1000.0) / clientCount; - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), transactionsPerSecond / 5); - keyCount = getOption(options, LiteralStringRef("keyCount"), 100); + testDuration = getOption(options, "testDuration"_sr, 120.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 1000.0) / clientCount; + actorCount = getOption(options, "actorsPerClient"_sr, transactionsPerSecond / 5); + keyCount = getOption(options, "keyCount"_sr, 100); readKey = StringRef(format("testkey%08x", deterministicRandom()->randomInt(0, keyCount))); } @@ -89,7 +89,7 @@ struct ReadHotDetectionWorkload : TestWorkload { wait(tr.onError(e)); } } - self->wholeRange = KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff")); + self->wholeRange = KeyRangeRef(""_sr, "\xff"_sr); // TraceEvent("RHDLog").detail("Phase", "DoneSetup"); return Void(); } diff --git a/fdbserver/workloads/ReadWrite.actor.cpp b/fdbserver/workloads/ReadWrite.actor.cpp index 96cfc38d52..d7a44cb6e1 100644 --- a/fdbserver/workloads/ReadWrite.actor.cpp +++ b/fdbserver/workloads/ReadWrite.actor.cpp @@ -386,23 +386,21 @@ struct ReadWriteWorkload : ReadWriteCommon { ReadWriteWorkload(WorkloadContext const& wcx) : ReadWriteCommon(wcx), dependentReads(false), adjacentReads(false), adjacentWrites(false) { - extraReadConflictRangesPerTransaction = - getOption(options, LiteralStringRef("extraReadConflictRangesPerTransaction"), 0); - extraWriteConflictRangesPerTransaction = - getOption(options, LiteralStringRef("extraWriteConflictRangesPerTransaction"), 0); - dependentReads = getOption(options, LiteralStringRef("dependentReads"), false); - inconsistentReads = getOption(options, LiteralStringRef("inconsistentReads"), false); - adjacentReads = getOption(options, LiteralStringRef("adjacentReads"), false); - adjacentWrites = getOption(options, LiteralStringRef("adjacentWrites"), false); - rampUpLoad = getOption(options, LiteralStringRef("rampUpLoad"), false); - rampSweepCount = getOption(options, LiteralStringRef("rampSweepCount"), 1); - rangeReads = getOption(options, LiteralStringRef("rangeReads"), false); - rampTransactionType = getOption(options, LiteralStringRef("rampTransactionType"), false); - rampUpConcurrency = getOption(options, LiteralStringRef("rampUpConcurrency"), false); - batchPriority = getOption(options, LiteralStringRef("batchPriority"), false); - descriptionString = getOption(options, LiteralStringRef("description"), LiteralStringRef("ReadWrite")); - if (hasOption(options, LiteralStringRef("transactionTag"))) { - transactionTag = getOption(options, LiteralStringRef("transactionTag"), ""_sr); + extraReadConflictRangesPerTransaction = getOption(options, "extraReadConflictRangesPerTransaction"_sr, 0); + extraWriteConflictRangesPerTransaction = getOption(options, "extraWriteConflictRangesPerTransaction"_sr, 0); + dependentReads = getOption(options, "dependentReads"_sr, false); + inconsistentReads = getOption(options, "inconsistentReads"_sr, false); + adjacentReads = getOption(options, "adjacentReads"_sr, false); + adjacentWrites = getOption(options, "adjacentWrites"_sr, false); + rampUpLoad = getOption(options, "rampUpLoad"_sr, false); + rampSweepCount = getOption(options, "rampSweepCount"_sr, 1); + rangeReads = getOption(options, "rangeReads"_sr, false); + rampTransactionType = getOption(options, "rampTransactionType"_sr, false); + rampUpConcurrency = getOption(options, "rampUpConcurrency"_sr, false); + batchPriority = getOption(options, "batchPriority"_sr, false); + descriptionString = getOption(options, "description"_sr, "ReadWrite"_sr); + if (hasOption(options, "transactionTag"_sr)) { + transactionTag = getOption(options, "transactionTag"_sr, ""_sr); } if (rampUpConcurrency) @@ -411,8 +409,8 @@ struct ReadWriteWorkload : ReadWriteCommon { { // with P(hotTrafficFraction) an access is directed to one of a fraction // of hot keys, else it is directed to a disjoint set of cold keys - hotKeyFraction = getOption(options, LiteralStringRef("hotKeyFraction"), 0.0); - double hotTrafficFraction = getOption(options, LiteralStringRef("hotTrafficFraction"), 0.0); + hotKeyFraction = getOption(options, "hotKeyFraction"_sr, 0.0); + double hotTrafficFraction = getOption(options, "hotTrafficFraction"_sr, 0.0); ASSERT(hotKeyFraction >= 0 && hotTrafficFraction <= 1); ASSERT(hotKeyFraction <= hotTrafficFraction); // hot keys should be actually hot! // p(Cold key) = (1-FHP) * (1-hkf) diff --git a/fdbserver/workloads/RemoveServersSafely.actor.cpp b/fdbserver/workloads/RemoveServersSafely.actor.cpp index 6be4a58cee..6fc3fab69d 100644 --- a/fdbserver/workloads/RemoveServersSafely.actor.cpp +++ b/fdbserver/workloads/RemoveServersSafely.actor.cpp @@ -44,17 +44,17 @@ struct RemoveServersSafelyWorkload : TestWorkload { RemoveServersSafelyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { enabled = !clientId && g_network->isSimulated(); // only do this on the "first" client, and only when in simulation - minMachinesToKill = getOption(options, LiteralStringRef("minMachinesToKill"), 1); - maxMachinesToKill = getOption(options, LiteralStringRef("maxMachinesToKill"), 10); + minMachinesToKill = getOption(options, "minMachinesToKill"_sr, 1); + maxMachinesToKill = getOption(options, "maxMachinesToKill"_sr, 10); maxMachinesToKill = std::max(minMachinesToKill, maxMachinesToKill); - maxSafetyCheckRetries = getOption(options, LiteralStringRef("maxSafetyCheckRetries"), 50); - minDelay = getOption(options, LiteralStringRef("minDelay"), 0.0); - maxDelay = getOption(options, LiteralStringRef("maxDelay"), 60.0); - kill1Timeout = getOption(options, LiteralStringRef("kill1Timeout"), 60.0); - kill2Timeout = getOption(options, LiteralStringRef("kill2Timeout"), 6000.0); + maxSafetyCheckRetries = getOption(options, "maxSafetyCheckRetries"_sr, 50); + minDelay = getOption(options, "minDelay"_sr, 0.0); + maxDelay = getOption(options, "maxDelay"_sr, 60.0); + kill1Timeout = getOption(options, "kill1Timeout"_sr, 60.0); + kill2Timeout = getOption(options, "kill2Timeout"_sr, 6000.0); killProcesses = deterministicRandom()->random01() < 0.5; if (g_network->isSimulated()) { - g_simulator.allowLogSetKills = false; + g_simulator->allowLogSetKills = false; } } @@ -80,7 +80,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Zoneid", it->locality.zoneId().get().toString()) .detail("MachineId", it->locality.machineId().get().toString()); - if (g_simulator.protectedAddresses.count(it->address) == 0) + if (g_simulator->protectedAddresses.count(it->address) == 0) processAddrs.push_back(pAddr); machineProcesses[machineIp].insert(pAddr); @@ -127,13 +127,13 @@ struct RemoveServersSafelyWorkload : TestWorkload { for (AddressExclusion ex : toKill1) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); - g_simulator.disableSwapToMachine(machine_ids[machineIp]); + g_simulator->disableSwapToMachine(machine_ids[machineIp]); } for (AddressExclusion ex : toKill2) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); - g_simulator.disableSwapToMachine(machine_ids[machineIp]); + g_simulator->disableSwapToMachine(machine_ids[machineIp]); } return Void(); @@ -178,7 +178,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { } } // Get the list of processes matching network address - for (auto processInfo : g_simulator.getAllProcesses()) { + for (auto processInfo : g_simulator->getAllProcesses()) { auto processNet = AddressExclusion(processInfo->address.ip, processInfo->address.port); if (processAddrs.find(processNet) != processAddrs.end()) { processes.push_back(processInfo); @@ -189,7 +189,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Failed", processInfo->failed) .detail("Excluded", processInfo->excluded) .detail("Rebooting", processInfo->rebooting) - .detail("Protected", g_simulator.protectedAddresses.count(processInfo->address)); + .detail("Protected", g_simulator->protectedAddresses.count(processInfo->address)); } else { TraceEvent("RemoveAndKill", functionId) .detail("Step", "ProcessNotToKill") @@ -198,7 +198,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Failed", processInfo->failed) .detail("Excluded", processInfo->excluded) .detail("Rebooting", processInfo->rebooting) - .detail("Protected", g_simulator.protectedAddresses.count(processInfo->address)); + .detail("Protected", g_simulator->protectedAddresses.count(processInfo->address)); } } TraceEvent("RemoveAndKill", functionId) @@ -223,9 +223,9 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("AddrTotal", procAddrs.size()) .detail("ProcTotal", procArray.size()) .detail("Addresses", describe(procAddrs)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); for (auto& procAddr : procAddrs) { - g_simulator.excludeAddress(NetworkAddress(procAddr.ip, procAddr.port, true, false)); + g_simulator->excludeAddress(NetworkAddress(procAddr.ip, procAddr.port, true, false)); } for (auto& procRecord : procArray) { procRecord->excluded = true; @@ -235,7 +235,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Process", describe(*procRecord)) .detail("Failed", procRecord->failed) .detail("Rebooting", procRecord->rebooting) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); } return procArray; } @@ -250,9 +250,9 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("AddrTotal", procAddrs.size()) .detail("ProcTotal", procArray.size()) .detail("Addresses", describe(procAddrs)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); for (auto& procAddr : procAddrs) { - g_simulator.includeAddress(NetworkAddress(procAddr.ip, procAddr.port, true, false)); + g_simulator->includeAddress(NetworkAddress(procAddr.ip, procAddr.port, true, false)); } for (auto& procRecord : procArray) { // Only change the exclusion member, if not failed since it will require a reboot to revive it @@ -264,7 +264,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Process", describe(*procRecord)) .detail("Failed", procRecord->failed) .detail("Rebooting", procRecord->rebooting) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); } return procArray; } @@ -307,7 +307,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { // Check if we can kill the added process bCanKillProcess = - g_simulator.canKillProcesses(processesLeft, processesDead, ISimulator::KillInstantly, nullptr); + g_simulator->canKillProcesses(processesLeft, processesDead, ISimulator::KillInstantly, nullptr); // Remove the added processes processesLeft.resize(processesLeft.size() - killProcArray.size()); @@ -350,7 +350,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", "exclude list first") .detail("ToKill", describe(toKill1)) .detail("KillTotal", toKill1.size()) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); // toKill1 may kill too many servers to make cluster unavailable. // Get the processes in toKill1 that are safe to kill @@ -361,7 +361,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", "exclude list first") .detail("ToKillModified", describe(toKill1)) .detail("KillTotalModified", toKill1.size()) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); self->excludeAddresses(toKill1); @@ -374,7 +374,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("KillTotal", toKill1.size()) .detail("Processes", killProcArray.size()) .detail("ToKill1", describe(toKill1)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); // Include the servers, if unable to exclude // Reinclude when buggify is on to increase the surface area of the next set of excludes @@ -385,7 +385,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", "include all first") .detail("KillTotal", toKill1.size()) .detail("ToKill", describe(toKill1)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); wait(includeServers(cx, std::vector(1))); wait(includeLocalities(cx, std::vector(), failed, true)); wait(includeLocalities(cx, std::vector(), !failed, true)); @@ -403,7 +403,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", "exclude list second") .detail("KillTotal", toKill2.size()) .detail("ToKill", describe(toKill2)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); self->excludeAddresses(toKill2); // The second set of machines is selected so that we can always make progress without it, even after the @@ -413,7 +413,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("ToKill2", describe(toKill2)) .detail("KillTotal", toKill2.size()) .detail("Processes", killProcArray.size()) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); wait(reportErrors(timeoutError(removeAndKill(self, cx, toKill2, bClearedFirst ? &toKill1 : nullptr, true), self->kill2Timeout), "RemoveServersSafelyError", @@ -423,14 +423,14 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", "excluded second list") .detail("KillTotal", toKill2.size()) .detail("ToKill", describe(toKill2)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); // Get the updated list of processes which may have changed due to reboots, deletes, etc TraceEvent("RemoveAndKill") .detail("Step", "include all second") .detail("KillTotal", toKill2.size()) .detail("ToKill", describe(toKill2)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); wait(includeServers(cx, std::vector(1))); wait(includeLocalities(cx, std::vector(), failed, true)); wait(includeLocalities(cx, std::vector(), !failed, true)); @@ -454,31 +454,31 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Step", removeViaClear ? "ClearProcesses" : "IgnoreProcesses") .detail("Addresses", describe(killAddrs)) .detail("Processes", killProcArray.size()) - .detail("ClusterAvailable", g_simulator.isAvailable()) + .detail("ClusterAvailable", g_simulator->isAvailable()) .detail("RemoveViaClear", removeViaClear); for (auto& killProcess : killProcArray) { - if (g_simulator.protectedAddresses.count(killProcess->address)) + if (g_simulator->protectedAddresses.count(killProcess->address)) TraceEvent("RemoveAndKill", functionId) .detail("Step", "NoKill Process") .detail("Process", describe(*killProcess)) .detail("Failed", killProcess->failed) .detail("Rebooting", killProcess->rebooting) - .detail("ClusterAvailable", g_simulator.isAvailable()) - .detail("Protected", g_simulator.protectedAddresses.count(killProcess->address)); + .detail("ClusterAvailable", g_simulator->isAvailable()) + .detail("Protected", g_simulator->protectedAddresses.count(killProcess->address)); else if (removeViaClear) { - g_simulator.rebootProcess(killProcess, ISimulator::RebootProcessAndDelete); + g_simulator->rebootProcess(killProcess, ISimulator::RebootProcessAndDelete); TraceEvent("RemoveAndKill", functionId) .detail("Step", "Clear Process") .detail("Process", describe(*killProcess)) .detail("Failed", killProcess->failed) .detail("Rebooting", killProcess->rebooting) - .detail("ClusterAvailable", g_simulator.isAvailable()) - .detail("Protected", g_simulator.protectedAddresses.count(killProcess->address)); + .detail("ClusterAvailable", g_simulator->isAvailable()) + .detail("Protected", g_simulator->protectedAddresses.count(killProcess->address)); } /* else { - g_simulator.killProcess( killProcess, ISimulator::KillInstantly ); - TraceEvent("RemoveAndKill", functionId).detail("Step", "Kill Process").detail("Process", describe(*killProcess)).detail("Failed", killProcess->failed).detail("Rebooting", killProcess->rebooting).detail("ClusterAvailable", g_simulator.isAvailable()).detail("Protected", g_simulator.protectedAddresses.count(killProcess->address)); + g_simulator->killProcess( killProcess, ISimulator::KillInstantly ); + TraceEvent("RemoveAndKill", functionId).detail("Step", "Kill Process").detail("Process", describe(*killProcess)).detail("Failed", killProcess->failed).detail("Rebooting", killProcess->rebooting).detail("ClusterAvailable", g_simulator->isAvailable()).detail("Protected", g_simulator->protectedAddresses.count(killProcess->address)); } */ } @@ -493,15 +493,15 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Addresses", describe(killAddrs)) .detail("Processes", killProcArray.size()) .detail("Zones", zoneIds.size()) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); for (auto& zoneId : zoneIds) { - killedMachine = g_simulator.killZone( + killedMachine = g_simulator->killZone( zoneId, removeViaClear ? ISimulator::RebootAndDelete : ISimulator::KillInstantly); TraceEvent(killedMachine ? SevInfo : SevWarn, "RemoveAndKill") .detail("Step", removeViaClear ? "Clear Machine" : "Kill Machine") .detail("ZoneId", zoneId) .detail(removeViaClear ? "Cleared" : "Killed", killedMachine) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); } } @@ -520,7 +520,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { // First clear the exclusion list and exclude the given list TraceEvent("RemoveAndKill", functionId) .detail("Step", "Including all") - .detail("ClusterAvailable", g_simulator.isAvailable()) + .detail("ClusterAvailable", g_simulator->isAvailable()) .detail("MarkExcludeAsFailed", markExcludeAsFailed); state bool failed = true; wait(includeServers(cx, std::vector(1))); @@ -528,7 +528,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { wait(includeLocalities(cx, std::vector(), !failed, true)); TraceEvent("RemoveAndKill", functionId) .detail("Step", "Included all") - .detail("ClusterAvailable", g_simulator.isAvailable()) + .detail("ClusterAvailable", g_simulator->isAvailable()) .detail("MarkExcludeAsFailed", markExcludeAsFailed); // Reinclude the addresses that were excluded, if present if (pIncAddrs) { @@ -629,7 +629,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("ToKill", describe(toKill)) .detail("Addresses", describe(toKillArray)) .detail("FailedAddresses", describe(toKillMarkFailedArray)) - .detail("ClusterAvailable", g_simulator.isAvailable()) + .detail("ClusterAvailable", g_simulator->isAvailable()) .detail("MarkExcludeAsFailed", markExcludeAsFailed); state bool excludeLocalitiesInsteadOfServers = deterministicRandom()->coinflip(); @@ -682,14 +682,14 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill", functionId) .detail("Step", "Wait For Server Exclusion") .detail("Addresses", describe(toKill)) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); wait(success(checkForExcludingServers(cx, toKillArray, true /* wait for exclusion */))); } TraceEvent("RemoveAndKill", functionId) .detail("Step", "coordinators auto") - .detail("DesiredCoordinators", g_simulator.desiredCoordinators) - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("DesiredCoordinators", g_simulator->desiredCoordinators) + .detail("ClusterAvailable", g_simulator->isAvailable()); // Setup the coordinators BEFORE the exclusion // Otherwise, we may end up with NotEnoughMachinesForCoordinators @@ -697,7 +697,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { state int nQuorum; while (true) { cycle++; - nQuorum = ((g_simulator.desiredCoordinators + 1) / 2) * 2 - 1; + nQuorum = ((g_simulator->desiredCoordinators + 1) / 2) * 2 - 1; CoordinatorsResult result = wait(changeQuorum(cx, autoQuorumChange(nQuorum))); TraceEvent(result == CoordinatorsResult::SUCCESS || result == CoordinatorsResult::SAME_NETWORK_ADDRESSES ? SevInfo @@ -707,7 +707,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { .detail("Result", (int)result) .detail("Attempt", cycle) .detail("Quorum", nQuorum) - .detail("DesiredCoordinators", g_simulator.desiredCoordinators); + .detail("DesiredCoordinators", g_simulator->desiredCoordinators); if (result == CoordinatorsResult::SUCCESS || result == CoordinatorsResult::SAME_NETWORK_ADDRESSES) break; } @@ -716,19 +716,19 @@ struct RemoveServersSafelyWorkload : TestWorkload { } else { TraceEvent("RemoveAndKill", functionId) .detail("Step", "nothing to clear") - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); } TraceEvent("RemoveAndKill", functionId) .detail("Step", "done") - .detail("ClusterAvailable", g_simulator.isAvailable()); + .detail("ClusterAvailable", g_simulator->isAvailable()); return Void(); } static std::vector getServers() { std::vector machines; - std::vector all = g_simulator.getAllProcesses(); + std::vector all = g_simulator->getAllProcesses(); for (int i = 0; i < all.size(); i++) { if (all[i]->name == std::string("Server") && all[i]->isAvailableClass()) { machines.push_back(all[i]); @@ -793,7 +793,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { addressToIndexMap[workers[i].address] = i; } - std::vector processes = g_simulator.getAllProcesses(); + std::vector processes = g_simulator->getAllProcesses(); for (auto process : processes) { if (addressToIndexMap.find(process->address) != addressToIndexMap.end()) { if (workers[addressToIndexMap[process->address]].locality.processId().present()) { diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 854612333b..14a5e69a34 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -40,23 +40,21 @@ struct ReportConflictingKeysWorkload : TestWorkload { ReportConflictingKeysWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), invalidReports("InvalidReports"), commits("commits"), conflicts("Conflicts"), xacts("Transactions") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - // transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), 1); - keyPrefix = unprintable( - getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("ReportConflictingKeysWorkload")) - .toString()); - keyBytes = getOption(options, LiteralStringRef("keyBytes"), 64); + testDuration = getOption(options, "testDuration"_sr, 10.0); + // transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0) / clientCount; + actorCount = getOption(options, "actorsPerClient"_sr, 1); + keyPrefix = unprintable(getOption(options, "keyPrefix"_sr, "ReportConflictingKeysWorkload"_sr).toString()); + keyBytes = getOption(options, "keyBytes"_sr, 64); - readConflictRangeCount = getOption(options, LiteralStringRef("readConflictRangeCountPerTx"), 1); - writeConflictRangeCount = getOption(options, LiteralStringRef("writeConflictRangeCountPerTx"), 1); + readConflictRangeCount = getOption(options, "readConflictRangeCountPerTx"_sr, 1); + writeConflictRangeCount = getOption(options, "writeConflictRangeCountPerTx"_sr, 1); ASSERT(readConflictRangeCount >= 1 && writeConflictRangeCount >= 1); // modeled by geometric distribution: (1 - prob) / prob = mean - 1, where we add at least one conflictRange to // each tx addReadConflictRangeProb = (readConflictRangeCount - 1.0) / readConflictRangeCount; addWriteConflictRangeProb = (writeConflictRangeCount - 1.0) / writeConflictRangeCount; ASSERT(keyPrefix.size() + 8 <= keyBytes); // make sure the string format is valid - nodeCount = getOption(options, LiteralStringRef("nodeCount"), 100); + nodeCount = getOption(options, "nodeCount"_sr, 100); } std::string description() const override { return "ReportConflictingKeysWorkload"; } @@ -187,9 +185,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { // check API correctness if (foundConflict) { // \xff\xff/transaction/conflicting_keys is always initialized to false, skip it here - state KeyRange ckr = - KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysRange.begin)), - LiteralStringRef("\xff\xff").withPrefix(conflictingKeysRange.begin)); + state KeyRange ckr = KeyRangeRef(keyAfter(""_sr.withPrefix(conflictingKeysRange.begin)), + "\xff\xff"_sr.withPrefix(conflictingKeysRange.begin)); // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here state Future conflictingKeyRangesFuture = tr2->getRange(ckr, CLIENT_KNOBS->TOO_MANY); diff --git a/fdbserver/workloads/RestoreBackup.actor.cpp b/fdbserver/workloads/RestoreBackup.actor.cpp index c08fc7de70..41f09365b0 100644 --- a/fdbserver/workloads/RestoreBackup.actor.cpp +++ b/fdbserver/workloads/RestoreBackup.actor.cpp @@ -37,10 +37,10 @@ struct RestoreBackupWorkload final : TestWorkload { StopWhenDone stopWhenDone{ false }; RestoreBackupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - backupDir = getOption(options, LiteralStringRef("backupDir"), LiteralStringRef("file://simfdb/backups/")); - tag = getOption(options, LiteralStringRef("tag"), LiteralStringRef("default")); - delayFor = getOption(options, LiteralStringRef("delayFor"), 10.0); - stopWhenDone.set(getOption(options, LiteralStringRef("stopWhenDone"), false)); + backupDir = getOption(options, "backupDir"_sr, "file://simfdb/backups/"_sr); + tag = getOption(options, "tag"_sr, "default"_sr); + delayFor = getOption(options, "delayFor"_sr, 10.0); + stopWhenDone.set(getOption(options, "stopWhenDone"_sr, false)); } static constexpr const char* DESCRIPTION = "RestoreBackup"; diff --git a/fdbserver/workloads/RestoreFromBlob.actor.cpp b/fdbserver/workloads/RestoreFromBlob.actor.cpp index 9d072bb731..793d5e88b1 100644 --- a/fdbserver/workloads/RestoreFromBlob.actor.cpp +++ b/fdbserver/workloads/RestoreFromBlob.actor.cpp @@ -35,16 +35,13 @@ struct RestoreFromBlobWorkload : TestWorkload { static constexpr const char* DESCRIPTION = "RestoreFromBlob"; RestoreFromBlobWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - restoreAfter = getOption(options, LiteralStringRef("restoreAfter"), 10.0); - backupTag = getOption(options, LiteralStringRef("backupTag"), BackupAgentBase::getDefaultTag()); - auto backupURLString = - getOption(options, LiteralStringRef("backupURL"), LiteralStringRef("http://0.0.0.0:10000")).toString(); - auto accessKeyEnvVar = - getOption(options, LiteralStringRef("accessKeyVar"), LiteralStringRef("BLOB_ACCESS_KEY")).toString(); - auto secretKeyEnvVar = - getOption(options, LiteralStringRef("secretKeyVar"), LiteralStringRef("BLOB_SECRET_KEY")).toString(); - bool provideKeys = getOption(options, LiteralStringRef("provideKeys"), false); - waitForComplete.set(getOption(options, LiteralStringRef("waitForComplete"), true)); + restoreAfter = getOption(options, "restoreAfter"_sr, 10.0); + backupTag = getOption(options, "backupTag"_sr, BackupAgentBase::getDefaultTag()); + auto backupURLString = getOption(options, "backupURL"_sr, "http://0.0.0.0:10000"_sr).toString(); + auto accessKeyEnvVar = getOption(options, "accessKeyVar"_sr, "BLOB_ACCESS_KEY"_sr).toString(); + auto secretKeyEnvVar = getOption(options, "secretKeyVar"_sr, "BLOB_SECRET_KEY"_sr).toString(); + bool provideKeys = getOption(options, "provideKeys"_sr, false); + waitForComplete.set(getOption(options, "waitForComplete"_sr, true)); if (provideKeys) { updateBackupURL(backupURLString, accessKeyEnvVar, "", secretKeyEnvVar, ""); } diff --git a/fdbserver/workloads/Rollback.actor.cpp b/fdbserver/workloads/Rollback.actor.cpp index 3c3ea8efa2..cadff1a1f4 100644 --- a/fdbserver/workloads/Rollback.actor.cpp +++ b/fdbserver/workloads/Rollback.actor.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "flow/DeterministicRandom.h" #include "fdbclient/NativeAPI.actor.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" @@ -31,23 +32,31 @@ // The workload first clogs network link between the chosen proxy and all tLogs but the unclogTlog; // While the network is still clogged, the workload kills the proxy and clogs the unclogged tlog's interface. // Note: The clogged network link's latency will become "clogDuration". -struct RollbackWorkload : TestWorkload { - bool enableFailures, multiple, enabled; - double meanDelay, clogDuration, testDuration; +struct RollbackWorkload : FailureInjectionWorkload { + bool enableFailures = false, multiple = true, enabled; + double meanDelay = 20.0, clogDuration = clogDuration = 3.0, testDuration = 10.0; - RollbackWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { + RollbackWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) {} + + RollbackWorkload(WorkloadContext const& wcx) : FailureInjectionWorkload(wcx) { enabled = !clientId; // only do this on the "first" client - meanDelay = getOption(options, LiteralStringRef("meanDelay"), 20.0); // Only matters if multiple==true - clogDuration = getOption(options, LiteralStringRef("clogDuration"), 3.0); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - enableFailures = getOption(options, LiteralStringRef("enableFailures"), false); - multiple = getOption(options, LiteralStringRef("multiple"), true); + meanDelay = getOption(options, "meanDelay"_sr, meanDelay); // Only matters if multiple==true + clogDuration = getOption(options, "clogDuration"_sr, clogDuration); + testDuration = getOption(options, "testDuration"_sr, testDuration); + enableFailures = getOption(options, "enableFailures"_sr, enableFailures); + multiple = getOption(options, "multiple"_sr, multiple); + } + + void initFailureInjectionMode(DeterministicRandom& random, unsigned count) override { + enabled = clientId == 0; + multiple = random.coinflip(); + enableFailures = random.random01() < 0.2; } std::string description() const override { return "RollbackWorkload"; } Future setup(Database const& cx) override { return Void(); } Future start(Database const& cx) override { - if (&g_simulator == g_network && enabled) + if (g_simulator == g_network && enabled) return timeout(reportErrors(rollbackFailureWorker(cx, this, meanDelay), "RollbackFailureWorkerError"), testDuration, Void()); @@ -83,8 +92,8 @@ struct RollbackWorkload : TestWorkload { for (int t = 0; t < tlogs.size(); t++) { if (t != utIndex) { - g_simulator.clogPair(proxy.address().ip, tlogs[t].address().ip, self->clogDuration); - // g_simulator.clogInterface( g_simulator.getProcess( system.tlogs[t].commit.getEndpoint() ), + g_simulator->clogPair(proxy.address().ip, tlogs[t].address().ip, self->clogDuration); + // g_simulator->clogInterface( g_simulator->getProcess( system.tlogs[t].commit.getEndpoint() ), // self->clogDuration, ClogAll ); } } @@ -95,11 +104,11 @@ struct RollbackWorkload : TestWorkload { // Kill the proxy and clog the unclogged tlog if (self->enableFailures) { - g_simulator.killProcess(g_simulator.getProcessByAddress(proxy.address()), ISimulator::KillInstantly); - g_simulator.clogInterface(uncloggedTLog.ip, self->clogDuration, ClogAll); + g_simulator->killProcess(g_simulator->getProcessByAddress(proxy.address()), ISimulator::KillInstantly); + g_simulator->clogInterface(uncloggedTLog.ip, self->clogDuration, ClogAll); } else { - g_simulator.clogInterface(proxy.address().ip, self->clogDuration, ClogAll); - g_simulator.clogInterface(uncloggedTLog.ip, self->clogDuration, ClogAll); + g_simulator->clogInterface(proxy.address().ip, self->clogDuration, ClogAll); + g_simulator->clogInterface(uncloggedTLog.ip, self->clogDuration, ClogAll); } return Void(); } @@ -122,3 +131,4 @@ struct RollbackWorkload : TestWorkload { }; WorkloadFactory RollbackWorkloadFactory("Rollback"); +FailureInjectorFactory RollbackFailureInjectorFactory; diff --git a/fdbserver/workloads/RyowCorrectness.actor.cpp b/fdbserver/workloads/RyowCorrectness.actor.cpp index dfaa5bfc8c..07290cf9df 100644 --- a/fdbserver/workloads/RyowCorrectness.actor.cpp +++ b/fdbserver/workloads/RyowCorrectness.actor.cpp @@ -29,7 +29,7 @@ #define TRACE_TRANSACTION 0 #if TRACE_TRANSACTION -StringRef debugKey = LiteralStringRef("0000000000uldlamzpspf"); +StringRef debugKey = "0000000000uldlamzpspf"_sr; #endif // A struct representing an operation to be performed on a transaction @@ -62,8 +62,8 @@ struct RyowCorrectnessWorkload : ApiWorkload { int opsPerTransaction; RyowCorrectnessWorkload(WorkloadContext const& wcx) : ApiWorkload(wcx, 1) { - duration = getOption(options, LiteralStringRef("duration"), 60); - opsPerTransaction = getOption(options, LiteralStringRef("opsPerTransaction"), 50); + duration = getOption(options, "duration"_sr, 60); + opsPerTransaction = getOption(options, "opsPerTransaction"_sr, 50); } std::string description() const override { return "RyowCorrectness"; } @@ -142,7 +142,7 @@ struct RyowCorrectnessWorkload : ApiWorkload { void pushKVPair(std::vector& results, Key const& key, Optional const& value) { RangeResult result; if (!value.present()) - result.push_back_deep(result.arena(), KeyValueRef(key, LiteralStringRef("VALUE_NOT_PRESENT"))); + result.push_back_deep(result.arena(), KeyValueRef(key, "VALUE_NOT_PRESENT"_sr)); else result.push_back_deep(result.arena(), KeyValueRef(key, value.get())); diff --git a/fdbserver/workloads/SaveAndKill.actor.cpp b/fdbserver/workloads/SaveAndKill.actor.cpp index 0b56a08d82..29e5194e10 100644 --- a/fdbserver/workloads/SaveAndKill.actor.cpp +++ b/fdbserver/workloads/SaveAndKill.actor.cpp @@ -46,7 +46,7 @@ struct SaveAndKillWorkload : TestWorkload { std::string description() const override { return "SaveAndKillWorkload"; } Future setup(Database const& cx) override { - g_simulator.disableSwapsToAll(); + g_simulator->disableSwapsToAll(); return Void(); } Future start(Database const& cx) override { return _start(this); } @@ -60,12 +60,12 @@ struct SaveAndKillWorkload : TestWorkload { ini.LoadFile(self->restartInfo.c_str()); ini.SetValue("RESTORE", "isRestoring", format("%d", self->isRestoring).c_str()); - ini.SetValue("META", "processesPerMachine", format("%d", g_simulator.processesPerMachine).c_str()); - ini.SetValue("META", "listenersPerProcess", format("%d", g_simulator.listenersPerProcess).c_str()); - ini.SetValue("META", "desiredCoordinators", format("%d", g_simulator.desiredCoordinators).c_str()); - ini.SetValue("META", "connectionString", g_simulator.connectionString.c_str()); - ini.SetValue("META", "testerCount", format("%d", g_simulator.testerCount).c_str()); - ini.SetValue("META", "tssMode", format("%d", g_simulator.tssMode).c_str()); + ini.SetValue("META", "processesPerMachine", format("%d", g_simulator->processesPerMachine).c_str()); + ini.SetValue("META", "listenersPerProcess", format("%d", g_simulator->listenersPerProcess).c_str()); + ini.SetValue("META", "desiredCoordinators", format("%d", g_simulator->desiredCoordinators).c_str()); + ini.SetValue("META", "connectionString", g_simulator->connectionString.c_str()); + ini.SetValue("META", "testerCount", format("%d", g_simulator->testerCount).c_str()); + ini.SetValue("META", "tssMode", format("%d", g_simulator->tssMode).c_str()); ini.SetValue("META", "mockDNS", INetworkConnections::net()->convertMockDNSToString().c_str()); ini.SetBoolValue("META", "enableEncryption", SERVER_KNOBS->ENABLE_ENCRYPTION); @@ -73,8 +73,9 @@ struct SaveAndKillWorkload : TestWorkload { ini.SetBoolValue("META", "enableStorageServerEncryption", SERVER_KNOBS->ENABLE_STORAGE_SERVER_ENCRYPTION); ini.SetBoolValue("META", "enableBlobGranuleEncryption", SERVER_KNOBS->ENABLE_BLOB_GRANULE_ENCRYPTION); - std::vector processes = g_simulator.getAllProcesses(); - std::map rebootingProcesses = g_simulator.currentlyRebootingProcesses; + std::vector processes = g_simulator->getAllProcesses(); + std::map rebootingProcesses = + g_simulator->currentlyRebootingProcesses; std::map allProcessesMap; for (const auto& [_, process] : rebootingProcesses) { if (allProcessesMap.find(process->dataFolder) == allProcessesMap.end() && !process->isSpawnedKVProcess()) { @@ -139,14 +140,14 @@ struct SaveAndKillWorkload : TestWorkload { ini.SaveFile(self->restartInfo.c_str()); for (auto process = allProcessesMap.begin(); process != allProcessesMap.end(); process++) { - g_simulator.killProcess(process->second, ISimulator::Reboot); + g_simulator->killProcess(process->second, ISimulator::Reboot); } for (i = 0; i < 100; i++) { wait(delay(0.0)); } - g_simulator.stop(); + g_simulator->stop(); return Void(); } diff --git a/fdbserver/workloads/SelectorCorrectness.actor.cpp b/fdbserver/workloads/SelectorCorrectness.actor.cpp index 5cb94e8f7d..7391850be4 100644 --- a/fdbserver/workloads/SelectorCorrectness.actor.cpp +++ b/fdbserver/workloads/SelectorCorrectness.actor.cpp @@ -35,12 +35,12 @@ struct SelectorCorrectnessWorkload : TestWorkload { SelectorCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries") { - minOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 10); - maxOperationsPerTransaction = getOption(options, LiteralStringRef("minOperationsPerTransaction"), 50); - maxKeySpace = getOption(options, LiteralStringRef("maxKeySpace"), 10); - maxOffset = getOption(options, LiteralStringRef("maxOffset"), 20); - testReadYourWrites = getOption(options, LiteralStringRef("testReadYourWrites"), true); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); + minOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 10); + maxOperationsPerTransaction = getOption(options, "minOperationsPerTransaction"_sr, 50); + maxKeySpace = getOption(options, "maxKeySpace"_sr, 10); + maxOffset = getOption(options, "maxOffset"_sr, 20); + testReadYourWrites = getOption(options, "testReadYourWrites"_sr, true); + testDuration = getOption(options, "testDuration"_sr, 10.0); } std::string description() const override { return "SelectorCorrectness"; } diff --git a/fdbserver/workloads/Serializability.actor.cpp b/fdbserver/workloads/Serializability.actor.cpp index a191ae01ed..2ae286b54a 100644 --- a/fdbserver/workloads/Serializability.actor.cpp +++ b/fdbserver/workloads/Serializability.actor.cpp @@ -65,9 +65,9 @@ struct SerializabilityWorkload : TestWorkload { }; SerializabilityWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), success(true) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 30.0); - numOps = getOption(options, LiteralStringRef("numOps"), 21); - nodes = getOption(options, LiteralStringRef("nodes"), 1000); + testDuration = getOption(options, "testDuration"_sr, 30.0); + numOps = getOption(options, "numOps"_sr, 21); + nodes = getOption(options, "nodes"_sr, 1000); adjacentKeys = false; // deterministicRandom()->random01() < 0.5; valueSizeRange = std::make_pair(0, 100); diff --git a/fdbserver/workloads/Sideband.actor.cpp b/fdbserver/workloads/Sideband.actor.cpp index 15a8f09122..d992972d43 100644 --- a/fdbserver/workloads/Sideband.actor.cpp +++ b/fdbserver/workloads/Sideband.actor.cpp @@ -59,8 +59,8 @@ struct SidebandWorkload : TestWorkload { SidebandWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), messages("Messages"), consistencyErrors("Causal Consistency Errors"), keysUnexpectedlyPresent("KeysUnexpectedlyPresent") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - operationsPerSecond = getOption(options, LiteralStringRef("operationsPerSecond"), 50.0); + testDuration = getOption(options, "testDuration"_sr, 10.0); + operationsPerSecond = getOption(options, "operationsPerSecond"_sr, 50.0); } std::string description() const override { return "SidebandWorkload"; } @@ -151,7 +151,7 @@ struct SidebandWorkload : TestWorkload { ++self->keysUnexpectedlyPresent; break; } - tr.set(messageKey, LiteralStringRef("deadbeef")); + tr.set(messageKey, "deadbeef"_sr); wait(tr.commit()); commitVersion = tr.getCommittedVersion(); break; diff --git a/fdbserver/workloads/SidebandSingle.actor.cpp b/fdbserver/workloads/SidebandSingle.actor.cpp index b8481bf8e7..7cea389502 100644 --- a/fdbserver/workloads/SidebandSingle.actor.cpp +++ b/fdbserver/workloads/SidebandSingle.actor.cpp @@ -44,8 +44,8 @@ struct SidebandSingleWorkload : TestWorkload { SidebandSingleWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), messages("Messages"), consistencyErrors("Causal Consistency Errors"), keysUnexpectedlyPresent("KeysUnexpectedlyPresent") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - operationsPerSecond = getOption(options, LiteralStringRef("operationsPerSecond"), 50.0); + testDuration = getOption(options, "testDuration"_sr, 10.0); + operationsPerSecond = getOption(options, "operationsPerSecond"_sr, 50.0); } std::string description() const override { return "SidebandSingleWorkload"; } @@ -94,7 +94,7 @@ struct SidebandSingleWorkload : TestWorkload { // first set, this is the "old" value, always retry loop { try { - tr0.set(messageKey, LiteralStringRef("oldbeef")); + tr0.set(messageKey, "oldbeef"_sr); wait(tr0.commit()); break; } catch (Error& e) { @@ -104,7 +104,7 @@ struct SidebandSingleWorkload : TestWorkload { // second set, the checker should see this, no retries on unknown result loop { try { - tr.set(messageKey, LiteralStringRef("deadbeef")); + tr.set(messageKey, "deadbeef"_sr); wait(tr.commit()); commitVersion = tr.getCommittedVersion(); break; @@ -145,7 +145,7 @@ struct SidebandSingleWorkload : TestWorkload { .detail("LocalReadVersion", tr.getReadVersion().get()); // will assert that ReadVersion is set ++self->consistencyErrors; - } else if (val.get() != LiteralStringRef("deadbeef")) { + } else if (val.get() != "deadbeef"_sr) { // If we read something NOT "deadbeef" and there was no commit_unknown_result, // the cache somehow read a stale version of our key if (message.second != invalidVersion) { diff --git a/fdbserver/workloads/SimpleAtomicAdd.actor.cpp b/fdbserver/workloads/SimpleAtomicAdd.actor.cpp index 503bcba2a3..b65710baf6 100644 --- a/fdbserver/workloads/SimpleAtomicAdd.actor.cpp +++ b/fdbserver/workloads/SimpleAtomicAdd.actor.cpp @@ -35,12 +35,12 @@ struct SimpleAtomicAddWorkload : TestWorkload { std::vector> clients; SimpleAtomicAddWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - addValue = getOption(options, LiteralStringRef("addValue"), 1); - iterations = getOption(options, LiteralStringRef("iterations"), 100); - initialize = getOption(options, LiteralStringRef("initialize"), false); - initialValue = getOption(options, LiteralStringRef("initialValue"), 0); - sumKey = getOption(options, LiteralStringRef("sumKey"), LiteralStringRef("sumKey")); + testDuration = getOption(options, "testDuration"_sr, 10.0); + addValue = getOption(options, "addValue"_sr, 1); + iterations = getOption(options, "iterations"_sr, 100); + initialize = getOption(options, "initialize"_sr, false); + initialValue = getOption(options, "initialValue"_sr, 0); + sumKey = getOption(options, "sumKey"_sr, "sumKey"_sr); } std::string description() const override { return "SimpleAtomicAdd"; } diff --git a/fdbserver/workloads/SkewedReadWrite.actor.cpp b/fdbserver/workloads/SkewedReadWrite.actor.cpp index 23b2fcf442..f537a0a1e5 100644 --- a/fdbserver/workloads/SkewedReadWrite.actor.cpp +++ b/fdbserver/workloads/SkewedReadWrite.actor.cpp @@ -49,7 +49,7 @@ struct SkewedReadWriteWorkload : ReadWriteCommon { int hotServerCount = 0, currentHotRound = -1; SkewedReadWriteWorkload(WorkloadContext const& wcx) : ReadWriteCommon(wcx) { - descriptionString = getOption(options, LiteralStringRef("description"), LiteralStringRef("SkewedReadWrite")); + descriptionString = getOption(options, "description"_sr, "SkewedReadWrite"_sr); hotServerFraction = getOption(options, "hotServerFraction"_sr, 0.2); hotServerShardFraction = getOption(options, "hotServerShardFraction"_sr, 1.0); hotReadWriteServerOverlap = getOption(options, "hotReadWriteServerOverlap"_sr, 0.0); diff --git a/fdbserver/workloads/SnapTest.actor.cpp b/fdbserver/workloads/SnapTest.actor.cpp index 2629666cfa..d74477a379 100644 --- a/fdbserver/workloads/SnapTest.actor.cpp +++ b/fdbserver/workloads/SnapTest.actor.cpp @@ -114,14 +114,12 @@ public: // ctor & dtor std::string workloadName = "SnapTest"; maxRetryCntToRetrieveMessage = 10; - numSnaps = getOption(options, LiteralStringRef("numSnaps"), 0); - maxSnapDelay = getOption(options, LiteralStringRef("maxSnapDelay"), 25.0); - testID = getOption(options, LiteralStringRef("testID"), 0); - restartInfoLocation = - getOption(options, LiteralStringRef("restartInfoLocation"), LiteralStringRef("simfdb/restartInfo.ini")) - .toString(); + numSnaps = getOption(options, "numSnaps"_sr, 0); + maxSnapDelay = getOption(options, "maxSnapDelay"_sr, 25.0); + testID = getOption(options, "testID"_sr, 0); + restartInfoLocation = getOption(options, "restartInfoLocation"_sr, "simfdb/restartInfo.ini"_sr).toString(); skipCheck = false; - retryLimit = getOption(options, LiteralStringRef("retryLimit"), 5); + retryLimit = getOption(options, "retryLimit"_sr, 5); } public: // workload functions @@ -148,7 +146,7 @@ public: // workload functions loop { try { Standalone keyStr = - LiteralStringRef("\xff/SnapTestFailStatus/").withSuffix(StringRef(self->snapUID.toString())); + "\xff/SnapTestFailStatus/"_sr.withSuffix(StringRef(self->snapUID.toString())); TraceEvent("TestKeyStr").detail("Value", keyStr); tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); Optional val = wait(tr.get(keyStr)); @@ -232,7 +230,7 @@ public: // workload functions loop { self->snapUID = deterministicRandom()->randomUniqueID(); try { - StringRef snapCmdRef = LiteralStringRef("/bin/snap_create.sh"); + StringRef snapCmdRef = "/bin/snap_create.sh"_sr; Future status = snapCreate(cx, snapCmdRef, self->snapUID); wait(status); break; @@ -290,7 +288,7 @@ public: // workload functions } for (int i = 0; i < kvRange.size(); i++) { - if (kvRange[i].key.startsWith(LiteralStringRef("snapKey"))) { + if (kvRange[i].key.startsWith("snapKey"_sr)) { std::string tmp1 = kvRange[i].key.substr(7).toString(); int64_t id = strtol(tmp1.c_str(), nullptr, 0); if (id % 2 != 0) { @@ -321,7 +319,7 @@ public: // workload functions loop { self->snapUID = deterministicRandom()->randomUniqueID(); try { - StringRef snapCmdRef = LiteralStringRef("/bin/snap_create1.sh"); + StringRef snapCmdRef = "/bin/snap_create1.sh"_sr; Future status = snapCreate(cx, snapCmdRef, self->snapUID); wait(status); break; diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index c3096bc60c..dbf84101ac 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -28,6 +28,7 @@ #include "fdbclient/Schemas.h" #include "fdbclient/SpecialKeySpace.actor.h" #include "fdbserver/Knobs.h" +#include "fdbclient/TenantManagement.actor.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" #include "flow/IRandom.h" @@ -46,17 +47,17 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { SpecialKeySpaceCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), wrongResults("Wrong Results"), keysCount("Number of generated keys") { - minKeysPerRange = getOption(options, LiteralStringRef("minKeysPerRange"), 1); - maxKeysPerRange = getOption(options, LiteralStringRef("maxKeysPerRange"), 100); - rangeCount = getOption(options, LiteralStringRef("rangeCount"), 10); - keyBytes = getOption(options, LiteralStringRef("keyBytes"), 16); - valBytes = getOption(options, LiteralStringRef("valueBytes"), 16); - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 100.0); - actorCount = getOption(options, LiteralStringRef("actorCount"), 1); - absoluteRandomProb = getOption(options, LiteralStringRef("absoluteRandomProb"), 0.5); + minKeysPerRange = getOption(options, "minKeysPerRange"_sr, 1); + maxKeysPerRange = getOption(options, "maxKeysPerRange"_sr, 100); + rangeCount = getOption(options, "rangeCount"_sr, 10); + keyBytes = getOption(options, "keyBytes"_sr, 16); + valBytes = getOption(options, "valueBytes"_sr, 16); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 100.0); + actorCount = getOption(options, "actorCount"_sr, 1); + absoluteRandomProb = getOption(options, "absoluteRandomProb"_sr, 0.5); // Controls the relative size of read/write conflict ranges and the number of random getranges - conflictRangeSizeFactor = getOption(options, LiteralStringRef("conflictRangeSizeFactor"), 10); + conflictRangeSizeFactor = getOption(options, "conflictRangeSizeFactor"_sr, 10); ASSERT(conflictRangeSizeFactor >= 1); } @@ -128,7 +129,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // This test is not valid for API versions smaller than 630 return; } - f = success(ryw.get(LiteralStringRef("\xff\xff/status/json"))); + f = success(ryw.get("\xff\xff/status/json"_sr)); CODE_PROBE(!f.isReady(), "status json not ready"); } ASSERT(f.isError()); @@ -288,24 +289,152 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { GetRangeLimits randomLimits() { // TODO : fix knobs for row_unlimited - int rowLimits = deterministicRandom()->randomInt(1, keysCount.getValue() + 1); + int rowLimits = deterministicRandom()->randomInt(0, keysCount.getValue() + 1); // The largest key's bytes is longest prefix bytes + 1(for '/') + generated key bytes // 8 here refers to bytes of KeyValueRef int byteLimits = deterministicRandom()->randomInt( 1, keysCount.getValue() * (keyBytes + (rangeCount + 1) + valBytes + 8) + 1); - return GetRangeLimits(rowLimits, byteLimits); + auto limit = GetRangeLimits(rowLimits, byteLimits); + // minRows is always initilized to 1 + if (limit.rows == 0) + limit.minRows = 0; + return limit; } ACTOR Future testSpecialKeySpaceErrors(Database cx_, SpecialKeySpaceCorrectnessWorkload* self) { - Database cx = cx_->clone(); + state Database cx = cx_->clone(); + try { + wait(success(TenantAPI::createTenant(cx.getReference(), TenantName("foo"_sr)))); + } catch (Error& e) { + ASSERT(e.code() == error_code_tenant_already_exists || e.code() == error_code_actor_cancelled); + } state Reference tx = makeReference(cx); + state Reference tenantTx = + makeReference(cx, TenantName("foo"_sr)); + // Use new transactions that may use default tenant rather than re-use tx + // This is because tx will reject raw access for later tests if default tenant is set + state Reference defaultTx1 = makeReference(cx); + state Reference defaultTx2 = makeReference(cx); + state bool disableRyw = deterministicRandom()->coinflip(); + // tenant transaction accessing modules that do not support tenants + // tenant getRange + try { + wait(success(tenantTx->getRange(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT), + CLIENT_KNOBS->TOO_MANY))); + ASSERT(false); + } catch (Error& e) { + if (e.code() == error_code_actor_cancelled) + throw; + ASSERT(e.code() == error_code_illegal_tenant_access); + tenantTx->reset(); + } + // tenant set + commit + try { + tenantTx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); + tenantTx->set(SpecialKeySpace::getManagementApiCommandPrefix("consistencycheck"), ValueRef()); + wait(tenantTx->commit()); + ASSERT(false); + } catch (Error& e) { + if (e.code() == error_code_actor_cancelled) + throw; + ASSERT(e.code() == error_code_illegal_tenant_access); + tenantTx->reset(); + } + // tenant clear + try { + tenantTx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); + tenantTx->clear(SpecialKeySpace::getManagementApiCommandRange("exclude")); + ASSERT(false); + } catch (Error& e) { + if (e.code() == error_code_actor_cancelled) + throw; + ASSERT(e.code() == error_code_illegal_tenant_access); + tenantTx->reset(); + } + // tenant check that conflict ranges stay the same after commit + // and depending on if RYW is disabled + { + state RangeResult readresult1; + state RangeResult readresult2; + state RangeResult writeResult1; + state RangeResult writeResult2; + try { + if (disableRyw) { + defaultTx1->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); + } + defaultTx1->addReadConflictRange(singleKeyRange("testKeylll"_sr)); + defaultTx1->addWriteConflictRange(singleKeyRange("testKeylll"_sr)); + wait(store(readresult1, defaultTx1->getRange(readConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY))); + wait(store(writeResult1, defaultTx1->getRange(writeConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY))); + wait(defaultTx1->commit()); + CODE_PROBE(true, "conflict range tenant commit succeeded"); + } catch (Error& e) { + if (e.code() == error_code_actor_cancelled) + throw; + CODE_PROBE(true, "conflict range tenant commit error thrown"); + } + wait(store(readresult2, defaultTx1->getRange(readConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY))); + wait(store(writeResult2, defaultTx1->getRange(writeConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY))); + ASSERT(readresult1 == readresult2); + ASSERT(writeResult1 == writeResult2); + defaultTx1->reset(); + } + // proper conflict ranges + loop { + try { + if (disableRyw) { + defaultTx1->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); + defaultTx2->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); + } + defaultTx1->setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); + defaultTx2->setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); + wait(success(defaultTx1->getReadVersion())); + wait(success(defaultTx2->getReadVersion())); + defaultTx1->addReadConflictRange(singleKeyRange("foo"_sr)); + defaultTx1->addWriteConflictRange(singleKeyRange("foo"_sr)); + defaultTx2->addWriteConflictRange(singleKeyRange("foo"_sr)); + wait(defaultTx2->commit()); + try { + wait(defaultTx1->commit()); + ASSERT(false); + } catch (Error& e) { + state Error err = e; + if (err.code() != error_code_not_committed) { + wait(defaultTx1->onError(err)); + wait(defaultTx2->onError(err)); + continue; + } + // Read conflict ranges of defaultTx1 and check for "foo" with no tenant prefix + state RangeResult readConflictRange = + wait(defaultTx1->getRange(readConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY)); + state RangeResult writeConflictRange = + wait(defaultTx1->getRange(writeConflictRangeKeysRange, CLIENT_KNOBS->TOO_MANY)); + state RangeResult conflictKeys = + wait(defaultTx1->getRange(conflictingKeysRange, CLIENT_KNOBS->TOO_MANY)); + + // size is 2 because singleKeyRange includes the key after + ASSERT(readConflictRange.size() == 2 && + readConflictRange.begin()->key == readConflictRangeKeysRange.begin.withSuffix("foo"_sr)); + ASSERT(writeConflictRange.size() == 2 && + writeConflictRange.begin()->key == writeConflictRangeKeysRange.begin.withSuffix("foo"_sr)); + ASSERT(conflictKeys.size() == 2 && + conflictKeys.begin()->key == conflictingKeysRange.begin.withSuffix("foo"_sr)); + defaultTx1->reset(); + defaultTx2->reset(); + break; + } + } catch (Error& e) { + if (e.code() == error_code_actor_cancelled) + throw; + wait(defaultTx2->onError(e)); + } + } // begin key outside module range try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - wait(success(tx->getRange( - KeyRangeRef(LiteralStringRef("\xff\xff/transactio"), LiteralStringRef("\xff\xff/transaction0")), - CLIENT_KNOBS->TOO_MANY))); + wait(success(tx->getRange(KeyRangeRef("\xff\xff/transactio"_sr, "\xff\xff/transaction0"_sr), + CLIENT_KNOBS->TOO_MANY))); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -316,9 +445,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // end key outside module range try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - wait(success(tx->getRange( - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction1")), - CLIENT_KNOBS->TOO_MANY))); + wait(success(tx->getRange(KeyRangeRef("\xff\xff/transaction/"_sr, "\xff\xff/transaction1"_sr), + CLIENT_KNOBS->TOO_MANY))); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -329,9 +457,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // both begin and end outside module range try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - wait(success(tx->getRange( - KeyRangeRef(LiteralStringRef("\xff\xff/transaction"), LiteralStringRef("\xff\xff/transaction1")), - CLIENT_KNOBS->TOO_MANY))); + wait(success(tx->getRange(KeyRangeRef("\xff\xff/transaction"_sr, "\xff\xff/transaction1"_sr), + CLIENT_KNOBS->TOO_MANY))); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -342,9 +469,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // legal range read using the module range try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - wait(success(tx->getRange( - KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")), - CLIENT_KNOBS->TOO_MANY))); + wait(success(tx->getRange(KeyRangeRef("\xff\xff/transaction/"_sr, "\xff\xff/transaction0"_sr), + CLIENT_KNOBS->TOO_MANY))); CODE_PROBE(true, "read transaction special keyrange"); tx->reset(); } catch (Error& e) { @@ -354,8 +480,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED); - const KeyRef startKey = LiteralStringRef("\xff\xff/transactio"); - const KeyRef endKey = LiteralStringRef("\xff\xff/transaction1"); + const KeyRef startKey = "\xff\xff/transactio"_sr; + const KeyRef endKey = "\xff\xff/transaction1"_sr; RangeResult result = wait(tx->getRange(KeyRangeRef(startKey, endKey), GetRangeLimits(CLIENT_KNOBS->TOO_MANY))); // The whole transaction module should be empty @@ -367,9 +493,9 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // end keySelector inside module range, *** a tricky corner case *** try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - tx->addReadConflictRange(singleKeyRange(LiteralStringRef("testKey"))); + tx->addReadConflictRange(singleKeyRange("testKey"_sr)); KeySelector begin = KeySelectorRef(readConflictRangeKeysRange.begin, false, 1); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/transaction0"), false, 0); + KeySelector end = KeySelectorRef("\xff\xff/transaction0"_sr, false, 0); wait(success(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY)))); CODE_PROBE(true, "end key selector inside module range"); tx->reset(); @@ -379,9 +505,9 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // No module found error case with keys try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - wait(success(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/A_no_module_related_prefix"), - LiteralStringRef("\xff\xff/I_am_also_not_in_any_module")), - CLIENT_KNOBS->TOO_MANY))); + wait(success(tx->getRange( + KeyRangeRef("\xff\xff/A_no_module_related_prefix"_sr, "\xff\xff/I_am_also_not_in_any_module"_sr), + CLIENT_KNOBS->TOO_MANY))); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -392,8 +518,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // No module found error with KeySelectors, *** a tricky corner case *** try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - KeySelector begin = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_i_am_not_a_module"), false, 1); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_to_be_the_final_one"), false, 2); + KeySelector begin = KeySelectorRef("\xff\xff/zzz_i_am_not_a_module"_sr, false, 1); + KeySelector end = KeySelectorRef("\xff\xff/zzz_to_be_the_final_one"_sr, false, 2); wait(success(tx->getRange(begin, end, CLIENT_KNOBS->TOO_MANY))); ASSERT(false); } catch (Error& e) { @@ -405,7 +531,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // begin and end keySelectors clamp up to the boundary of the module try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - const KeyRef key = LiteralStringRef("\xff\xff/cluster_file_path"); + const KeyRef key = "\xff\xff/cluster_file_path"_sr; KeySelector begin = KeySelectorRef(key, false, 0); KeySelector end = KeySelectorRef(keyAfter(key), false, 2); RangeResult result = wait(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY))); @@ -416,8 +542,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { } try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - tx->addReadConflictRange(singleKeyRange(LiteralStringRef("readKey"))); - const KeyRef key = LiteralStringRef("\xff\xff/transaction/a_to_be_the_first"); + tx->addReadConflictRange(singleKeyRange("readKey"_sr)); + const KeyRef key = "\xff\xff/transaction/a_to_be_the_first"_sr; KeySelector begin = KeySelectorRef(key, false, 0); KeySelector end = KeySelectorRef(key, false, 2); RangeResult result = wait(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY))); @@ -430,7 +556,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // Writes are disabled by default try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef()); + tx->set("\xff\xff/I_am_not_a_range_can_be_written"_sr, ValueRef()); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) throw; @@ -441,7 +567,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); - tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef()); + tx->set("\xff\xff/I_am_not_a_range_can_be_written"_sr, ValueRef()); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -465,8 +591,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // base key of the end key selector not in (\xff\xff, \xff\xff\xff), throw key_outside_legal_range() try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - const KeySelector startKeySelector = KeySelectorRef(LiteralStringRef("\xff\xff/test"), true, -200); - const KeySelector endKeySelector = KeySelectorRef(LiteralStringRef("test"), true, -10); + const KeySelector startKeySelector = KeySelectorRef("\xff\xff/test"_sr, true, -200); + const KeySelector endKeySelector = KeySelectorRef("test"_sr, true, -10); RangeResult result = wait(tx->getRange(startKeySelector, endKeySelector, GetRangeLimits(CLIENT_KNOBS->TOO_MANY))); ASSERT(false); @@ -479,9 +605,9 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // test case when registered range is the same as the underlying module try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); - state RangeResult result = wait(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), - LiteralStringRef("\xff\xff/worker_interfaces0")), - CLIENT_KNOBS->TOO_MANY)); + state RangeResult result = + wait(tx->getRange(KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr), + CLIENT_KNOBS->TOO_MANY)); // Note: there's possibility we get zero workers if (result.size()) { state KeyValueRef entry = deterministicRandom()->randomChoice(result); @@ -544,8 +670,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { tx->addWriteConflictRange(range); } // TODO test that fails if we don't wait on tx->pendingReads() - referenceTx->set(range.begin, LiteralStringRef("1")); - referenceTx->set(range.end, LiteralStringRef("0")); + referenceTx->set(range.begin, "1"_sr); + referenceTx->set(range.end, "0"_sr); } if (!read && deterministicRandom()->coinflip()) { try { @@ -558,7 +684,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { CODE_PROBE(true, "Read write conflict range of committed transaction"); } try { - wait(success(tx->get(LiteralStringRef("\xff\xff/1314109/i_hope_this_isn't_registered")))); + wait(success(tx->get("\xff\xff/1314109/i_hope_this_isn't_registered"_sr))); ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) @@ -666,7 +792,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { ValueRef()); } RangeResult result = wait(tx->getRange( - KeyRangeRef(LiteralStringRef("options/"), LiteralStringRef("options0")) + KeyRangeRef("options/"_sr, "options0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin), CLIENT_KNOBS->TOO_MANY)); ASSERT(!result.more && result.size() < CLIENT_KNOBS->TOO_MANY); @@ -678,8 +804,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { try { tx->setOption(FDBTransactionOptions::RAW_ACCESS); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); - tx->set(LiteralStringRef("Invalid_Network_Address") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("exclude")), + tx->set("Invalid_Network_Address"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("exclude")), ValueRef()); wait(tx->commit()); ASSERT(false); @@ -709,7 +834,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); // test getRange state RangeResult result = wait(tx->getRange( - KeyRangeRef(LiteralStringRef("process/class_type/"), LiteralStringRef("process/class_type0")) + KeyRangeRef("process/class_type/"_sr, "process/class_type0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin), CLIENT_KNOBS->TOO_MANY)); ASSERT(!result.more && result.size() < CLIENT_KNOBS->TOO_MANY); @@ -737,10 +862,10 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { state Key addr = Key("process/class_type/" + formatIpPort(worker.address.ip, worker.address.port)) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin); - tx->set(addr, LiteralStringRef("InvalidProcessType")); + tx->set(addr, "InvalidProcessType"_sr); // test ryw Optional processType = wait(tx->get(addr)); - ASSERT(processType.present() && processType.get() == LiteralStringRef("InvalidProcessType")); + ASSERT(processType.present() && processType.get() == "InvalidProcessType"_sr); // test ryw disabled tx->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); Optional originalProcessType = wait(tx->get(addr)); @@ -779,7 +904,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { // test getRange tx->setOption(FDBTransactionOptions::RAW_ACCESS); state RangeResult class_source_result = wait(tx->getRange( - KeyRangeRef(LiteralStringRef("process/class_source/"), LiteralStringRef("process/class_source0")) + KeyRangeRef("process/class_source/"_sr, "process/class_source0"_sr) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin), CLIENT_KNOBS->TOO_MANY)); ASSERT(!class_source_result.more && class_source_result.size() < CLIENT_KNOBS->TOO_MANY); @@ -797,7 +922,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { if (kv.key == addr) { ASSERT(kv.value.toString() == worker.processClass.sourceString()); // Default source string is command_line - ASSERT(kv.value == LiteralStringRef("command_line")); + ASSERT(kv.value == "command_line"_sr); found = true; break; } @@ -825,7 +950,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { .detail("ClassSource", class_source.present() ? class_source.get().toString() : "__Nothing"); // Very rarely, we get an empty worker list, thus no class_source data if (class_source.present()) - ASSERT(class_source.get() == LiteralStringRef("set_class")); + ASSERT(class_source.get() == "set_class"_sr); tx->reset(); } else { // If no worker process returned, skip the test @@ -976,9 +1101,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { Optional res = wait(tx->get(coordinatorsKey)); ASSERT(res.present()); // Otherwise, database is in a bad state state ClusterConnectionString cs(res.get().toString()); - Optional coordinator_processes_key = - wait(tx->get(LiteralStringRef("processes") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")))); + Optional coordinator_processes_key = wait( + tx->get("processes"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")))); ASSERT(coordinator_processes_key.present()); state std::vector process_addresses; boost::split( @@ -1004,7 +1128,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { state std::vector old_coordinators_processes; state bool possible_to_add_coordinator; state KeyRange coordinators_key_range = - KeyRangeRef(LiteralStringRef("process/"), LiteralStringRef("process0")) + KeyRangeRef("process/"_sr, "process0"_sr) .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")); loop { try { @@ -1017,9 +1141,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { ? deterministicRandom()->randomAlphaNumeric(8) : ccStr.clusterKeyName().toString(); // get current coordinators - Optional processes_key = - wait(tx->get(LiteralStringRef("processes") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")))); + Optional processes_key = wait(tx->get( + "processes"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")))); ASSERT(processes_key.present()); boost::split( old_coordinators_processes, processes_key.get().toString(), [](char c) { return c == ','; }); @@ -1060,12 +1183,12 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { for (const auto& address : old_coordinators_processes) { new_processes_key += "," + address; } - tx->set(LiteralStringRef("processes") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), - Value(new_processes_key)); + tx->set( + "processes"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), + Value(new_processes_key)); // update cluster description - tx->set(LiteralStringRef("cluster_description") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), + tx->set("cluster_description"_sr.withPrefix( + SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), Value(new_cluster_description)); wait(tx->commit()); ASSERT(false); @@ -1132,9 +1255,9 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { new_processes_key += new_processes_key.size() ? "," : ""; new_processes_key += address; } - tx->set(LiteralStringRef("processes") - .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), - Value(new_processes_key)); + tx->set( + "processes"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("coordinators")), + Value(new_processes_key)); wait(tx->commit()); ASSERT(false); } catch (Error& e) { @@ -1212,8 +1335,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { CLIENT_KNOBS->TOO_MANY)); // By default, data_distribution/mode := "-1" ASSERT(!ddKVs.more && ddKVs.size() == 1); - ASSERT(ddKVs[0].key == LiteralStringRef("mode").withPrefix( - SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"))); + ASSERT(ddKVs[0].key == + "mode"_sr.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"))); ASSERT(ddKVs[0].value == Value(boost::lexical_cast(-1))); tx->reset(); break; @@ -1313,8 +1436,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { tx->setOption(FDBTransactionOptions::RAW_ACCESS); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"); - tx->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("0")); - tx->set(LiteralStringRef("rebalance_ignored").withPrefix(ddPrefix), + tx->set("mode"_sr.withPrefix(ddPrefix), "0"_sr); + tx->set("rebalance_ignored"_sr.withPrefix(ddPrefix), BinaryWriter::toValue(ddIgnoreValue, Unversioned())); wait(tx->commit()); tx->reset(); @@ -1356,8 +1479,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { tx->clear(ignoreSSFailuresZoneString.withPrefix( SpecialKeySpace::getManagementApiCommandPrefix("maintenance"))); KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"); - tx->clear(LiteralStringRef("mode").withPrefix(ddPrefix)); - tx->clear(LiteralStringRef("rebalance_ignored").withPrefix(ddPrefix)); + tx->clear("mode"_sr.withPrefix(ddPrefix)); + tx->clear("rebalance_ignored"_sr.withPrefix(ddPrefix)); wait(tx->commit()); tx->reset(); break; @@ -1399,14 +1522,14 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { Version readVersion = wait(tr1->getReadVersion()); tr2->setVersion(readVersion); KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"); - tr1->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("1")); + tr1->set("mode"_sr.withPrefix(ddPrefix), "1"_sr); wait(tr1->commit()); // randomly read the moveKeysLockOwnerKey/moveKeysLockWriteKey // both of them should be grabbed when changing dd mode wait(success( tr2->get(deterministicRandom()->coinflip() ? moveKeysLockOwnerKey : moveKeysLockWriteKey))); // tr2 shoulde never succeed, just write to a key to make it not a read-only transaction - tr2->set(LiteralStringRef("unused_key"), LiteralStringRef("")); + tr2->set("unused_key"_sr, ""_sr); wait(tr2->commit()); ASSERT(false); // commit should always fail due to conflict } catch (Error& e) { diff --git a/fdbserver/workloads/StatusWorkload.actor.cpp b/fdbserver/workloads/StatusWorkload.actor.cpp index 29279fc036..52fbb43860 100644 --- a/fdbserver/workloads/StatusWorkload.actor.cpp +++ b/fdbserver/workloads/StatusWorkload.actor.cpp @@ -39,11 +39,10 @@ struct StatusWorkload : TestWorkload { StatusWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), requests("Status requests issued"), replies("Status replies received"), errors("Status Errors"), totalSize("Status reply size sum") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - requestsPerSecond = getOption(options, LiteralStringRef("requestsPerSecond"), 0.5); - enableLatencyBands = - getOption(options, LiteralStringRef("enableLatencyBands"), deterministicRandom()->random01() < 0.5); - auto statusSchemaStr = getOption(options, LiteralStringRef("schema"), JSONSchemas::statusSchema); + testDuration = getOption(options, "testDuration"_sr, 10.0); + requestsPerSecond = getOption(options, "requestsPerSecond"_sr, 0.5); + enableLatencyBands = getOption(options, "enableLatencyBands"_sr, deterministicRandom()->random01() < 0.5); + auto statusSchemaStr = getOption(options, "schema"_sr, JSONSchemas::statusSchema); if (statusSchemaStr.size()) { json_spirit::mValue schema = readJSONStrictly(statusSchemaStr.toString()); parsedSchema = schema.get_obj(); diff --git a/fdbserver/workloads/Storefront.actor.cpp b/fdbserver/workloads/Storefront.actor.cpp index cd036074c5..e1c2b72baa 100644 --- a/fdbserver/workloads/Storefront.actor.cpp +++ b/fdbserver/workloads/Storefront.actor.cpp @@ -43,15 +43,12 @@ struct StorefrontWorkload : TestWorkload { StorefrontWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries"), spuriousCommitFailures("Spurious Commit Failures"), totalLatency("Total Latency") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 1000.0); - actorCount = - getOption(options, LiteralStringRef("actorsPerClient"), std::max((int)(transactionsPerSecond / 100), 1)); - maxOrderSize = getOption(options, LiteralStringRef("maxOrderSize"), 20); - itemCount = - getOption(options, LiteralStringRef("itemCount"), transactionsPerSecond * clientCount * maxOrderSize); - minExpectedTransactionsPerSecond = - transactionsPerSecond * getOption(options, LiteralStringRef("expectedRate"), 0.9); + testDuration = getOption(options, "testDuration"_sr, 10.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 1000.0); + actorCount = getOption(options, "actorsPerClient"_sr, std::max((int)(transactionsPerSecond / 100), 1)); + maxOrderSize = getOption(options, "maxOrderSize"_sr, 20); + itemCount = getOption(options, "itemCount"_sr, transactionsPerSecond * clientCount * maxOrderSize); + minExpectedTransactionsPerSecond = transactionsPerSecond * getOption(options, "expectedRate"_sr, 0.9); } std::string description() const override { return "StorefrontWorkload"; } @@ -202,8 +199,7 @@ struct StorefrontWorkload : TestWorkload { ACTOR Future tableBalancer(Database cx, StorefrontWorkload* self, KeyRangeRef keyRange) { state std::vector>> accumulators; - accumulators.push_back( - self->orderAccumulator(cx, self, KeyRangeRef(LiteralStringRef("/orders/f"), LiteralStringRef("/orders0")))); + accumulators.push_back(self->orderAccumulator(cx, self, KeyRangeRef("/orders/f"_sr, "/orders0"_sr))); for (int c = 0; c < 15; c++) accumulators.push_back(self->orderAccumulator( cx, self, KeyRangeRef(Key(format("/orders/%x", c)), Key(format("/orders/%x", c + 1))))); diff --git a/fdbserver/workloads/StreamingRead.actor.cpp b/fdbserver/workloads/StreamingRead.actor.cpp index 71b3df8bbb..891c835d2e 100644 --- a/fdbserver/workloads/StreamingRead.actor.cpp +++ b/fdbserver/workloads/StreamingRead.actor.cpp @@ -40,17 +40,17 @@ struct StreamingReadWorkload : TestWorkload { StreamingReadWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), readKeys("Keys Read"), readValueBytes("Value Bytes Read"), latencies(2000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - actorCount = getOption(options, LiteralStringRef("actorCount"), 20); - readsPerTransaction = getOption(options, LiteralStringRef("readsPerTransaction"), 10); - rangesPerTransaction = getOption(options, LiteralStringRef("rangesPerTransaction"), 1); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), 100000); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); - valueBytes = std::max(getOption(options, LiteralStringRef("valueBytes"), 96), 16); + testDuration = getOption(options, "testDuration"_sr, 10.0); + actorCount = getOption(options, "actorCount"_sr, 20); + readsPerTransaction = getOption(options, "readsPerTransaction"_sr, 10); + rangesPerTransaction = getOption(options, "rangesPerTransaction"_sr, 1); + nodeCount = getOption(options, "nodeCount"_sr, 100000); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); + valueBytes = std::max(getOption(options, "valueBytes"_sr, 96), 16); std::string valueFormat = "%016llx" + std::string(valueBytes - 16, '.'); - warmingDelay = getOption(options, LiteralStringRef("warmingDelay"), 0.0); + warmingDelay = getOption(options, "warmingDelay"_sr, 0.0); constantValue = Value(format(valueFormat.c_str(), 42)); - readSequentially = getOption(options, LiteralStringRef("readSequentially"), false); + readSequentially = getOption(options, "readSequentially"_sr, false); } std::string description() const override { return "StreamingRead"; } diff --git a/fdbserver/workloads/SubmitBackup.actor.cpp b/fdbserver/workloads/SubmitBackup.actor.cpp index aa4dd13d9b..dc87722bb3 100644 --- a/fdbserver/workloads/SubmitBackup.actor.cpp +++ b/fdbserver/workloads/SubmitBackup.actor.cpp @@ -39,13 +39,13 @@ struct SubmitBackupWorkload final : TestWorkload { IncrementalBackupOnly incremental{ false }; SubmitBackupWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - backupDir = getOption(options, LiteralStringRef("backupDir"), LiteralStringRef("file://simfdb/backups/")); - tag = getOption(options, LiteralStringRef("tag"), LiteralStringRef("default")); - delayFor = getOption(options, LiteralStringRef("delayFor"), 10.0); - initSnapshotInterval = getOption(options, LiteralStringRef("initSnapshotInterval"), 0); - snapshotInterval = getOption(options, LiteralStringRef("snapshotInterval"), 1e8); - stopWhenDone.set(getOption(options, LiteralStringRef("stopWhenDone"), true)); - incremental.set(getOption(options, LiteralStringRef("incremental"), false)); + backupDir = getOption(options, "backupDir"_sr, "file://simfdb/backups/"_sr); + tag = getOption(options, "tag"_sr, "default"_sr); + delayFor = getOption(options, "delayFor"_sr, 10.0); + initSnapshotInterval = getOption(options, "initSnapshotInterval"_sr, 0); + snapshotInterval = getOption(options, "snapshotInterval"_sr, 1e8); + stopWhenDone.set(getOption(options, "stopWhenDone"_sr, true)); + incremental.set(getOption(options, "incremental"_sr, false)); } static constexpr const char* DESCRIPTION = "SubmitBackup"; diff --git a/fdbserver/workloads/SuspendProcesses.actor.cpp b/fdbserver/workloads/SuspendProcesses.actor.cpp index ce1a5c05e2..7b24b11eeb 100644 --- a/fdbserver/workloads/SuspendProcesses.actor.cpp +++ b/fdbserver/workloads/SuspendProcesses.actor.cpp @@ -34,10 +34,9 @@ struct SuspendProcessesWorkload : TestWorkload { double waitTimeDuration; SuspendProcessesWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - prefixSuspendProcesses = - getOption(options, LiteralStringRef("prefixesSuspendProcesses"), std::vector()); - waitTimeDuration = getOption(options, LiteralStringRef("waitTimeDuration"), 0); - suspendTimeDuration = getOption(options, LiteralStringRef("suspendTimeDuration"), 0); + prefixSuspendProcesses = getOption(options, "prefixesSuspendProcesses"_sr, std::vector()); + waitTimeDuration = getOption(options, "waitTimeDuration"_sr, 0); + suspendTimeDuration = getOption(options, "suspendTimeDuration"_sr, 0); } std::string description() const override { return "SuspendProcesses"; } @@ -51,16 +50,14 @@ struct SuspendProcessesWorkload : TestWorkload { try { tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr.setOption(FDBTransactionOptions::LOCK_AWARE); - RangeResult kvs = wait(tr.getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), - LiteralStringRef("\xff\xff/worker_interfaces0")), - CLIENT_KNOBS->TOO_MANY)); + RangeResult kvs = + wait(tr.getRange(KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr), + CLIENT_KNOBS->TOO_MANY)); ASSERT(!kvs.more); std::vector> suspendProcessInterfaces; for (auto it : kvs) { - auto ip_port = - (it.key.endsWith(LiteralStringRef(":tls")) ? it.key.removeSuffix(LiteralStringRef(":tls")) - : it.key) - .removePrefix(LiteralStringRef("\xff\xff/worker_interfaces/")); + auto ip_port = (it.key.endsWith(":tls"_sr) ? it.key.removeSuffix(":tls"_sr) : it.key) + .removePrefix("\xff\xff/worker_interfaces/"_sr); for (auto& killProcess : self->prefixSuspendProcesses) { if (boost::starts_with(ip_port.toString().c_str(), killProcess.c_str())) { suspendProcessInterfaces.push_back(it.value); diff --git a/fdbserver/workloads/TagThrottleApi.actor.cpp b/fdbserver/workloads/TagThrottleApi.actor.cpp index b24ead205f..b1b06d2f8f 100644 --- a/fdbserver/workloads/TagThrottleApi.actor.cpp +++ b/fdbserver/workloads/TagThrottleApi.actor.cpp @@ -33,7 +33,7 @@ struct TagThrottleApiWorkload : TestWorkload { constexpr static const char* NAME = "TagThrottleApi"; TagThrottleApiWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); + testDuration = getOption(options, "testDuration"_sr, 10.0); autoThrottleEnabled = SERVER_KNOBS->AUTO_TAG_THROTTLING_ENABLED; } diff --git a/fdbserver/workloads/TargetedKill.actor.cpp b/fdbserver/workloads/TargetedKill.actor.cpp index f9c39e499a..2ba29b814f 100644 --- a/fdbserver/workloads/TargetedKill.actor.cpp +++ b/fdbserver/workloads/TargetedKill.actor.cpp @@ -39,12 +39,12 @@ struct TargetedKillWorkload : TestWorkload { TargetedKillWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { enabled = !clientId; // only do this on the "first" client - killAt = getOption(options, LiteralStringRef("killAt"), 5.0); - reboot = getOption(options, LiteralStringRef("reboot"), false); - suspendDuration = getOption(options, LiteralStringRef("suspendDuration"), 1.0); - machineToKill = getOption(options, LiteralStringRef("machineToKill"), LiteralStringRef("master")).toString(); - killAllMachineProcesses = getOption(options, LiteralStringRef("killWholeMachine"), false); - numKillStorages = getOption(options, LiteralStringRef("numKillStorages"), 1); + killAt = getOption(options, "killAt"_sr, 5.0); + reboot = getOption(options, "reboot"_sr, false); + suspendDuration = getOption(options, "suspendDuration"_sr, 1.0); + machineToKill = getOption(options, "machineToKill"_sr, "master"_sr).toString(); + killAllMachineProcesses = getOption(options, "killWholeMachine"_sr, false); + numKillStorages = getOption(options, "numKillStorages"_sr, 1); } std::string description() const override { return "TargetedKillWorkload"; } @@ -61,8 +61,8 @@ struct TargetedKillWorkload : TestWorkload { NetworkAddress address, Database cx, TargetedKillWorkload* self) { - if (&g_simulator == g_network) { - g_simulator.killInterface(address, ISimulator::KillInstantly); + if (g_simulator == g_network) { + g_simulator->killInterface(address, ISimulator::KillInstantly); return Void(); } diff --git a/fdbserver/workloads/TaskBucketCorrectness.actor.cpp b/fdbserver/workloads/TaskBucketCorrectness.actor.cpp index 2e2f265b19..6f4adb4b57 100644 --- a/fdbserver/workloads/TaskBucketCorrectness.actor.cpp +++ b/fdbserver/workloads/TaskBucketCorrectness.actor.cpp @@ -71,7 +71,7 @@ struct SayHelloTaskFunc : TaskFuncBase { state Key key = StringRef("Hello_" + deterministicRandom()->randomUniqueID().toString()); state Key value; - auto itor = task->params.find(LiteralStringRef("name")); + auto itor = task->params.find("name"_sr); if (itor != task->params.end()) { value = itor->value; TraceEvent("TaskBucketCorrectnessSayHello").detail("SayHelloTaskFunc", printable(itor->value)); @@ -79,11 +79,11 @@ struct SayHelloTaskFunc : TaskFuncBase { ASSERT(false); } - if (!task->params[LiteralStringRef("chained")].compare(LiteralStringRef("false"))) { + if (!task->params["chained"_sr].compare("false"_sr)) { wait(done->set(tr, taskBucket)); } else { - int subtaskCount = atoi(task->params[LiteralStringRef("subtaskCount")].toString().c_str()); - int currTaskNumber = atoi(value.removePrefix(LiteralStringRef("task_")).toString().c_str()); + int subtaskCount = atoi(task->params["subtaskCount"_sr].toString().c_str()); + int currTaskNumber = atoi(value.removePrefix("task_"_sr).toString().c_str()); TraceEvent("TaskBucketCorrectnessSayHello") .detail("SubtaskCount", subtaskCount) .detail("CurrTaskNumber", currTaskNumber); @@ -94,9 +94,9 @@ struct SayHelloTaskFunc : TaskFuncBase { SayHelloTaskFunc::version, StringRef(), deterministicRandom()->randomInt(0, 2)); - new_task->params[LiteralStringRef("name")] = StringRef(format("task_%d", currTaskNumber + 1)); - new_task->params[LiteralStringRef("chained")] = task->params[LiteralStringRef("chained")]; - new_task->params[LiteralStringRef("subtaskCount")] = task->params[LiteralStringRef("subtaskCount")]; + new_task->params["name"_sr] = StringRef(format("task_%d", currTaskNumber + 1)); + new_task->params["chained"_sr] = task->params["chained"_sr]; + new_task->params["subtaskCount"_sr] = task->params["subtaskCount"_sr]; Reference taskDone = futureBucket->future(tr); new_task->params[Task::reservedTaskParamKeyDone] = taskDone->key; taskBucket->addTask(tr, new_task); @@ -112,7 +112,7 @@ struct SayHelloTaskFunc : TaskFuncBase { return Void(); } }; -StringRef SayHelloTaskFunc::name = LiteralStringRef("SayHello"); +StringRef SayHelloTaskFunc::name = "SayHello"_sr; REGISTER_TASKFUNC(SayHelloTaskFunc); struct SayHelloToEveryoneTaskFunc : TaskFuncBase { @@ -141,15 +141,15 @@ struct SayHelloToEveryoneTaskFunc : TaskFuncBase { state std::vector> vectorFuture; int subtaskCount = 1; - if (!task->params[LiteralStringRef("chained")].compare(LiteralStringRef("false"))) { - subtaskCount = atoi(task->params[LiteralStringRef("subtaskCount")].toString().c_str()); + if (!task->params["chained"_sr].compare("false"_sr)) { + subtaskCount = atoi(task->params["subtaskCount"_sr].toString().c_str()); } for (int i = 0; i < subtaskCount; ++i) { auto new_task = makeReference( SayHelloTaskFunc::name, SayHelloTaskFunc::version, StringRef(), deterministicRandom()->randomInt(0, 2)); - new_task->params[LiteralStringRef("name")] = StringRef(format("task_%d", i)); - new_task->params[LiteralStringRef("chained")] = task->params[LiteralStringRef("chained")]; - new_task->params[LiteralStringRef("subtaskCount")] = task->params[LiteralStringRef("subtaskCount")]; + new_task->params["name"_sr] = StringRef(format("task_%d", i)); + new_task->params["chained"_sr] = task->params["chained"_sr]; + new_task->params["subtaskCount"_sr] = task->params["subtaskCount"_sr]; Reference taskDone = futureBucket->future(tr); new_task->params[Task::reservedTaskParamKeyDone] = taskDone->key; taskBucket->addTask(tr, new_task); @@ -160,14 +160,14 @@ struct SayHelloToEveryoneTaskFunc : TaskFuncBase { wait(taskBucket->finish(tr, task)); Key key = StringRef("Hello_" + deterministicRandom()->randomUniqueID().toString()); - Value value = LiteralStringRef("Hello, Everyone!"); + Value value = "Hello, Everyone!"_sr; TraceEvent("TaskBucketCorrectnessSayHello").detail("SayHelloToEveryoneTaskFunc", printable(value)); tr->set(key, value); return Void(); } }; -StringRef SayHelloToEveryoneTaskFunc::name = LiteralStringRef("SayHelloToEveryone"); +StringRef SayHelloToEveryoneTaskFunc::name = "SayHelloToEveryone"_sr; REGISTER_TASKFUNC(SayHelloToEveryoneTaskFunc); struct SaidHelloTaskFunc : TaskFuncBase { @@ -195,14 +195,14 @@ struct SaidHelloTaskFunc : TaskFuncBase { wait(taskBucket->finish(tr, task)); Key key = StringRef("Hello_" + deterministicRandom()->randomUniqueID().toString()); - Value value = LiteralStringRef("Said hello to everyone!"); + Value value = "Said hello to everyone!"_sr; TraceEvent("TaskBucketCorrectnessSayHello").detail("SaidHelloTaskFunc", printable(value)); tr->set(key, value); return Void(); } }; -StringRef SaidHelloTaskFunc::name = LiteralStringRef("SaidHello"); +StringRef SaidHelloTaskFunc::name = "SaidHello"_sr; REGISTER_TASKFUNC(SaidHelloTaskFunc); // A workload which test the correctness of TaskBucket @@ -211,8 +211,8 @@ struct TaskBucketCorrectnessWorkload : TestWorkload { int subtaskCount; TaskBucketCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - chained = getOption(options, LiteralStringRef("chained"), false); - subtaskCount = getOption(options, LiteralStringRef("subtaskCount"), 20); + chained = getOption(options, "chained"_sr, false); + subtaskCount = getOption(options, "subtaskCount"_sr, 20); } std::string description() const override { return "TaskBucketCorrectness"; } @@ -228,11 +228,11 @@ struct TaskBucketCorrectnessWorkload : TestWorkload { Reference futureBucket, bool chained, int subtaskCount) { - state Key addedInitKey(LiteralStringRef("addedInitTasks")); + state Key addedInitKey("addedInitTasks"_sr); Optional> res = wait(tr->get(addedInitKey)); if (res.present()) return Void(); - tr->set(addedInitKey, LiteralStringRef("true")); + tr->set(addedInitKey, "true"_sr); Reference allDone = futureBucket->future(tr); auto task = makeReference(SayHelloToEveryoneTaskFunc::name, @@ -240,8 +240,8 @@ struct TaskBucketCorrectnessWorkload : TestWorkload { allDone->key, deterministicRandom()->randomInt(0, 2)); - task->params[LiteralStringRef("chained")] = chained ? LiteralStringRef("true") : LiteralStringRef("false"); - task->params[LiteralStringRef("subtaskCount")] = StringRef(format("%d", subtaskCount)); + task->params["chained"_sr] = chained ? "true"_sr : "false"_sr; + task->params["subtaskCount"_sr] = StringRef(format("%d", subtaskCount)); taskBucket->addTask(tr, task); auto taskDone = makeReference( SaidHelloTaskFunc::name, SaidHelloTaskFunc::version, StringRef(), deterministicRandom()->randomInt(0, 2)); @@ -251,9 +251,9 @@ struct TaskBucketCorrectnessWorkload : TestWorkload { ACTOR Future _start(Database cx, TaskBucketCorrectnessWorkload* self) { state Reference tr(new ReadYourWritesTransaction(cx)); - state Subspace taskSubspace(LiteralStringRef("backup-agent")); - state Reference taskBucket(new TaskBucket(taskSubspace.get(LiteralStringRef("tasks")))); - state Reference futureBucket(new FutureBucket(taskSubspace.get(LiteralStringRef("futures")))); + state Subspace taskSubspace("backup-agent"_sr); + state Reference taskBucket(new TaskBucket(taskSubspace.get("tasks"_sr))); + state Reference futureBucket(new FutureBucket(taskSubspace.get("futures"_sr))); try { if (self->clientId == 0) { @@ -319,8 +319,7 @@ struct TaskBucketCorrectnessWorkload : TestWorkload { data.insert(format("task_%d", i)); } - RangeResult values = wait(tr->getRange( - KeyRangeRef(LiteralStringRef("Hello_\x00"), LiteralStringRef("Hello_\xff")), CLIENT_KNOBS->TOO_MANY)); + RangeResult values = wait(tr->getRange(KeyRangeRef("Hello_\x00"_sr, "Hello_\xff"_sr), CLIENT_KNOBS->TOO_MANY)); if (values.size() != data.size()) { TraceEvent(SevError, "CheckSayHello") .detail("CountNotMatchIs", values.size()) @@ -355,53 +354,53 @@ TEST_CASE("/fdbclient/TaskBucket/Subspace") { print_subspace_key(subspace_test, 0); ASSERT(subspace_test.key().toString() == ""); - Subspace subspace_test1(LiteralStringRef("abc")); + Subspace subspace_test1("abc"_sr); print_subspace_key(subspace_test1, 1); - ASSERT(subspace_test1.key() == LiteralStringRef("abc")); + ASSERT(subspace_test1.key() == "abc"_sr); Tuple t = Tuple::makeTuple("user"_sr); Subspace subspace_test2(t); print_subspace_key(subspace_test2, 2); - ASSERT(subspace_test2.key() == LiteralStringRef("\x01user\x00")); + ASSERT(subspace_test2.key() == "\x01user\x00"_sr); - Subspace subspace_test3(t, LiteralStringRef("abc")); + Subspace subspace_test3(t, "abc"_sr); print_subspace_key(subspace_test3, 3); - ASSERT(subspace_test3.key() == LiteralStringRef("abc\x01user\x00")); + ASSERT(subspace_test3.key() == "abc\x01user\x00"_sr); Tuple t1 = Tuple::makeTuple(1); Subspace subspace_test4(t1); print_subspace_key(subspace_test4, 4); - ASSERT(subspace_test4.key() == LiteralStringRef("\x15\x01")); + ASSERT(subspace_test4.key() == "\x15\x01"_sr); t.append(123); - Subspace subspace_test5(t, LiteralStringRef("abc")); + Subspace subspace_test5(t, "abc"_sr); print_subspace_key(subspace_test5, 5); - ASSERT(subspace_test5.key() == LiteralStringRef("abc\x01user\x00\x15\x7b")); + ASSERT(subspace_test5.key() == "abc\x01user\x00\x15\x7b"_sr); // Subspace pack printf("%d==========%s===%d\n", 6, printable(subspace_test5.pack(t)).c_str(), subspace_test5.pack(t).size()); - ASSERT(subspace_test5.pack(t) == LiteralStringRef("abc\x01user\x00\x15\x7b\x01user\x00\x15\x7b")); + ASSERT(subspace_test5.pack(t) == "abc\x01user\x00\x15\x7b\x01user\x00\x15\x7b"_sr); printf("%d==========%s===%d\n", 7, printable(subspace_test5.pack(t1)).c_str(), subspace_test5.pack(t1).size()); - ASSERT(subspace_test5.pack(t1) == LiteralStringRef("abc\x01user\x00\x15\x7b\x15\x01")); + ASSERT(subspace_test5.pack(t1) == "abc\x01user\x00\x15\x7b\x15\x01"_sr); // Subspace getItem Subspace subspace_test6(t); - Subspace subspace_test7 = subspace_test6.get(LiteralStringRef("subitem")); + Subspace subspace_test7 = subspace_test6.get("subitem"_sr); print_subspace_key(subspace_test7, 8); - ASSERT(subspace_test7.key() == LiteralStringRef("\x01user\x00\x15\x7b\x01subitem\x00")); + ASSERT(subspace_test7.key() == "\x01user\x00\x15\x7b\x01subitem\x00"_sr); // Subspace unpack Tuple t2 = subspace_test6.unpack(subspace_test7.key()); Subspace subspace_test8(t2); print_subspace_key(subspace_test8, 9); - ASSERT(subspace_test8.key() == LiteralStringRef("\x01subitem\x00")); + ASSERT(subspace_test8.key() == "\x01subitem\x00"_sr); // pack Tuple t3 = Tuple::makeTuple(""_sr); printf("%d==========%s===%d\n", 10, printable(subspace_test5.pack(t3)).c_str(), subspace_test5.pack(t3).size()); ASSERT(subspace_test5.pack(t3) == subspace_test5.pack(StringRef())); - ASSERT(subspace_test5.pack(t3) == LiteralStringRef("abc\x01user\x00\x15\x7b\x01\x00")); + ASSERT(subspace_test5.pack(t3) == "abc\x01user\x00\x15\x7b\x01\x00"_sr); printf("%d==========%s===%d\n", 11, @@ -414,8 +413,8 @@ TEST_CASE("/fdbclient/TaskBucket/Subspace") { subspace_test5.range(t3).end.size()); ASSERT(subspace_test5.range(t3).end == subspace_test5.get(StringRef()).range().end); - StringRef def = LiteralStringRef("def"); - StringRef ghi = LiteralStringRef("ghi"); + StringRef def = "def"_sr; + StringRef ghi = "ghi"_sr; t3.append(def); t3.append(ghi); printf("%d==========%s===%d\n", 13, printable(subspace_test5.pack(t3)).c_str(), subspace_test5.pack(t3).size()); diff --git a/fdbserver/workloads/TenantManagementConcurrencyWorkload.actor.cpp b/fdbserver/workloads/TenantManagementConcurrencyWorkload.actor.cpp index f74baa34e8..8911417c37 100644 --- a/fdbserver/workloads/TenantManagementConcurrencyWorkload.actor.cpp +++ b/fdbserver/workloads/TenantManagementConcurrencyWorkload.actor.cpp @@ -92,7 +92,7 @@ struct TenantManagementConcurrencyWorkload : TestWorkload { return _setup(cx, this); } ACTOR static Future _setup(Database cx, TenantManagementConcurrencyWorkload* self) { - state ClusterConnectionString connectionString(g_simulator.extraDatabases[0]); + state ClusterConnectionString connectionString(g_simulator->extraDatabases[0]); Reference threadSafeHandle = wait(unsafeThreadFutureToFuture(ThreadSafeDatabase::createFromExistingDatabase(cx))); @@ -141,7 +141,7 @@ struct TenantManagementConcurrencyWorkload : TestWorkload { } if (self->useMetacluster) { - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = makeReference(connectionString); self->dataDb = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } else { @@ -342,4 +342,4 @@ struct TenantManagementConcurrencyWorkload : TestWorkload { }; WorkloadFactory TenantManagementConcurrencyWorkloadFactory( - "TenantManagementConcurrency"); \ No newline at end of file + "TenantManagementConcurrency"); diff --git a/fdbserver/workloads/TenantManagementWorkload.actor.cpp b/fdbserver/workloads/TenantManagementWorkload.actor.cpp index 3ecdf4d776..aff6f1b611 100644 --- a/fdbserver/workloads/TenantManagementWorkload.actor.cpp +++ b/fdbserver/workloads/TenantManagementWorkload.actor.cpp @@ -127,7 +127,7 @@ struct TenantManagementWorkload : TestWorkload { localTenantGroupNamePrefix = format("%stenantgroup_%d_", tenantNamePrefix.toString().c_str(), clientId); bool defaultUseMetacluster = false; - if (clientId == 0 && g_network->isSimulated() && !g_simulator.extraDatabases.empty()) { + if (clientId == 0 && g_network->isSimulated() && !g_simulator->extraDatabases.empty()) { defaultUseMetacluster = deterministicRandom()->coinflip(); } @@ -181,7 +181,7 @@ struct TenantManagementWorkload : TestWorkload { DataClusterEntry entry; entry.capacity.numTenantGroups = 1e9; wait(MetaclusterAPI::registerCluster( - self->mvDb, self->dataClusterName, g_simulator.extraDatabases[0], entry)); + self->mvDb, self->dataClusterName, g_simulator->extraDatabases[0], entry)); } state Transaction tr(cx); @@ -218,8 +218,8 @@ struct TenantManagementWorkload : TestWorkload { } if (self->useMetacluster) { - ASSERT(g_simulator.extraDatabases.size() == 1); - auto extraFile = makeReference(g_simulator.extraDatabases[0]); + ASSERT(g_simulator->extraDatabases.size() == 1); + auto extraFile = makeReference(g_simulator->extraDatabases[0]); self->dataDb = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } else { self->dataDb = cx; diff --git a/fdbserver/workloads/ThreadSafety.actor.cpp b/fdbserver/workloads/ThreadSafety.actor.cpp index f235aa58b1..c3a6103996 100644 --- a/fdbserver/workloads/ThreadSafety.actor.cpp +++ b/fdbserver/workloads/ThreadSafety.actor.cpp @@ -123,9 +123,9 @@ struct ThreadSafetyWorkload : TestWorkload { ThreadSafetyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), stopped(false) { - threadsPerClient = getOption(options, LiteralStringRef("threadsPerClient"), 3); - threadDuration = getOption(options, LiteralStringRef("threadDuration"), 60.0); - numKeys = getOption(options, LiteralStringRef("numKeys"), 100); + threadsPerClient = getOption(options, "threadsPerClient"_sr, 3); + threadDuration = getOption(options, "threadDuration"_sr, 60.0); + numKeys = getOption(options, "numKeys"_sr, 100); commitBarrier.setNumRequired(threadsPerClient); diff --git a/fdbserver/workloads/Throttling.actor.cpp b/fdbserver/workloads/Throttling.actor.cpp index 2a28e141c2..afcff1ea39 100644 --- a/fdbserver/workloads/Throttling.actor.cpp +++ b/fdbserver/workloads/Throttling.actor.cpp @@ -83,12 +83,12 @@ struct ThrottlingWorkload : KVWorkload { static constexpr const char* NAME = "Throttling"; ThrottlingWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), transactionsCommitted(0) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); - actorsPerClient = getOption(options, LiteralStringRef("actorsPerClient"), 10); - writesPerTransaction = getOption(options, LiteralStringRef("writesPerTransaction"), 10); - readsPerTransaction = getOption(options, LiteralStringRef("readsPerTransaction"), 10); - throttlingMultiplier = getOption(options, LiteralStringRef("throttlingMultiplier"), 0.5); - int maxBurst = getOption(options, LiteralStringRef("maxBurst"), 1000); + testDuration = getOption(options, "testDuration"_sr, 60.0); + actorsPerClient = getOption(options, "actorsPerClient"_sr, 10); + writesPerTransaction = getOption(options, "writesPerTransaction"_sr, 10); + readsPerTransaction = getOption(options, "readsPerTransaction"_sr, 10); + throttlingMultiplier = getOption(options, "throttlingMultiplier"_sr, 0.5); + int maxBurst = getOption(options, "maxBurst"_sr, 1000); tokenBucket.maxBurst = maxBurst; } @@ -131,13 +131,13 @@ struct ThrottlingWorkload : KVWorkload { state json_spirit::mValue logSchema = readJSONStrictly(JSONSchemas::logHealthSchema.toString()).get_obj(); loop { try { - RangeResult result = wait( - tr.getRange(prefixRange(LiteralStringRef("\xff\xff/metrics/health/")), CLIENT_KNOBS->TOO_MANY)); + RangeResult result = + wait(tr.getRange(prefixRange("\xff\xff/metrics/health/"_sr), CLIENT_KNOBS->TOO_MANY)); ASSERT(!result.more); for (const auto& [k, v] : result) { - ASSERT(k.startsWith(LiteralStringRef("\xff\xff/metrics/health/"))); + ASSERT(k.startsWith("\xff\xff/metrics/health/"_sr)); auto valueObj = readJSONStrictly(v.toString()).get_obj(); - if (k.removePrefix(LiteralStringRef("\xff\xff/metrics/health/")) == LiteralStringRef("aggregate")) { + if (k.removePrefix("\xff\xff/metrics/health/"_sr) == "aggregate"_sr) { CODE_PROBE(true, "Test aggregate health metrics schema"); std::string errorStr; if (!schemaMatch(aggregateSchema, valueObj, errorStr, SevError, true)) { @@ -148,10 +148,9 @@ struct ThrottlingWorkload : KVWorkload { } auto tpsLimit = valueObj.at("tps_limit").get_real(); self->tokenBucket.transactionRate = tpsLimit * self->throttlingMultiplier / self->clientCount; - } else if (k.removePrefix(LiteralStringRef("\xff\xff/metrics/health/")) - .startsWith(LiteralStringRef("storage/"))) { + } else if (k.removePrefix("\xff\xff/metrics/health/"_sr).startsWith("storage/"_sr)) { CODE_PROBE(true, "Test storage health metrics schema"); - UID::fromString(k.removePrefix(LiteralStringRef("\xff\xff/metrics/health/storage/")) + UID::fromString(k.removePrefix("\xff\xff/metrics/health/storage/"_sr) .toString()); // Will throw if it's not a valid uid std::string errorStr; if (!schemaMatch(storageSchema, valueObj, errorStr, SevError, true)) { @@ -160,10 +159,9 @@ struct ThrottlingWorkload : KVWorkload { .detail("JSON", json_spirit::write_string(json_spirit::mValue(v.toString()))); self->correctSpecialKeys = false; } - } else if (k.removePrefix(LiteralStringRef("\xff\xff/metrics/health/")) - .startsWith(LiteralStringRef("log/"))) { + } else if (k.removePrefix("\xff\xff/metrics/health/"_sr).startsWith("log/"_sr)) { CODE_PROBE(true, "Test log health metrics schema"); - UID::fromString(k.removePrefix(LiteralStringRef("\xff\xff/metrics/health/log/")) + UID::fromString(k.removePrefix("\xff\xff/metrics/health/log/"_sr) .toString()); // Will throw if it's not a valid uid std::string errorStr; if (!schemaMatch(logSchema, valueObj, errorStr, SevError, true)) { diff --git a/fdbserver/workloads/Throughput.actor.cpp b/fdbserver/workloads/Throughput.actor.cpp index 099154a171..a8e1d8106b 100644 --- a/fdbserver/workloads/Throughput.actor.cpp +++ b/fdbserver/workloads/Throughput.actor.cpp @@ -310,55 +310,52 @@ struct ThroughputWorkload : TestWorkload { auto multi = makeReference(); measurer = multi; - targetLatency = getOption(options, LiteralStringRef("targetLatency"), 0.05); + targetLatency = getOption(options, "targetLatency"_sr, 0.05); - int keyCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)100000); - int keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); - int maxValueBytes = getOption(options, LiteralStringRef("valueBytes"), 100); - int minValueBytes = getOption(options, LiteralStringRef("minValueBytes"), maxValueBytes); - double sweepDuration = getOption(options, LiteralStringRef("sweepDuration"), 0); - double sweepDelay = getOption(options, LiteralStringRef("sweepDelay"), 0); + int keyCount = getOption(options, "nodeCount"_sr, (uint64_t)100000); + int keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); + int maxValueBytes = getOption(options, "valueBytes"_sr, 100); + int minValueBytes = getOption(options, "minValueBytes"_sr, maxValueBytes); + double sweepDuration = getOption(options, "sweepDuration"_sr, 0); + double sweepDelay = getOption(options, "sweepDelay"_sr, 0); - auto AType = - Reference(new RWTransactor(getOption(options, LiteralStringRef("readsPerTransactionA"), 10), - getOption(options, LiteralStringRef("writesPerTransactionA"), 0), - keyCount, - keyBytes, - minValueBytes, - maxValueBytes)); - auto BType = - Reference(new RWTransactor(getOption(options, LiteralStringRef("readsPerTransactionB"), 5), - getOption(options, LiteralStringRef("writesPerTransactionB"), 5), - keyCount, - keyBytes, - minValueBytes, - maxValueBytes)); + auto AType = Reference(new RWTransactor(getOption(options, "readsPerTransactionA"_sr, 10), + getOption(options, "writesPerTransactionA"_sr, 0), + keyCount, + keyBytes, + minValueBytes, + maxValueBytes)); + auto BType = Reference(new RWTransactor(getOption(options, "readsPerTransactionB"_sr, 5), + getOption(options, "writesPerTransactionB"_sr, 5), + keyCount, + keyBytes, + minValueBytes, + maxValueBytes)); if (sweepDuration > 0) { op = Reference(new SweepTransactor(sweepDuration, sweepDelay, AType, BType)); } else { - op = Reference( - new ABTransactor(getOption(options, LiteralStringRef("alpha"), 0.1), AType, BType)); + op = Reference(new ABTransactor(getOption(options, "alpha"_sr, 0.1), AType, BType)); } - double measureDelay = getOption(options, LiteralStringRef("measureDelay"), 50.0); - double measureDuration = getOption(options, LiteralStringRef("measureDuration"), 10.0); + double measureDelay = getOption(options, "measureDelay"_sr, 50.0); + double measureDuration = getOption(options, "measureDuration"_sr, 10.0); multi->ms.push_back(Reference(new MeasureSinglePeriod(measureDelay, measureDuration))); - double measurePeriod = getOption(options, LiteralStringRef("measurePeriod"), 0.0); + double measurePeriod = getOption(options, "measurePeriod"_sr, 0.0); std::vector periodicMetrics = - getOption(options, LiteralStringRef("measurePeriodicMetrics"), std::vector()); + getOption(options, "measurePeriodicMetrics"_sr, std::vector()); if (measurePeriod) { ASSERT(periodicMetrics.size() != 0); multi->ms.push_back(Reference(new MeasurePeriodically( measurePeriod, std::set(periodicMetrics.begin(), periodicMetrics.end())))); } - Pgain = getOption(options, LiteralStringRef("ProportionalGain"), 0.1); - Igain = getOption(options, LiteralStringRef("IntegralGain"), 0.005); + Pgain = getOption(options, "ProportionalGain"_sr, 0.1); + Igain = getOption(options, "IntegralGain"_sr, 0.005); testDuration = measureDelay + measureDuration; - // testDuration = getOption( options, LiteralStringRef("testDuration"), measureDelay + measureDuration ); + // testDuration = getOption( options, "testDuration"_sr, measureDelay + measureDuration ); } std::string description() const override { return "Throughput"; } diff --git a/fdbserver/workloads/TimeKeeperCorrectness.actor.cpp b/fdbserver/workloads/TimeKeeperCorrectness.actor.cpp index 89f20c145f..acaa025d05 100644 --- a/fdbserver/workloads/TimeKeeperCorrectness.actor.cpp +++ b/fdbserver/workloads/TimeKeeperCorrectness.actor.cpp @@ -29,7 +29,7 @@ struct TimeKeeperCorrectnessWorkload : TestWorkload { std::map inMemTimeKeeper; TimeKeeperCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 20.0); + testDuration = getOption(options, "testDuration"_sr, 20.0); } std::string description() const override { return "TimeKeeperCorrectness"; } diff --git a/fdbserver/workloads/TriggerRecovery.actor.cpp b/fdbserver/workloads/TriggerRecovery.actor.cpp index ee5fc2fd9a..14d739a833 100644 --- a/fdbserver/workloads/TriggerRecovery.actor.cpp +++ b/fdbserver/workloads/TriggerRecovery.actor.cpp @@ -35,10 +35,10 @@ struct TriggerRecoveryLoopWorkload : TestWorkload { Optional currentNumOfResolvers; TriggerRecoveryLoopWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - startTime = getOption(options, LiteralStringRef("startTime"), 0.0); - numRecoveries = getOption(options, LiteralStringRef("numRecoveries"), deterministicRandom()->randomInt(1, 10)); - delayBetweenRecoveries = getOption(options, LiteralStringRef("delayBetweenRecoveries"), 0.0); - killAllProportion = getOption(options, LiteralStringRef("killAllProportion"), 0.1); + startTime = getOption(options, "startTime"_sr, 0.0); + numRecoveries = getOption(options, "numRecoveries"_sr, deterministicRandom()->randomInt(1, 10)); + delayBetweenRecoveries = getOption(options, "delayBetweenRecoveries"_sr, 0.0); + killAllProportion = getOption(options, "killAllProportion"_sr, 0.1); ASSERT((numRecoveries > 0) && (startTime >= 0) && (delayBetweenRecoveries >= 0)); TraceEvent(SevInfo, "TriggerRecoveryLoopSetup") .detail("StartTime", startTime) @@ -112,16 +112,14 @@ struct TriggerRecoveryLoopWorkload : TestWorkload { try { tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr.setOption(FDBTransactionOptions::LOCK_AWARE); - RangeResult kvs = wait(tr.getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), - LiteralStringRef("\xff\xff/worker_interfaces0")), - CLIENT_KNOBS->TOO_MANY)); + RangeResult kvs = + wait(tr.getRange(KeyRangeRef("\xff\xff/worker_interfaces/"_sr, "\xff\xff/worker_interfaces0"_sr), + CLIENT_KNOBS->TOO_MANY)); ASSERT(!kvs.more); std::map address_interface; for (auto it : kvs) { - auto ip_port = - (it.key.endsWith(LiteralStringRef(":tls")) ? it.key.removeSuffix(LiteralStringRef(":tls")) - : it.key) - .removePrefix(LiteralStringRef("\xff\xff/worker_interfaces/")); + auto ip_port = (it.key.endsWith(":tls"_sr) ? it.key.removeSuffix(":tls"_sr) : it.key) + .removePrefix("\xff\xff/worker_interfaces/"_sr); address_interface[ip_port] = it.value; } for (auto it : address_interface) { @@ -129,7 +127,7 @@ struct TriggerRecoveryLoopWorkload : TestWorkload { BinaryReader::fromStringRef(it.second, IncludeVersion()) .reboot.send(RebootRequest()); else - tr.set(LiteralStringRef("\xff\xff/reboot_worker"), it.second); + tr.set("\xff\xff/reboot_worker"_sr, it.second); } TraceEvent(SevInfo, "TriggerRecoveryLoop_AttempedKillAll").log(); return Void(); diff --git a/fdbserver/workloads/UnitTests.actor.cpp b/fdbserver/workloads/UnitTests.actor.cpp index 4d0ac0c87e..8bafb1a262 100644 --- a/fdbserver/workloads/UnitTests.actor.cpp +++ b/fdbserver/workloads/UnitTests.actor.cpp @@ -42,10 +42,12 @@ void forceLinkRESTClientTests(); void forceLinkRESTUtilsTests(); void forceLinkRESTKmsConnectorTest(); void forceLinkCompressionUtilsTest(); +void forceLinkAtomicTests(); struct UnitTestWorkload : TestWorkload { bool enabled; std::string testPattern; + Optional testsIgnored; int testRunLimit; UnitTestParameters testParams; bool cleanupAfterTests; @@ -59,6 +61,9 @@ struct UnitTestWorkload : TestWorkload { totalSimTime("Total flow time (s)") { enabled = !clientId; // only do this on the "first" client testPattern = getOption(options, "testsMatching"_sr, Value()).toString(); + if (hasOption(options, "testsIgnored"_sr)) { + testsIgnored = getOption(options, "testsIgnored"_sr, Value()).toString(); + } testRunLimit = getOption(options, "maxTestCases"_sr, -1); if (g_network->isSimulated()) { testParams.setDataDir(getOption(options, "dataDir"_sr, "simfdb/unittests/"_sr).toString()); @@ -81,7 +86,7 @@ struct UnitTestWorkload : TestWorkload { forceLinkMemcpyTests(); forceLinkMemcpyPerfTests(); forceLinkStreamCipherTests(); - void forceLinkBlobCipherTests(); + forceLinkBlobCipherTests(); forceLinkParallelStreamTests(); forceLinkSimExternalConnectionTests(); forceLinkMutationLogReaderTests(); @@ -94,6 +99,7 @@ struct UnitTestWorkload : TestWorkload { forceLinkRESTUtilsTests(); forceLinkRESTKmsConnectorTest(); forceLinkCompressionUtilsTest(); + forceLinkAtomicTests(); } std::string description() const override { return "UnitTests"; } @@ -115,11 +121,16 @@ struct UnitTestWorkload : TestWorkload { m.push_back(totalSimTime.getMetric()); } + bool testMatched(std::string const& testName) const { + return StringRef(testName).startsWith(testPattern) && + (!testsIgnored.present() || !StringRef(testName).startsWith(testsIgnored.get())); + } + ACTOR static Future runUnitTests(UnitTestWorkload* self) { state std::vector tests; for (auto test = g_unittests.tests; test != nullptr; test = test->next) { - if (StringRef(test->name).startsWith(self->testPattern)) { + if (self->testMatched(test->name)) { ++self->testsAvailable; tests.push_back(test); } @@ -132,7 +143,9 @@ struct UnitTestWorkload : TestWorkload { fprintf(stdout, "Found %zu tests\n", tests.size()); if (tests.size() == 0) { - TraceEvent(SevError, "NoMatchingUnitTests").detail("TestPattern", self->testPattern); + TraceEvent(SevError, "NoMatchingUnitTests") + .detail("TestPattern", self->testPattern) + .detail("TestsIgnored", self->testsIgnored); ++self->testsFailed; return Void(); } diff --git a/fdbserver/workloads/Unreadable.actor.cpp b/fdbserver/workloads/Unreadable.actor.cpp index ee9dce883c..10781db717 100644 --- a/fdbserver/workloads/Unreadable.actor.cpp +++ b/fdbserver/workloads/Unreadable.actor.cpp @@ -32,8 +32,8 @@ struct UnreadableWorkload : TestWorkload { std::vector> clients; UnreadableWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)100000); + testDuration = getOption(options, "testDuration"_sr, 600.0); + nodeCount = getOption(options, "nodeCount"_sr, (uint64_t)100000); } std::string description() const override { return "Unreadable"; } diff --git a/fdbserver/workloads/VersionStamp.actor.cpp b/fdbserver/workloads/VersionStamp.actor.cpp index 770aa66277..946958e744 100644 --- a/fdbserver/workloads/VersionStamp.actor.cpp +++ b/fdbserver/workloads/VersionStamp.actor.cpp @@ -46,16 +46,16 @@ struct VersionStampWorkload : TestWorkload { bool allowMetadataVersionKey; VersionStampWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)10000); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 4); - failIfDataLost = getOption(options, LiteralStringRef("failIfDataLost"), true); - const Key prefix = getOption(options, LiteralStringRef("prefix"), LiteralStringRef("VS_")); - vsKeyPrefix = LiteralStringRef("K_").withPrefix(prefix); - vsValuePrefix = LiteralStringRef("V_").withPrefix(prefix); - validateExtraDB = getOption(options, LiteralStringRef("validateExtraDB"), false); - soleOwnerOfMetadataVersionKey = getOption(options, LiteralStringRef("soleOwnerOfMetadataVersionKey"), false); + testDuration = getOption(options, "testDuration"_sr, 60.0); + transactionsPerSecond = getOption(options, "transactionsPerSecond"_sr, 5000.0); + nodeCount = getOption(options, "nodeCount"_sr, (uint64_t)10000); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 4); + failIfDataLost = getOption(options, "failIfDataLost"_sr, true); + const Key prefix = getOption(options, "prefix"_sr, "VS_"_sr); + vsKeyPrefix = "K_"_sr.withPrefix(prefix); + vsValuePrefix = "V_"_sr.withPrefix(prefix); + validateExtraDB = getOption(options, "validateExtraDB"_sr, false); + soleOwnerOfMetadataVersionKey = getOption(options, "soleOwnerOfMetadataVersionKey"_sr, false); } std::string description() const override { return "VersionStamp"; } @@ -156,9 +156,9 @@ struct VersionStampWorkload : TestWorkload { ACTOR Future _check(Database cx, VersionStampWorkload* self) { if (self->validateExtraDB) { - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); cx = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } state ReadYourWritesTransaction tr(cx); @@ -315,10 +315,10 @@ struct VersionStampWorkload : TestWorkload { state double lastTime = now(); state Database extraDB; - if (!g_simulator.extraDatabases.empty()) { - ASSERT(g_simulator.extraDatabases.size() == 1); + if (!g_simulator->extraDatabases.empty()) { + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); } @@ -347,7 +347,7 @@ struct VersionStampWorkload : TestWorkload { } else if (oldVSFormat) { versionStampValue = value; } else { - versionStampValue = value.withSuffix(LiteralStringRef("\x00\x00\x00\x00")); + versionStampValue = value.withSuffix("\x00\x00\x00\x00"_sr); } state bool ryw = deterministicRandom()->coinflip(); @@ -385,7 +385,7 @@ struct VersionStampWorkload : TestWorkload { } catch (Error& e) { err = e; - if (err.code() == error_code_database_locked && !g_simulator.extraDatabases.empty()) { + if (err.code() == error_code_database_locked && !g_simulator->extraDatabases.empty()) { //TraceEvent("VST_CommitDatabaseLocked"); cx_is_primary = !cx_is_primary; tr = ReadYourWritesTransaction(cx_is_primary ? cx : extraDB); diff --git a/fdbserver/workloads/WatchAndWait.actor.cpp b/fdbserver/workloads/WatchAndWait.actor.cpp index 0d227923d3..a8017949a9 100644 --- a/fdbserver/workloads/WatchAndWait.actor.cpp +++ b/fdbserver/workloads/WatchAndWait.actor.cpp @@ -36,12 +36,12 @@ struct WatchAndWaitWorkload : TestWorkload { PerfIntCounter triggers, retries; WatchAndWaitWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), triggers("Triggers"), retries("Retries") { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - watchCount = getOption(options, LiteralStringRef("watchCount"), (uint64_t)10000); - nodeCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)100000); - nodePrefix = getOption(options, LiteralStringRef("nodePrefix"), (int64_t)-1); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 4); - triggerWatches = getOption(options, LiteralStringRef("triggerWatches"), false); + testDuration = getOption(options, "testDuration"_sr, 600.0); + watchCount = getOption(options, "watchCount"_sr, (uint64_t)10000); + nodeCount = getOption(options, "nodeCount"_sr, (uint64_t)100000); + nodePrefix = getOption(options, "nodePrefix"_sr, (int64_t)-1); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 4); + triggerWatches = getOption(options, "triggerWatches"_sr, false); if (watchCount > nodeCount) { watchCount = nodeCount; diff --git a/fdbserver/workloads/Watches.actor.cpp b/fdbserver/workloads/Watches.actor.cpp index 47a030b061..37f3e624d2 100644 --- a/fdbserver/workloads/Watches.actor.cpp +++ b/fdbserver/workloads/Watches.actor.cpp @@ -36,10 +36,10 @@ struct WatchesWorkload : TestWorkload { std::vector nodeOrder; WatchesWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), cycles("Cycles"), cycleLatencies(sampleSize) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); - nodes = getOption(options, LiteralStringRef("nodeCount"), 100); - extraPerNode = getOption(options, LiteralStringRef("extraPerNode"), 1000); - keyBytes = std::max(getOption(options, LiteralStringRef("keyBytes"), 16), 16); + testDuration = getOption(options, "testDuration"_sr, 600.0); + nodes = getOption(options, "nodeCount"_sr, 100); + extraPerNode = getOption(options, "extraPerNode"_sr, 1000); + keyBytes = std::max(getOption(options, "keyBytes"_sr, 16), 16); for (int i = 0; i < nodes + 1; i++) nodeOrder.push_back(i); diff --git a/fdbserver/workloads/WatchesSameKeyCorrectness.actor.cpp b/fdbserver/workloads/WatchesSameKeyCorrectness.actor.cpp index 44ac1e5892..eddd67cb4e 100644 --- a/fdbserver/workloads/WatchesSameKeyCorrectness.actor.cpp +++ b/fdbserver/workloads/WatchesSameKeyCorrectness.actor.cpp @@ -31,17 +31,17 @@ struct WatchesSameKeyWorkload : TestWorkload { std::vector> cases; WatchesSameKeyWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { - numWatches = getOption(options, LiteralStringRef("numWatches"), 3); + numWatches = getOption(options, "numWatches"_sr, 3); } std::string description() const override { return "WatchesSameKeyCorrectness"; } Future setup(Database const& cx) override { - cases.push_back(case1(cx, LiteralStringRef("foo1"), this)); - cases.push_back(case2(cx, LiteralStringRef("foo2"), this)); - cases.push_back(case3(cx, LiteralStringRef("foo3"), this)); - cases.push_back(case4(cx, LiteralStringRef("foo4"), this)); - cases.push_back(case5(cx, LiteralStringRef("foo5"), this)); + cases.push_back(case1(cx, "foo1"_sr, this)); + cases.push_back(case2(cx, "foo2"_sr, this)); + cases.push_back(case3(cx, "foo3"_sr, this)); + cases.push_back(case4(cx, "foo4"_sr, this)); + cases.push_back(case5(cx, "foo5"_sr, this)); return Void(); } diff --git a/fdbserver/workloads/WriteBandwidth.actor.cpp b/fdbserver/workloads/WriteBandwidth.actor.cpp index 516f153761..f77ce0584e 100644 --- a/fdbserver/workloads/WriteBandwidth.actor.cpp +++ b/fdbserver/workloads/WriteBandwidth.actor.cpp @@ -40,12 +40,12 @@ struct WriteBandwidthWorkload : KVWorkload { WriteBandwidthWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), loadTime(0.0), transactions("Transactions"), retries("Retries"), commitLatencies(2000), GRVLatencies(2000) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - keysPerTransaction = getOption(options, LiteralStringRef("keysPerTransaction"), 100); + testDuration = getOption(options, "testDuration"_sr, 10.0); + keysPerTransaction = getOption(options, "keysPerTransaction"_sr, 100); valueString = std::string(maxValueBytes, '.'); - warmingDelay = getOption(options, LiteralStringRef("warmingDelay"), 0.0); - maxInsertRate = getOption(options, LiteralStringRef("maxInsertRate"), 1e12); + warmingDelay = getOption(options, "warmingDelay"_sr, 0.0); + maxInsertRate = getOption(options, "maxInsertRate"_sr, 1e12); } std::string description() const override { return "WriteBandwidth"; } diff --git a/fdbserver/workloads/WriteDuringRead.actor.cpp b/fdbserver/workloads/WriteDuringRead.actor.cpp index e8e2eb2ee1..1d35aeaada 100644 --- a/fdbserver/workloads/WriteDuringRead.actor.cpp +++ b/fdbserver/workloads/WriteDuringRead.actor.cpp @@ -58,15 +58,14 @@ struct WriteDuringReadWorkload : TestWorkload { WriteDuringReadWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), transactions("Transactions"), retries("Retries"), success(true) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); - slowModeStart = getOption(options, LiteralStringRef("slowModeStart"), 1000.0); - numOps = getOption(options, LiteralStringRef("numOps"), 21); - rarelyCommit = getOption(options, LiteralStringRef("rarelyCommit"), false); - maximumTotalData = getOption(options, LiteralStringRef("maximumTotalData"), 3e6); - maximumDataWritten = - getOption(options, LiteralStringRef("maximumDataWritten"), std::numeric_limits::max()); - minNode = getOption(options, LiteralStringRef("minNode"), 0); - useSystemKeys = getOption(options, LiteralStringRef("useSystemKeys"), deterministicRandom()->random01() < 0.5); + testDuration = getOption(options, "testDuration"_sr, 60.0); + slowModeStart = getOption(options, "slowModeStart"_sr, 1000.0); + numOps = getOption(options, "numOps"_sr, 21); + rarelyCommit = getOption(options, "rarelyCommit"_sr, false); + maximumTotalData = getOption(options, "maximumTotalData"_sr, 3e6); + maximumDataWritten = getOption(options, "maximumDataWritten"_sr, std::numeric_limits::max()); + minNode = getOption(options, "minNode"_sr, 0); + useSystemKeys = getOption(options, "useSystemKeys"_sr, deterministicRandom()->random01() < 0.5); adjacentKeys = deterministicRandom()->random01() < 0.5; initialKeyDensity = deterministicRandom()->random01(); // This fraction of keys are present before the first // transaction (and after an unknown result) @@ -89,11 +88,11 @@ struct WriteDuringReadWorkload : TestWorkload { CODE_PROBE(adjacentKeys && (nodes + minNode) > CLIENT_KNOBS->KEY_SIZE_LIMIT, "WriteDuringReadWorkload testing large keys"); - useExtraDB = !g_simulator.extraDatabases.empty(); + useExtraDB = !g_simulator->extraDatabases.empty(); if (useExtraDB) { - ASSERT(g_simulator.extraDatabases.size() == 1); + ASSERT(g_simulator->extraDatabases.size() == 1); auto extraFile = - makeReference(ClusterConnectionString(g_simulator.extraDatabases[0])); + makeReference(ClusterConnectionString(g_simulator->extraDatabases[0])); extraDB = Database::createDatabase(extraFile, ApiVersion::LATEST_VERSION); useSystemKeys = false; } @@ -105,7 +104,7 @@ struct WriteDuringReadWorkload : TestWorkload { } maxClearSize = 1 << deterministicRandom()->randomInt(0, 20); - conflictRange = KeyRangeRef(LiteralStringRef("\xfe"), LiteralStringRef("\xfe\x00")); + conflictRange = KeyRangeRef("\xfe"_sr, "\xfe\x00"_sr); if (clientId == 0) TraceEvent("RYWConfiguration") .detail("Nodes", nodes) @@ -683,7 +682,7 @@ struct WriteDuringReadWorkload : TestWorkload { loop { wait(delay(now() - startTime > self->slowModeStart || - (g_network->isSimulated() && g_simulator.speedUpSimulation) + (g_network->isSimulated() && g_simulator->speedUpSimulation) ? 1.0 : 0.1)); try { diff --git a/fdbserver/workloads/WriteTagThrottling.actor.cpp b/fdbserver/workloads/WriteTagThrottling.actor.cpp index ad639b2a96..b2e5f66a43 100644 --- a/fdbserver/workloads/WriteTagThrottling.actor.cpp +++ b/fdbserver/workloads/WriteTagThrottling.actor.cpp @@ -66,23 +66,23 @@ struct WriteTagThrottlingWorkload : KVWorkload { WriteTagThrottlingWorkload(WorkloadContext const& wcx) : KVWorkload(wcx), badActorReadLatency(SAMPLE_SIZE), goodActorReadLatency(SAMPLE_SIZE), badActorCommitLatency(SAMPLE_SIZE), goodActorCommitLatency(SAMPLE_SIZE) { - testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0); - badOpRate = getOption(options, LiteralStringRef("badOpRate"), 0.9); - numWritePerTr = getOption(options, LiteralStringRef("numWritePerTr"), 1); - numReadPerTr = getOption(options, LiteralStringRef("numReadPerTr"), 1); - numClearPerTr = getOption(options, LiteralStringRef("numClearPerTr"), 1); - hotRangeRate = getOption(options, LiteralStringRef("hotRangeRate"), 0.1); - populateData = getOption(options, LiteralStringRef("populateData"), true); + testDuration = getOption(options, "testDuration"_sr, 120.0); + badOpRate = getOption(options, "badOpRate"_sr, 0.9); + numWritePerTr = getOption(options, "numWritePerTr"_sr, 1); + numReadPerTr = getOption(options, "numReadPerTr"_sr, 1); + numClearPerTr = getOption(options, "numClearPerTr"_sr, 1); + hotRangeRate = getOption(options, "hotRangeRate"_sr, 0.1); + populateData = getOption(options, "populateData"_sr, true); - writeThrottle = getOption(options, LiteralStringRef("writeThrottle"), false); - badActorPerClient = getOption(options, LiteralStringRef("badActorPerClient"), 1); - goodActorPerClient = getOption(options, LiteralStringRef("goodActorPerClient"), 1); + writeThrottle = getOption(options, "writeThrottle"_sr, false); + badActorPerClient = getOption(options, "badActorPerClient"_sr, 1); + goodActorPerClient = getOption(options, "goodActorPerClient"_sr, 1); actorCount = goodActorPerClient + badActorPerClient; keyCount = getOption(options, - LiteralStringRef("keyCount"), + "keyCount"_sr, std::max(3000, clientCount * actorCount * 3)); // enough keys to avoid too many conflicts - trInterval = actorCount * 1.0 / getOption(options, LiteralStringRef("trPerSecond"), 1000); + trInterval = actorCount * 1.0 / getOption(options, "trPerSecond"_sr, 1000); if (badActorPerClient > 0) { rangeEachBadActor = keyCount / (clientCount * badActorPerClient); } diff --git a/fdbservice/ServiceBase.cpp b/fdbservice/ServiceBase.cpp index 8e54cdc3d6..36122183c7 100644 --- a/fdbservice/ServiceBase.cpp +++ b/fdbservice/ServiceBase.cpp @@ -357,4 +357,4 @@ void CServiceBase::WriteErrorLogEntry(const char* function, int error) { WriteEventLogEntry(message, EVENTLOG_ERROR_TYPE); } -#pragma endregion \ No newline at end of file +#pragma endregion diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index 09b979cf37..88aa79f48b 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -30,13 +30,13 @@ add_flow_target(STATIC_LIBRARY NAME flow_sampling SRCS ${FLOW_SRCS}) add_flow_target(LINK_TEST NAME flowlinktest SRCS LinkTest.cpp) target_link_libraries(flowlinktest PRIVATE flow stacktrace) -find_package(ZLIB) -if(ZLIB_FOUND) - target_compile_definitions(flow PUBLIC ZLIB_LIB_SUPPORTED) - target_link_libraries(flow PUBLIC ZLIB::ZLIB) -else() - message(STATUS "ZLIB package not found") -endif() +#find_package(ZLIB) +#if(ZLIB_FOUND) +# target_compile_definitions(flow PUBLIC ZLIB_LIB_SUPPORTED) +# target_link_libraries(flow PUBLIC ZLIB::ZLIB) +#else() +# message(STATUS "ZLIB package not found") +#endif() foreach(ft flow flow_sampling flowlinktest) target_include_directories(${ft} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include") diff --git a/flow/CompressedInt.actor.cpp b/flow/CompressedInt.actor.cpp index ef889cf23f..0081f79c1d 100644 --- a/flow/CompressedInt.actor.cpp +++ b/flow/CompressedInt.actor.cpp @@ -86,12 +86,12 @@ void testCompressedInt(IntType n, StringRef rep = StringRef()) { } TEST_CASE("/flow/compressed_ints") { - testCompressedInt(-2, LiteralStringRef("\x7e")); - testCompressedInt(-1, LiteralStringRef("\x7f")); - testCompressedInt(0, LiteralStringRef("\x80")); - testCompressedInt(1, LiteralStringRef("\x81")); - testCompressedInt(2, LiteralStringRef("\x82")); - testCompressedInt(0x4000000000000000, LiteralStringRef("\xFF\xC0\x40\x00\x00\x00\x00\x00\x00\x00")); + testCompressedInt(-2, "\x7e"_sr); + testCompressedInt(-1, "\x7f"_sr); + testCompressedInt(0, "\x80"_sr); + testCompressedInt(1, "\x81"_sr); + testCompressedInt(2, "\x82"_sr); + testCompressedInt(0x4000000000000000, "\xFF\xC0\x40\x00\x00\x00\x00\x00\x00\x00"_sr); int64_t n = 0; for (int i = 0; i < 10000000; ++i) { diff --git a/flow/EncryptUtils.cpp b/flow/EncryptUtils.cpp index 55ae6c4d1c..ee79aee5c4 100644 --- a/flow/EncryptUtils.cpp +++ b/flow/EncryptUtils.cpp @@ -24,6 +24,10 @@ #include #include +const EncryptCipherDomainName FDB_SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_NAME = "FdbSystemKeyspaceEncryptDomain"_sr; +const EncryptCipherDomainName FDB_DEFAULT_ENCRYPT_DOMAIN_NAME = "FdbDefaultEncryptDomain"_sr; +const EncryptCipherDomainName FDB_ENCRYPT_HEADER_DOMAIN_NAME = "FdbEncryptHeaderDomain"_sr; + EncryptCipherMode encryptModeFromString(const std::string& modeStr) { if (modeStr == "NONE") { return ENCRYPT_CIPHER_MODE_NONE; diff --git a/flow/Histogram.cpp b/flow/Histogram.cpp index 63662f67f2..19a4946a23 100644 --- a/flow/Histogram.cpp +++ b/flow/Histogram.cpp @@ -206,8 +206,7 @@ std::string Histogram::drawHistogram() { TEST_CASE("/flow/histogram/smoke_test") { { - Reference h = - Histogram::getHistogram(LiteralStringRef("smoke_test"), LiteralStringRef("counts"), Histogram::Unit::bytes); + Reference h = Histogram::getHistogram("smoke_test"_sr, "counts"_sr, Histogram::Unit::bytes); h->sample(0); ASSERT(h->buckets[0] == 1); @@ -222,15 +221,13 @@ TEST_CASE("/flow/histogram/smoke_test") { ASSERT(h->buckets[0] == 0); h->sample(0); ASSERT(h->buckets[0] == 1); - h = Histogram::getHistogram( - LiteralStringRef("smoke_test"), LiteralStringRef("counts2"), Histogram::Unit::bytes); + h = Histogram::getHistogram("smoke_test"_sr, "counts2"_sr, Histogram::Unit::bytes); // confirm that old h was deallocated. - h = Histogram::getHistogram(LiteralStringRef("smoke_test"), LiteralStringRef("counts"), Histogram::Unit::bytes); + h = Histogram::getHistogram("smoke_test"_sr, "counts"_sr, Histogram::Unit::bytes); ASSERT(h->buckets[0] == 0); - h = Histogram::getHistogram( - LiteralStringRef("smoke_test"), LiteralStringRef("times"), Histogram::Unit::microseconds); + h = Histogram::getHistogram("smoke_test"_sr, "times"_sr, Histogram::Unit::microseconds); h->sampleSeconds(0.000000); h->sampleSeconds(0.0000019); diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 68279c26ba..f6734b44b7 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -163,7 +163,9 @@ void FlowKnobs::initialize(Randomize randomize, IsSimulated isSimulated) { //AsyncFileKAIO init( MAX_OUTSTANDING, 64 ); init( MIN_SUBMIT, 10 ); - init( DISK_METRIC_LOGGING_INTERVAL, 5.0 ); + init( SQLITE_DISK_METRIC_LOGGING_INTERVAL, 5.0 ); + init( KAIO_LATENCY_LOGGING_INTERVAL, 30.0 ); + init( KAIO_LATENCY_SAMPLE_SIZE, 30000 ); init( PAGE_WRITE_CHECKSUM_HISTORY, 0 ); if( randomize && BUGGIFY ) PAGE_WRITE_CHECKSUM_HISTORY = 10000000; init( DISABLE_POSIX_KERNEL_AIO, 0 ); @@ -238,6 +240,7 @@ void FlowKnobs::initialize(Randomize randomize, IsSimulated isSimulated) { init( SIM_SPEEDUP_AFTER_SECONDS, 450 ); init( MAX_TRACE_LINES, 1'000'000 ); init( CODE_COV_TRACE_EVENT_SEVERITY, 10 ); // Code coverage TraceEvent severity level + init( ENABLE_SIMULATION_IMPROVEMENTS, false ); // Separate normal workloads and failure injection //TDMetrics init( MAX_METRICS, 600 ); @@ -296,6 +299,8 @@ void FlowKnobs::initialize(Randomize randomize, IsSimulated isSimulated) { init( ENCRYPT_KEY_REFRESH_INTERVAL, isSimulated ? 60 : 8 * 60 ); if ( randomize && BUGGIFY) { ENCRYPT_KEY_REFRESH_INTERVAL = deterministicRandom()->randomInt(2, 10); } init( TOKEN_CACHE_SIZE, 100 ); + init( ENCRYPT_KEY_CACHE_LOGGING_INTERVAL, 5.0 ); + init( ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE, 1000 ); // REST Client init( RESTCLIENT_MAX_CONNECTIONPOOL_SIZE, 10 ); diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index fcfe2bfd87..6ea6a6775c 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -1393,29 +1393,29 @@ ACTOR Future Net2::logTimeOffset() { } void Net2::initMetrics() { - bytesReceived.init(LiteralStringRef("Net2.BytesReceived")); - countWriteProbes.init(LiteralStringRef("Net2.CountWriteProbes")); - countReadProbes.init(LiteralStringRef("Net2.CountReadProbes")); - countReads.init(LiteralStringRef("Net2.CountReads")); - countWouldBlock.init(LiteralStringRef("Net2.CountWouldBlock")); - countWrites.init(LiteralStringRef("Net2.CountWrites")); - countRunLoop.init(LiteralStringRef("Net2.CountRunLoop")); - countCantSleep.init(LiteralStringRef("Net2.CountCantSleep")); - countWontSleep.init(LiteralStringRef("Net2.CountWontSleep")); - countTimers.init(LiteralStringRef("Net2.CountTimers")); - countTasks.init(LiteralStringRef("Net2.CountTasks")); - countYields.init(LiteralStringRef("Net2.CountYields")); - countYieldBigStack.init(LiteralStringRef("Net2.CountYieldBigStack")); - countYieldCalls.init(LiteralStringRef("Net2.CountYieldCalls")); - countASIOEvents.init(LiteralStringRef("Net2.CountASIOEvents")); - countYieldCallsTrue.init(LiteralStringRef("Net2.CountYieldCallsTrue")); - countRunLoopProfilingSignals.init(LiteralStringRef("Net2.CountRunLoopProfilingSignals")); - countTLSPolicyFailures.init(LiteralStringRef("Net2.CountTLSPolicyFailures")); - priorityMetric.init(LiteralStringRef("Net2.Priority")); - awakeMetric.init(LiteralStringRef("Net2.Awake")); - slowTaskMetric.init(LiteralStringRef("Net2.SlowTask")); - countLaunchTime.init(LiteralStringRef("Net2.CountLaunchTime")); - countReactTime.init(LiteralStringRef("Net2.CountReactTime")); + bytesReceived.init("Net2.BytesReceived"_sr); + countWriteProbes.init("Net2.CountWriteProbes"_sr); + countReadProbes.init("Net2.CountReadProbes"_sr); + countReads.init("Net2.CountReads"_sr); + countWouldBlock.init("Net2.CountWouldBlock"_sr); + countWrites.init("Net2.CountWrites"_sr); + countRunLoop.init("Net2.CountRunLoop"_sr); + countCantSleep.init("Net2.CountCantSleep"_sr); + countWontSleep.init("Net2.CountWontSleep"_sr); + countTimers.init("Net2.CountTimers"_sr); + countTasks.init("Net2.CountTasks"_sr); + countYields.init("Net2.CountYields"_sr); + countYieldBigStack.init("Net2.CountYieldBigStack"_sr); + countYieldCalls.init("Net2.CountYieldCalls"_sr); + countASIOEvents.init("Net2.CountASIOEvents"_sr); + countYieldCallsTrue.init("Net2.CountYieldCallsTrue"_sr); + countRunLoopProfilingSignals.init("Net2.CountRunLoopProfilingSignals"_sr); + countTLSPolicyFailures.init("Net2.CountTLSPolicyFailures"_sr); + priorityMetric.init("Net2.Priority"_sr); + awakeMetric.init("Net2.Awake"_sr); + slowTaskMetric.init("Net2.SlowTask"_sr); + countLaunchTime.init("Net2.CountLaunchTime"_sr); + countReactTime.init("Net2.CountReactTime"_sr); } bool Net2::checkRunnable() { @@ -2145,7 +2145,7 @@ THREAD_HANDLE startThreadF(F&& func) { return g_network->startThread(Thing::start, t); } -TEST_CASE("/flow/Net2/ThreadSafeQueue/Interface") { +TEST_CASE("flow/Net2/ThreadSafeQueue/Interface") { ThreadSafeQueue tq; ASSERT(!tq.pop().present()); ASSERT(tq.canSleep()); @@ -2186,7 +2186,7 @@ struct QueueTestThreadState { } }; -TEST_CASE("/flow/Net2/ThreadSafeQueue/Threaded") { +TEST_CASE("flow/Net2/ThreadSafeQueue/Threaded") { // Uses ThreadSafeQueue from multiple threads. Verifies that all pushed elements are popped, maintaining the // ordering within a thread. noUnseed = true; // multi-threading inherently non-deterministic @@ -2310,7 +2310,7 @@ void net2_test(){ reqs.resize(10000); for(int i=0; i<10000; i++) { TestGVR &req = reqs[i]; - req.key = LiteralStringRef("Foobar"); + req.key = "Foobar"_sr; SerializeSource what(req); diff --git a/flow/Platform.actor.cpp b/flow/Platform.actor.cpp index aa2e231cca..f5d27d15f5 100644 --- a/flow/Platform.actor.cpp +++ b/flow/Platform.actor.cpp @@ -32,7 +32,6 @@ #include "flow/Arena.h" #include "flow/StreamCipher.h" -#include "flow/BlobCipher.h" #include "flow/ScopeExit.h" #include "flow/Trace.h" #include "flow/Error.h" @@ -468,24 +467,22 @@ void getMachineRAMInfo(MachineRAMInfo& memInfo) { } std::map request = { - { LiteralStringRef("MemTotal:"), 0 }, { LiteralStringRef("MemFree:"), 0 }, - { LiteralStringRef("MemAvailable:"), -1 }, { LiteralStringRef("Active(file):"), 0 }, - { LiteralStringRef("Inactive(file):"), 0 }, { LiteralStringRef("SwapTotal:"), 0 }, - { LiteralStringRef("SwapFree:"), 0 }, { LiteralStringRef("SReclaimable:"), 0 }, + { "MemTotal:"_sr, 0 }, { "MemFree:"_sr, 0 }, { "MemAvailable:"_sr, -1 }, { "Active(file):"_sr, 0 }, + { "Inactive(file):"_sr, 0 }, { "SwapTotal:"_sr, 0 }, { "SwapFree:"_sr, 0 }, { "SReclaimable:"_sr, 0 }, }; std::stringstream memInfoStream; memInfoStream << fileStream.rdbuf(); getMemoryInfo(request, memInfoStream); - int64_t memFree = request[LiteralStringRef("MemFree:")]; - int64_t pageCache = request[LiteralStringRef("Active(file):")] + request[LiteralStringRef("Inactive(file):")]; - int64_t slabReclaimable = request[LiteralStringRef("SReclaimable:")]; - int64_t usedSwap = request[LiteralStringRef("SwapTotal:")] - request[LiteralStringRef("SwapFree:")]; + int64_t memFree = request["MemFree:"_sr]; + int64_t pageCache = request["Active(file):"_sr] + request["Inactive(file):"_sr]; + int64_t slabReclaimable = request["SReclaimable:"_sr]; + int64_t usedSwap = request["SwapTotal:"_sr] - request["SwapFree:"_sr]; - memInfo.total = 1024 * request[LiteralStringRef("MemTotal:")]; - if (request[LiteralStringRef("MemAvailable:")] != -1) { - memInfo.available = 1024 * (request[LiteralStringRef("MemAvailable:")] - usedSwap); + memInfo.total = 1024 * request["MemTotal:"_sr]; + if (request["MemAvailable:"_sr] != -1) { + memInfo.available = 1024 * (request["MemAvailable:"_sr] - usedSwap); } else { memInfo.available = 1024 * (std::max(0, @@ -2493,7 +2490,7 @@ bool createDirectory(std::string const& directory) { const uint8_t separatorChar = CANONICAL_PATH_SEPARATOR; StringRef separator(&separatorChar, 1); -StringRef dotdot = LiteralStringRef(".."); +StringRef dotdot = ".."_sr; std::string cleanPath(std::string const& path) { std::vector finalParts; @@ -3157,19 +3154,19 @@ void outOfMemory() { char* demangled = abi::__cxa_demangle(i->first, nullptr, nullptr, nullptr); if (demangled) { s = demangled; - if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::"))) - s = s.substr(LiteralStringRef("(anonymous namespace)::").size()); + if (StringRef(s).startsWith("(anonymous namespace)::"_sr)) + s = s.substr("(anonymous namespace)::"_sr.size()); free(demangled); } else s = i->first; #else s = i->first; - if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::"))) - s = s.substr(LiteralStringRef("class `anonymous namespace'::").size()); - else if (StringRef(s).startsWith(LiteralStringRef("class "))) - s = s.substr(LiteralStringRef("class ").size()); - else if (StringRef(s).startsWith(LiteralStringRef("struct "))) - s = s.substr(LiteralStringRef("struct ").size()); + if (StringRef(s).startsWith("class `anonymous namespace'::"_sr)) + s = s.substr("class `anonymous namespace'::"_sr.size()); + else if (StringRef(s).startsWith("class "_sr)) + s = s.substr("class "_sr.size()); + else if (StringRef(s).startsWith("struct "_sr)) + s = s.substr("struct "_sr.size()); #endif typeNames.emplace_back(s, i->first); } @@ -3249,10 +3246,10 @@ void outOfMemory() { } // Because the lambda used with nftw below cannot capture -int __eraseDirectoryRecurseiveCount; +int __eraseDirectoryRecursiveCount; int eraseDirectoryRecursive(std::string const& dir) { - __eraseDirectoryRecurseiveCount = 0; + __eraseDirectoryRecursiveCount = 0; #ifdef _WIN32 system(("rd /s /q \"" + dir + "\"").c_str()); #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) @@ -3261,7 +3258,7 @@ int eraseDirectoryRecursive(std::string const& dir) { [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int { int r = remove(fpath); if (r == 0) - ++__eraseDirectoryRecurseiveCount; + ++__eraseDirectoryRecursiveCount; return r; }, 64, @@ -3278,7 +3275,7 @@ int eraseDirectoryRecursive(std::string const& dir) { #error Port me! #endif // INJECT_FAULT( platform_error, "eraseDirectoryRecursive" ); - return __eraseDirectoryRecurseiveCount; + return __eraseDirectoryRecursiveCount; } TmpFile::TmpFile() : filename("") { @@ -3623,6 +3620,12 @@ void platformInit() { #endif } +std::vector> g_crashHandlerCallbacks; + +void registerCrashHandlerCallback(void (*f)()) { + g_crashHandlerCallbacks.push_back(f); +} + // The crashHandler function is registered to handle signals before the process terminates. // Basic information about the crash is printed/traced, and stdout and trace events are flushed. void crashHandler(int sig) { @@ -3635,7 +3638,10 @@ void crashHandler(int sig) { StreamCipherKey::cleanup(); StreamCipher::cleanup(); - BlobCipherKeyCache::cleanup(); + + for (auto& f : g_crashHandlerCallbacks) { + f(); + } fprintf(error ? stderr : stdout, "SIGNAL: %s (%d)\n", strsignal(sig), sig); if (error) { @@ -3990,21 +3996,19 @@ TEST_CASE("/flow/Platform/getMemoryInfo") { "VmallocUsed: 410576 kB\n"; std::map request = { - { LiteralStringRef("MemTotal:"), 0 }, { LiteralStringRef("MemFree:"), 0 }, - { LiteralStringRef("MemAvailable:"), 0 }, { LiteralStringRef("Buffers:"), 0 }, - { LiteralStringRef("Cached:"), 0 }, { LiteralStringRef("SwapTotal:"), 0 }, - { LiteralStringRef("SwapFree:"), 0 }, + { "MemTotal:"_sr, 0 }, { "MemFree:"_sr, 0 }, { "MemAvailable:"_sr, 0 }, { "Buffers:"_sr, 0 }, + { "Cached:"_sr, 0 }, { "SwapTotal:"_sr, 0 }, { "SwapFree:"_sr, 0 }, }; std::stringstream memInfoStream(memString); getMemoryInfo(request, memInfoStream); - ASSERT(request[LiteralStringRef("MemTotal:")] == 24733228); - ASSERT(request[LiteralStringRef("MemFree:")] == 2077580); - ASSERT(request[LiteralStringRef("MemAvailable:")] == 0); - ASSERT(request[LiteralStringRef("Buffers:")] == 266940); - ASSERT(request[LiteralStringRef("Cached:")] == 16798292); - ASSERT(request[LiteralStringRef("SwapTotal:")] == 25165820); - ASSERT(request[LiteralStringRef("SwapFree:")] == 23680228); + ASSERT(request["MemTotal:"_sr] == 24733228); + ASSERT(request["MemFree:"_sr] == 2077580); + ASSERT(request["MemAvailable:"_sr] == 0); + ASSERT(request["Buffers:"_sr] == 266940); + ASSERT(request["Cached:"_sr] == 16798292); + ASSERT(request["SwapTotal:"_sr] == 25165820); + ASSERT(request["SwapFree:"_sr] == 23680228); for (auto& item : request) { fmt::print("{}:{}\n", item.first.toString().c_str(), item.second); } @@ -4049,13 +4053,13 @@ TEST_CASE("/flow/Platform/getMemoryInfo") { std::stringstream memInfoStream1(memString1); getMemoryInfo(request, memInfoStream1); - ASSERT(request[LiteralStringRef("MemTotal:")] == 31856496); - ASSERT(request[LiteralStringRef("MemFree:")] == 25492716); - ASSERT(request[LiteralStringRef("MemAvailable:")] == 28470756); - ASSERT(request[LiteralStringRef("Buffers:")] == 313644); - ASSERT(request[LiteralStringRef("Cached:")] == 2956444); - ASSERT(request[LiteralStringRef("SwapTotal:")] == 0); - ASSERT(request[LiteralStringRef("SwapFree:")] == 0); + ASSERT(request["MemTotal:"_sr] == 31856496); + ASSERT(request["MemFree:"_sr] == 25492716); + ASSERT(request["MemAvailable:"_sr] == 28470756); + ASSERT(request["Buffers:"_sr] == 313644); + ASSERT(request["Cached:"_sr] == 2956444); + ASSERT(request["SwapTotal:"_sr] == 0); + ASSERT(request["SwapFree:"_sr] == 0); for (auto& item : request) { fmt::print("{}:{}\n", item.first.toString().c_str(), item.second); } diff --git a/flow/SystemMonitor.cpp b/flow/SystemMonitor.cpp index 6ff7decbd6..473a6a97f0 100644 --- a/flow/SystemMonitor.cpp +++ b/flow/SystemMonitor.cpp @@ -315,19 +315,19 @@ SystemStatistics customSystemMonitor(std::string const& eventName, StatisticsSta char* demangled = abi::__cxa_demangle(i->first, nullptr, nullptr, nullptr); if (demangled) { s = demangled; - if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::"))) - s = s.substr(LiteralStringRef("(anonymous namespace)::").size()); + if (StringRef(s).startsWith("(anonymous namespace)::"_sr)) + s = s.substr("(anonymous namespace)::"_sr.size()); free(demangled); } else s = i->first; #else s = i->first; - if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::"))) - s = s.substr(LiteralStringRef("class `anonymous namespace'::").size()); - else if (StringRef(s).startsWith(LiteralStringRef("class "))) - s = s.substr(LiteralStringRef("class ").size()); - else if (StringRef(s).startsWith(LiteralStringRef("struct "))) - s = s.substr(LiteralStringRef("struct ").size()); + if (StringRef(s).startsWith("class `anonymous namespace'::"_sr)) + s = s.substr("class `anonymous namespace'::"_sr.size()); + else if (StringRef(s).startsWith("class "_sr)) + s = s.substr("class "_sr.size()); + else if (StringRef(s).startsWith("struct "_sr)) + s = s.substr("struct "_sr.size()); #endif typeNames.emplace_back(s, i->first); } diff --git a/flow/TDMetric.cpp b/flow/TDMetric.cpp index a84c57072c..016205524a 100644 --- a/flow/TDMetric.cpp +++ b/flow/TDMetric.cpp @@ -21,15 +21,15 @@ #include "flow/TDMetric.actor.h" #include "flow/flow.h" -const StringRef BaseEventMetric::metricType = LiteralStringRef("Event"); +const StringRef BaseEventMetric::metricType = "Event"_sr; template <> -const StringRef Int64Metric::metricType = LiteralStringRef("Int64"); +const StringRef Int64Metric::metricType = "Int64"_sr; template <> -const StringRef DoubleMetric::metricType = LiteralStringRef("Double"); +const StringRef DoubleMetric::metricType = "Double"_sr; template <> -const StringRef BoolMetric::metricType = LiteralStringRef("Bool"); +const StringRef BoolMetric::metricType = "Bool"_sr; template <> -const StringRef StringMetric::metricType = LiteralStringRef("String"); +const StringRef StringMetric::metricType = "String"_sr; std::string reduceFilename(std::string const& filename) { std::string r = filename; @@ -60,29 +60,29 @@ std::string reduceFilename(std::string const& filename) { } void MetricKeyRef::writeField(BinaryWriter& wr) const { - wr.serializeBytes(LiteralStringRef("\x01")); + wr.serializeBytes("\x01"_sr); wr.serializeBytes(fieldName); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(fieldType); - wr.serializeBytes(LiteralStringRef("\x00")); + wr.serializeBytes("\x00"_sr); } void MetricKeyRef::writeMetricName(BinaryWriter& wr) const { - wr.serializeBytes(LiteralStringRef("\x01")); + wr.serializeBytes("\x01"_sr); wr.serializeBytes(name.name); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(name.type); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(address); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(name.id); - wr.serializeBytes(LiteralStringRef("\x00")); + wr.serializeBytes("\x00"_sr); } const Standalone MetricKeyRef::packLatestKey() const { BinaryWriter wr(Unversioned()); wr.serializeBytes(prefix); - wr.serializeBytes(LiteralStringRef("\x01TDMetricsLastValue\x00")); + wr.serializeBytes("\x01TDMetricsLastValue\x00"_sr); writeMetricName(wr); return wr.toValue(); } @@ -91,9 +91,9 @@ const Standalone MetricKeyRef::packDataKey(int64_t time) const { BinaryWriter wr(Unversioned()); wr.serializeBytes(prefix); if (isField()) - wr.serializeBytes(LiteralStringRef("\x01TDFieldData\x00")); + wr.serializeBytes("\x01TDFieldData\x00"_sr); else - wr.serializeBytes(LiteralStringRef("\x01TDMetricData\x00")); + wr.serializeBytes("\x01TDMetricData\x00"_sr); writeMetricName(wr); if (isField()) writeField(wr); @@ -107,15 +107,15 @@ const Standalone MetricKeyRef::packFieldRegKey() const { ASSERT(isField()); BinaryWriter wr(Unversioned()); wr.serializeBytes(prefix); - wr.serializeBytes(LiteralStringRef("\x01TDFields\x00\x01")); + wr.serializeBytes("\x01TDFields\x00\x01"_sr); wr.serializeBytes(name.name); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(name.type); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(fieldName); - wr.serializeBytes(LiteralStringRef("\x00\x01")); + wr.serializeBytes("\x00\x01"_sr); wr.serializeBytes(fieldType); - wr.serializeBytes(LiteralStringRef("\x00")); + wr.serializeBytes("\x00"_sr); return wr.toValue(); } diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 39a6062912..4717247d16 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -163,11 +163,11 @@ public: bool logTraceEventMetrics; void initMetrics() { - SevErrorNames.init(LiteralStringRef("TraceEvents.SevError")); - SevWarnAlwaysNames.init(LiteralStringRef("TraceEvents.SevWarnAlways")); - SevWarnNames.init(LiteralStringRef("TraceEvents.SevWarn")); - SevInfoNames.init(LiteralStringRef("TraceEvents.SevInfo")); - SevDebugNames.init(LiteralStringRef("TraceEvents.SevDebug")); + SevErrorNames.init("TraceEvents.SevError"_sr); + SevWarnAlwaysNames.init("TraceEvents.SevWarnAlways"_sr); + SevWarnNames.init("TraceEvents.SevWarn"_sr); + SevInfoNames.init("TraceEvents.SevInfo"_sr); + SevDebugNames.init("TraceEvents.SevDebug"_sr); logTraceEventMetrics = true; } @@ -600,7 +600,7 @@ public: NetworkAddress getAddressIndex() { // ahm // if( g_network->isSimulated() ) - // return g_simulator.getCurrentProcess()->address; + // return g_simulator->getCurrentProcess()->address; // else return g_network->getLocalAddress(); } diff --git a/flow/flow.cpp b/flow/flow.cpp index 06a3241f09..0da59e6d76 100644 --- a/flow/flow.cpp +++ b/flow/flow.cpp @@ -525,7 +525,7 @@ TEST_CASE("/flow/FlatBuffers/Standalone") { ASSERT(in == out); } { - StringRef in = LiteralStringRef("foobar"); + StringRef in = "foobar"_sr; Standalone out; ObjectWriter writer(Unversioned()); writer.serialize(in); diff --git a/flow/include/flow/EncryptUtils.h b/flow/include/flow/EncryptUtils.h index e20af33d1e..800817d410 100644 --- a/flow/include/flow/EncryptUtils.h +++ b/flow/include/flow/EncryptUtils.h @@ -29,22 +29,27 @@ #include #include -#define ENCRYPT_INVALID_DOMAIN_ID -1 -#define ENCRYPT_INVALID_CIPHER_KEY_ID 0 -#define ENCRYPT_INVALID_RANDOM_SALT 0 - #define AUTH_TOKEN_SIZE 32 -#define SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID -2 -#define ENCRYPT_HEADER_DOMAIN_ID -3 - -const std::string FDB_DEFAULT_ENCRYPT_DOMAIN_NAME = "FdbDefaultEncryptDomain"; - using EncryptCipherDomainId = int64_t; using EncryptCipherDomainNameRef = StringRef; +using EncryptCipherDomainName = Standalone; using EncryptCipherBaseKeyId = uint64_t; using EncryptCipherRandomSalt = uint64_t; +constexpr const EncryptCipherDomainId INVALID_ENCRYPT_DOMAIN_ID = -1; +constexpr const EncryptCipherDomainId SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_ID = -2; +constexpr const EncryptCipherDomainId ENCRYPT_HEADER_DOMAIN_ID = -3; +constexpr const EncryptCipherDomainId FDB_DEFAULT_ENCRYPT_DOMAIN_ID = -4; + +constexpr const EncryptCipherBaseKeyId INVALID_ENCRYPT_CIPHER_KEY_ID = 0; + +constexpr const EncryptCipherRandomSalt INVALID_ENCRYPT_RANDOM_SALT = 0; + +extern const EncryptCipherDomainName FDB_SYSTEM_KEYSPACE_ENCRYPT_DOMAIN_NAME; +extern const EncryptCipherDomainName FDB_DEFAULT_ENCRYPT_DOMAIN_NAME; +extern const EncryptCipherDomainName FDB_ENCRYPT_HEADER_DOMAIN_NAME; + typedef enum { ENCRYPT_CIPHER_MODE_NONE = 0, ENCRYPT_CIPHER_MODE_AES_256_CTR = 1, diff --git a/flow/include/flow/EventTypes.actor.h b/flow/include/flow/EventTypes.actor.h index 95abffff0f..ce6bee51ab 100644 --- a/flow/include/flow/EventTypes.actor.h +++ b/flow/include/flow/EventTypes.actor.h @@ -22,11 +22,11 @@ // When actually compiled (NO_INTELLISENSE), include the generated version of this file. In intellisense use the source // version. -#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_EVENTTYPES_ACTOR_G_H) -#define FDBCLIENT_EVENTTYPES_ACTOR_G_H +#if defined(NO_INTELLISENSE) && !defined(FLOW_EVENTTYPES_ACTOR_G_H) +#define FLOW_EVENTTYPES_ACTOR_G_H #include "flow/EventTypes.actor.g.h" -#elif !defined(FDBCLIENT_EVENTTYPES_ACTOR_H) -#define FDBCLIENT_EVENTTYPESS_ACTOR_H +#elif !defined(FLOW_EVENTTYPES_ACTOR_H) +#define FLOW_EVENTTYPES_ACTOR_H #include "flow/flow.h" #include "flow/TDMetric.actor.h" diff --git a/flow/include/flow/IRateControl.h b/flow/include/flow/IRateControl.h index 28a2408537..bd2c0b8004 100644 --- a/flow/include/flow/IRateControl.h +++ b/flow/include/flow/IRateControl.h @@ -53,7 +53,13 @@ public: // Replenish budget based on time since last update double ts = now(); // returnUnused happens to do exactly what we want here - returnUnused((ts - m_last_update) / m_seconds * m_limit); + auto unused = double(m_limit) * (ts - m_last_update) / m_seconds; + if (unused >= double(std::numeric_limits::max())) { + // prevent int overflow + m_budget = m_limit; + } else { + returnUnused(int(unused)); + } m_last_update = ts; m_budget -= n; // If budget is still >= 0 then it's safe to use the allowance right now. diff --git a/flow/include/flow/Knobs.h b/flow/include/flow/Knobs.h index fee670e880..8f82f3f4fa 100644 --- a/flow/include/flow/Knobs.h +++ b/flow/include/flow/Knobs.h @@ -227,7 +227,9 @@ public: // AsyncFileKAIO int MAX_OUTSTANDING; int MIN_SUBMIT; - double DISK_METRIC_LOGGING_INTERVAL; + double SQLITE_DISK_METRIC_LOGGING_INTERVAL; + double KAIO_LATENCY_LOGGING_INTERVAL; + int KAIO_LATENCY_SAMPLE_SIZE; int PAGE_WRITE_CHECKSUM_HISTORY; int DISABLE_POSIX_KERNEL_AIO; @@ -288,6 +290,7 @@ public: int SIM_CONNECT_ERROR_MODE; double SIM_SPEEDUP_AFTER_SECONDS; int MAX_TRACE_LINES; + bool ENABLE_SIMULATION_IMPROVEMENTS; // Tracefiles int ZERO_LENGTH_FILE_PAD; @@ -359,6 +362,8 @@ public: // Encryption int64_t ENCRYPT_CIPHER_KEY_CACHE_TTL; int64_t ENCRYPT_KEY_REFRESH_INTERVAL; + double ENCRYPT_KEY_CACHE_LOGGING_INTERVAL; + double ENCRYPT_KEY_CACHE_LOGGING_SAMPLE_SIZE; // Authorization int TOKEN_CACHE_SIZE; diff --git a/flow/include/flow/Net2Packet.h b/flow/include/flow/Net2Packet.h index 5031a924b8..d9c10a3bcb 100644 --- a/flow/include/flow/Net2Packet.h +++ b/flow/include/flow/Net2Packet.h @@ -44,9 +44,8 @@ class UnsentPacketQueue : NonCopyable { public: UnsentPacketQueue() : unsent_first(0), unsent_last(0), - sendQueueLatencyHistogram(Histogram::getHistogram(LiteralStringRef("UnsentPacketQueue"), - LiteralStringRef("QueueWait"), - Histogram::Unit::microseconds)) {} + sendQueueLatencyHistogram( + Histogram::getHistogram("UnsentPacketQueue"_sr, "QueueWait"_sr, Histogram::Unit::microseconds)) {} ~UnsentPacketQueue() { discardAll(); diff --git a/flow/include/flow/Platform.h b/flow/include/flow/Platform.h index 5e25d0cf51..2fd32a71aa 100644 --- a/flow/include/flow/Platform.h +++ b/flow/include/flow/Platform.h @@ -794,7 +794,14 @@ EXTERNC void flushAndExit(int exitCode); // Initilization code that's run at the beginning of every entry point (except fdbmonitor) void platformInit(); +// Register a callback which will run as part of the crash handler. Use in conjunction with registerCrashHandler. +// The callback being added should be simple and unlikely to fail, otherwise it will fail the crash handler, +// preventing necessary logging being printed. Also, the crash handler may not be comprehensive in handling all +// failure cases. +void registerCrashHandlerCallback(void (*f)()); + void registerCrashHandler(); + void setupRunLoopProfiler(); EXTERNC void setProfilingEnabled(int enabled); diff --git a/flow/include/flow/SystemMonitor.h b/flow/include/flow/SystemMonitor.h index 528b659807..d6ba0d540a 100644 --- a/flow/include/flow/SystemMonitor.h +++ b/flow/include/flow/SystemMonitor.h @@ -92,53 +92,46 @@ struct NetworkData { double countReactTime; void init() { - bytesSent = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.BytesSent")); - countPacketsReceived = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountPacketsReceived")); - countPacketsGenerated = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountPacketsGenerated")); - bytesReceived = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.BytesReceived")); - countWriteProbes = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountWriteProbes")); - countReadProbes = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountReadProbes")); - countReads = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountReads")); - countWouldBlock = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountWouldBlock")); - countWrites = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountWrites")); - countRunLoop = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountRunLoop")); - countCantSleep = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountCantSleep")); - countWontSleep = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountWontSleep")); - countTimers = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountTimers")); - countTasks = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountTasks")); - countYields = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountYields")); - countYieldBigStack = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountYieldBigStack")); - countYieldCalls = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountYieldCalls")); - countASIOEvents = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountASIOEvents")); - countYieldCallsTrue = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountYieldCallsTrue")); - countRunLoopProfilingSignals = - Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountRunLoopProfilingSignals")); - countConnEstablished = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountConnEstablished")); - countConnClosedWithError = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountConnClosedWithError")); - countConnClosedWithoutError = - Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountConnClosedWithoutError")); - countTLSPolicyFailures = Int64Metric::getValueOrDefault(LiteralStringRef("Net2.CountTLSPolicyFailures")); - countLaunchTime = DoubleMetric::getValueOrDefault(LiteralStringRef("Net2.CountLaunchTime")); - countReactTime = DoubleMetric::getValueOrDefault(LiteralStringRef("Net2.CountReactTime")); - countFileLogicalWrites = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountLogicalWrites")); - countFileLogicalReads = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountLogicalReads")); - countAIOSubmit = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountAIOSubmit")); - countAIOCollect = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountAIOCollect")); - countFileCacheWrites = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheWrites")); - countFileCacheReads = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheReads")); - countFileCacheWritesBlocked = - Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheWritesBlocked")); - countFileCacheReadsBlocked = - Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheReadsBlocked")); - countFileCachePageReadsMerged = - Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCachePageReadsMerged")); - countFileCacheFinds = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheFinds")); - countFileCacheReadBytes = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCacheReadBytes")); - countFilePageCacheHits = Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCachePageReadsHit")); - countFilePageCacheMisses = - Int64Metric::getValueOrDefault(LiteralStringRef("AsyncFile.CountCachePageReadsMissed")); - countFilePageCacheEvictions = - Int64Metric::getValueOrDefault(LiteralStringRef("EvictablePageCache.CacheEvictions")); + bytesSent = Int64Metric::getValueOrDefault("Net2.BytesSent"_sr); + countPacketsReceived = Int64Metric::getValueOrDefault("Net2.CountPacketsReceived"_sr); + countPacketsGenerated = Int64Metric::getValueOrDefault("Net2.CountPacketsGenerated"_sr); + bytesReceived = Int64Metric::getValueOrDefault("Net2.BytesReceived"_sr); + countWriteProbes = Int64Metric::getValueOrDefault("Net2.CountWriteProbes"_sr); + countReadProbes = Int64Metric::getValueOrDefault("Net2.CountReadProbes"_sr); + countReads = Int64Metric::getValueOrDefault("Net2.CountReads"_sr); + countWouldBlock = Int64Metric::getValueOrDefault("Net2.CountWouldBlock"_sr); + countWrites = Int64Metric::getValueOrDefault("Net2.CountWrites"_sr); + countRunLoop = Int64Metric::getValueOrDefault("Net2.CountRunLoop"_sr); + countCantSleep = Int64Metric::getValueOrDefault("Net2.CountCantSleep"_sr); + countWontSleep = Int64Metric::getValueOrDefault("Net2.CountWontSleep"_sr); + countTimers = Int64Metric::getValueOrDefault("Net2.CountTimers"_sr); + countTasks = Int64Metric::getValueOrDefault("Net2.CountTasks"_sr); + countYields = Int64Metric::getValueOrDefault("Net2.CountYields"_sr); + countYieldBigStack = Int64Metric::getValueOrDefault("Net2.CountYieldBigStack"_sr); + countYieldCalls = Int64Metric::getValueOrDefault("Net2.CountYieldCalls"_sr); + countASIOEvents = Int64Metric::getValueOrDefault("Net2.CountASIOEvents"_sr); + countYieldCallsTrue = Int64Metric::getValueOrDefault("Net2.CountYieldCallsTrue"_sr); + countRunLoopProfilingSignals = Int64Metric::getValueOrDefault("Net2.CountRunLoopProfilingSignals"_sr); + countConnEstablished = Int64Metric::getValueOrDefault("Net2.CountConnEstablished"_sr); + countConnClosedWithError = Int64Metric::getValueOrDefault("Net2.CountConnClosedWithError"_sr); + countConnClosedWithoutError = Int64Metric::getValueOrDefault("Net2.CountConnClosedWithoutError"_sr); + countTLSPolicyFailures = Int64Metric::getValueOrDefault("Net2.CountTLSPolicyFailures"_sr); + countLaunchTime = DoubleMetric::getValueOrDefault("Net2.CountLaunchTime"_sr); + countReactTime = DoubleMetric::getValueOrDefault("Net2.CountReactTime"_sr); + countFileLogicalWrites = Int64Metric::getValueOrDefault("AsyncFile.CountLogicalWrites"_sr); + countFileLogicalReads = Int64Metric::getValueOrDefault("AsyncFile.CountLogicalReads"_sr); + countAIOSubmit = Int64Metric::getValueOrDefault("AsyncFile.CountAIOSubmit"_sr); + countAIOCollect = Int64Metric::getValueOrDefault("AsyncFile.CountAIOCollect"_sr); + countFileCacheWrites = Int64Metric::getValueOrDefault("AsyncFile.CountCacheWrites"_sr); + countFileCacheReads = Int64Metric::getValueOrDefault("AsyncFile.CountCacheReads"_sr); + countFileCacheWritesBlocked = Int64Metric::getValueOrDefault("AsyncFile.CountCacheWritesBlocked"_sr); + countFileCacheReadsBlocked = Int64Metric::getValueOrDefault("AsyncFile.CountCacheReadsBlocked"_sr); + countFileCachePageReadsMerged = Int64Metric::getValueOrDefault("AsyncFile.CountCachePageReadsMerged"_sr); + countFileCacheFinds = Int64Metric::getValueOrDefault("AsyncFile.CountCacheFinds"_sr); + countFileCacheReadBytes = Int64Metric::getValueOrDefault("AsyncFile.CountCacheReadBytes"_sr); + countFilePageCacheHits = Int64Metric::getValueOrDefault("AsyncFile.CountCachePageReadsHit"_sr); + countFilePageCacheMisses = Int64Metric::getValueOrDefault("AsyncFile.CountCachePageReadsMissed"_sr); + countFilePageCacheEvictions = Int64Metric::getValueOrDefault("EvictablePageCache.CacheEvictions"_sr); } }; diff --git a/flow/include/flow/TDMetric.actor.h b/flow/include/flow/TDMetric.actor.h index f100da0007..e4e4097587 100644 --- a/flow/include/flow/TDMetric.actor.h +++ b/flow/include/flow/TDMetric.actor.h @@ -358,7 +358,7 @@ struct Descriptor { using fields = std::tuple<>; typedef make_index_sequence_impl<0, index_sequence<>, std::tuple_size::value>::type field_indexes; - static StringRef typeName() { return LiteralStringRef(""); } + static StringRef typeName() { return ""_sr; } #endif }; @@ -430,9 +430,7 @@ struct FieldValueBlockEncoding { template <> struct FieldValueBlockEncoding { - inline void write(BinaryWriter& w, bool v) { - w.serializeBytes(v ? LiteralStringRef("\x01") : LiteralStringRef("\x00")); - } + inline void write(BinaryWriter& w, bool v) { w.serializeBytes(v ? "\x01"_sr : "\x00"_sr); } bool read(BinaryReader& r) { uint8_t* v = (uint8_t*)r.readBytes(sizeof(uint8_t)); return *v != 0; @@ -716,7 +714,7 @@ struct MakeEventField { }; struct TimeDescriptor { - static StringRef name() { return LiteralStringRef("Time"); } + static StringRef name() { return "Time"_sr; } }; struct BaseMetric { diff --git a/flow/include/flow/error_definitions.h b/flow/include/flow/error_definitions.h index d4d33d9ef2..e967caead3 100755 --- a/flow/include/flow/error_definitions.h +++ b/flow/include/flow/error_definitions.h @@ -100,6 +100,7 @@ ERROR( data_move_dest_team_not_found, 1076, "Dest team was not found for data mo ERROR( blob_worker_full, 1077, "Blob worker cannot take on more granule assignments" ) ERROR( grv_proxy_memory_limit_exceeded, 1078, "GetReadVersion proxy memory limit exceeded" ) ERROR( blob_granule_request_failed, 1079, "BlobGranule request failed" ) +ERROR( storage_too_many_feed_streams, 1080, "Too many feed streams to a single storage server" ) ERROR( broken_promise, 1100, "Broken promise" ) ERROR( operation_cancelled, 1101, "Asynchronous operation cancelled" ) @@ -316,28 +317,28 @@ ERROR( json_malformed, 2401, "JSON string was malformed") ERROR( json_eof_expected, 2402, "JSON string did not terminate where expected") // 2500 - disk snapshot based backup errors -ERROR( snap_disable_tlog_pop_failed, 2500, "Failed to disable tlog pops") -ERROR( snap_storage_failed, 2501, "Failed to snapshot storage nodes") -ERROR( snap_tlog_failed, 2502, "Failed to snapshot TLog nodes") -ERROR( snap_coord_failed, 2503, "Failed to snapshot coordinator nodes") -ERROR( snap_enable_tlog_pop_failed, 2504, "Failed to enable tlog pops") -ERROR( snap_path_not_whitelisted, 2505, "Snapshot create binary path not whitelisted") -ERROR( snap_not_fully_recovered_unsupported, 2506, "Unsupported when the cluster is not fully recovered") -ERROR( snap_log_anti_quorum_unsupported, 2507, "Unsupported when log anti quorum is configured") -ERROR( snap_with_recovery_unsupported, 2508, "Cluster recovery during snapshot operation not supported") -ERROR( snap_invalid_uid_string, 2509, "The given uid string is not a 32-length hex string") +ERROR( snap_disable_tlog_pop_failed, 2500, "Failed to disable tlog pops" ) +ERROR( snap_storage_failed, 2501, "Failed to snapshot storage nodes" ) +ERROR( snap_tlog_failed, 2502, "Failed to snapshot TLog nodes" ) +ERROR( snap_coord_failed, 2503, "Failed to snapshot coordinator nodes" ) +ERROR( snap_enable_tlog_pop_failed, 2504, "Failed to enable tlog pops" ) +ERROR( snap_path_not_whitelisted, 2505, "Snapshot create binary path not whitelisted" ) +ERROR( snap_not_fully_recovered_unsupported, 2506, "Unsupported when the cluster is not fully recovered" ) +ERROR( snap_log_anti_quorum_unsupported, 2507, "Unsupported when log anti quorum is configured" ) +ERROR( snap_with_recovery_unsupported, 2508, "Cluster recovery during snapshot operation not supported" ) +ERROR( snap_invalid_uid_string, 2509, "The given uid string is not a 32-length hex string" ) // 27XX - Encryption operations errors -ERROR( encrypt_ops_error, 2700, "Encryption operation error") -ERROR( encrypt_header_metadata_mismatch, 2701, "Encryption header metadata mismatch") -ERROR( encrypt_key_not_found, 2702, "Expected encryption key is missing") -ERROR( encrypt_key_ttl_expired, 2703, "Expected encryption key TTL has expired") -ERROR( encrypt_header_authtoken_mismatch, 2704, "Encryption header authentication token mismatch") -ERROR( encrypt_update_cipher, 2705, "Attempt to update encryption cipher key") -ERROR( encrypt_invalid_id, 2706, "Invalid encryption cipher details") -ERROR( encrypt_keys_fetch_failed, 2707, "Encryption keys fetch from external KMS failed") -ERROR( encrypt_invalid_kms_config, 2708, "Invalid encryption/kms configuration: discovery-url, validation-token, endpoint etc.") -ERROR( encrypt_unsupported, 2709, "Encryption not supported") +ERROR( encrypt_ops_error, 2700, "Encryption operation error" ) +ERROR( encrypt_header_metadata_mismatch, 2701, "Encryption header metadata mismatch" ) +ERROR( encrypt_key_not_found, 2702, "Expected encryption key is missing" ) +ERROR( encrypt_key_ttl_expired, 2703, "Expected encryption key TTL has expired" ) +ERROR( encrypt_header_authtoken_mismatch, 2704, "Encryption header authentication token mismatch" ) +ERROR( encrypt_update_cipher, 2705, "Attempt to update encryption cipher key" ) +ERROR( encrypt_invalid_id, 2706, "Invalid encryption cipher details" ) +ERROR( encrypt_keys_fetch_failed, 2707, "Encryption keys fetch from external KMS failed" ) +ERROR( encrypt_invalid_kms_config, 2708, "Invalid encryption/kms configuration: discovery-url, validation-token, endpoint etc." ) +ERROR( encrypt_unsupported, 2709, "Encryption not supported" ) // 4xxx Internal errors (those that should be generated only by bugs) are decimal 4xxx ERROR( unknown_error, 4000, "An unknown error occurred" ) // C++ exception not of type Error diff --git a/flow/include/flow/serialize.h b/flow/include/flow/serialize.h index 513340fc38..4c0deddcc7 100644 --- a/flow/include/flow/serialize.h +++ b/flow/include/flow/serialize.h @@ -453,17 +453,17 @@ public: void serializeAsTuple(StringRef str) { size_t last_pos = 0; - serializeBytes(LiteralStringRef("\x01")); + serializeBytes("\x01"_sr); for (size_t pos = 0; pos < str.size(); ++pos) { if (str[pos] == '\x00') { serializeBytes(str.substr(last_pos, pos - last_pos)); - serializeBytes(LiteralStringRef("\x00\xff")); + serializeBytes("\x00\xff"_sr); last_pos = pos + 1; } } serializeBytes(str.substr(last_pos, str.size() - last_pos)); - serializeBytes(LiteralStringRef("\x00")); + serializeBytes("\x00"_sr); } void serializeAsTuple(bool t) { diff --git a/flowbench/BenchEncrypt.cpp b/flowbench/BenchEncrypt.cpp index 41d923a1af..ca10cfc83e 100644 --- a/flowbench/BenchEncrypt.cpp +++ b/flowbench/BenchEncrypt.cpp @@ -47,7 +47,7 @@ static void bench_encrypt(benchmark::State& state) { auto key = StreamCipherKey::getGlobalCipherKey(); auto iv = getRandomIV(); auto data = getKey(bytes); - while (state.KeepRunning()) { + for (auto _ : state) { for (int chunk = 0; chunk < chunks; ++chunk) { benchmark::DoNotOptimize(encrypt(key, iv, data.begin() + chunk * chunkSize, chunkSize)); } @@ -64,7 +64,7 @@ static void bench_decrypt(benchmark::State& state) { auto iv = getRandomIV(); auto data = getKey(bytes); auto encrypted = encrypt(key, iv, data.begin(), data.size()); - while (state.KeepRunning()) { + for (auto _ : state) { Arena arena; DecryptionStreamCipher decryptor(key, iv); for (int chunk = 0; chunk < chunks; ++chunk) { diff --git a/flowbench/BenchHash.cpp b/flowbench/BenchHash.cpp index 40d15db4ab..92d4a5e5a1 100644 --- a/flowbench/BenchHash.cpp +++ b/flowbench/BenchHash.cpp @@ -58,7 +58,7 @@ template static void bench_hash(benchmark::State& state) { auto length = 1 << state.range(0); auto key = getKey(length); - while (state.KeepRunning()) { + for (auto _ : state) { hash(key, length); } state.SetItemsProcessed(static_cast(state.iterations())); diff --git a/flowbench/BenchIterate.cpp b/flowbench/BenchIterate.cpp index 793c3fc307..8f2e8c6921 100644 --- a/flowbench/BenchIterate.cpp +++ b/flowbench/BenchIterate.cpp @@ -50,7 +50,7 @@ static void bench_iterate(benchmark::State& state) { auto kv = getKV(size, size); ListImpl mutations; populate(mutations, items, size, kv.key, kv.value); - while (state.KeepRunning()) { + for (auto _ : state) { for (const auto& mutation : mutations) { benchmark::DoNotOptimize(mutation); } diff --git a/flowbench/BenchMem.cpp b/flowbench/BenchMem.cpp index 8696461efd..6db2ce28a8 100644 --- a/flowbench/BenchMem.cpp +++ b/flowbench/BenchMem.cpp @@ -31,7 +31,7 @@ static void bench_memcmp(benchmark::State& state) { memset(b2.get(), 0, kLength); b2.get()[kLength - 1] = 1; - while (state.KeepRunning()) { + for (auto _ : state) { benchmark::DoNotOptimize(memcmp(b1.get(), b2.get(), kLength)); } } @@ -42,7 +42,7 @@ static void bench_memcpy(benchmark::State& state) { std::unique_ptr b2{ new char[kLength] }; memset(b1.get(), 0, kLength); - while (state.KeepRunning()) { + for (auto _ : state) { benchmark::DoNotOptimize(memcpy(b2.get(), b1.get(), kLength)); } } diff --git a/flowbench/BenchMetadataCheck.cpp b/flowbench/BenchMetadataCheck.cpp index c028e3deec..48d2b0bcde 100644 --- a/flowbench/BenchMetadataCheck.cpp +++ b/flowbench/BenchMetadataCheck.cpp @@ -29,17 +29,17 @@ static const std::array mutations = { MutationRef(MutationRef::Type::ClearRange, normalKeys.begin, normalKeys.end), - MutationRef(MutationRef::Type::ClearRange, LiteralStringRef("a"), LiteralStringRef("b")), - MutationRef(MutationRef::Type::ClearRange, LiteralStringRef("aaaaaaaaaa"), LiteralStringRef("bbbbbbbbbb")), + MutationRef(MutationRef::Type::ClearRange, "a"_sr, "b"_sr), + MutationRef(MutationRef::Type::ClearRange, "aaaaaaaaaa"_sr, "bbbbbbbbbb"_sr), MutationRef(MutationRef::Type::ClearRange, normalKeys.begin, systemKeys.end), MutationRef(MutationRef::Type::ClearRange, - LiteralStringRef("a").withPrefix(systemKeys.begin), - LiteralStringRef("b").withPrefix(systemKeys.begin)), + "a"_sr.withPrefix(systemKeys.begin), + "b"_sr.withPrefix(systemKeys.begin)), }; static void bench_check_metadata1(benchmark::State& state) { const auto& m = mutations[state.range(0)]; - while (state.KeepRunning()) { + for (auto _ : state) { benchmark::DoNotOptimize(KeyRangeRef(m.param1, m.param2).intersects(systemKeys)); } state.SetItemsProcessed(static_cast(state.iterations())); @@ -47,7 +47,7 @@ static void bench_check_metadata1(benchmark::State& state) { static void bench_check_metadata2(benchmark::State& state) { const auto& m = mutations[state.range(0)]; - while (state.KeepRunning()) { + for (auto _ : state) { benchmark::DoNotOptimize(m.param2.size() > 1 && m.param2[0] == systemKeys.begin[0]); } state.SetItemsProcessed(static_cast(state.iterations())); diff --git a/flowbench/BenchPopulate.cpp b/flowbench/BenchPopulate.cpp index 35213b4171..37dacc0478 100644 --- a/flowbench/BenchPopulate.cpp +++ b/flowbench/BenchPopulate.cpp @@ -35,7 +35,7 @@ static void bench_populate(benchmark::State& state) { size_t items = state.range(0); size_t size = state.range(1); auto kv = getKV(size, size); - while (state.KeepRunning()) { + for (auto _ : state) { Standalone> mutations; mutations.reserve(mutations.arena(), items); for (int i = 0; i < items; ++i) { diff --git a/flowbench/BenchRandom.cpp b/flowbench/BenchRandom.cpp index 47d607cffe..bd478a4ac5 100644 --- a/flowbench/BenchRandom.cpp +++ b/flowbench/BenchRandom.cpp @@ -23,9 +23,8 @@ #include "flow/IRandom.h" static void bench_random(benchmark::State& state) { - while (state.KeepRunning()) { - double r = deterministicRandom()->random01(); - benchmark::DoNotOptimize(r); + for (auto _ : state) { + benchmark::DoNotOptimize(deterministicRandom()->random01()); } state.SetItemsProcessed(static_cast(state.iterations())); } diff --git a/flowbench/BenchRef.cpp b/flowbench/BenchRef.cpp index 297e32a66b..434abf38c4 100644 --- a/flowbench/BenchRef.cpp +++ b/flowbench/BenchRef.cpp @@ -71,7 +71,7 @@ struct Factory { template static void bench_ref_create_and_destroy(benchmark::State& state) { - while (state.KeepRunning()) { + for (auto _ : state) { auto ptr = Factory::create(); benchmark::DoNotOptimize(ptr); Factory::cleanup(ptr); @@ -82,7 +82,7 @@ static void bench_ref_create_and_destroy(benchmark::State& state) { template static void bench_ref_copy(benchmark::State& state) { auto ptr = Factory::create(); - while (state.KeepRunning()) { + for (auto _ : state) { auto ptr2 = ptr; benchmark::DoNotOptimize(ptr2); } diff --git a/flowbench/BenchSamples.cpp b/flowbench/BenchSamples.cpp new file mode 100644 index 0000000000..687a371048 --- /dev/null +++ b/flowbench/BenchSamples.cpp @@ -0,0 +1,101 @@ +/* + * BenchSamples.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2022 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/benchmark.h" +#include "flow/IRandom.h" +#include "flowbench/GlobalData.h" +#include "fdbrpc/Stats.h" +#include "flow/Histogram.h" + +static void bench_continuousSampleInt(benchmark::State& state) { + ContinuousSample cs(state.range(0)); + InputGenerator data(1e6, []() { return deterministicRandom()->randomInt64(0, 1e9); }); + + for (auto _ : state) { + cs.addSample(data.next()); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_continuousSampleInt)->Arg(1000)->Arg(10000)->ReportAggregatesOnly(true); + +static void bench_continuousSampleLatency(benchmark::State& state) { + ContinuousSample cs(state.range(0)); + InputGenerator data(1e6, []() { return deterministicRandom()->random01() * 2.0; }); + + for (auto _ : state) { + cs.addSample(data.next()); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_continuousSampleLatency)->Arg(1000)->Arg(10000)->ReportAggregatesOnly(true); + +static void bench_latencyBands(benchmark::State& state) { + constexpr double max = 2.0; + InputGenerator data(1e6, []() { return deterministicRandom()->random01() * max; }); + LatencyBands lb("test", UID(), 5); + for (int i = 0; i < state.range(0); ++i) { + lb.addThreshold(max * (double)(i + 1) / (state.range(0) + 1)); + } + + for (auto _ : state) { + lb.addMeasurement(data.next(), false); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_latencyBands)->Arg(1)->Arg(4)->Arg(7)->Arg(10)->ReportAggregatesOnly(true); + +static void bench_histogramInt(benchmark::State& state) { + Reference h = Histogram::getHistogram("histogramTest"_sr, "bytes"_sr, Histogram::Unit::bytes); + InputGenerator data(1e6, []() { return deterministicRandom()->randomInt64(0, 1e9); }); + + for (auto _ : state) { + h->sample(data.next()); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_histogramInt)->ReportAggregatesOnly(true); + +static void bench_histogramPct(benchmark::State& state) { + Reference h = Histogram::getHistogram("histogramTest"_sr, "pct"_sr, Histogram::Unit::percentageLinear); + InputGenerator data(1e6, []() { return deterministicRandom()->random01() * 1.5; }); + + for (auto _ : state) { + h->samplePercentage(data.next()); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_histogramPct)->ReportAggregatesOnly(true); + +static void bench_histogramTime(benchmark::State& state) { + Reference h = Histogram::getHistogram("histogramTest"_sr, "latency"_sr, Histogram::Unit::microseconds); + InputGenerator data(1e6, []() { return deterministicRandom()->random01() * 5; }); + + for (auto _ : state) { + h->sampleSeconds(data.next()); + } + + state.SetItemsProcessed(state.iterations()); +} +BENCHMARK(bench_histogramTime)->ReportAggregatesOnly(true); diff --git a/flowbench/BenchTimer.cpp b/flowbench/BenchTimer.cpp index 90a542a3f9..1299fd1c07 100644 --- a/flowbench/BenchTimer.cpp +++ b/flowbench/BenchTimer.cpp @@ -23,17 +23,15 @@ #include "flow/Platform.h" static void bench_timer(benchmark::State& state) { - while (state.KeepRunning()) { - double time = timer(); - benchmark::DoNotOptimize(time); + for (auto _ : state) { + benchmark::DoNotOptimize(timer()); } state.SetItemsProcessed(static_cast(state.iterations())); } static void bench_timer_monotonic(benchmark::State& state) { - while (state.KeepRunning()) { - double time = timer_monotonic(); - benchmark::DoNotOptimize(time); + for (auto _ : state) { + benchmark::DoNotOptimize(timer_monotonic()); } state.SetItemsProcessed(static_cast(state.iterations())); } diff --git a/flowbench/BenchVersionVector.cpp b/flowbench/BenchVersionVector.cpp index db8d1036cd..45079bf139 100644 --- a/flowbench/BenchVersionVector.cpp +++ b/flowbench/BenchVersionVector.cpp @@ -34,7 +34,7 @@ static void bench_vv_getdelta(benchmark::State& benchState) { i = 0; const int64_t numDeltas = benchState.range(1); - while (benchState.KeepRunning()) { + for (auto _ : benchState) { vv.setVersion(Tag(0, i++), ++version); i %= tags; diff --git a/flowbench/BenchVersionVectorSerialization.cpp b/flowbench/BenchVersionVectorSerialization.cpp index ea8de78fd8..66c14549f5 100644 --- a/flowbench/BenchVersionVectorSerialization.cpp +++ b/flowbench/BenchVersionVectorSerialization.cpp @@ -41,7 +41,7 @@ static void bench_serializable_traits_version(benchmark::State& state) { size_t size = 0; VersionVector deserializedVV; - while (state.KeepRunning()) { + for (auto _ : state) { Standalone msg = ObjectWriter::toValue(serializedVV, Unversioned()); // Capture the serialized buffer size. @@ -71,7 +71,7 @@ static void bench_dynamic_size_traits_version(benchmark::State& state) { size_t size = 0; VersionVector deserializedVV; - while (state.KeepRunning()) { + for (auto _ : state) { size = dynamic_size_traits::size(serializedVV, context); uint8_t* buf = context.allocate(size); diff --git a/flowbench/include/flowbench/GlobalData.h b/flowbench/include/flowbench/GlobalData.h index 3f2d1f53db..66ba8c283f 100644 --- a/flowbench/include/flowbench/GlobalData.h +++ b/flowbench/include/flowbench/GlobalData.h @@ -28,4 +28,32 @@ KeyValueRef getKV(size_t keySize, size_t valueSize); KeyRef getKey(size_t keySize); +// Pre-generate a vector of T using a lambda then return them +// via next() one by one with wrap-around +template +struct InputGenerator { + InputGenerator() {} + + template + InputGenerator(int n, Fn genFunc) { + ASSERT(n > 0); + data.reserve(n); + for (int i = 0; i < n; ++i) { + data.push_back(genFunc()); + } + lastIndex = -1; + } + + const T& next() { + if (++lastIndex == data.size()) { + lastIndex = 0; + } + + return data[lastIndex]; + } + + std::vector data; + int lastIndex; +}; + #endif diff --git a/recipes/go-recipes/blob.go b/recipes/go-recipes/blob.go index f6590acfde..744101a77a 100644 --- a/recipes/go-recipes/blob.go +++ b/recipes/go-recipes/blob.go @@ -29,6 +29,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + const CHUNK_SIZE int = 5 func write_blob(t fdb.Transactor, blob_subspace subspace.Subspace, blob []byte) (err error) { @@ -79,7 +81,7 @@ func read_blob(t fdb.ReadTransactor, blob_subspace subspace.Subspace) ([]byte, e } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/doc.go b/recipes/go-recipes/doc.go index 9ae34ad10c..40edd60ffa 100644 --- a/recipes/go-recipes/doc.go +++ b/recipes/go-recipes/doc.go @@ -32,6 +32,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(trtr fdb.Transactor, sub subspace.Subspace) error { _, err := trtr.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(sub) @@ -220,7 +222,7 @@ func (doc Doc) GetDoc(trtr fdb.Transactor, doc_id int) interface{} { } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/graph.go b/recipes/go-recipes/graph.go index 9487b09e89..b46d33b26d 100644 --- a/recipes/go-recipes/graph.go +++ b/recipes/go-recipes/graph.go @@ -30,6 +30,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(trtr fdb.Transactor, sub subspace.Subspace) error { _, err := trtr.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(sub) @@ -125,7 +127,7 @@ func (graph *Graph) get_in_neighbors(trtr fdb.Transactor, node int) ([]int, erro } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/indirect.go b/recipes/go-recipes/indirect.go index e49d9640e3..20f8c5c90f 100644 --- a/recipes/go-recipes/indirect.go +++ b/recipes/go-recipes/indirect.go @@ -30,6 +30,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(trtr fdb.Transactor, sub subspace.Subspace) error { _, err := trtr.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(sub) @@ -94,7 +96,7 @@ func (wrkspc Workspace) Session(foo func(directory.DirectorySubspace)) (err erro } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/multi.go b/recipes/go-recipes/multi.go index 06311feb41..5ced2da6c9 100644 --- a/recipes/go-recipes/multi.go +++ b/recipes/go-recipes/multi.go @@ -30,6 +30,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(db fdb.Transactor, ss subspace.Subspace) { db.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(ss) @@ -131,8 +133,7 @@ func (multi MultiMap) MultiIsElement(trtr fdb.Transactor, index, value interface } func main() { - - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/priority.go b/recipes/go-recipes/priority.go index 897253ce8a..3323c85630 100644 --- a/recipes/go-recipes/priority.go +++ b/recipes/go-recipes/priority.go @@ -31,6 +31,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(trtr fdb.Transactor, sub subspace.Subspace) error { _, err := trtr.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(sub) @@ -118,7 +120,7 @@ func (prty Priority) Peek(trtr fdb.Transactor, max bool) interface{} { } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/queue.go b/recipes/go-recipes/queue.go index e1d0091755..c87725e7fe 100644 --- a/recipes/go-recipes/queue.go +++ b/recipes/go-recipes/queue.go @@ -30,6 +30,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + type EmptyQueueError struct{} func (q EmptyQueueError) Error() string { @@ -108,7 +110,7 @@ func (q *Queue) FirstItem(trtr fdb.Transactor) (interface{}, error) { func main() { fmt.Println("Queue Example Program") - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/recipes/go-recipes/table.go b/recipes/go-recipes/table.go index b2b9b5493e..704b4d70c4 100644 --- a/recipes/go-recipes/table.go +++ b/recipes/go-recipes/table.go @@ -30,6 +30,8 @@ import ( "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) +const API_VERSION int = 720 + func clear_subspace(trtr fdb.Transactor, sub subspace.Subspace) error { _, err := trtr.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(sub) @@ -145,7 +147,7 @@ func (tbl Table) TableGetCol(tr fdb.ReadTransactor, col int) ([]interface{}, err } func main() { - fdb.MustAPIVersion(720) + fdb.MustAPIVersion(API_VERSION) db := fdb.MustOpenDefault() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 055916d35b..d4aeaed61c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -139,6 +139,9 @@ if(WITH_PYTHON) add_fdb_test(TEST_FILES fast/CloggedSideband.toml) add_fdb_test(TEST_FILES fast/CompressionUtilsUnit.toml) add_fdb_test(TEST_FILES fast/ConfigureLocked.toml) + add_fdb_test(TEST_FILES fast/ConfigIncrement.toml) + add_fdb_test(TEST_FILES fast/ConfigIncrementChangeCoordinators.toml) + add_fdb_test(TEST_FILES fast/ConfigIncrementWithKills.toml) add_fdb_test(TEST_FILES fast/ConstrainedRandomSelector.toml) add_fdb_test(TEST_FILES fast/CycleAndLock.toml) add_fdb_test(TEST_FILES fast/CycleTest.toml) @@ -210,8 +213,7 @@ if(WITH_PYTHON) add_fdb_test(TEST_FILES rare/CheckRelocation.toml) add_fdb_test(TEST_FILES rare/ClogUnclog.toml) add_fdb_test(TEST_FILES rare/CloggedCycleWithKills.toml) - add_fdb_test(TEST_FILES rare/ConfigIncrement.toml) - add_fdb_test(TEST_FILES rare/ConfigIncrementWithKills.toml) + add_fdb_test(TEST_FILES rare/ConfigDBUnitTest.toml) add_fdb_test(TEST_FILES rare/ConflictRangeCheck.toml) add_fdb_test(TEST_FILES rare/ConflictRangeRYOWCheck.toml) add_fdb_test(TEST_FILES rare/CycleRollbackClogged.toml) @@ -388,6 +390,7 @@ if(WITH_PYTHON) NAME command_line_argument_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/argument_parsing/test_argument_parsing.py ${CMAKE_BINARY_DIR} ) + set_tests_properties(command_line_argument_test PROPERTIES ENVIRONMENT "FDB_CLUSTER_FILE=${CMAKE_BINARY_DIR}/fdb.cluster") endif() verify_testing() diff --git a/tests/TestRunner/upgrade_test.py b/tests/TestRunner/upgrade_test.py index abf63a32c5..8f43e0cddf 100755 --- a/tests/TestRunner/upgrade_test.py +++ b/tests/TestRunner/upgrade_test.py @@ -14,12 +14,15 @@ import time from binary_download import FdbBinaryDownloader, SUPPORTED_VERSIONS, CURRENT_VERSION, FUTURE_VERSION from local_cluster import LocalCluster, random_secret_string +TENANT_API_VERSION = 720 + CLUSTER_ACTIONS = ["wiggle"] HEALTH_CHECK_TIMEOUT_SEC = 5 PROGRESS_CHECK_TIMEOUT_SEC = 30 TESTER_STATS_INTERVAL_SEC = 5 TRANSACTION_RETRY_LIMIT = 100 RUN_WITH_GDB = False +CLEANUP_ON_EXIT = True def version_from_str(ver_str): @@ -161,12 +164,13 @@ class UpgradeTest: def __enter__(self): print("Starting cluster version {}".format(self.cluster_version)) self.cluster.start_cluster() - self.cluster.create_database(enable_tenants=(self.api_version >= 720)) + self.cluster.create_database(enable_tenants=(self.api_version >= TENANT_API_VERSION)) return self def __exit__(self, xc_type, exc_value, traceback): self.cluster.stop_cluster() - shutil.rmtree(self.tmp_dir) + if CLEANUP_ON_EXIT: + shutil.rmtree(self.tmp_dir) # Determine FDB API version matching the upgrade path def determine_api_version(self): @@ -425,6 +429,11 @@ if __name__ == "__main__": help="Do not dump cluster log on error", action="store_true", ) + parser.add_argument( + "--no-cleanup-on-error", + help="In case of an error do not remove any of the generated files", + action="store_true", + ) parser.add_argument("--blob-granules-enabled", help="Enable blob granules", action="store_true") parser.add_argument("--run-with-gdb", help="Execute the tester binary from gdb", action="store_true") args = parser.parse_args() @@ -450,5 +459,7 @@ if __name__ == "__main__": test.dump_warnings_in_logs() if errcode != 0 and not args.disable_log_dump: test.dump_cluster_logs() + if errcode != 0 and args.no_cleanup_on_error: + CLEANUP_ON_EXIT = False sys.exit(errcode) diff --git a/tests/rare/ConfigIncrement.toml b/tests/fast/ConfigIncrement.toml similarity index 78% rename from tests/rare/ConfigIncrement.toml rename to tests/fast/ConfigIncrement.toml index eee28710f6..2684e90df9 100644 --- a/tests/rare/ConfigIncrement.toml +++ b/tests/fast/ConfigIncrement.toml @@ -17,3 +17,8 @@ testTitle = 'ConfigIncrement' machinesToLeave = 3 reboot = true testDuration = 10.0 + + [[test.workload]] + testName = 'ChangeConfig' + maxDelayBeforeChange = 120.0 + coordinators = 'auto' diff --git a/tests/fast/ConfigIncrementChangeCoordinators.toml b/tests/fast/ConfigIncrementChangeCoordinators.toml new file mode 100644 index 0000000000..caf5f10309 --- /dev/null +++ b/tests/fast/ConfigIncrementChangeCoordinators.toml @@ -0,0 +1,28 @@ +# Similar to the ConfigIncrement workload, but with a larger amount of time +# between transactions to allow a coordinator change to take place in between +# transactions. +[configuration] +configDB = 'paxos' + +[[test]] +testTitle = 'ConfigIncrementChangeCoordinators' + + [[test.workload]] + testName = 'ConfigIncrement' + incrementActors = 2 + incrementsPerActor = 10 + meanSleepWithinTransactions = 0.01 + meanSleepBetweenTransactions = 10 + + [[test.workload]] + testName = 'Attrition' + machinesToKill = 10 + machinesToLeave = 3 + reboot = true + testDuration = 10.0 + + [[test.workload]] + testName = 'ChangeConfig' + maxDelayBeforeChange = 120.0 + coordinators = 'auto' + coordinatorChanges = 2 diff --git a/tests/rare/ConfigIncrementWithKills.toml b/tests/fast/ConfigIncrementWithKills.toml similarity index 62% rename from tests/rare/ConfigIncrementWithKills.toml rename to tests/fast/ConfigIncrementWithKills.toml index 6d840f0103..ea2b6b4a70 100644 --- a/tests/rare/ConfigIncrementWithKills.toml +++ b/tests/fast/ConfigIncrementWithKills.toml @@ -2,7 +2,7 @@ configDB = 'paxos' [[test]] -testTitle = 'ConfigIncrement' +testTitle = 'ConfigIncrementWithKills' [[test.workload]] testName = 'ConfigIncrement' @@ -14,3 +14,9 @@ testTitle = 'ConfigIncrement' [[test.workload]] testName = 'Attrition' reboot = false + + [[test.workload]] + testName = 'ChangeConfig' + maxDelayBeforeChange = 120.0 + coordinators = 'auto' + coordinatorChanges = 2 diff --git a/tests/fast/RandomUnitTests.toml b/tests/fast/RandomUnitTests.toml index 265921f84d..e34a71fe46 100644 --- a/tests/fast/RandomUnitTests.toml +++ b/tests/fast/RandomUnitTests.toml @@ -5,5 +5,8 @@ startDelay = 0 [[test.workload]] testName = 'UnitTests' - maxTestCases = 1 + maxTestCases = 10 testsMatching = '/' + # ConfigDB tests persist state, so running these tests + # sequentially can cause issues + testsIgnored = '/fdbserver/ConfigDB/' diff --git a/tests/fast/SpecialKeySpaceCorrectness.toml b/tests/fast/SpecialKeySpaceCorrectness.toml index d88477d29d..fcabb8222e 100644 --- a/tests/fast/SpecialKeySpaceCorrectness.toml +++ b/tests/fast/SpecialKeySpaceCorrectness.toml @@ -1,3 +1,6 @@ +[configuration] +allowDisablingTenants = false + [[test]] testTitle = 'SpecialKeySpaceCorrectnessTest' diff --git a/tests/rare/ConfigDBUnitTest.toml b/tests/rare/ConfigDBUnitTest.toml new file mode 100644 index 0000000000..3aca62613e --- /dev/null +++ b/tests/rare/ConfigDBUnitTest.toml @@ -0,0 +1,9 @@ +[[test]] +testTitle = 'ConfigDBUnitTest' +useDB = false +startDelay = 0 + + [[test.workload]] + testName = 'UnitTests' + maxTestCases = 1 + testsMatching = '/fdbserver/ConfigDB/' diff --git a/tests/restarting/from_7.1.0/ConfigureTestRestart-2.toml b/tests/restarting/from_7.1.0/ConfigureTestRestart-2.toml index 8230bec479..96f1ed9ea9 100644 --- a/tests/restarting/from_7.1.0/ConfigureTestRestart-2.toml +++ b/tests/restarting/from_7.1.0/ConfigureTestRestart-2.toml @@ -1,4 +1,4 @@ -[[configuration]] +[configuration] randomlyRenameZoneId=true [[test]]